将时间戳从CET / CEST转换为UTC

时间:2020-05-04 13:28:33

标签: c# datetime timezone

我目前正在编写一种工具,用于分析具有自定义非标准文件格式的文件。 这些文件包含带有时间戳的多个测量点的数据(每30秒测量一次)。这些时间戳位于CET / CEST时区,这意味着当时间从夏季更改为冬季时,文件中将有多个具有相同时间戳的测量值,因为每个时间戳在2:00和3:00之间早晨将在文件中存在两次(一次是在时间更改之前,另一次是在时间更改之后)。

我已经能够从文件中解析出我需要的所有时间戳数据(年,月,日,小时和分钟),但我还需要将其转换为UTC。 .NET Framework或第三方库中是否有任何功能可以进行此转换?

我已经尝试使用TimeZoneInfo.ConvertTimeToUtc,但是我已经需要一个DateTime对象用于我尚不知道如何创建的对象。 我不能简单地使用其中一个DateTime构造函数创建一个新的DateTime对象,因为可以说由于夏时制到冬令时间的变化,我提供了一个存在于文件中两次的时间戳-如何知道时间戳是否来自以前还是时间改变后?

我认为在处理时间戳时这可能是一个比较常见的问题,但是我仍然无法找到解决该问题的好方法。我是否需要手动跟踪文件中时间更改的时间,以便将时间正确地转换为UTC?如果是这样,我该怎么办?

更新: 这是我要解决的问题的非常简化的示例: 这可能是我的工具从文件中的时间戳解析的一些数据:

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设置为凌晨1:00,因此我们将CEST更改为CET。 相反的事情发生在大约6个月后(显然也早6个月),此时夏令时开始,时钟向前移动一小时。

我的工具所解析的文件中没有任何迹象表明时间戳是否在CET或CEST中,因此我的解析逻辑需要对此进行处理。 我基本上有一个循环,循环访问文件中的行,解析时间戳,然后调用此函数以获取每个时间戳的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!
}

到目前为止,该代码显然无法正确运行,因为它只是假定时间戳记为UTC。

2 个答案:

答案 0 :(得分:3)

如果您知道每个文件总有一天的时间,并且因为每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);
}

顺便说一句,对于我的Noda Time项目,此代码将使IMO更为简单,但是我不一定建议您仅出于此目的而切换。如果您要进行大量的日期/时间工作,那么值得一看。

答案 1 :(得分:0)

如果基于夏/冬,DateTimeOffset中的所有内容都为+ 1 / + 2,那么如果要将ToUniversalTime保存为Offset +0,则只需保存ToUniversalTime。

var input = new DateTimeOffset(2000,1,1,23,59,00, TimeSpan.FromHours(1));
var utc = cet.ToUniversalTime();
相关问题