【问题标题】:Parsing ISO8601 date/time with DateTime struct使用 DateTime 结构解析 ISO8601 日期/时间
【发布时间】:2013-08-29 13:30:26
【问题描述】:

我正在尝试使用 .NET 的 DateTime 结构解析 ISO8601 格式的日期/时间字符串。

为了让我全面了解问题,我将使用 .NET 和 JavaScript 执行测试。我目前在英国(英国夏令时,即 UTC+01:00)。

我对ISO8601的理解是:

  • 字符串后缀“Z”时,时间以UTC表示。
  • 当字符串后缀为“+/-hh:mm”时,时间表示为 当地时间,其中“+/-hh:mm”表示与 UTC 的偏移量。

考虑以下 ISO8601 日期/时间格式字符串:

"1987-01-05T08:45:30.500+0100"

鉴于我上面的观点,这个字符串表示本地时间“08:45:30”,UTC时间“07:45:30”

当前时区测试 (.NET)

DateTime now = DateTime.Now;
Console.WriteLine(now.ToLocalTime()); // 27/08/2013 12:02:43
Console.WriteLine(now.ToUniversalTime()); // 27/08/2013 11:02:43

当前时区测试 (JavaScript)

var now = new Date(Date.now());
now.toString(); // Tue Aug 27 2013 12:03:46 GMT+0100 (GMT Daylight Time)
now.toUTCString(); // Tue, 27 Aug 2013 11:03:46 GMT

除了两个示例之间的微小(分钟/秒)差异之外,它们返回的结果与我对英国夏令时 (UTC+01:00) 的预期完全一致。 分钟/秒的差异是因为我不能同时运行 .NET 测试和 JavaScript 测试。

现在让我们使用我的 ISO8601 日期/时间格式字符串:

解析 ISO8601 格式字符串 (.NET)

DateTime dt = DateTime.Parse("1987-01-05T08:45:30.500+0100");
Console.WriteLine(dt.ToLocalTime()); // 05/01/1987 07:45:30
Console.WriteLine(dt.ToUniversalTime()); // 05/01/1987 07:45:30

解析 ISO8601 格式字符串 (JavaScript)

var dt = new Date("1987-01-05T08:45:30.500+0100");
dt.toString(); // Mon Jan 05 1987 07:45:30 GMT+0000 (GMT Standard Time)
dt.toUTCString(); //Mon, 05 Jan 1987 07:45:30 GMT

这似乎与显示日期/时间“now”的示例不一致。当“现在”示例以英国夏令时间 (UTC+01:00) 显示时,为什么这显示好像我在英国冬季时间 (UTC+00:00)?

如果我更改我的时区设置,我会得到本地/世界时的预期结果,但如果它们设置为我当前的时间/时区,这似乎会给出不一致的结果。

编辑: 简而言之,当我尝试解析字符串时,就好像 .NET 和 JavaScript 都忽略了夏令时/英国夏令时 (UTC+01:00)。结果在冬天是正确的,当我实际改变我的时间/区域时也是正确的......但就目前而言,这在我在英国测试过的“any”机器上是不正确的。

【问题讨论】:

  • 我认为+0100被忽略了,它可能取决于本地计算机上的Regional settings。我已经测试了代码,它向我展示了 07:45:3002:45:30 与您的测试不同。
  • 这是您在 UTC 时区生活/编码所获得的...解析 总是 返回本地时间,但在您的情况下,UTC 和本地是无操作的。
  • 其实你的代码应该是Local: 05/01/1987 07:45:30UTC: 05/01/1987 08:45:30+0100只是指定时间是UTC,所以所有的本地时间都是UTC time - 1
  • @KingKing,请看我的编辑,谢谢。
  • @series0ne 我不得不说你的例子对我来说工作正常(在我的机器上),实际上+0100 意味着实际的Local time 是 1 小时少于UTC time (不比你想象的要大)。它只是表明time stringUTC

标签: c# .net datetime utc iso8601


【解决方案1】:

英国在夏季(8 月 27 日,示例中的“现在”)使用夏令时(他们的时钟提前一小时),但在冬季不使用(1 月 5 日,根据您对“1987-01-05T08:45:30.500+0100”的解析)。

实际上,英国在冬季使用 UTC。你的机器好像有一个英国的TimeZoneInfo。您可以使用TimeZoneInfo.Local.DisplayName(自.NET 3.5 起)或TimeZone.CurrentTimeZone.StandardName(旧)进行检查。

您可以通过dt.IsDaylightSavingTime()查看。

加法:

我的回答只是关于 .NET(但也许同样适用于 JavaScript?)。您提供的示例完全按预期工作。 1987 年 1 月的日期和时间将转换为您当地的区域,据推测是英国,而 1987 年 1 月,英国是 +0000,因为它是冬天。您提供的时间字符串标有 +0100(就好像它来自德国或英国以东一小时的其他国家一样),并且在解析字符串时确认了这一点。 1987 年夏天的日期会正确地转换为不同的日期,因为在 1987 年夏天,英国(以及所有 EEC)采用夏令时(夏令时)。

总结:在解释时间时会考虑偏移指示符或区域说明符+0100。这将在您的计算机上转换为英国时间。如果您想转换为 UTC 而不是转换为机器的本地时间,请使用采用 DateTimeStyles 枚举并包含标志 DateTimeStyles.AdjustToUniversal 的重载。

如果您想要一个更好地表示时间绝对区域的值,请考虑使用结构DateTimeOffset 而不是DateTime。您还可以考虑 NODA 时间而不是 .NET 类型。

【讨论】:

  • 没错。我最终发现我想要说明的一点是 .NET 和 JavaScript 似乎都没有保留偏移量。因此,您始终可以返回 UTC 时间。我猜这是因为如果保留了偏移量,它将与用户机器不一致。即如果偏移量为 +0100 且用户时区为 +0800,则用户机器上的偏移量(本地/时间)将不一致......如果这有意义吗?
  • @series0ne 我认为你错了。请尝试将字符串 "1987-01-05T08:45:30.500+0100" 更改为例如"1987-07-05T08:45:30.500+0100"(夏天)。 编辑: 另外,为了减少混淆,请尝试使用带有远离英国的区域的字符串,例如 +1100 或其他东西。区域被考虑
  • 我已经编辑了整个问题,您介意再看一下吗?谢谢。
【解决方案2】:

ToLocalTime 和 ToUniversalTime 确实考虑了夏令时,并且做得非常好。至少在 .Net 中,Javascript Date 对象存在缺陷,我建议在 js 中使用 moment.js 和 moment-timezone.js 进行转换。

这是我的单元测试和结果:

        var now = DateTime.Now;
        Console.WriteLine(now.ToLocalTime());
        Console.WriteLine(now.ToUniversalTime());

        //Test 1 TimeZone UTC+1 London, etc..
        //current day 20th July = BST

        /*  07/20/2015 01:06:43
            07/20/2015 00:06:43*/

        //set day to 20th Januray UK winter time

        /*  01/20/2015 01:07:55
            01/20/2015 01:07:55*/

为了在 .Net 中获得上述结果,您需要 a) 将时区设置为 UTC+2 b) 在 ToLocalTime() 中遇到故障,该故障已被修复,MSDN 提到了 XP 上的缺陷转换日期时。

最后 DateTime.Now 返回本地时间,因此使用 DateTime.Now.ToLocalTime() 有点多余,您很可能会得到意想不到的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 2014-11-14
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多