中的TimeZoneInfo和DST存在问题网
本文关键字:问题 存在 DST TimeZoneInfo 中的 | 更新日期: 2023-09-27 18:00:33
我正在一个简单的应用程序中工作,将一些Unix时间戳日期转换为本地时间。我正在打印UTC时间和"东南美洲标准时间"->(GMT-03:00)巴西利亚。下面的代码运行良好,但似乎与DST:有关
public static void Main (string[] args)
{
long[] timestamps = {1413685800L, 1413689400L, 1424568600L, 1424572200L, 1424575800L};
string formatUtc = "{0:dd MMM yyyy HH:mm:ss}";
string formatLocal = "{0:dd MMM yyyy HH:mm:ss z}";
TimeZoneInfo tzBr = null;
tzBr = TimeZoneInfo.FindSystemTimeZoneById("E. South America Standard Time");
DateTime dt;
Console.WriteLine("UTC't't't'tAmerica/Sao_Paulo");
Console.WriteLine("---------------------------------------------------------");
foreach (long ts in timestamps) {
dt = new DateTime(1970,1,1,0,0,0,0,System.DateTimeKind.Utc).AddSeconds(ts);
Console.Write(string.Format(formatUtc, dt));
dt = TimeZoneInfo.ConvertTime(dt, TimeZoneInfo.Utc, tzBr);
Console.WriteLine("'t't" + string.Format(formatLocal, dt));
}
}
我在三台不同的机器上测试了这个代码,得到了以下结果:
Windows 7(.Net):
UTC America/Sao_Paulo
---------------------------------------------------------
19 out 2014 02:30:00 18 out 2014 23:30:00 -3
19 out 2014 03:30:00 19 out 2014 01:30:00 -2
22 fev 2015 01:30:00 21 fev 2015 23:30:00 -3 <- Wrong!
22 fev 2015 02:30:00 21 fev 2015 23:30:00 -3
22 fev 2015 03:30:00 22 fev 2015 00:30:00 -3
另一个Windows7盒子(.Net):
UTC America/Sao_Paulo
---------------------------------------------------------
19 out 2014 02:30:00 -3 18 out 2014 23:30:00 -3
19 out 2014 03:30:00 -3 19 out 2014 01:30:00 -3 <- Wrong!
22 fev 2015 01:30:00 -3 21 fev 2015 23:30:00 -3 <- Wrong!
22 fev 2015 02:30:00 -3 21 fev 2015 23:30:00 -3
22 fev 2015 03:30:00 -3 22 fev 2015 00:30:00 -3
Linux Fedora 22(Mono):
UTC America/Sao_Paulo
---------------------------------------------------------
19 out 2014 02:30:00 18 out 2014 23:30:00 -3
19 out 2014 03:30:00 19 out 2014 01:30:00 -2
22 fev 2015 01:30:00 21 fev 2015 22:30:00 -2 <- Wrong!
22 fev 2015 02:30:00 21 fev 2015 23:30:00 -2 <- Wrong!
22 fev 2015 03:30:00 22 fev 2015 00:30:00 -3
Java应用程序的预期结果(BRT表示-3,BRST表示-2):
UTC America/Sao_Paulo
---------------------------------------------------------
19 Out 2014 02:30:00 UTC 18 Out 2014 23:30:00 BRT
19 Out 2014 03:30:00 UTC 19 Out 2014 01:30:00 BRST
22 Fev 2015 01:30:00 UTC 21 Fev 2015 23:30:00 BRST
22 Fev 2015 02:30:00 UTC 21 Fev 2015 23:30:00 BRT
22 Fev 2015 03:30:00 UTC 22 Fev 2015 00:30:00 BRT
对我遗漏的东西有什么建议吗?
您可能只是忽略了这样一个事实,即Windows时区数据与Java使用的IANA数据不同,并且您的两个Windows 7框可能应用了一组不同的Windows更新。恐怕我不想猜测Mono到底在用什么。
你可能想考虑的一个选项是使用我的Noda Time库,它使用IANA数据(并允许你使用你想要的任何版本的数据),以及通常更好的API,IMO
using System;
using NodaTime;
using NodaTime.Text;
class Test
{
public static void Main (string[] args)
{
long[] timestamps = {1413685800L, 1413689400L, 1424568600L, 1424572200L, 1424575800L};
var zone = DateTimeZoneProviders.Tzdb["America/Sao_Paulo"];
var instantPattern = InstantPattern.CreateWithInvariantCulture("dd MMM yyyy HH:mm:ss");
var zonedPattern = ZonedDateTimePattern.CreateWithInvariantCulture
("dd MMM yyyy HH:mm:ss o<g> (x)", null);
foreach (long ts in timestamps) {
var instant = Instant.FromSecondsSinceUnixEpoch(ts);
var zonedDateTime = instant.InZone(zone);
Console.WriteLine("{0} UTC - {1}",
instantPattern.Format(instant),
zonedPattern.Format(zonedDateTime));
}
}
}
输出:
19 Oct 2014 02:30:00 UTC - 18 Oct 2014 23:30:00 -03 (BRT)
19 Oct 2014 03:30:00 UTC - 19 Oct 2014 01:30:00 -02 (BRST)
22 Feb 2015 01:30:00 UTC - 21 Feb 2015 23:30:00 -02 (BRST)
22 Feb 2015 02:30:00 UTC - 21 Feb 2015 23:30:00 -03 (BRT)
22 Feb 2015 03:30:00 UTC - 22 Feb 2015 00:30:00 -03 (BRT)
我同意Jon的观点,Noda Time在这种情况下要好得多。我强烈建议你配合他的实施。
然而,只是为了解释你的结果:
-
在最后一行中,将
dt
变量格式化为字符串。此变量为DateTime
类型,其.Kind
为DateTimeKind.Unspecified
。 -
formatLocal
格式化程序包含用于返回时区偏移量的z
令牌。 -
将
z
格式说明符与DateTime
一起应用时,将计算Kind
。对于Utc
类,它发射"+0"
。对于Local
类型,它发出计算机运行的本地时区的偏移量。对于Unspecified
类,它被视为局部。
因此,偏移量不一定来自您转换到的时区,而是来自您本地计算机的时区!
MSDN这样评价z
说明符:
在
DateTime
值的情况下;z";自定义格式说明符表示本地操作系统时区与协调世界时(UTC)的带符号偏移量,以小时为单位。它不反映实例的DateTime.Kind
属性的值因此;z";不建议将格式说明符与DateTime
值一起使用。对于
DateTimeOffset values
,此格式说明符表示DateTimeOffset
值相对于UTC的偏移量(以小时为单位)。
这个措辞有点不正确,因为DateTimeKind.Utc
确实返回了"+0"
,但我认为你明白了。您应该使用DateTimeOffset
。
DateTimeOffset epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero);
foreach (long ts in timestamps)
{
DateTimeOffset dto = epoch.AddSeconds(ts);
Console.Write(formatUtc, dto);
dto = TimeZoneInfo.ConvertTime(dto, tzBr);
Console.WriteLine("'t't" + formatLocal, dto);
}
UTC America/Sao_Paulo
---------------------------------------------------------
19 Oct 2014 02:30:00 18 Oct 2014 23:30:00 -3
19 Oct 2014 03:30:00 19 Oct 2014 01:30:00 -2
22 Feb 2015 01:30:00 21 Feb 2015 23:30:00 -2
22 Feb 2015 02:30:00 21 Feb 2015 23:30:00 -3
22 Feb 2015 03:30:00 22 Feb 2015 00:30:00 -3