我正在一个简单的应用程序中将一些Unix时间戳日期转换为本地时间。我打印两者,UTC时间和" E.南美标准时间" - > (格林尼治标准时间-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
另一个Windows 7盒子(.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
对我失踪的事情有任何建议吗?
答案 0 :(得分:3)
嗯,您可能只是错过了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)
答案 1 :(得分:1)
我同意Jon的观点,Noda Time对于这种情况要好得多。我强烈建议你继续他的实施。
然而,只是为了解释你的结果:
在最后一行中,将dt
变量格式化为字符串。此变量为DateTime
类型,其.Kind
为DateTimeKind.Unspecified
。
您的formatLocal
格式化程序包含z
令牌以返回时区偏移量。
当您使用z
应用DateTime
格式说明符时,会评估Kind
。对于Utc
种,它会发出"+0"
。对于Local
种类,它会为计算机运行的本地时区发出偏移量。对于Unspecified
种类,它被视为 local 。
因此,偏移量不一定来自您转换的时区,而是来自您当地计算机的时区!
MSDN says this about the z
specifier:
使用
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