web-dev-qa-db-ja.com

DateTime 'Z'形式指定子はどこにありますか?

[更新フォーマット指定子はフォーマット文字列と同じものではありません。書式指定子はカスタム書式文字列の一部であり、書式文字列は「ストック」であり、カスタマイズを提供しません。私の問題はフォーマットではなく指定子にあります]

'zzz'書式指定子を使用する書式文字列を使用してDateTime変換を往復しようとしていますが、これは現地時間にバインドされていることがわかっています。したがって、UTC日付時刻でラウンドトリップを試みると、DateTimeInvalidLocalFormat例外がスローされます。

UTC DateTimeは、現地時間にのみ正しい形式のテキストに変換されています。これは、出力にローカルタイムゾーンオフセットが含まれる 'z'形式指定子を使用してDateTime.ToStringを呼び出すときに発生する可能性があります。 その場合、UTC時間を指定する 'Z'形式指定子を使用する、またはテキストでDateTimeを永続化する推奨方法である 'o'形式文字列を使用します。これは、XmlConvertまたはDataSetによってシリアル化されるDateTimeを渡すときにも発生する可能性があります。 XmlConvert.ToStringを使用する場合は、XmlDateTimeSerializationMode.RoundtripKindを渡して正しくシリアル化してください。 DataSetを使用している場合、DataColumnオブジェクトのDateTimeModeをDataSetDateTime.Utcに設定します。

この提案に基づいて、コードを機能させるために必要なことは、 'zzz'を 'ZZZ'に置き換えるだけで、UTC形式に立つことができます。問題は、「Z」がドキュメントのどこにも見つからず、私が試みる「Z」フォーマットの組み合わせ、つまり「Z」、「ZZ」、「ZZZ」は、常にリテラルのように扱われるZでDateTimeインスタンスを変換するだけです。

誰かが例外メッセージの作者に伝えることなく「Z」を実装するのを忘れましたか、またはハッキングせずに「+0000」で有効なローカル時間オフセットを交換する方法がありませんか?

コード例:

// This is the format with 'zzzzz' representing local time offset
const string format = "ddd MMM dd HH:mm:ss zzzzz yyyy";

// create a UTC time
const string expected = "Fri Dec 19 17:24:18 +0000 2008";
var time = new DateTime(2008, 12, 19, 17, 24, 18, 0, DateTimeKind.Utc);

// If you're using a debugger this will rightfully throw an exception
// with .NET 3.5 SP1 because 'z' is for local time only; however, the exception
// asks me to use the 'Z' specifier for UTC times, but it doesn't exist, so it
// just spits out 'Z' as a literal.
var actual = time.ToString(format, CultureInfo.InvariantCulture);

Assert.AreEqual(expected, actual);
56
Daniel Crenna

「K」フォーマット指定子は、おそらく役に立つでしょう。これは、大文字の「Z」の使用に言及していると思われる唯一のものです。

「Z」は、DateTimesのユニークなケースです。リテラル「Z」は、実際にはUTC時間のISO 8601日時標準の一部です。 「Z」(Zulu)が時間の終わりに付けられると、その時間はUTCであるため、実際には文字Zは時間の一部です。これは、おそらく.NETの日付形式ライブラリにいくつかの問題を作成します。これは、実際には形式指定子ではなくリテラルであるためです。

59
Andy White

DateTimeを使用すると、変数内に日付と時刻を格納できます。

日付は現地時間またはUTC時間であり、それはあなた次第です。

たとえば、私はイタリアにいます(+2 UTC)

var dt1 = new DateTime(2011, 6, 27, 12, 0, 0); // store 2011-06-27 12:00:00
var dt2 = dt1.ToUniversalTime()  // store 2011-06-27 10:00:00

だから、タイムゾーンを含むdt1とdt2を印刷するとどうなりますか?

dt1.ToString("MM/dd/yyyy hh:mm:ss z") 
// Compiler alert...
// Output: 06/27/2011 12:00:00 +2

dt2.ToString("MM/dd/yyyy hh:mm:ss z") 
// Compiler alert...
// Output: 06/27/2011 10:00:00 +2

dt1およびdt2には、日付と時刻の情報のみが含まれます。 dt1およびdt2にはタイムゾーンオフセットが含まれていません。

「+ 2」がdt1およびdt2変数に含まれていない場合、どこから来るのでしょうか?

それはマシンのクロック設定から来ます。

コンパイラは、「zzz」形式を使用すると、「DATE + TIME」(dt1とdt2に格納される)+ 「TIMEZONE OFFSET」( dt1およびdt2はDateTyme型であるため)であり、コードを実行しているサーバーマシンのオフセットを使用します。

コンパイラは「警告:コードの出力はマシンクロックオフセットに依存しています」と表示します。

ロンドン(+1 UTC)にあるサーバーでこのコードを実行すると、結果は完全に異なります。 "+ 2"の代わりに "+ 1 = "

...
dt1.ToString("MM/dd/yyyy hh:mm:ss z") 
// Output: 06/27/2011 12:00:00 +1

dt2.ToString("MM/dd/yyyy hh:mm:ss z") 
// Output: 06/27/2011 10:00:00 +1

適切な解決策は、DateTimeの代わりにDateTimeOffsetデータ型を使用することです。 2008バージョン以降のsql Serverおよび3.5バージョン以降の.Netフレームワークで利用可能です。

7
Marco Staffoli

文字列を介した日付のラウンドトリップは常に苦痛でした...しかし、 'o'指定子がUTC状態をキャプチャするラウンドトリップに使用するものであることを示すドキュメントです。解析時に、元のファイルがUTCである場合、結果は通常Kind == Utcになります。シリアル化する前に、日付を常にUTCまたはローカルに正規化してから、選択した正規化のパーサーに指示することが最善の方法であることがわかりました。

DateTime now = DateTime.Now;
DateTime utcNow = now.ToUniversalTime();

string nowStr = now.ToString( "o" );
string utcNowStr = utcNow.ToString( "o" );

now = DateTime.Parse( nowStr );
utcNow = DateTime.Parse( nowStr, null, DateTimeStyles.AdjustToUniversal );

Debug.Assert( now == utcNow );
5
Paul Alexander

このページ MSDNには、標準のDateTime形式の文字列がリストされており、「Z」を使用した文字列は除外されていません。

更新:日付文字列の残りの部分も正しいパターンに従っていることを確認する必要があります(送信したものの例を提供していないため、送信したかどうかを判断するのは困難です)。 UTC形式が機能するには、次のようになります。

// yyyy'-'MM'-'dd HH':'mm':'ss'Z'
DateTime utcTime = DateTime.Parse("2009-05-07 08:17:25Z");
2
Fredrik Mörk
Label1.Text = dt.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z");

出力されます:

07 Mai 2009 | 08:16 | 13 | +02:00 | +02 | +2

私はデンマークにいます。GMTからのオフセットは+2時間で、魔女は正しいです。

CLIENT Offsetを取得する必要がある場合は、 little trick をチェックすることをお勧めします。ページは、GMTが+00:00の英国のサーバーにあり、ご覧のとおり、ローカルGMTオフセットを取得します。


あなたのコメントに関して、私はしました:

DateTime dt1 = DateTime.Now;
DateTime dt2 = dt1.ToUniversalTime();

Label1.Text = dt1.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z");
Label2.Text = dt2.ToString("dd MMM yyyy | hh:mm | FF | ZZZ | ZZ | Z");

私はこれを取得します:

07 Mai 2009 | 08:24 | 14 | +02:00 | +02 | +2
07 Mai 2009 | 06:24 | 14 | ZZZ | ZZ | Z 

私は例外を受け取りません、ただ...それは大文字のZで何もしません:(

申し訳ありませんが、何か不足していますか?


カスタムの日付と時刻のフォーマット文字列 に関するMSDNを注意深く読んでください

大文字の「Z」はサポートされていません。

2
balexandre

私はDateTimeOffsetを扱っていましたが、残念ながら「o」は「Z」ではなく「+0000」を出力します。

だから私は結局:

dateTimeOffset.UtcDateTime.ToString("o")
0
Kugel