Странный вопрос сериализации массива DateTime и обратно - PullRequest
0 голосов
/ 22 января 2020

У меня возникла странная проблема при сериализации большого массива DateTime (каждая временная метка имеет тип UT C). Я выполнил следующие шаги:

  • преобразование каждого DateTime в тип long через DateTime.ToBinary()
  • преобразование long[] в byte[] через Buffer.BlockCopy
  • запись байтового массива в файловый поток
  • чтение в байтовом массиве через файловый поток
  • преобразование byte[] в long[] через Buffer.BlockCopy
  • преобразовать каждый long обратно в DateTime с помощью DateTime.FromBinary(long)

Проблема заключается в том, что DateTimes не совпадают между исходным массивом и окончательным массивом. Фактически, некоторые из отметок времени отображаются как год 2059 или около того, когда исходный массив строго содержал отметки времени прошлого.

Я запускаю всю процедуру на своем локальном компьютере в Windows 10, поэтому не должно быть проблем с часовым поясом или проблем с порядком байтов. Может кто-нибудь помочь?

Вот так я конвертирую метки времени типа DateTime в long[]:

var dataCollection = new DataCollection(header.DataProviderId, DateTimeKind.Utc, header.Symbol, header.QuoteType, header.Compression)
        {
            TimeStamps = quotes.Select(x => x.TimeStamp.ToBinary()).ToArray(),
            Bid = quotes.Select(x => x.Bid).ToArray(),
            Ask = quotes.Select(x => x.Ask).ToArray()
        };

Вот преобразования между long[] -> byte[] и назад:

public static byte[] SerializeBlockCopy<T>(Array sourceArray, long sourceStartIndex, long numberItemsToSerialize)
    {
        var targetArraySize = numberItemsToSerialize * Marshal.SizeOf(typeof(T));
        var targetArray = new byte[targetArraySize];
        Buffer.BlockCopy(sourceArray, (int)sourceStartIndex, targetArray, 0, (int)targetArraySize);
        return targetArray;
    }

    public static T[] DeserializeBlockCopy<T>(byte[] sourceArray)
    {
        var targetArraySize = sourceArray.Length / Marshal.SizeOf(typeof(T));
        var targetArray = new T[targetArraySize];
        Buffer.BlockCopy(sourceArray, 0, targetArray,0, sourceArray.Length);
        return targetArray;
    }

1 Ответ

1 голос
/ 22 января 2020

Я не могу обнаружить никаких ошибок. Вот пример программы, которую я протестировал с вашими методами, и они работали как ожидалось:

public static void Main()
{
    var random = new Random();
    var sourceDates = Enumerable.Range(1, 100)
        .Select(i => DateTime.UtcNow.Add(TimeSpan.FromDays(random.Next(-1000, 1000))))
        .ToList();

    var values = sourceDates.Select(date => date.ToBinary()).ToArray();
    var asBytes = SerializeBlockCopy(values, 0, values.Length);
    var filename = Path.GetTempFileName();
    WriteToFile(asBytes, filename);

    var bytesFromFile = ReadFromFile(filename);
    var back = DeserializeBlockCopy<long>(bytesFromFile);
    File.Delete(filename);
    var destinationValues = back.Select(value => DateTime.FromBinary(value)).ToList();
    var pairs = sourceDates.Zip(destinationValues, (s, d) => (s, d));

    foreach (var pair in pairs)
    {
        Console.WriteLine($"{pair.s} -> {pair.d}");
    }

    Console.WriteLine($"Both are equal: {sourceDates.SequenceEqual(destinationValues)}");
}

public static void WriteToFile(byte[] source, string filename)
{
    using (var writer = new FileStream(filename, FileMode.Truncate, FileAccess.Write))
    {
        writer.Write(source, 0, source.Length);
    }
}

public static byte[] ReadFromFile(string filename)
{
    return File.ReadAllBytes(filename);
}

Для небольшого улучшения вы можете изменить сигнатуру вашего метода сериализации на:

public static byte[] SerializeBlockCopy<T>(T[] sourceArray, long sourceStartIndex, long numberItemsToSerialize)

Тогда вам не нужно предоставлять аргумент generi c, вызывается вывод типа, но это не объясняет вашу ошибку.

...