うわー、これはダブルワーミーです!私はこの投稿に偶然出くわしましたが、とても古く、OPにコードが表示されなかったため、何も投稿しませんでした。でも、好奇心が最高だったのでチェックしてみました。
.NET BCLのみを使用する:
string tzid1 = "W. Australia Standard Time"; // Perth
TimeZoneInfo tz1 = TimeZoneInfo.FindSystemTimeZoneById(tzid1);
string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
TimeZoneInfo tz2 = TimeZoneInfo.FindSystemTimeZoneById(tzid2);
DateTime dt1 = new DateTime(2006, 1, 1, 2, 0, 0);
Debug.WriteLine(dt1); // 1/1/2006 2:00:00 AM
DateTime dt2 = TimeZoneInfo.ConvertTime(dt1, tz1, tz2);
Debug.WriteLine(dt2); // 12/31/2005 11:30:00 PM
DateTime dt3 = TimeZoneInfo.ConvertTime(dt2, tz2, tz1);
Debug.WriteLine(dt3); // 1/1/2006 3:00:00 AM
案の定、OPが説明した矛盾があります。最初は、これは何らかのDSTの問題によるものだと思ったので、スリランカとパースをチェックしました。どちらも2006年に移行しましたが、この日付ではどちらもそれに近いところはありませんでした。DateTimeOffset
それでも、あいまいさの問題を回避するために、を使用して確認する必要があると思いました。
string tzid1 = "W. Australia Standard Time"; // Perth
TimeZoneInfo tz1 = TimeZoneInfo.FindSystemTimeZoneById(tzid1);
string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
TimeZoneInfo tz2 = TimeZoneInfo.FindSystemTimeZoneById(tzid2);
DateTime dt = new DateTime(2006, 1, 1, 2, 0, 0);
DateTimeOffset dto1 = new DateTimeOffset(dt, tz1.GetUtcOffset(dt));
Debug.WriteLine(dto1); // 1/1/2006 2:00:00 AM +08:00
DateTimeOffset dto2 = TimeZoneInfo.ConvertTime(dto1, tz2);
Debug.WriteLine(dto2); // 12/31/2005 11:30:00 PM +05:30
DateTimeOffset dto3 = TimeZoneInfo.ConvertTime(dto2, tz1);
Debug.WriteLine(dto3); // 1/1/2006 3:00:00 AM +09:00
そして、それはまだオフです。目標時間はにあるべきだと考えていることがわかりますが+09:00
、パースは2006年12月3日までそれに切り替えませんでした。1月には明らかにまだ+08:00
でした。
それで、私は思った... 野田救助の時間!
まず、同じWindows.NETBCLタイムゾーンを使用して確認しましょう。
string tzid1 = "W. Australia Standard Time"; // Perth
DateTimeZone tz1 = DateTimeZoneProviders.Bcl[tzid1];
string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
DateTimeZone tz2 = DateTimeZoneProviders.Bcl[tzid2];
LocalDateTime ldt1 = new LocalDateTime(2006, 1, 1, 2, 0, 0);
ZonedDateTime zdt1 = ldt1.InZoneStrictly(tz1);
Debug.WriteLine(zdt1.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
ZonedDateTime zdt2 = zdt1.WithZone(tz2);
Debug.WriteLine(zdt2.ToDateTimeOffset()); // 12/31/2005 11:30:00 PM +05:30
ZonedDateTime zdt3 = zdt1.WithZone(tz1);
Debug.WriteLine(zdt3.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
ねえ、それはそれを修正したようですよね?もしそうなら、それは問題がWindowsタイムゾーンデータにないことを意味します。なぜなら、NodaTimeのBCLプロバイダーはまったく同じデータを使用するからです。したがって、に実際に欠陥があるはずですTimeZoneInfo.ConvertTime
。Whammy#1があります。
それで、それがすべてうまくいっていることを確認するために、IANATZDBデータで同じことを試してみましょう。結局のところ、より正確であることが知られています。
string tzid1 = "Australia/Perth";
DateTimeZone tz1 = DateTimeZoneProviders.Tzdb[tzid1];
string tzid2 = "Asia/Colombo"; // Sri Jeyawardenepura
DateTimeZone tz2 = DateTimeZoneProviders.Tzdb[tzid2];
LocalDateTime ldt1 = new LocalDateTime(2006, 1, 1, 2, 0, 0);
ZonedDateTime zdt1 = ldt1.InZoneStrictly(tz1);
Debug.WriteLine(zdt1.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
ZonedDateTime zdt2 = zdt1.WithZone(tz2);
Debug.WriteLine(zdt2.ToDateTimeOffset()); // 1/1/2006 12:00:00 AM +06:00
ZonedDateTime zdt3 = zdt1.WithZone(tz1);
Debug.WriteLine(zdt3.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
そして、私の友達は、Whammy#2です。中間時間が+06:00
オフセットを使用していることに注意してください。これはエラーだと思いましたが、ここでもう一度確認すると、TZDBデータが正しいことがわかりました。スリランカはその時+06:00
でした。4月までに切り替わりませんでした+05:30
。
したがって、Whammysを要約すると:
- Windowsの
TimeZoneInfo.ConvertTime
機能に欠陥があるようです。
- ゾーンのWindowsタイムゾーンデータ
"Sri Lanka Standard Time"
が正しくありません。
常にNodaTimeとTZDBを使用する方がよいでしょう。
アップデート
"W. Australia Standard Time"
最初の問題がクラスによるゾーンの解釈方法にあることを特定するのを手伝ってくれたJonSkeetに感謝しTimeZoneInfo
ます。
.NET Frameworkの参照ソースコードをさらに深く掘り下げましたが、これはプライベート静的メソッドで発生していると思いますTimeZoneInfo.GetIsDaylightSavingsFromUtc
。DSTが常に同じ暦年に開始および停止するとは限らないことを彼らは考慮していないと思います。
この場合、彼らは2005年に2006年の調整ルールを適用し、endTime
の1/2/2005
前にstartTime
を取得してい12/4/2005
ます。彼らは、これが2006年になるはずだと(誤って1年を追加することによって)調整しようとしますが、データが逆の順序であるとは考えていません。
この問題は、冬にDSTを開始するすべてのタイムゾーン(オーストラリアなど)で発生する可能性があり、移行ルールが変更されるたびに何らかの形で発生します。これは2006年に発生しました。
ここでMicrosoftConnectに関する問題を提起しました。
私が言及した「2番目の苦痛」は、スリランカの履歴データがWindowsのタイムゾーンレジストリキーに存在しないためです。