9

TimeZoneInfo.ConvertTimeメソッドを使用して、時間をあるものから別のものに変換しています。

日時を2006年1月1日午前2時にパースからSriJeyawardenepuraに変換している間、2005年1月31日午後11時30分に変換されました。

同じ時刻(2005年1月31日午後11時30分)をSri JeyawardenepuraからPerthに変換している間、2006年1月1日午前3時00分に変換されました。

タイムゾーンの変換に1時間の違いがあるのはなぜですか?

4

4 に答える 4

12

うわー、これはダブルワーミーです!私はこの投稿に偶然出くわしましたが、とても古く、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.ConvertTimeWhammy#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年の調整ルールを適用し、endTime1/2/2005前にstartTimeを取得してい12/4/2005ます。彼らは、これが2006年になるはずだと(誤って1年を追加することによって)調整しようとしますが、データが逆の順序であるとは考えていません。

この問題は、冬にDSTを開始するすべてのタイムゾーン(オーストラリアなど)で発生する可能性があり、移行ルールが変更されるたびに何らかの形で発生します。これは2006年に発生しました。

ここでMicrosoftConnectに関する問題を提起しました。

私が言及した「2番目の苦痛」は、スリランカの履歴データがWindowsのタイムゾーンレジストリキーに存在しないためです。

于 2013-09-06T03:17:06.177 に答える
6

マットの答えにもう少し情報を追加するだけで、BCLはパースの独自のデータについて非常に混乱しているようです。2005年の終わり頃に2つの移行があったと思われます。1つはUTCの午後4時、もう1つは8時間後です。

デモ:

using System;

class Test
{    
    static void Main()        
    {
        var id = "W. Australia Standard Time"; // Perth
        var zone = TimeZoneInfo.FindSystemTimeZoneById(id);
        var utc1 = new DateTime(2005, 12, 31, 15, 59, 0, DateTimeKind.Utc);
        var utc2 = new DateTime(2005, 12, 31, 16, 00, 0, DateTimeKind.Utc);
        var utc3 = new DateTime(2005, 12, 31, 23, 59, 0, DateTimeKind.Utc);
        var utc4 = new DateTime(2006, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        Console.WriteLine(zone.GetUtcOffset(utc1));
        Console.WriteLine(zone.GetUtcOffset(utc2));
        Console.WriteLine(zone.GetUtcOffset(utc3));
        Console.WriteLine(zone.GetUtcOffset(utc4));
    }
} 

結果:

08:00:00 // 3:59pm UTC
09:00:00 // 4:00pm UTC
09:00:00 // 11:59pm UTC
08:00:00 // 12:00am UTC the next day

これは非常に奇妙なことであり、リビアのタイムゾーンの破損に関連している可能性があります。ただし、2つの遷移はなく、1つだけ誤って配置されています。

于 2013-09-10T19:35:18.083 に答える
3

確実にするために、特定のコードを投稿する必要があります。たとえば、一方の変換では夏時間が適用され、もう一方の変換では適用されないという問題が発生する可能性があります。

タイムゾーン管理には微妙な違いがあるかもしれません。優れた概要については、このJonSkeetブログを確認することをお勧めします。

実際、.NETタイムクラスを正しく使用するのは非常に難しいため、JonはNodaTimeと呼ばれるJoda-Timeから.NETへの移植を引き受けました。

複数のタイムゾーンをサポートするプロジェクトについては、真剣に検討する価値があります。

于 2012-04-04T08:16:34.753 に答える
1

時間を変換するときに夏時間を考慮しましたか?次のリンクを参照してください。そうすれば答えが得られます。表示される時間は絶対に正しいです

http://www.timeanddate.com/worldclock/timezone.html?n=196&syear=2000

于 2012-04-04T08:29:14.237 に答える