web-dev-qa-db-ja.com

DateTime.Parse( "2012-09-30T23:00:00.0000000Z")は常にDateTimeKind.Localに変換されます

UTC形式のDateTimeを表す文字列を解析したい。

私の文字列表現には、文字列がUTC時間を表すことを示すZulu時間仕様が含まれています。

var myDate = DateTime.Parse("2012-09-30T23:00:00.0000000Z");    

上記から、myDate.KindはDateTimeKind.Utcであると予想されますが、DatetimeKind.Localです。

私は何を間違っていますか、UTC時間を表す文字列を解析する方法は?

どうもありがとう!

57

Noda Time プロジェクトを個人的に使用します。 (確かに私は著者として偏見がありますが、それはよりきれいになります...)しかし、あなたがそれをすることができないなら...

DateTime.ParseExactを使用して、期待する正確な形式を指定し、解析コードにDateTimeStyles.AssumeUniversalおよびDateTimeStyles.AdjustToUniversalを含めます。

using System;
using System.Globalization;

class Test
{
    static void Main()        
    {
        var date = DateTime.ParseExact("2012-09-30T23:00:00.0000000Z",
                                       "yyyy-MM-dd'T'HH:mm:ss.fffffff'Z'",
                                       CultureInfo.InvariantCulture,
                                       DateTimeStyles.AssumeUniversal |
                                       DateTimeStyles.AdjustToUniversal);
        Console.WriteLine(date);
        Console.WriteLine(date.Kind);
    }
}

AdjustToUniversalを使用せずにデフォルトでローカルに調整する理由はまったくありませんが、気にしないでください...)

編集:ちょうどmattytommoの提案に対する私の反対を拡大するために、私はそれが情報を失うことを証明することを目指しました。私はこれまで失敗しました-しかし、非常に独特な方法で。これをご覧ください-2012年10月28日午前2時(UTC午前1時)に時計が戻るヨーロッパ/ロンドンタイムゾーンで実行されています。

DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");
Console.WriteLine(local1 == local2); // True

DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
Console.WriteLine(utc1 == utc2); // False. Hmm.

「DSTの有無」フラグが保存されているように見えますどこかですが、どこで解決できるかがわからないでしょう。 TimeZoneInfo.ConvertTimeToUtc stateのドキュメント

dateTimeがあいまいな時間に対応する場合、このメソッドはソースタイムゾーンの標準時間であると想定します。

それはappearではありませんlocal2...

編集:さて、それはさらに奇妙になります-それはあなたが使用しているフレームワークのバージョンに依存します。このプログラムを検討してください。

using System;
using System.Globalization;

class Test
{
    static void Main()        
    {
        DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
        DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");

        DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
        DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
        Console.WriteLine(utc1);
        Console.WriteLine(utc2);

        DateTime utc3 = local1.ToUniversalTime();
        DateTime utc4 = local2.ToUniversalTime();
        Console.WriteLine(utc3);
        Console.WriteLine(utc4);
    }
}

したがって、これは2つのdifferent UTC値を取り、DateTime.Parseで解析してから、2つの異なる方法でUTCに変換します。

.NET 3.5での結果:

28/10/2012 01:30:00 // Look - we've lost information
28/10/2012 01:30:00
28/10/2012 00:30:00 // But ToUniversalTime() seems okay...
28/10/2012 01:30:00

.NET 4.5ベータでの結果:

28/10/2012 00:30:00 // It's okay!
28/10/2012 01:30:00
28/10/2012 00:30:00
28/10/2012 01:30:00
81
Jon Skeet

いつものように、ジョンの答えは非常に包括的なものです。とはいえ、DateTimeStyles.RoundtripKindについてはまだ誰も言及していません。 DateTimeを文字列に変換し、同じDateTimeに戻す場合(DateTime.Kind設定の保持を含む)、DateTimeStyles.RoundtripKindフラグを使用します。

ジョンが言ったように、正しいことは、DateTimeオブジェクトを文字列に変換するときに「O」フォーマッタを使用することです。これにより、精度とタイムゾーンの両方の情報が保持されます。繰り返しになりますが、Jonが言ったように、逆変換するときはDateTime.ParseExactを使用します。ただし、DateTimeStyles.RoundtripKindを使用すると、常に何を入れたのかがわかります。

var now = DateTime.UtcNow;
var strNow = now.ToString("O");
var newNow = DateTime.ParseExact(strNow, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);

上記のコードでは、newNowはUTCであるという事実を含め、nowとまったく同じ時間です。 DateTime.Nowの代わりにDateTime.UtcNowを除いて同じコードを実行すると、nowの正確なコピーがnewNowとして返されますが、今回は現地時間です。

私の目的では、これは正しいことでした。なぜなら、渡されて変換されたものはすべて、まったく同じものに変換されるようにしたかったからです。

27
Simon Gillbee

次を使用してTimeZoneInfoクラスを使用します。

var myDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.Parse("2012-09-30T23:00:00.0000000Z"));
4
mattytommo

以前に同様の問題に遭遇し、数時間後に(そして髪を引っ張って)後で DateTime.SpecifyKind を使用してしまいました:

DateTime.SpecifyKind(inputDate, DateTimeKind.Utc);

上記のコメントでも誰かがこれを避けていると思います。

2
vandsh

パーサーメソッドには次の形式を使用できます。yyyy-MM-ddTHH:mm:ss.ffffffK

これにより、最後にタイムゾーン情報が適切に処理されます( 。NET 2.0以降 )。

RE: ISO 8601

2
port443