【问题标题】:Converting timestamp from CET / CEST to UTC将时间戳从 CET / CEST 转换为 UTC
【发布时间】:2020-08-18 22:44:06
【问题描述】:

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

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

我已经尝试过使用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 调回一小时到 2: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。

【问题讨论】:

  • 您能否提供一些示例时间戳(来自您的文件)和您的解析逻辑?
  • @PeterCsala 为什么?我从 CET / CEST 时间戳解析了年、月、日、分和秒,我需要从中创建一个 UTC DateTime 对象。文件结构和解析逻辑与我要解决的问题有什么关系?我不想详细介绍文件结构,因为它非常复杂且令人困惑。
  • @Chris - 您可能需要使用其他 TimeZoneInfo 方法,包括 IsAmbiguousTimeGetAmbiguousTimeOffsets,以及您自己的消歧逻辑。很乐意在这里为您提供帮助,但是对于此类问题,我们确实需要查看一些数据和代码才能为您提供帮助。如果您现有的代码太复杂,请提供说明您的问题的代码。请阅读 Stack Overflow 帮助中心的 How to create a Minimal, Reproducible Example。谢谢。
  • (至少提供您正在使用的数据的示例,这样人们就不必猜测了。)
  • @MattJohnson-Pint 我刚刚更新了问题并添加了一个示例。如果有帮助,请告诉我。

标签: c# datetime timezone


【解决方案1】:

如果您知道每个文件总是有一天,并且您始终可以检测到更改,因为您每 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 上会更简单,但我不一定建议你仅仅为了这个而切换。如果您正在做大量的日期/时间工作,那么值得一看。

【讨论】:

  • 谢谢!我认为您需要在将偏移量分配给 selectedOffset 时切换偏移量。除此之外,我设法实现了与您的示例类似的方法,现在它似乎工作得很好!
  • @Chris: 嗯...我假设偏移量会按照它们有效的顺序返回,但也许不会......会调查。无论如何,很高兴它为你解决了:)
【解决方案2】:

如果您在 DateTimeOffset 中拥有基于夏季/冬季的 +1/+2 的所有内容,那么如果您想使用 Offset +0 保存它,您只需保存 ToUniversalTime。

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

【讨论】:

  • 但我在 DateTimeOffset 对象中没有任何内容?这里的问题是实际计算出偏移量,而不是将其转换为 UTC。
  • @Chris 您无法获得计算偏移量的代码,您必须在时间切换时循环和切换。没有元数据的代码如何知道时间是 CEST 还是 CET? en.wikipedia.org/wiki/Summer_time_in_Europe 有一个日期表。
猜你喜欢
  • 2019-03-31
  • 2022-01-11
  • 1970-01-01
  • 2019-07-22
  • 2022-11-13
  • 1970-01-01
  • 2020-12-03
  • 1970-01-01
  • 2016-01-13
相关资源
最近更新 更多