Преобразование метки времени из CET / CEST в UTC - PullRequest
1 голос
/ 04 мая 2020

В настоящее время я пишу инструмент, который анализирует файлы с нестандартным нестандартным форматом. Файлы содержат данные для нескольких точек измерения с временными метками (одно измерение каждые 30 секунд). Эти временные метки находятся в часовом поясе CET / CEST, что означает, что, когда, например, время изменяется с летнего на зимнее время, в файле будет несколько измерений с одной и той же временной меткой, поскольку каждая метка времени между 2:00 и 3:00 в В файле дважды будет указано утро (одно до и одно после измененного времени).

Я уже могу проанализировать все нужные мне метки времени (год, месяц, день, часы и минуты) из файл, но мне также нужно преобразовать его в UT C. Есть ли какая-либо функциональность в. NET Framework или в сторонней библиотеке для выполнения этого преобразования?

Я уже пытался использовать TimeZoneInfo.ConvertTimeToUtc, но мне уже нужно иметь объект DateTime для того, что я не делаю ' не знаю, как создать еще. Я не могу просто создать новый объект DateTime, используя один из конструкторов DateTime, потому что, скажем, я предоставляю одну из временных меток, которая существует дважды в файле из-за перехода с летнего на зимнее время. Как он узнает, что эта метка времени была раньше? или после того, как время изменилось?

Я думал, что это, вероятно, довольно распространенная проблема при работе с метками времени, но я пока не смог найти хорошего решения для своей проблемы. Нужно ли вручную отслеживать, когда происходит изменение времени в файле, чтобы правильно преобразовать время в UT C? Если да, то как я могу это сделать?

ОБНОВЛЕНИЕ: Вот очень упрощенный пример проблемы, которую я пытаюсь решить: это могут быть некоторые данные, которые мой инструмент проанализировал по временным меткам в file:

Day: 27 - Month: 10 - Year: 2019 - Hour: 1 - Minute: 45 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 2 - Minute: 0 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 2 - Minute: 15 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 2 - Minute: 30 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 2 - Minute: 45 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 2 - Minute: 0 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 2 - Minute: 15 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 2 - Minute: 30 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 2 - Minute: 45 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 3 - Minute: 0 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 3 - Minute: 15 - Second: 0 
Day: 27 - Month: 10 - Year: 2019 - Hour: 3 - Minute: 30 - Second: 0

(на самом деле данных в час намного больше, но для примера это не имеет значения)

В этом примере часы устанавливаются на один час назад из 3 : С 00:00 до 2:00 утра, потому что здесь заканчивается летнее время, и поэтому мы переходим с CEST на CET. Противоположная вещь происходит ~ 6 месяцев спустя (и, очевидно, также 6 месяцев назад), когда начинается период перехода на летнее время и часы перемещаются на один час вперед.

В файлах нет никаких признаков того, что мой инструмент анализирует, находится ли временная метка в CET или CEST, поэтому мой логин разбора c должен это обработать. В основном у меня есть al oop, который перебирает строки в файле, анализирует метки времени и затем вызывает эту функцию, чтобы получить объект DateTime для каждой метки времени:

private static DateTime toDateTimeUTC(int year, int month, int day, int hour, int minute, int second)
{
    // ToDo

    return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc); // This is WRONG!
}

Этот код, очевидно, не работает правильно пока, потому что это только предполагает, что метки времени находятся в UT C.

Ответы [ 2 ]

2 голосов
/ 05 мая 2020

Если вы знаете, что у вас всегда будет один день на файл, и вы всегда можете обнаружить изменение, потому что вы записываете данные каждые 30 секунд, вы можете определить, когда локальная временная метка движется назад, и определить, какое неоднозначное смещение для используйте этот путь. Примерно так:

TimeZoneInfo zone = ...; // I assume you've got this already
bool afterFallBack = false;
DateTime previousUnspecifiedTimestamp = DateTime.MinValue;
foreach (var line in log)
{
    var timestampText = ...; // Take the timestamp from the line
    // Parse the timestamp without performing any time zone conversions
    // (The "unspecified" part of the name refers to the DateTimeKind.Unspecified.)
    // TODO: Check the format
    var unspecifiedTimestamp = DateTime.ParseExact(timestampText,
        "yyyy-MM-dd'T'HH:mm:ss", timestampText, CultureInfo.InvariantCulture);

    // Detect "fall back" so we know which 
    if (unspecifiedTimestamp < previousUnspecifiedTimestamp)
    {
        afterFallBack = true;
    }
    previousUnspecifiedTimestamp = unspecifiedTimestamp;

    DateTime utcTimestamp = ConvertToZone(zone, unspecifiedTimestamp, afterFallBack);
    // Process the log entry
}

// Method extracted for testability and tidiness
private static DateTime ConvertToZone(
    TimeZoneInfo zone, DateTime dateTime, bool useLaterAmbiguousOffset)
{
    if (!zone.IsAmbiguousTime(dateTime))
    {
        return TimeZoneInfo.ConvertToUtc(dateTime, zone);
    }
    // The offsets returned by this appear to be in order of "smallest offset"
    // to "largest offset" - which means that the offset that's observed later is 
    // the one that occurs at the start of the array. This isn't actually
    // documented... you could order the offsets for added certainty,
    // but I'd be surprised if this changed.
    var offsets = zone.GetAmbiguousTimeOffsets(dateTime);
    var chosenOffset = useLaterAmbiguousOffset ? offsets[0] : offset[1];
    return DateTime.SpecifyKind(dateTime - chosenOffset, DateTimeKind.Utc);
}

Кстати, этот код IMO был бы проще с моим проектом Noda Time , но я бы не рекомендовал вам переключаться только ради этого. этого. Если вы выполняете какое-то значительное количество работы с датой / временем, это стоит посмотреть.

0 голосов
/ 05 мая 2020

Если у вас есть все в DateTimeOffset с + 1 / + 2 на основе лета / зимы, то вы просто сохраняете ToUniversalTime, если хотите сохранить его с Offset + 0.

var input = new DateTimeOffset(2000,1,1,23,59,00, TimeSpan.FromHours(1));
var utc = cet.ToUniversalTime();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...