一口に日時の文字列化といっても、日付・時刻を表す形式・書式にはいくつか種類があり、また国や地域によって表記が異なる場合もあるため、どのような書式を使って文字列化するかを定める必要があります。 DateTime.ToStringやConsole.WriteLine、String.Formatなどのメソッドでは、書式を指定して日時を文字列化することが出来るようになっています。 逆に、DateTime.Parseなどのメソッドを使えば、書式に従って文字列からDateTimeに変換することができます。

ここではDateTime・DateTimeOffsetの文字列化および日付と時刻の書式、文字列への/からの変換について見ていきます。

なお、本文中にあるいくつかのサンプルコードについて、実行環境に設定されているタイムゾーン・言語・書式等によって実行結果・出力内容が変わるものもが存在します。 特に明記していない場合は、日本標準時(UTC+9)・日本語の環境での実行結果となります。

文字列への変換

DateTime.ToStringおよびDateTimeOffset.ToStringメソッドを用いることでDateTime・DateTimeOffsetの表す日時を文字列化することができます。

DateTime/DateTimeOffset.ToStringメソッドで日時を文字列に変換する
using System;

class Sample {
  static void Main()
  {
    var dt = DateTime.Now;

    Console.WriteLine(dt.ToString()); // DateTimeを文字列に変換して表示

    var dto = DateTimeOffset.Now;

    Console.WriteLine(dto.ToString()); // DateTimeOffsetを文字列に変換して表示
  }
}
DateTime/DateTimeOffset.ToStringメソッドで日時を文字列に変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim dt As DateTime = DateTime.Now

    Console.WriteLine(dt.ToString()) ' DateTimeを文字列に変換して表示

    Dim dto As DateTimeOffset = DateTimeOffset.Now

    Console.WriteLine(dto.ToString()) ' DateTimeOffsetを文字列に変換して表示
  End Sub
End Class
実行結果例
2013/04/01 15:00:30
2013/04/01 15:00:30 +09:00

ToStringメソッドに何も引数を指定しない場合、「一般的な日付と日時の形式」と定義されている形式に従って日時が文字列化されます。 一方、ToStringメソッドの引数に書式指定子を指定すると、その書式に従って日時が文字列化されるようになります。 書式指定子には、あらかじめ一定の書式が定義されている標準の書式指定子と、独自に書式を定義することができるカスタム書式指定子があります。

標準の書式

.NETでは、標準の日付と時刻の書式が用意されていて、その中から目的に応じた書式を選ぶことが出来ます。 以下は、DateTime・DateTimeOffsetに対していくつかの標準の書式指定子を指定して文字列化した例です。

以下の例で使用している書式はローカライズされる書式(実行環境の国と地域の設定によって結果が異なる書式)です。 そのため、年月日の順序や区切りに用いられる記号、月や曜日の表記など、実行環境の言語設定にしたがって実行結果が変わります。

標準の書式指定子を使ってDateTime/DateTimeOffsetを文字列に変換する(ローカライズされる書式の例)
using System;

class Sample {
  static void Main()
  {
    var dt = DateTime.Now;

    Console.WriteLine("D : {0}", dt.ToString("D"));  // 長い日付の形式
    Console.WriteLine("d : {0}", dt.ToString("d"));  // 短い日付の形式
    Console.WriteLine("T : {0}", dt.ToString("T"));  // 長い時刻の形式
    Console.WriteLine("t : {0}", dt.ToString("t"));  // 短い時刻の形式
    Console.WriteLine("F : {0}", dt.ToString("F"));  // 完全な日付と日時の形式 (長い形式)
    Console.WriteLine("u : {0}", dt.ToString("u"));  // UTCでの完全な日付と日時の形式 (短い形式)
    Console.WriteLine("G : {0}", dt.ToString("G"));  // 一般的な日付と日時の形式 (書式指定子を指定しなかった場合のデフォルト)
    Console.WriteLine();

    var dto = DateTimeOffset.Now;

    Console.WriteLine("D : {0}", dto.ToString("D"));  // 長い日付の形式
    Console.WriteLine("d : {0}", dto.ToString("d"));  // 短い日付の形式
    Console.WriteLine("T : {0}", dto.ToString("T"));  // 長い時刻の形式
    Console.WriteLine("t : {0}", dto.ToString("t"));  // 短い時刻の形式
    Console.WriteLine("F : {0}", dto.ToString("F"));  // 完全な日付と日時の形式 (長い形式)
    Console.WriteLine("u : {0}", dto.ToString("u"));  // UTCでの完全な日付と日時の形式 (短い形式)
    Console.WriteLine("G : {0}", dto.ToString("G"));  // 一般的な日付と日時の形式 (書式指定子を指定しなかった場合のデフォルト)
    Console.WriteLine();
  }
}
標準の書式指定子を使ってDateTime/DateTimeOffsetを文字列に変換する(ローカライズされる書式の例)
Imports System

Class Sample
  Shared Sub Main()
    Dim dt As DateTime = DateTime.Now

    Console.WriteLine("D : {0}", dt.ToString("D"))  ' 長い日付の形式
    Console.WriteLine("d : {0}", dt.ToString("d"))  ' 短い日付の形式
    Console.WriteLine("T : {0}", dt.ToString("T"))  ' 長い時刻の形式
    Console.WriteLine("t : {0}", dt.ToString("t"))  ' 短い時刻の形式
    Console.WriteLine("F : {0}", dt.ToString("F"))  ' 完全な日付と日時の形式 (長い形式)
    Console.WriteLine("u : {0}", dt.ToString("u"))  ' UTCでの完全な日付と日時の形式 (短い形式)
    Console.WriteLine("G : {0}", dt.ToString("G"))  ' 一般的な日付と日時の形式 (書式指定子を指定しなかった場合のデフォルト)
    Console.WriteLine()

    Dim dto As DateTimeOffset = DateTimeOffset.Now

    Console.WriteLine("D : {0}", dto.ToString("D"))  ' 長い日付の形式
    Console.WriteLine("d : {0}", dto.ToString("d"))  ' 短い日付の形式
    Console.WriteLine("T : {0}", dto.ToString("T"))  ' 長い時刻の形式
    Console.WriteLine("t : {0}", dto.ToString("t"))  ' 短い時刻の形式
    Console.WriteLine("F : {0}", dto.ToString("F"))  ' 完全な日付と日時の形式 (長い形式)
    Console.WriteLine("u : {0}", dto.ToString("u"))  ' UTCでの完全な日付と日時の形式 (短い形式)
    Console.WriteLine("G : {0}", dto.ToString("G"))  ' 一般的な日付と日時の形式 (書式指定子を指定しなかった場合のデフォルト)
    Console.WriteLine()
  End Sub
End Class
ja-JPでの実行結果例
D : 2013年4月1日
d : 2013/04/01
T : 15:00:30
t : 15:00
F : 2013年4月1日 15:00:30
u : 2013-04-01 15:00:30Z
G : 2013/04/01 15:00:30

D : 2013年4月1日
d : 2013/04/01
T : 15:00:30
t : 15:00
F : 2013年4月1日 15:00:30
u : 2013-04-01 06:00:30Z
G : 2013/04/01 15:00:30

en-USでの実行結果例
D : Monday, April 01, 2013
d : 4/1/2013
T : 3:00:30 PM
t : 3:00 PM
F : Monday, April 01, 2013 3:00:30 PM
u : 2013-04-01 15:00:30Z
G : 4/1/2013 3:00:30 PM

D : Monday, April 01, 2013
d : 4/1/2013
T : 3:00:30 PM
t : 3:00 PM
F : Monday, April 01, 2013 3:00:30 PM
u : 2013-04-01 06:00:30Z
G : 4/1/2013 3:00:30 PM


一方、ローカライズされる書式とは異なり、ローカライズされない書式の書式指定子も用意されています。 これらの書式では、実行環境によらず常に同じ結果が得られます。

標準の書式指定子を使ってDateTime/DateTimeOffsetを文字列に変換する(ローカライズされない書式の例)
using System;

class Sample {
  static void Main()
  {
    var dt = DateTime.Now;

    Console.WriteLine("r : {0}", dt.ToString("r"));  // RFC1123形式
    Console.WriteLine("o : {0}", dt.ToString("o"));  // ISO8601(W3C-DTF)形式
    Console.WriteLine("s : {0}", dt.ToString("s"));  // ISO8601形式に似た、ソートに適した形式
    Console.WriteLine("u : {0}", dt.ToString("u"));  // ISO8601形式に似た、ソートに適した形式 (UTC)
    Console.WriteLine();

    var dto = DateTimeOffset.Now;

    Console.WriteLine("r : {0}", dto.ToString("r"));  // RFC1123形式
    Console.WriteLine("o : {0}", dto.ToString("o"));  // ISO8601(W3C-DTF)形式
    Console.WriteLine("s : {0}", dto.ToString("s"));  // ISO8601形式に似た、ソートに適した形式
    Console.WriteLine("u : {0}", dto.ToString("u"));  // ISO8601形式に似た、ソートに適した形式 (UTC)
    Console.WriteLine();
  }
}
標準の書式指定子を使ってDateTime/DateTimeOffsetを文字列に変換する(ローカライズされない書式の例)
Imports System

Class Sample
  Shared Sub Main()
    Dim dt As DateTime = DateTime.Now

    Console.WriteLine("r : {0}", dt.ToString("r"))  ' RFC1123形式
    Console.WriteLine("o : {0}", dt.ToString("o"))  ' ISO8601(W3C-DTF)形式
    Console.WriteLine("s : {0}", dt.ToString("s"))  ' ISO8601形式に似た、ソートに適した形式
    Console.WriteLine("u : {0}", dt.ToString("u"))  ' ISO8601形式に似た、ソートに適した形式 (UTC)
    Console.WriteLine()

    Dim dto As DateTimeOffset = DateTimeOffset.Now

    Console.WriteLine("r : {0}", dto.ToString("r"))  ' RFC1123形式
    Console.WriteLine("o : {0}", dto.ToString("o"))  ' ISO8601(W3C-DTF)形式
    Console.WriteLine("s : {0}", dto.ToString("s"))  ' ISO8601形式に似た、ソートに適した形式
    Console.WriteLine("u : {0}", dto.ToString("u"))  ' ISO8601形式に似た、ソートに適した形式 (UTC)
    Console.WriteLine()
  End Sub
End Class
実行結果例
r : Mon, 01 Apr 2013 15:00:30 GMT
o : 2013-04-01T15:00:30.1230000+09:00
s : 2013-04-01T15:00:30
u : 2013-04-01 15:00:30Z

r : Mon, 01 Apr 2013 06:00:30 GMT
o : 2013-04-01T15:00:30.1230000+09:00
s : 2013-04-01T15:00:30
u : 2013-04-01 06:00:30Z

このように標準の書式にはいくつかの種類があり、ローカライズされるものとそうでないものが存在します。 さらに、書式によってはUTCへの変換が行われた上で文字列化されるものもあります。 各書式のより詳細な解説については書式指定子 §.日付と時刻の書式指定子を参照してください。 また、書式とローカライズに関しては別途解説します。


なお、DateTimeにはToString以外にも文字列化を行う次のようなメソッドが用意されています。 いずれも、標準の書式指定子を指定してToStringメソッドを呼び出した場合と同じです。

DateTimeの文字列化を行うメソッド
メソッド 得られる文字列 等価な書式指定子
ToLongDateString 長い日付の形式 D
ToLongTimeString 長い時刻の形式 T
ToShortDateString 短い日付の形式 d
ToShortTimeString 短い時刻の形式 t

これらのメソッドで使用される書式はローカライズされる書式であるため、得られる文字列は実行環境の言語設定によって異なります。

定義済みの書式でDateTimeを文字列に変換する
using System;

class Sample {
  static void Main()
  {
    var now = DateTime.Now;

    Console.WriteLine("{0} {1}", now.ToString("D"), now.ToLongDateString());  // 長い日付の形式
    Console.WriteLine("{0} {1}", now.ToString("T"), now.ToLongTimeString());  // 長い時刻の形式
    Console.WriteLine("{0} {1}", now.ToString("d"), now.ToShortDateString()); // 短い日付の形式
    Console.WriteLine("{0} {1}", now.ToString("t"), now.ToShortTimeString()); // 短い時刻の形式
  }
}
定義済みの書式でDateTimeを文字列に変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim dt As DateTime = DateTime.Now

    Console.WriteLine("{0} {1}", now.ToString("D"), now.ToLongDateString())  ' 長い日付の形式
    Console.WriteLine("{0} {1}", now.ToString("T"), now.ToLongTimeString())  ' 長い時刻の形式
    Console.WriteLine("{0} {1}", now.ToString("d"), now.ToShortDateString()) ' 短い日付の形式
    Console.WriteLine("{0} {1}", now.ToString("t"), now.ToShortTimeString()) ' 短い時刻の形式
  End Sub
End Class
ja-JPでの実行結果例
2013年4月1日 2013年4月1日
15:00:30 15:00:30
2013/04/01 2013/04/01
15:00 15:00

DateTimeOffsetにはこういったメソッドは用意されていないため、ToStringメソッドと書式指定子を組み合わせて文字列化する必要があります。

カスタム書式

標準の書式の他にも、独自に書式を定義して文字列化することもできます。 例えば、年月だけ出力したい、西暦の桁数を指定したい、時刻を12時間制にしたい、ミリ秒部分の精度を指定して文字列化したい、といったように、標準の書式では定義されていない書式で文字列化したい場合にはカスタム書式指定子を使用することができます。

次の例では、いくつかのカスタム書式指定子を使ってDateTime・DateTimeOffsetを文字列化しています。

カスタム書式指定子を使ってDateTime/DateTimeOffsetを文字列に変換する
using System;

class Sample {
  static void Main()
  {
    var dt = DateTime.Now;

    Console.WriteLine(dt.ToString());
    Console.WriteLine("M/d/yy            : {0}", dt.ToString("M/d/yy"));          // 月/日/年2桁
    Console.WriteLine("yyyy/MM/dd        : {0}", dt.ToString("yyyy/MM/dd"));      // 年4桁/月(0埋めあり)/日(0埋めあり)
    Console.WriteLine("M月d日 dddd       : {0}", dt.ToString("M月d日 dddd"));     // x月y日 曜日
    Console.WriteLine("tt hh時mm分ss秒   : {0}", dt.ToString("tt hh時mm分ss秒"));  // [午前|午後] x時y分z秒 (0埋めあり、12時間制)
    Console.WriteLine("H:m:s.ff          : {0}", dt.ToString("H:m:s.ff"));        // 時:分:秒.秒の端数 (0埋めなし、24時間制、1/10秒単位で表示)
    Console.WriteLine();

    var dto = DateTimeOffset.Now;

    Console.WriteLine(dto.ToString());
    Console.WriteLine("tt hh時mm分ss秒   : {0}", dto.ToString("tt hh時mm分ss秒"));  // [午前|午後] x時y分z秒 (0埋めあり、12時間制)
    Console.WriteLine("H:m:s.ff          : {0}", dto.ToString("H:m:s.ff"));        // 時:分:秒.秒の端数 (0埋めなし、24時間制、1/10秒単位で表示)
    Console.WriteLine("HH:mm:ss (zzz)    : {0}", dto.ToString("HH:mm:ss (zzz)"));  // 時:分:秒 (オフセット値) (0埋めあり、24時間制)
    Console.WriteLine();
  }
}
カスタム書式指定子を使ってDateTime/DateTimeOffsetを文字列に変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim dt As DateTime = DateTime.Now

    Console.WriteLine(dt.ToString())
    Console.WriteLine("M/d/yy            : {0}", dt.ToString("M/d/yy"))          ' 月/日/年2桁
    Console.WriteLine("yyyy/MM/dd        : {0}", dt.ToString("yyyy/MM/dd"))      ' 年4桁/月(0埋めあり)/日(0埋めあり)
    Console.WriteLine("M月d日 dddd       : {0}", dt.ToString("M月d日 dddd"))     ' x月y日 曜日
    Console.WriteLine("tt hh時mm分ss秒   : {0}", dt.ToString("tt hh時mm分ss秒")) ' [午前|午後] x時y分z秒 (0埋めあり、12時間制)
    Console.WriteLine("H:m:s.ff          : {0}", dt.ToString("H:m:s.ff"))        ' 時:分:秒.秒の端数 (0埋めなし、24時間制、1/10秒単位で表示)
    Console.WriteLine()

    Dim dto As DateTimeOffset = DateTimeOffset.Now

    Console.WriteLine(dto.ToString())
    Console.WriteLine("tt hh時mm分ss秒   : {0}", dto.ToString("tt hh時mm分ss秒")) ' [午前|午後] x時y分z秒 (0埋めあり、12時間制)
    Console.WriteLine("H:m:s.ff          : {0}", dto.ToString("H:m:s.ff"))        ' 時:分:秒.秒の端数 (0埋めなし、24時間制、1/10秒単位で表示)
    Console.WriteLine("HH:mm:ss (zzz)    : {0}", dto.ToString("HH:mm:ss (zzz)"))  ' 時:分:秒 (オフセット値) (0埋めあり、24時間制)
    Console.WriteLine()
  End Sub
End Class
ja-JPでの実行結果例
2013/04/01 15:00:30
M/d/yy            : 4/1/13
yyyy/MM/dd        : 2013/04/01
M月d日 dddd       : 4月1日 月曜日
tt hh時mm分ss秒   : 午後 03時00分30秒
H:m:s.ff          : 15:0:30.12

2013/04/01 15:00:30 +09:00
tt hh時mm分ss秒   : 午後 03時00分30秒
H:m:s.ff          : 15:0:30.12
HH:mm:ss (zzz)    : 15:00:30 (+09:00)
en-USでの実行結果例
4/1/2013 3:00:30 PM
M/d/yy            : 4/1/13
yyyy/MM/dd        : 2013/04/01
M月d日 dddd       : 4月1日 Monday
tt hh時mm分ss秒   : PM 03時00分30秒
H:m:s.ff          : 15:0:30.12

4/1/2013 3:00:30 PM +09:00
tt hh時mm分ss秒   : PM 03時00分30秒
H:m:s.ff          : 15:0:30.12
HH:mm:ss (zzz)    : 15:00:30 (+09:00)

上記の実行結果からも分かるように、カスタム書式指定子にもローカライズされるものとそうでないものが存在するため、実行環境に設定されているカルチャ(ロケール)によっては結果が異なる個所があります。 日付・時刻の区切り記号もエスケープしない限りローカライズの対象となる点に注意が必要です。 上記以外のカスタム書式指定子やその詳細についてはカスタムの日付と時刻の書式指定文字列にまとめられています。


1文字だけの書式指定子は標準の書式指定子として扱われる点に注意が必要です。 例えば、24時間制で0埋めなしの時間部分を表すカスタム書式指定子Hを単独で指定した場合、これは標準の書式指定子として扱われます。 一方、標準の書式指定子Hは存在しないため、例外FormatExceptionがスローされます。

カスタム書式指定子を1文字だけ指定する場合は、先頭に%を付けることによって、それがカスタム書式指定子であることを明示する必要があります。

書式指定文字列において、単独の文字がカスタム書式指定子として扱われるようにする
using System;

class Sample {
  static void Main()
  {
    var dt = DateTime.Now;

    Console.WriteLine(dt.ToString());
    Console.WriteLine("%H : {0}", dt.ToString("%H")); // 時
    Console.WriteLine("%m : {0}", dt.ToString("%m")); // 分
    Console.WriteLine("%s : {0}", dt.ToString("%s")); // 秒
    Console.WriteLine("%d : {0}", dt.ToString("%d")); // 日
    Console.WriteLine("d  : {0}", dt.ToString("d"));  // 短い形式の日付 (標準の書式指定子)
    Console.WriteLine();

    var dto = DateTimeOffset.Now;

    Console.WriteLine(dto);
    Console.WriteLine("%H : {0}", dto.ToString("%H")); // 時
    Console.WriteLine("%m : {0}", dto.ToString("%m")); // 分
    Console.WriteLine("%s : {0}", dto.ToString("%s")); // 秒
    Console.WriteLine("%d : {0}", dto.ToString("%d")); // 日
    Console.WriteLine("d  : {0}", dto.ToString("d"));  // 短い形式の日付 (標準の書式指定子)
  }
}
書式指定文字列において、単独の文字がカスタム書式指定子として扱われるようにする
Imports System

Class Sample
  Shared Sub Main()
    Dim dt As DateTime = DateTime.Now

    Console.WriteLine(dt.ToString())
    Console.WriteLine("%H : {0}", dt.ToString("%H")) ' 時
    Console.WriteLine("%m : {0}", dt.ToString("%m")) ' 分
    Console.WriteLine("%s : {0}", dt.ToString("%s")) ' 秒
    Console.WriteLine("%d : {0}", dt.ToString("%d")) ' 日
    Console.WriteLine("d  : {0}", dt.ToString("d"))  ' 短い形式の日付 (標準の書式指定子)
    Console.WriteLine()

    Dim dto As DateTimeOffset = DateTimeOffset.Now

    Console.WriteLine(dto)
    Console.WriteLine("%H : {0}", dto.ToString("%H")) ' 時
    Console.WriteLine("%m : {0}", dto.ToString("%m")) ' 分
    Console.WriteLine("%s : {0}", dto.ToString("%s")) ' 秒
    Console.WriteLine("%d : {0}", dto.ToString("%d")) ' 日
    Console.WriteLine("d  : {0}", dto.ToString("d"))  ' 短い形式の日付 (標準の書式指定子)
  End Sub
End Class
ja-JPでの実行結果例
2013/04/01 15:00:30
%H : 15
%m : 0
%s : 30
%d : 1
d  : 2013/04/01

2013/04/01 15:00:30 +09:00
%H : 15
%m : 0
%s : 30
%d : 1
d  : 2013/04/01

複合書式

Console.WriteLineString.FormatStreamWriter.WriteLineなどのメソッドでは複合書式設定を用いることができます。 複合書式設定でも、ToStringメソッドの場合と同様にDateTime/DateTimeOffsetの書式を指定することができます。 また書式だけでなく、右詰め・左詰めといった内容も同時に指定することが可能になっています。

Console.WriteLineなどのメソッドで複合書式を使ってDateTime/DateTimeOffsetを文字列に変換する
using System;

class Sample {
  static void Main()
  {
    var dt = DateTime.Now;

    Console.WriteLine(dt);
    Console.WriteLine("|{0,15:d}|", dt);  // 短い日付の形式・幅15で右揃え
    Console.WriteLine("|{0,-15:d}|", dt); // 短い日付の形式・幅15で左揃え
    Console.WriteLine("{0:%y}-{0:%M}-{0:%d}", dt); // 年-月-日 (0埋めなし)
    Console.WriteLine();

    var dto = DateTimeOffset.Now;

    Console.WriteLine(dto);
    Console.WriteLine("|{0,15:d}|", dto);   // 短い日付の形式・幅15で右揃え
    Console.WriteLine("|{0,-15:d}|", dto);  // 短い日付の形式・幅15で左揃え
    Console.WriteLine("{0:%y}-{0:%M}-{0:%d}", dto); // 年-月-日 (0埋めなし)
  }
}
Console.WriteLineなどのメソッドで複合書式を使ってDateTime/DateTimeOffsetを文字列に変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim dt As DateTime = DateTime.Now

    Console.WriteLine(dt)
    Console.WriteLine("|{0,15:d}|", dt)  ' 短い日付の形式・幅15で右揃え
    Console.WriteLine("|{0,-15:d}|", dt) ' 短い日付の形式・幅15で左揃え
    Console.WriteLine("{0:%y}-{0:%M}-{0:%d}", dt) ' 年-月-日 (0埋めなし)
    Console.WriteLine()

    Dim dto As DateTimeOffset = DateTimeOffset.Now

    Console.WriteLine(dto)
    Console.WriteLine("|{0,15:d}|", dto)   ' 短い日付の形式・幅15で右揃え
    Console.WriteLine("|{0,-15:d}|", dto)  ' 短い日付の形式・幅15で左揃え
    Console.WriteLine("{0:%y}-{0:%M}-{0:%d}", dto) ' 年-月-日 (0埋めなし)
  End Sub
End Class
ja-JPでの実行結果例
2013/04/01 15:00:30
|     2013/04/01|
|2013/04/01     |
13-4-1

2013/04/01 15:00:30 +09:00
|     2013/04/01|
|2013/04/01     |
13-4-1

上記の例で使用している書式のうち%で始まるものは、複合書式設定特有の記述ではなく、カスタム書式指定子であること(標準の書式指定子として扱われないようにすること)を明示するためのものです。

複合書式設定についてより詳しくは書式指定子 §.複合書式設定と0埋め・右揃え・左揃えを参照してください。

書式とローカライズ

ToStringメソッドでは、書式に加えて引数に書式プロバイダと呼ばれるものを指定できます。 書式プロバイダとは、書式指定子からそれにに対応する形式の文字列に変換するためのものです。 その一例としてCultureInfoクラスは書式プロバイダとして機能します。 このため、CultureInfoをToStringメソッドの引数に指定することにより、特定カルチャでの形式に従ってDateTime・DateTimeOffsetを文字列化することができます。

次の例では、書式指定子として一般的な日付と日時の形式Gを指定し、書式プロバイダにはen-US(英語/アメリカ合衆国)、de-DE(ドイツ語/ドイツ)、ja-JP(日本語/日本)をそれぞれ指定し、その結果の違いを表示しています。

CultureInfoを書式プロバイダとして指定してDateTimeを文字列に変換する
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    // 書式プロバイダとして使用するカルチャ
    var enus = new CultureInfo("en-US");
    var dede = new CultureInfo("de-DE");
    var jajp = new CultureInfo("ja-JP");

    var dt = DateTime.Now;

    Console.WriteLine(dt.ToString("G", enus));  // en-USでの一般的な日付と日時の形式で文字列化
    Console.WriteLine(dt.ToString("G", dede));  // de-DEでの一般的な日付と日時の形式で文字列化
    Console.WriteLine(dt.ToString("G", jajp));  // ja-JPでの一般的な日付と日時の形式で文字列化
    Console.WriteLine(dt.ToString("G"));        // デフォルトの一般的な日付と日時の形式で文字列化
  }
}
CultureInfoを書式プロバイダとして指定してDateTimeを文字列に変換する
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    ' 書式プロバイダとして使用するカルチャ
    Dim enus As New CultureInfo("en-US")
    Dim dede As New CultureInfo("de-DE")
    Dim jajp As New CultureInfo("ja-JP")

    Dim dt As DateTime = DateTime.Now

    Console.WriteLine(dt.ToString("G", enus))  ' en-USでの一般的な日付と日時の形式で文字列化
    Console.WriteLine(dt.ToString("G", dede))  ' de-DEでの一般的な日付と日時の形式で文字列化
    Console.WriteLine(dt.ToString("G", jajp))  ' ja-JPでの一般的な日付と日時の形式で文字列化
    Console.WriteLine(dt.ToString("G"))        ' デフォルトの一般的な日付と日時の形式で文字列化
  End Sub
End Class
ja-JPでの実行結果例
4/1/2013 3:00:30 PM
01.04.2013 15:00:30
2013/04/01 15:00:30
2013/04/01 15:00:30

このように、カルチャによって「一般的な日付と日時の形式」として定義されている書式には違いがあるため、同じ書式指定子でもカルチャによって異なる文字列が返されます。 書式プロバイダを指定しなかった場合(省略した場合およびnull/Nothingを指定した場合)は、現在のスレッドに設定されているカルチャが書式プロバイダとして使用されます。

また、既に解説したとおり、書式にはローカライズされるものとそうでないものが存在します。 上記の例で使用した一般的な日付と日時の形式Gはローカライズされる書式ですが、ISO8601(W3C-DTF)形式oやRFC1123形式rなどはローカライズされない書式であるため、異なるカルチャを書式プロバイダとして指定しても得られる結果は常に同じとなります。

書式と書式プロバイダについては文字列と書式、カルチャと書式についてカルチャの基本・種類・カルチャ情報の取得およびカルチャと書式・テキスト処理・暦でより詳しく解説しているので、合わせてご覧ください。


月名や曜日名を英語などその他の言語での表記で取得する目的にも書式プロバイダを使うことができます。 次の例では、書式指定子として曜日の完全名dddd、月の完全名MMMM、書式プロバイダとしてen-US(英語/アメリカ合衆国)、de-DE(ドイツ語/ドイツ)、ja-JP(日本語/日本)を指定することで、英語・ドイツ語・日本語での曜日名・月名を取得・表示しています。

CultureInfoを書式プロバイダとして指定して各言語での曜日名・月名を取得する
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    // 書式プロバイダとして使用するカルチャ
    var enus = new CultureInfo("en-US");
    var dede = new CultureInfo("de-DE");
    var jajp = new CultureInfo("ja-JP");

    // カルチャ毎に曜日の完全名'dddd'で文字列化した結果を取得する
    Console.WriteLine("{0,-20}|{1,-20}|{2}", enus.Name, dede.Name, jajp.Name);
    Console.WriteLine(new string('-', 60));

    for (var day = 0; day < 7; day++) {
      var dt = (new DateTime(2013, 3, 31)).AddDays(day); // 日曜日から土曜日のDateTimeを作成

      Console.WriteLine("{0,-20}|{1,-20}|{2}",
                        dt.ToString("dddd", enus),
                        dt.ToString("dddd", dede),
                        dt.ToString("dddd", jajp));
    }

    // カルチャ毎に月の完全名'MMMM'で文字列化した結果を取得する
    Console.WriteLine(new string('-', 60));

    for (var month = 1; month <= 12; month++) {
      var dt = new DateTime(2013, month, 1); // 1月から12月のDateTimeを作成

      Console.WriteLine("{0,-20}|{1,-20}|{2}",
                        dt.ToString("MMMM", enus),
                        dt.ToString("MMMM", dede),
                        dt.ToString("MMMM", jajp));
    }
  }
}
CultureInfoを書式プロバイダとして指定して各言語での曜日名・月名を取得する
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    ' 書式プロバイダとして使用するカルチャ
    Dim enus As New CultureInfo("en-US")
    Dim dede As New CultureInfo("de-DE")
    Dim jajp As New CultureInfo("ja-JP")

    ' カルチャ毎に曜日の完全名'dddd'で文字列化した結果を取得する
    Console.WriteLine("{0,-20}|{1,-20}|{2}", enus.Name, dede.Name, jajp.Name)
    Console.WriteLine(New String("-"c, 60))

    For day As Integer = 0 To 6
      Dim dt As DateTime = (New DateTime(2013, 3, 31)).AddDays(day) ' 日曜日から土曜日のDateTimeを作成

      Console.WriteLine("{0,-20}|{1,-20}|{2}", _
                        dt.ToString("dddd", enus), _
                        dt.ToString("dddd", dede), _
                        dt.ToString("dddd", jajp))
    Next

    ' カルチャ毎に月の完全名'MMMM'で文字列化した結果を取得する
    Console.WriteLine(New String("-"c, 60))

    For month As Integer = 1 To 12
      Dim dt As New DateTime(2013, month, 1) ' 1月から12月のDateTimeを作成

      Console.WriteLine("{0,-20}|{1,-20}|{2}", _
                        dt.ToString("MMMM", enus), _
                        dt.ToString("MMMM", dede), _
                        dt.ToString("MMMM", jajp))
    Next
  End Sub
End Class
実行結果
en-US               |de-DE               |ja-JP
------------------------------------------------------------
Sunday              |Sonntag             |日曜日
Monday              |Montag              |月曜日
Tuesday             |Dienstag            |火曜日
Wednesday           |Mittwoch            |水曜日
Thursday            |Donnerstag          |木曜日
Friday              |Freitag             |金曜日
Saturday            |Samstag             |土曜日
------------------------------------------------------------
January             |Januar              |1月
February            |Februar             |2月
March               |März                |3月
April               |April               |4月
May                 |Mai                 |5月
June                |Juni                |6月
July                |Juli                |7月
August              |August              |8月
September           |September           |9月
October             |Oktober             |10月
November            |November            |11月
December            |Dezember            |12月

インバリアントカルチャ

カルチャにはインバリアントカルチャと呼ばれる特殊なカルチャが用意されています。 インバリアントカルチャでは、特定の言語や国・地域に依存しない書式が定義されています。 インバリアントカルチャを使用すると、ローカライズされる書式(例えば短い日付の形式d)を使って文字列化する場合でも、実行環境のカルチャの影響を受けず常に同じ結果が得られます。 インバリアントカルチャには、英語圏で使われるものを基本とする書式が用意されています。

インバリアントカルチャを指定してローカライズされる書式でDateTimeを文字列化する
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    var dt = DateTime.Now;

    // 短い日付の形式で文字列化
    Console.WriteLine(dt.ToString("d")); // 現在のカルチャ
    Console.WriteLine(dt.ToString("d", CultureInfo.InvariantCulture)); // インバリアントカルチャ
  }
}
インバリアントカルチャを指定してローカライズされる書式でDateTimeを文字列化する
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    Dim dt As DateTime = DateTime.Now

    ' 短い日付の形式で文字列化
    Console.WriteLine(dt.ToString("d")) ' 現在のカルチャ
    Console.WriteLine(dt.ToString("d", CultureInfo.InvariantCulture)) ' インバリアントカルチャ
  End Sub
End Class
ja-JPでの実行結果例
2013/04/01
04/01/2013

インバリアントカルチャが特に有用となるのは、異なるカルチャ間で日時を扱う場合です。 例として、文字列形式で日時のデータをやり取りすることを考えます。 ja-JPの環境で "2013年4月1日" の日付を短い日付の形式dで文字列化すると "2013/04/01" (年月日の順)となります。 これを仮にファイルに記録したとして、ファイルから読み込んだ文字列 "2013/04/01" をParseメソッドで復元すると、同じja-JPの環境なら2013年4月1日という日付が復元されます。 一方、fr-FRの環境では "2013/01/04" という文字列をParseメソッドで復元しようとすると "2013日1月4年" と解釈され、異なる日付として復元されてしまいます。

次の例は、そのような状況を再現するものです。 この例では、Thread.CurrentThread.CurrentCultureに設定されているカルチャを変更することにより、異なるカルチャとなる環境で実行した場合の状況を再現しています。

文字列化した日時を異なるカルチャ間で扱おうとして失敗する例
using System;
using System.Globalization;
using System.Threading;

class Sample {
  static void Main()
  {
    var now = DateTime.Now;

    // ja-JPの環境において、短い日付の形式で文字列化したとする
    Thread.CurrentThread.CurrentCulture = new CultureInfo("ja-JP");

    var str = now.ToString("d");

    Console.WriteLine(str);

    // fr-FRの環境において、文字列で表された日付を復元しようとする
    Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

    // 解析に失敗するため、ここでFormatExceptionがスローされる
    Console.WriteLine(DateTime.ParseExact(str, "d", null));
    // ハンドルされていない例外: System.FormatException: 文字列は有効な DateTime ではありませんでした。
    //    場所 System.DateTimeParse.ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style)
    //    場所 Sample.Main()
  }
}
文字列化した日時を異なるカルチャ間で扱おうとして失敗する例
Imports System
Imports System.Globalization
Imports System.Threading

Class Sample
  Shared Sub Main()
    Dim now As DateTime = DateTime.Now

    ' ja-JPの環境において、短い日付の形式で文字列化したとする
    Thread.CurrentThread.CurrentCulture = New CultureInfo("ja-JP")

    Dim str As String = now.ToString("d")

    Console.WriteLine(str)

    ' fr-FRの環境において、文字列で表された日付を復元しようとする
    Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-FR")

    ' 解析に失敗するため、ここでFormatExceptionがスローされる
    Console.WriteLine(DateTime.ParseExact(str, "d", Nothing))
    ' ハンドルされていない例外: System.FormatException: 文字列は有効な DateTime ではありませんでした。
    '    場所 System.DateTimeParse.ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style)
    '    場所 Sample.Main()
  End Sub
End Class

こういった問題を避けるためには、変換時と復元時でカルチャが異なる場合でも同じ形式となる書式を使う必要があります。 この例のようにローカライズされる書式で文字列化しなければならない場合でも、インバリアントカルチャを使えばカルチャによらず同じ形式とすることができます。 カルチャによって異なる動作となる場合や、インバリアントカルチャを使用したカルチャ依存の動作の回避についてはカルチャによる動作の違いとインバリアントカルチャを参照してください。

ほかにも、こういった場合にはローカライズされない書式を使うこともできます。 ISO8601(W3C-DTF)形式oやRFC1123形式rなどはローカライズされない書式であり、また言語や処理系にも依存しないフォーマットであるため、異なる実行環境や処理系との日時のやりとりに向いています。 これ以外の定義済みの書式については書式指定子 §.日付と時刻の書式指定子を参照してください。

文字列からの変換

日時には様々な表記が存在するため、文字列からDateTime・DateTimeOffsetへの変換はその逆よりも考慮すべき事柄が多く、すべての表記に対応しようとすると複雑になります。 まずはシンプルな例として、Parseメソッドを使った例を見ていきます。

DateTime/DateTimeOffset.Parseメソッドで文字列を日時に変換する
using System;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "Mon, 01 Apr 2013 15:00:30 GMT",     // RFC1123形式の文字列
      "2013-04-01T15:00:30.1230000+09:00", // ISO8601形式の文字列
      "2013年4月1日 15:00:30",             // 一般的な形式の文字列
    };

    // 文字列をDateTimeに変換
    foreach (var input in inputs) {
      Console.WriteLine("{0,-35} -> {1}", input, DateTime.Parse(input));
    }
    Console.WriteLine();

    // 文字列をDateTimeOffsetに変換
    foreach (var input in inputs) {
      Console.WriteLine("{0,-35} -> {1}", input, DateTimeOffset.Parse(input));
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.Parseメソッドで文字列を日時に変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "Mon, 01 Apr 2013 15:00:30 GMT", _
      "2013-04-01T15:00:30.1230000+09:00", _
      "2013年4月1日 15:00:30" _
    }

    ' 文字列をDateTimeに変換
    For Each input As String In inputs
      Console.WriteLine("{0,-35} -> {1}", input, DateTime.Parse(input))
    Next
    Console.WriteLine()

    ' 文字列をDateTimeOffsetに変換
    For Each input As String In inputs
      Console.WriteLine("{0,-35} -> {1}", input, DateTimeOffset.Parse(input))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
Mon, 01 Apr 2013 15:00:30 GMT       -> 2013/04/02 0:00:30
2013-04-01T15:00:30.1230000+09:00   -> 2013/04/01 15:00:30
2013年4月1日 15:00:30                  -> 2013/04/01 15:00:30

Mon, 01 Apr 2013 15:00:30 GMT       -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1230000+09:00   -> 2013/04/01 15:00:30 +09:00
2013年4月1日 15:00:30                  -> 2013/04/01 15:00:30 +09:00

DateTime.ParseおよびDateTimeOffset.Parseメソッドは、引数で与えられた文字列を解析し、DateTime・DateTimeOffsetに変換た結果を返します。 Parseメソッドは、標準の書式としてサポートされている形式での解析を試み、解析できた場合はその結果を返します。

Parseメソッドに指定された文字列が日時として解析できない場合や、日時として不正な形式・値の場合は、例外FormatExceptionがスローされます。

DateTime/DateTimeOffset.Parseメソッドで不正な日時の形式・値の文字列を変換する
using System;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "29 Feb 2012",
      "1/2/3",
      "31 Feb 2013",                        // 存在しない日付
      "1 Apr 10000",                        // 年数が最小(0)〜最大(9999)の範囲外
      "27:00:00",                           // 時刻が最小(0:0:0)〜最大(23:59:59)の範囲外
      "2013-04-01T15:00:30.1230000+15:00",  // オフセットが最小・最大(±14:00)の範囲外
      "+09:00",                             // オフセットのみの時刻
      "0123456",                            // 日時として判別できない文字列
    };

    // 文字列をDateTimeに変換
    foreach (var input in inputs) {
      try {
        Console.WriteLine("{0,-35} -> {1}", input, DateTime.Parse(input));
      }
      catch (FormatException) {
        // Parseメソッドで変換できない場合は、FormatExceptionがスローされる
        Console.WriteLine("{0,-35} -> FormatException", input);
      }
    }
    Console.WriteLine();

    // 文字列をDateTimeOffsetに変換
    foreach (var input in inputs) {
      try {
        Console.WriteLine("{0,-35} -> {1}", input, DateTimeOffset.Parse(input));
      }
      catch (FormatException) {
        Console.WriteLine("{0,-35} -> FormatException", input);
      }
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.Parseメソッドで不正な日時の形式・値の文字列を変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "29 Feb 2012", _
      "1/2/3", _
      "31 Feb 2013", _
      "1 Apr 10000", _
      "27:00:00", _
      "2013-04-01T15:00:30.1230000+15:00", _
      "+09:00", _
      "0123456" _
    }

    ' 文字列をDateTimeに変換
    For Each input As String In inputs
      Try
        Console.WriteLine("{0,-35} -> {1}", input, DateTime.Parse(input))
      Catch ex As FormatException
        ' Parseメソッドで変換できない場合は、FormatExceptionがスローされる
        Console.WriteLine("{0,-35} -> FormatException", input)
      End Try
    Next
    Console.WriteLine()

    ' 文字列をDateTimeOffsetに変換
    For Each input As String In inputs
      Try
        Console.WriteLine("{0,-35} -> {1}", input, DateTimeOffset.Parse(input))
      Catch ex As FormatException
        Console.WriteLine("{0,-35} -> FormatException", input)
      End Try
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
29 Feb 2012                         -> 2012/02/29 0:00:00
1/2/3                               -> 2001/02/03 0:00:00
31 Feb 2013                         -> FormatException
1 Apr 10000                         -> FormatException
27:00:00                            -> FormatException
2013-04-01T15:00:30.1230000+15:00   -> 2013/04/02 0:00:30
+09:00                              -> FormatException
0123456                             -> FormatException

29 Feb 2012                         -> 2012/02/29 0:00:00 +09:00
1/2/3                               -> 2001/02/03 0:00:00 +09:00
31 Feb 2013                         -> FormatException
1 Apr 10000                         -> FormatException
27:00:00                            -> FormatException
2013-04-01T15:00:30.1230000+15:00   -> FormatException
+09:00                              -> FormatException
0123456                             -> FormatException

Parseメソッドで変換できない場合にFormatExceptionがスローされることを望まない場合や、変換できた場合・できなかった場合で処理を分岐させたいような場合は、Parseメソッドの代わりにTryParseメソッドを使うこともできます。

不完全な日時の変換

Parseメソッドは、日付のみ・時刻のみといった不完全な日時を表す文字列も解析出来ます。 この際、時刻部分に欠損があれば0時0分0秒、日付部分に欠損があれば現在の日付を表すものとして扱われます。 DateTimeOffset.Parseメソッドの場合、オフセット値に欠損がある場合はローカル時刻のオフセット値が設定されます。

DateTime/DateTimeOffset.Parseメソッドで不完全な日時の文字列を変換する
using System;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "Mon, 01 Apr 2013",   // RFC1123形式・日付のみ
      "15:00:30",           // RFC1123形式・時刻のみ
      "2013-04",            // ISO8601形式・年月のみ
      "15:00:30.12-05:00",  // ISO8601形式・時刻のみ
      "15:00",              // ISO8601形式・時分のみ
      "2013年4月1日",       // 一般的な形式・日付のみ
    };

    // 文字列をDateTimeに変換
    foreach (var input in inputs) {
      Console.WriteLine("{0,-20} -> {1}", input, DateTime.Parse(input));
    }
    Console.WriteLine();

    // 文字列をDateTimeOffsetに変換
    foreach (var input in inputs) {
      Console.WriteLine("{0,-20} -> {1}", input, DateTimeOffset.Parse(input));
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.Parseメソッドで不完全な日時の文字列を変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "Mon, 01 Apr 2013", _
      "15:00:30", _
      "2013-04", _
      "15:00:30.12-05:00", _
      "15:00", _
      "2013年4月1日" _
    }

    ' 文字列をDateTimeに変換
    For Each input As String In inputs
      Console.WriteLine("{0,-20} -> {1}", input, DateTime.Parse(input))
    Next
    Console.WriteLine()

    ' 文字列をDateTimeOffsetに変換
    For Each input As String In inputs
      Console.WriteLine("{0,-20} -> {1}", input, DateTimeOffset.Parse(input))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
Mon, 01 Apr 2013     -> 2013/04/01 0:00:00
15:00:30             -> 2013/02/03 15:00:30
2013-04              -> 2013/04/01 0:00:00
15:00:30.12-05:00    -> 2013/02/04 5:00:30
15:00                -> 2013/02/03 15:00:00
2013年4月1日            -> 2013/04/01 0:00:00

Mon, 01 Apr 2013     -> 2013/04/01 0:00:00 +09:00
15:00:30             -> 2013/02/03 15:00:30 +09:00
2013-04              -> 2013/04/01 0:00:00 +09:00
15:00:30.12-05:00    -> 2013/02/03 15:00:30 -05:00
15:00                -> 2013/02/03 15:00:00 +09:00
2013年4月1日            -> 2013/04/01 0:00:00 +09:00

変換の試行 (TryParse)

文字列からDateTime・DateTimeOffsetへの変換には、Parseメソッドの他にもTryParseメソッドを使うことができます。 このメソッドでは変換できない場合にFormatExceptionをスローする代わりに、単に変換できなかったことを表すfalseを返します。 、DateTime・DateTimeOffsetに変換できた場合はTryParseメソッドの第二引数であるoutパラメータに変換結果が代入され、戻り値としてtrueが返されます。

DateTime/DateTimeOffset.TryParseメソッドで文字列からの変換を試みる
using System;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "29 Feb 2012",
      "31 Feb 2013",                        // 存在しない日付
      "27:00:00",                           // 時刻が最小(0:0:0)〜最大(23:59:59)の範囲外
      "2013-04-01T15:00:30.1230000+15:00",  // オフセットが最小・最大(±14:00)の範囲外
      "0123456",                            // 日時として判別できない文字列
    };

    foreach (var input in inputs) {
      DateTime dt;

      // 文字列からDateTimeへの変換を試みる
      if (DateTime.TryParse(input, out dt))
        Console.WriteLine("{0,-35} -> {1}", input, dt);
      else
        Console.WriteLine("{0,-35} -> (invalid format)", input);
    }
    Console.WriteLine();

    foreach (var input in inputs) {
      DateTimeOffset dto;

      // 文字列からDateTimeOffsetへの変換を試みる
      if (DateTimeOffset.TryParse(input, out dto))
        Console.WriteLine("{0,-35} -> {1}", input, dto);
      else
        Console.WriteLine("{0,-35} -> (invalid format)", input);
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.TryParseメソッドで文字列からの変換を試みる
Imports System

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "29 Feb 2012", _
      "31 Feb 2013", _
      "27:00:00", _
      "2013-04-01T15:00:30.1230000+15:00", _
      "0123456" _
    }

    For Each input As String In inputs
      Dim dt As DateTime

      ' 文字列からDateTimeへの変換を試みる
      If DateTime.TryParse(input, dt)
        Console.WriteLine("{0,-35} -> {1}", input, dt)
      Else
        Console.WriteLine("{0,-35} -> (invalid format)", input)
      End If
    Next
    Console.WriteLine()

    For Each input As String In inputs
      Dim dto As DateTimeOffset

      ' 文字列からDateTimeOffsetへの変換を試みる
      If DateTimeOffset.TryParse(input, dto)
        Console.WriteLine("{0,-35} -> {1}", input, dto)
      Else
        Console.WriteLine("{0,-35} -> (invalid format)", input)
      End If
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
29 Feb 2012                         -> 2012/02/29 0:00:00
31 Feb 2013                         -> (invalid format)
27:00:00                            -> (invalid format)
2013-04-01T15:00:30.1230000+15:00   -> 2013/04/02 0:00:30
0123456                             -> (invalid format)

29 Feb 2012                         -> 2012/02/29 0:00:00 +09:00
31 Feb 2013                         -> (invalid format)
27:00:00                            -> (invalid format)
2013-04-01T15:00:30.1230000+15:00   -> (invalid format)
0123456                             -> (invalid format)

書式とローカライズ

DateTime・DateTimeOffsetの文字列化に際してローカライズされる書式があるのと同様、ParseメソッドでもDateTime・DateTimeOffsetへの変換の際にローカライズされているものとして解釈される場合があります。

分かりやすい例として、"1/2/3"という文字列がja-JP(日本語/日本)・en-US(英語/アメリカ合衆国)・fr-FR(フランス語/フランス)の各環境ではどのように変換されるかその違いを見てみます。

DateTime/DateTimeOffset.Parseメソッドでの変換結果が実行環境により異なる例
using System;
using System.Globalization;
using System.Threading;

class Sample {
  static void Main()
  {
    var input = "1/2/3";

    foreach (var culture in new string[] {"ja-JP", "en-US", "fr-FR"}) {
      // カルチャを変更して文字列をDateTimeに変換
      Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);

      Console.WriteLine("{0} ({1}) -> {2:o}", input, culture, DateTime.Parse(input));
    }
    Console.WriteLine();

    foreach (var culture in new string[] {"ja-JP", "en-US", "fr-FR"}) {
      // カルチャを変更して文字列をDateTimeOffsetに変換
      Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);

      Console.WriteLine("{0} ({1}) -> {2:o}", input, culture, DateTimeOffset.Parse(input));
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.Parseメソッドでの変換結果が実行環境により異なる例
Imports System
Imports System.Globalization
Imports System.Threading

Class Sample
  Shared Sub Main()
    Dim input As String = "1/2/3"

    For Each culture As String In New String() {"ja-JP", "en-US", "fr-FR"}
      ' カルチャを変更して文字列をDateTimeに変換
      Thread.CurrentThread.CurrentCulture = New CultureInfo(culture)

      Console.WriteLine("{0} ({1}) -> {2:o}", input, culture, DateTime.Parse(input))
    Next
    Console.WriteLine()

    For Each culture As String In New String() {"ja-JP", "en-US", "fr-FR"}
      ' カルチャを変更して文字列をDateTimeOffsetに変換
      Thread.CurrentThread.CurrentCulture = New CultureInfo(culture)

      Console.WriteLine("{0} ({1}) -> {2:o}", input, culture, DateTimeOffset.Parse(input))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
1/2/3 (ja-JP) -> 2001-02-03T00:00:00.0000000
1/2/3 (en-US) -> 2003-01-02T00:00:00.0000000
1/2/3 (fr-FR) -> 2003-02-01T00:00:00.0000000

1/2/3 (ja-JP) -> 2001-02-03T00:00:00.0000000+09:00
1/2/3 (en-US) -> 2003-01-02T00:00:00.0000000+09:00
1/2/3 (fr-FR) -> 2003-02-01T00:00:00.0000000+09:00

上記のコードではThread.CurrentThreadを変更することで異なるカルチャに変更していますが、Thread.CurrentThreadのデフォルト値は実行環境によって異なるカルチャとなるため、つまり、実行環境によってParseメソッドの結果は異なることになります。 上記の例で言えば"1/2/3"という文字列は、ja-JPの環境では "(200)1年/2月/3日"、en-USの環境では "1月/2日/(200)3年"、fr-FRの環境では "1日/2月/(200)3年" として解釈されます。 このように、Parseメソッドに渡す文字列によっては、実行環境・カルチャによって解析結果が異なる場合がある点に注意する必要があります。


一方、RFC1123形式やISO8601形式などはカルチャに依存しない形式なので、これらの形式で表された文字列をParseメソッドで変換する場合は、実行環境・カルチャによって結果が異なるということはありません。

DateTime/DateTimeOffset.Parseメソッドでカルチャに依存しない形式で表現された日時を変換する
using System;
using System.Globalization;
using System.Threading;

class Sample {
  static void Main()
  {
    // ISO8601形式の日時 (カルチャに依存しない形式)
    var input = "2001-02-03T04:05:06";

    foreach (var culture in new string[] {"ja-JP", "en-US", "fr-FR"}) {
      // カルチャを変更して文字列をDateTimeに変換
      Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);

      Console.WriteLine("{0} ({1}) -> {2:o}", input, culture, DateTime.Parse(input));
    }
    Console.WriteLine();

    foreach (var culture in new string[] {"ja-JP", "en-US", "fr-FR"}) {
      // カルチャを変更して文字列をDateTimeOffsetに変換
      Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);

      Console.WriteLine("{0} ({1}) -> {2:o}", input, culture, DateTimeOffset.Parse(input));
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.Parseメソッドでカルチャに依存しない形式で表現された日時を変換する
Imports System
Imports System.Globalization
Imports System.Threading

Class Sample
  Shared Sub Main()
    ' ISO8601形式の日時 (カルチャに依存しない形式)
    Dim input As String = "2001-02-03T04:05:06"

    For Each culture As String In New String() {"ja-JP", "en-US", "fr-FR"}
      ' カルチャを変更して文字列をDateTimeに変換
      Thread.CurrentThread.CurrentCulture = New CultureInfo(culture)

      Console.WriteLine("{0} ({1}) -> {2:o}", input, culture, DateTime.Parse(input))
    Next
    Console.WriteLine()

    For Each culture As String In New String() {"ja-JP", "en-US", "fr-FR"}
      ' カルチャを変更して文字列をDateTimeOffsetに変換
      Thread.CurrentThread.CurrentCulture = New CultureInfo(culture)

      Console.WriteLine("{0} ({1}) -> {2:o}", input, culture, DateTimeOffset.Parse(input))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
2001-02-03T04:05:06 (ja-JP) -> 2001-02-03T04:05:06.0000000
2001-02-03T04:05:06 (en-US) -> 2001-02-03T04:05:06.0000000
2001-02-03T04:05:06 (fr-FR) -> 2001-02-03T04:05:06.0000000

2001-02-03T04:05:06 (ja-JP) -> 2001-02-03T04:05:06.0000000+09:00
2001-02-03T04:05:06 (en-US) -> 2001-02-03T04:05:06.0000000+09:00
2001-02-03T04:05:06 (fr-FR) -> 2001-02-03T04:05:06.0000000+09:00


Parseメソッドでは、ToStringメソッドと同様に書式プロバイダを指定して変換を行うことも出来るようになっています。 例えば、入力される文字列を常にen-USの環境で使われる形式として解釈したい、といった場合にはParseメソッドの第二引数に書式プロバイダとしてen-USのCultureInfoを指定します。 指定しなかった場合やnull/Nothingを指定した場合は、現在のカルチャで使われる形式で表記されているものとして解釈されます。

DateTime/DateTimeOffset.Parseメソッドでカルチャを指定して文字列を変換する
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    var input = "1/2/3";

    Console.WriteLine(DateTime.Parse(input));       // 文字列は現在のカルチャの書式で解析される
    Console.WriteLine(DateTime.Parse(input, null)); // 文字列は現在のカルチャの書式で解析される
    Console.WriteLine(DateTime.Parse(input, new CultureInfo("en-US"))); // 文字列はen-USの書式で解析される
    Console.WriteLine();

    Console.WriteLine(DateTimeOffset.Parse(input));       // 文字列は現在のカルチャの書式で解析される
    Console.WriteLine(DateTimeOffset.Parse(input, null)); // 文字列は現在のカルチャの書式で解析される
    Console.WriteLine(DateTimeOffset.Parse(input, new CultureInfo("en-US"))); // 文字列はen-USの書式で解析される
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.Parseメソッドでカルチャを指定して文字列を変換する
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    Dim input As String = "1/2/3"

    Console.WriteLine(DateTime.Parse(input))          ' 文字列は現在のカルチャの書式で解析される
    Console.WriteLine(DateTime.Parse(input, Nothing)) ' 文字列は現在のカルチャの書式で解析される
    Console.WriteLine(DateTime.Parse(input, New CultureInfo("en-US"))) ' 文字列はen-USの書式で解析される
    Console.WriteLine()

    Console.WriteLine(DateTimeOffset.Parse(input))          ' 文字列は現在のカルチャの書式で解析される
    Console.WriteLine(DateTimeOffset.Parse(input, Nothing)) ' 文字列は現在のカルチャの書式で解析される
    Console.WriteLine(DateTimeOffset.Parse(input, New CultureInfo("en-US"))) ' 文字列はen-USの書式で解析される
    Console.WriteLine()
  End Sub
End Class
実行結果
2001/02/03 0:00:00
2001/02/03 0:00:00
2003/01/02 0:00:00

2001/02/03 0:00:00 +09:00
2001/02/03 0:00:00 +09:00
2003/01/02 0:00:00 +09:00

Parseメソッドでも書式プロバイダとしてインバリアントカルチャを指定することができます。

書式を指定した変換 (ParseExact・TryParseExact)

Parseメソッドでは、サポートされているすべての書式を使ってDateTime・DateTimeOffsetへの変換を試行し、変換できた場合はその結果を返します。 つまり、書式に関わらず日時と解釈できるものはすべて変換します。 一方、変換しようとする日時の書式があらかじめ定まっている場合など、目的の書式と完全に一致するものだけを変換し、それ以外の書式のものは不正な値として扱いたい場合があります。 そういった場合にはParseExactメソッドを使うことができます。

ParseExactメソッドでは、一つ以上の書式を指定し、文字列がその書式と完全に一致した場合はその値を変換した結果を返します。 指定されたどの書式とも一致しない場合は例外FormatExceptionをスローします。

次の例では、書式としてo(ISO8601形式)を指定し、日時を表すいくつかの文字列の変換を行った結果を表示しています。

DateTime/DateTimeOffset.ParseExactメソッドで指定された書式に一致する文字列のみを変換する
using System;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "Mon, 01 Apr 2013 15:00:30 GMT",     // RFC1123形式の文字列
      "2013年4月1日 15:00:30",             // 一般的な形式の文字列
      "2013-04-01T15:00:30.1230000+09:00", // ISO8601形式の文字列
      "2013-04-01T15:00:30.1230000",       // ISO8601形式の文字列 (オフセット無し)
      "2013-04-01",                        // ISO8601形式の文字列 (日付のみ)
      "15:00:30",                          // ISO8601形式の文字列 (時刻のみ)
    };

    foreach (var input in inputs) {
      try {
        // 書式"o"で文字列をDateTimeに変換
        Console.WriteLine("{0,-35} -> {1}", input, DateTime.ParseExact(input, "o", null));
      }
      catch (FormatException) {
        // 書式と一致しない場合はFormatExceptionとなる
        Console.WriteLine("{0,-35} -> FormatException", input);
      }
    }
    Console.WriteLine();

    foreach (var input in inputs) {
      try {
        // 書式"o"で文字列をDateTimeOffsetに変換
        Console.WriteLine("{0,-35} -> {1}", input, DateTimeOffset.ParseExact(input, "o", null));
      }
      catch (FormatException) {
        Console.WriteLine("{0,-35} -> FormatException", input);
      }
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.ParseExactメソッドで指定された書式に一致する文字列のみを変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "Mon, 01 Apr 2013 15:00:30 GMT", _
      "2013年4月1日 15:00:30", _
      "2013-04-01T15:00:30.1230000+09:00", _
      "2013-04-01T15:00:30.1230000", _
      "2013-04-01", _
      "15:00:30" _
    }

    For Each input As String In inputs
      Try 
        ' 書式"o"で文字列をDateTimeに変換
        Console.WriteLine("{0,-35} -> {1}", input, DateTime.ParseExact(input, "o", Nothing))
      Catch ex As FormatException
        ' 書式と一致しない場合はFormatExceptionとなる
        Console.WriteLine("{0,-35} -> FormatException", input)
      End Try
    Next
    Console.WriteLine()

    For Each input As String In inputs
      Try 
        ' 書式"o"で文字列をDateTimeOffsetに変換
        Console.WriteLine("{0,-35} -> {1}", input, DateTimeOffset.ParseExact(input, "o", Nothing))
      Catch ex As FormatException
        Console.WriteLine("{0,-35} -> FormatException", input)
      End Try
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
Mon, 01 Apr 2013 15:00:30 GMT       -> FormatException
2013年4月1日 15:00:30                  -> FormatException
2013-04-01T15:00:30.1230000+09:00   -> 2013/04/01 15:00:30
2013-04-01T15:00:30.1230000         -> 2013/04/01 15:00:30
2013-04-01                          -> FormatException
15:00:30                            -> FormatException

Mon, 01 Apr 2013 15:00:30 GMT       -> FormatException
2013年4月1日 15:00:30                  -> FormatException
2013-04-01T15:00:30.1230000+09:00   -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1230000         -> 2013/04/01 15:00:30 +09:00
2013-04-01                          -> FormatException
15:00:30                            -> FormatException

文字列化の場合と同様に、書式には標準およびカスタム書式指定子を指定することができます。 上記の結果からも分かるとおり、書式と完全に一致する場合のみ変換が行われるため、日付と時刻両方の形式が定義されている標準書式指定子の場合は、日付のみ・時刻のみなど一部が一致していても不正な文字列として扱われ、変換は失敗します。 日付のみ・時刻のみを変換したい場合はParseメソッドを使用するか、日付のみ・時刻のみが含まれる書式指定子(標準またはカスタム)を指定する必要があります。

ParseExactメソッドもParseメソッドと同様に書式プロバイダを指定することができます。 ただし、ParseExactメソッドには書式プロバイダを省略できるバージョンのオーバーロードが用意されていないので、常に何らかのIFormatProviderを指定する必要があります。 上記の例では、省略する代わりにnull/Nothingを指定しています。


また、Parseメソッドに対するTryParseメソッドと同様、ParseExactメソッドにも変換できなかった場合でも例外をスローしないTryParseExactメソッドが存在します。 例外をキャッチするかわりに、変換できたかどうかで条件分岐させたい場合はTryParseExactメソッドを使うことができます。

DateTime/DateTimeOffset.TryParseExactメソッドで書式を指定して文字列からの変換を試みる
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "Mon, 01 Apr 2013 15:00:30 GMT",     // RFC1123形式の文字列
      "2013年4月1日 15:00:30",             // 一般的な形式の文字列
      "2013-04-01T15:00:30.1230000+09:00", // ISO8601形式の文字列
      "2013-04-01T15:00:30.1230000",       // ISO8601形式の文字列 (オフセット無し)
      "2013-04-01",                        // ISO8601形式の文字列 (日付のみ)
      "15:00:30",                          // ISO8601形式の文字列 (時刻のみ)
    };

    foreach (var input in inputs) {
      DateTime dt;

      // 書式"o"で文字列からDateTimeへの変換を試行する
      if (DateTime.TryParseExact(input, "o", null, DateTimeStyles.None, out dt))
        Console.WriteLine("{0,-35} -> {1}", input, dt);
      else
        Console.WriteLine("{0,-35} -> (invalid format)", input);
    }
    Console.WriteLine();

    foreach (var input in inputs) {
      DateTimeOffset dto;

      // 書式"o"で文字列からDateTimeOffsetへの変換を試行する
      if (DateTimeOffset.TryParseExact(input, "o", null, DateTimeStyles.None, out dto))
        Console.WriteLine("{0,-35} -> {1}", input, dto);
      else
        Console.WriteLine("{0,-35} -> (invalid format)", input);
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.TryParseExactメソッドで書式を指定して文字列からの変換を試みる
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "Mon, 01 Apr 2013 15:00:30 GMT", _
      "2013年4月1日 15:00:30", _
      "2013-04-01T15:00:30.1230000+09:00", _
      "2013-04-01T15:00:30.1230000", _
      "2013-04-01", _
      "15:00:30" _
    }

    For Each input As String In inputs
      Dim dt As DateTime

      ' 書式"o"で文字列からDateTimeへの変換を試行する
      If DateTime.TryParseExact(input, "o", Nothing, DateTimeStyles.None, dt) Then
        Console.WriteLine("{0,-35} -> {1}", input, dt)
      Else
        Console.WriteLine("{0,-35} -> (invalid format)", input)
      End If
    Next
    Console.WriteLine()

    For Each input As String In inputs
      Dim dto As DateTimeOffset

      ' 書式"o"で文字列からDateTimeOffsetへの変換を試行する
      If DateTimeOffset.TryParseExact(input, "o", Nothing, DateTimeStyles.None, dto) Then
        Console.WriteLine("{0,-35} -> {1}", input, dto)
      Else
        Console.WriteLine("{0,-35} -> (invalid format)", input)
      End If
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
Mon, 01 Apr 2013 15:00:30 GMT       -> (invalid format)
2013年4月1日 15:00:30                  -> (invalid format)
2013-04-01T15:00:30.1230000+09:00   -> 2013/04/01 15:00:30
2013-04-01T15:00:30.1230000         -> 2013/04/01 15:00:30
2013-04-01                          -> (invalid format)
15:00:30                            -> (invalid format)

Mon, 01 Apr 2013 15:00:30 GMT       -> (invalid format)
2013年4月1日 15:00:30                  -> (invalid format)
2013-04-01T15:00:30.1230000+09:00   -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1230000         -> 2013/04/01 15:00:30 +09:00
2013-04-01                          -> (invalid format)
15:00:30                            -> (invalid format)

このように、変換できたかどうかの結果はtrue/falseの値として返される、変換できた場合は結果をoutパラメータで受け取る、といった点はTryParseメソッドと同様です。 一方、TryParseExactメソッドでは変換時のオプションをDateTimeStylesで指定する必要があります。 DateTimeStylesについては別途解説します。


ParseExact・TryParseExactメソッドで複数の書式を許容するようにしたい場合は、次の例のように、その書式を配列で指定します。 どれか一つでも一致する書式があれば、変換に成功します。

DateTime/DateTimeOffset.TryParseExactメソッドで複数の書式を指定して文字列からの変換を試みる
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "Mon, 01 Apr 2013 15:00:30 GMT",     // RFC1123形式の文字列
      "2013年4月1日 15:00:30",             // 一般的な形式の文字列
      "2013-04-01T15:00:30.1230000+09:00", // ISO8601形式の文字列
      "2013-04-01T15:00:30.1230000",       // ISO8601形式の文字列 (オフセット無し)
      "2013-04-01",                        // ISO8601形式の文字列 (日付のみ)
      "15:00:30",                          // ISO8601形式の文字列 (時刻のみ)
    };

    // 変換時に許容する書式
    var formats = new string[] {
      "o",          // 一般的な形式
      "yyyy-MM-dd", // 日付のみの形式
      "HH:mm:ss",   // 時刻のみの形式
    };

    foreach (var input in inputs) {
      DateTime dt;

      // 指定した書式で文字列からDateTimeへの変換を試行
      if (DateTime.TryParseExact(input, formats, null, DateTimeStyles.None, out dt))
        Console.WriteLine("{0,-35} -> {1}", input, dt);
      else
        Console.WriteLine("{0,-35} -> (invalid format)", input);
    }
    Console.WriteLine();

    foreach (var input in inputs) {
      DateTimeOffset dto;

      // 指定した書式で文字列からDateTimeOffsetへの変換を試行
      if (DateTimeOffset.TryParseExact(input, formats, null, DateTimeStyles.None, out dto))
        Console.WriteLine("{0,-35} -> {1}", input, dto);
      else
        Console.WriteLine("{0,-35} -> (invalid format)", input);
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.TryParseExactメソッドで複数の書式を指定して文字列からの変換を試みる
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "Mon, 01 Apr 2013 15:00:30 GMT", _
      "2013年4月1日 15:00:30", _
      "2013-04-01T15:00:30.1230000+09:00", _
      "2013-04-01T15:00:30.1230000", _
      "2013-04-01", _
      "15:00:30" _
    }

    ' 変換時に許容する書式
    Dim formats() As String = New String() { _
      "o", _
      "yyyy-MM-dd", _
      "HH:mm:ss" _
    }

    For Each input As String In inputs
      Dim dt As DateTime

      ' 指定した書式で文字列からDateTimeへの変換を試行
      If DateTime.TryParseExact(input, formats, Nothing, DateTimeStyles.None, dt) Then
        Console.WriteLine("{0,-35} -> {1}", input, dt)
      Else
        Console.WriteLine("{0,-35} -> (invalid format)", input)
      End If
    Next
    Console.WriteLine()

    For Each input As String In inputs
      Dim dto As DateTimeOffset

      ' 指定した書式で文字列からDateTimeOffsetへの変換を試行
      If DateTimeOffset.TryParseExact(input, formats, Nothing, DateTimeStyles.None, dto) Then
        Console.WriteLine("{0,-35} -> {1}", input, dto)
      Else
        Console.WriteLine("{0,-35} -> (invalid format)", input)
      End If
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
Mon, 01 Apr 2013 15:00:30 GMT       -> (invalid format)
2013年4月1日 15:00:30                  -> (invalid format)
2013-04-01T15:00:30.1230000+09:00   -> 2013/04/01 15:00:30
2013-04-01T15:00:30.1230000         -> 2013/04/01 15:00:30
2013-04-01                          -> 2013/04/01 0:00:00
15:00:30                            -> 2013/03/08 15:00:30

Mon, 01 Apr 2013 15:00:30 GMT       -> (invalid format)
2013年4月1日 15:00:30                  -> (invalid format)
2013-04-01T15:00:30.1230000+09:00   -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1230000         -> 2013/04/01 15:00:30 +09:00
2013-04-01                          -> 2013/04/01 0:00:00 +09:00
15:00:30                            -> 2013/03/08 15:00:30 +09:00

変換時のオプション (DateTimeStyles)

Parse/TryParseメソッドParseExact/TryParseExactメソッドでは、引数にDateTimeStylesの値を指定することで解析時の動作オプションを指定することが出来ます。 例えば、文字列中の空白をどう扱うか、文字列にタイムゾーンが指定されていない場合にローカル時刻もしくはUTCのどちらと見なすかといったオプションを指定することができます。 DateTimeStylesには次のような値が用意されていて、この中から複数の値を組み合わせて指定することができます。

空白に関するオプション
AllowLeadingWhite, AllowTrailingWhite, AllowInnerWhite, AllowWhiteSpaces
タイムゾーン・時刻の種類に関するオプション
AssumeLocal, AssumeUniversal, AdjustToUniversal, RoudtripKind
その他のオプション
NoCurrentDateDefault, None

このうち、DateTimeStyles.Noneはオプションを一切指定しなかった場合と同じもので、これが既定の動作となります。 また、このうちいくつかのオプションはDateTimeとDateTimeOffsetで異なる動作のもの・使用できないものもあります。 DateTimeStylesのそれぞれのオプションと、その動作について詳しく見ていきます。

空白に関するオプション

DateTimeStylesの空白に関するオプションには次のようなものがあります。

AllowLeadingWhite
文字列中の日時に先行する空白文字を無視する。
AllowTrailingWhite
文字列中の日時に後続する空白文字を無視する。
AllowInnerWhite
文字列中の日時の途中に存在する空白文字を無視する。
AllowWhiteSpaces
文字列中の日時に含まれる空白文字を無視する。
(上記のAllowLeadingWhite, AllowTrailingWhite, AllowInnerWhiteを組み合わせたものと同じ)

これらのオプションは厳密に書式を指定するParseExactメソッドで特に効果を発揮します。 書式だけでは定義できない空白の表記ゆれを許容するかどうかをこのオプションで指定することが出来ます。 次の表は、様々な個所に空白を含む文字列をParseExactメソッドで解析した場合の結果の違いをまとめたものです。

実行結果
DateTime.ParseExact
(input)             |None                  |AllowLeadingWhite     |AllowTrailingWhite    |AllowInnerWhite       |AllowWhiteSpaces      
'2013-04-01'        |2013/04/01 0:00:00    |2013/04/01 0:00:00    |2013/04/01 0:00:00    |2013/04/01 0:00:00    |2013/04/01 0:00:00    |
' 2013-04-01'       |(false)               |2013/04/01 0:00:00    |(false)               |2013/04/01 0:00:00    |2013/04/01 0:00:00    |
'2013-04-01 '       |(false)               |(false)               |2013/04/01 0:00:00    |(false)               |2013/04/01 0:00:00    |
' 2013-04-01 '      |(false)               |(false)               |(false)               |(false)               |2013/04/01 0:00:00    |
'2013- 4-01'        |(false)               |(false)               |(false)               |2013/04/01 0:00:00    |2013/04/01 0:00:00    |
'2013 - 4 - 1'      |(false)               |(false)               |(false)               |2013/04/01 0:00:00    |2013/04/01 0:00:00    |
' 2013 -4 - 1 '     |(false)               |(false)               |(false)               |(false)               |2013/04/01 0:00:00    |

DateTimeOffset.ParseExact
(input)             |None                        |AllowLeadingWhite           |AllowTrailingWhite          |AllowInnerWhite             |AllowWhiteSpaces            
'2013-04-01'        |2013/04/01 0:00:00 +09:00   |2013/04/01 0:00:00 +09:00   |2013/04/01 0:00:00 +09:00   |2013/04/01 0:00:00 +09:00   |2013/04/01 0:00:00 +09:00   |
' 2013-04-01'       |(false)                     |2013/04/01 0:00:00 +09:00   |(false)                     |2013/04/01 0:00:00 +09:00   |2013/04/01 0:00:00 +09:00   |
'2013-04-01 '       |(false)                     |(false)                     |2013/04/01 0:00:00 +09:00   |(false)                     |2013/04/01 0:00:00 +09:00   |
' 2013-04-01 '      |(false)                     |(false)                     |(false)                     |(false)                     |2013/04/01 0:00:00 +09:00   |
'2013- 4-01'        |(false)                     |(false)                     |(false)                     |2013/04/01 0:00:00 +09:00   |2013/04/01 0:00:00 +09:00   |
'2013 - 4 - 1'      |(false)                     |(false)                     |(false)                     |2013/04/01 0:00:00 +09:00   |2013/04/01 0:00:00 +09:00   |
' 2013 -4 - 1 '     |(false)                     |(false)                     |(false)                     |(false)                     |2013/04/01 0:00:00 +09:00   |

DateTime/DateTimeOffset.TryParseExactメソッドでDateTimeStylesを指定して文字列中の空白の扱いを指定する
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    var format = "yyyy-M-d"; // "年-月-日"(0埋めなし)の書式
    var inputs = new string[] {
      // ISO8601形式の文字列
      "2013-04-01",     // 空白を含まない
      " 2013-04-01",    // 空白が先行する
      "2013-04-01 ",    // 空白が後続する
      " 2013-04-01 ",   // 前後に空白がある
      "2013- 4-01",     // 年と月の間に空白がある
      "2013 - 4 - 1",   // 年月日と区切り記号の間に空白がある
      " 2013 -4 - 1 ",  // 上記の例の複合
    };
    var styles = new DateTimeStyles[] {
      DateTimeStyles.None,
      DateTimeStyles.AllowLeadingWhite,
      DateTimeStyles.AllowTrailingWhite,
      DateTimeStyles.AllowInnerWhite,
      DateTimeStyles.AllowWhiteSpaces,
    };

    // DateTimeStylesとDateTimeへの変換結果の違いを表示
    Console.WriteLine("DateTime.ParseExact");
    Console.WriteLine("{0,-20}|{1,-22}|{2,-22}|{3,-22}|{4,-22}|{5,-22}",
                      "(input)",
                      "None",
                      "AllowLeadingWhite",
                      "AllowTrailingWhite",
                      "AllowInnerWhite",
                      "AllowWhiteSpaces");

    foreach (var input in inputs) {
      Console.Write("{0,-20}|", "'" + input + "'");

      foreach (DateTimeStyles style in styles) {
        DateTime dt;

        if (DateTime.TryParseExact(input, format, null, style, out dt))
          Console.Write("{0,-22}|", dt);
        else
          Console.Write("{0,-22}|", "(false)");
      }

      Console.WriteLine();
    }
    Console.WriteLine();

    // DateTimeStylesとDateTimeOffsetへの変換結果の違いを表示
    Console.WriteLine("DateTimeOffset.ParseExact");
    Console.WriteLine("{0,-20}|{1,-28}|{2,-28}|{3,-28}|{4,-28}|{5,-28}",
                      "(input)",
                      "None",
                      "AllowLeadingWhite",
                      "AllowTrailingWhite",
                      "AllowInnerWhite",
                      "AllowWhiteSpaces");

    foreach (var input in inputs) {
      Console.Write("{0,-20}|", "'" + input + "'");

      foreach (DateTimeStyles style in styles) {
        DateTimeOffset dto;

        if (DateTimeOffset.TryParseExact(input, format, null, DateTimeStyles.AssumeLocal | style, out dto))
          Console.Write("{0,-28}|", dto);
        else
          Console.Write("{0,-28}|", "(false)");
      }

      Console.WriteLine();
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.TryParseExactメソッドでDateTimeStylesを指定して文字列中の空白の扱いを指定する
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    Dim format As String = "yyyy-M-d" ' "年-月-日"(0埋めなし)の書式
    Dim inputs() As String = New String() { _
      "2013-04-01", _
      " 2013-04-01", _
      "2013-04-01 ", _
      " 2013-04-01 ", _
      "2013- 4-01", _
      "2013 - 4 - 1", _
      " 2013 -4 - 1 " _
    }
    Dim styles() As DateTimeStyles = New DateTimeStyles() { _
      DateTimeStyles.None, _
      DateTimeStyles.AllowLeadingWhite, _
      DateTimeStyles.AllowTrailingWhite, _
      DateTimeStyles.AllowInnerWhite, _
      DateTimeStyles.AllowWhiteSpaces _
    }

    ' DateTimeStylesとDateTimeへの変換結果の違いを表示
    Console.WriteLine("DateTime.ParseExact")
    Console.WriteLine("{0,-20}|{1,-22}|{2,-22}|{3,-22}|{4,-22}|{5,-22}", _
                      "(input)", _
                      "None", _
                      "AllowLeadingWhite", _
                      "AllowTrailingWhite", _
                      "AllowInnerWhite", _
                      "AllowWhiteSpaces")

    For Each input As String In inputs
      Console.Write("{0,-20}|", "'" + input + "'")

      For Each style As DateTimeStyles In styles
        Dim dt As DateTime

        If DateTime.TryParseExact(input, format, Nothing, style, dt)
          Console.Write("{0,-22}|", dt)
        Else
          Console.Write("{0,-22}|", "(false)")
        End If
      Next

      Console.WriteLine()
    Next
    Console.WriteLine()

    ' DateTimeStylesとDateTimeOffsetへの変換結果の違いを表示
    Console.WriteLine("DateTimeOffset.ParseExact")
    Console.WriteLine("{0,-20}|{1,-28}|{2,-28}|{3,-28}|{4,-28}|{5,-28}", _
                      "(input)", _
                      "None", _
                      "AllowLeadingWhite", _
                      "AllowTrailingWhite", _
                      "AllowInnerWhite", _
                      "AllowWhiteSpaces")

    For Each input As String In inputs
      Console.Write("{0,-20}|", "'" + input + "'")

      For Each style As DateTimeStyles In styles
        Dim dto As DateTimeOffset

        If DateTimeOffset.TryParseExact(input, format, Nothing, DateTimeStyles.AssumeLocal Or style, dto) Then
          Console.Write("{0,-28}|", dto)
        Else
          Console.Write("{0,-28}|", "(false)")
        End If
      Next

      Console.WriteLine()
    Next
    Console.WriteLine()
  End Sub
End Class

なお、この例では日付のみを解析する際に時刻をローカル時刻であると仮定するために、DateTimeOffset.ParseExactの呼び出し時にDateTimeStyles.AssumeLocalを指定しています。

タイムゾーン・時刻の種類に関するオプション

DateTimeStylesのタイムゾーン・時刻の種類に関するオプションには次のようなものがあります。

AssumeLocal
文字列中にオフセット値またはタイムゾーン指定子が無い場合、ローカル時刻を表すものとして扱う。
AssumeUniversalおよびRoundtripKindとは排他的なオプションで、同時には指定できない。
AssumeUniversal
文字列中にオフセット値またはタイムゾーン指定子が無い場合、UTCを表すものとして扱う。
AssumeLocalおよびRoundtripKindとは排他的なオプションで、同時には指定できない。
AdjustToUniversal
解析したのちに、日時をUTCに変換する。
RoudtripKindとは排他的なオプションで、同時には指定できない。
DateTime.Parseでは、文字列中にオフセット値またはタイムゾーン指定子が無い場合、UTCへの変換は行われず、KindプロパティにDateTimeKind.Unspecifiedが設定される。
RoudtripKind
解析した日時の種類を維持する。
AdjustToUniversalとは排他的なオプションで、同時には指定できない。
DateTimeOffset.Parseでは、このオプションは無視される(動作に影響しない)。
DateTime.Parseでは、文字列中のオフセット値またはタイムゾーン指定子によってDateTime.Kindが次のように設定される。
UTCの場合
DateTime.KindプロパティにDateTimeKind.Utcが設定される。
UTC以外のオフセット値の場合
ローカル時刻に変換され、DateTime.KindプロパティにDateTimeKind.Localが設定される。
指定されていない場合
DateTime.KindプロパティにDateTimeKind.Unspecifiedが設定される。

AssumeLocal・AssumeUniversal

タイムゾーン・時刻の種類に関するオプションのうち、AssumeLocalおよびAssumeUniversalは文字列中の日時がどのタイムゾーンも表さない場合にローカル時刻もしくはUTCと見なすように指定するオプションです。 当然、オフセット値が指定されている場合は、その値が使用されます。

UTC+9の環境での実行結果例
DateTime.ParseExact
2013-04-01T15:00:30.1234567         (None           ) -> 2013/04/01 15:00:30 (Unspecified)
2013-04-01T15:00:30.1234567         (AssumeLocal    ) -> 2013/04/01 15:00:30 (Local)
2013-04-01T15:00:30.1234567         (AssumeUniversal) -> 2013/04/02 0:00:30 (Local)

2013-04-01T15:00:30.1234567+00:00   (None           ) -> 2013/04/02 0:00:30 (Local)
2013-04-01T15:00:30.1234567+00:00   (AssumeLocal    ) -> 2013/04/02 0:00:30 (Local)
2013-04-01T15:00:30.1234567+00:00   (AssumeUniversal) -> 2013/04/02 0:00:30 (Local)

2013-04-01T15:00:30.1234567+09:00   (None           ) -> 2013/04/01 15:00:30 (Local)
2013-04-01T15:00:30.1234567+09:00   (AssumeLocal    ) -> 2013/04/01 15:00:30 (Local)
2013-04-01T15:00:30.1234567+09:00   (AssumeUniversal) -> 2013/04/01 15:00:30 (Local)

2013-04-01T15:00:30.1234567-05:00   (None           ) -> 2013/04/02 5:00:30 (Local)
2013-04-01T15:00:30.1234567-05:00   (AssumeLocal    ) -> 2013/04/02 5:00:30 (Local)
2013-04-01T15:00:30.1234567-05:00   (AssumeUniversal) -> 2013/04/02 5:00:30 (Local)


DateTimeOffset.ParseExact
2013-04-01T15:00:30.1234567         (None           ) -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1234567         (AssumeLocal    ) -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1234567         (AssumeUniversal) -> 2013/04/01 15:00:30 +00:00

2013-04-01T15:00:30.1234567+00:00   (None           ) -> 2013/04/01 15:00:30 +00:00
2013-04-01T15:00:30.1234567+00:00   (AssumeLocal    ) -> 2013/04/01 15:00:30 +00:00
2013-04-01T15:00:30.1234567+00:00   (AssumeUniversal) -> 2013/04/01 15:00:30 +00:00

2013-04-01T15:00:30.1234567+09:00   (None           ) -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1234567+09:00   (AssumeLocal    ) -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1234567+09:00   (AssumeUniversal) -> 2013/04/01 15:00:30 +09:00

2013-04-01T15:00:30.1234567-05:00   (None           ) -> 2013/04/01 15:00:30 -05:00
2013-04-01T15:00:30.1234567-05:00   (AssumeLocal    ) -> 2013/04/01 15:00:30 -05:00
2013-04-01T15:00:30.1234567-05:00   (AssumeUniversal) -> 2013/04/01 15:00:30 -05:00

DateTime/DateTimeOffset.ParseExactメソッドでDateTimeStylesを指定し、時刻をローカル時刻またはUTCとみなして変換する
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "2013-04-01T15:00:30.1234567",        // タイムゾーンの明記無し
      "2013-04-01T15:00:30.1234567+00:00",  // UTC+0での時刻
      "2013-04-01T15:00:30.1234567+09:00",  // UTC+9での時刻
      "2013-04-01T15:00:30.1234567-05:00",  // UTC-5での時刻
    };
    var styles = new DateTimeStyles[] {
      DateTimeStyles.None,              // オプション指定なし (デフォルトの動作)
      DateTimeStyles.AssumeLocal,       // 時刻はローカル時刻とみなす
      DateTimeStyles.AssumeUniversal,   // 時刻はUTCとみなす
    };

    // 文字列をDateTimeへ変換
    Console.WriteLine("DateTime.ParseExact");
    foreach (var input in inputs) {
      foreach (var style in styles) {
        var dt = DateTime.ParseExact(input, "o", null, style);

        Console.WriteLine("{0,-35} ({1,-15}) -> {2} ({3})", input, style, dt, dt.Kind);
      }
      Console.WriteLine();
    }
    Console.WriteLine();

    // 文字列をDateTimeOffsetへ変換
    Console.WriteLine("DateTimeOffset.ParseExact");
    foreach (var input in inputs) {
      foreach (var style in styles) {
        try {
          var dto = DateTimeOffset.ParseExact(input, "o", null, style);

          Console.WriteLine("{0,-35} ({1,-15}) -> {2}", input, style, dto);
        }
        catch (FormatException) {
          Console.WriteLine("{0,-35} ({1,-15}) -> (FormatException)", input, style);
        }
      }
      Console.WriteLine();
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.ParseExactメソッドでDateTimeStylesを指定し、時刻をローカル時刻またはUTCとみなして変換する
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "2013-04-01T15:00:30.1234567", _
      "2013-04-01T15:00:30.1234567+00:00", _
      "2013-04-01T15:00:30.1234567+09:00", _
      "2013-04-01T15:00:30.1234567-05:00" _
    }
    Dim styles() As DateTimeStyles = New DateTimeStyles() { _
      DateTimeStyles.None, _
      DateTimeStyles.AssumeLocal, _
      DateTimeStyles.AssumeUniversal _
    }

    ' 文字列をDateTimeへ変換
    Console.WriteLine("DateTime.ParseExact")
    For Each input As String In inputs
      For Each style As DateTimeStyles In styles
        Dim dt As DateTime = DateTime.ParseExact(input, "o", Nothing, style)

        Console.WriteLine("{0,-35} ({1,-15}) -> {2} ({3})", input, style, dt, dt.Kind)
      Next
      Console.WriteLine()
    Next
    Console.WriteLine()

    ' 文字列をDateTimeOffsetへ変換
    Console.WriteLine("DateTimeOffset.ParseExact")
    For Each input As String In inputs
      For Each style As DateTimeStyles In styles
        Try
          Dim dto As DateTimeOffset = DateTimeOffset.ParseExact(input, "o", Nothing, style)

          Console.WriteLine("{0,-35} ({1,-15}) -> {2}", input, style, dto)
        Catch ex As FormatException
          Console.WriteLine("{0,-35} ({1,-15}) -> (FormatException)", input, style)
        End Try
      Next
      Console.WriteLine()
    Next
    Console.WriteLine()
  End Sub
End Class

AdjustToUniversal・RoudtripKind

タイムゾーン・時刻の種類に関するオプションのうち、AdjustToUniversalおよびRoudtripKindは解析した日時の補正に関するオプションです。

AdjustToUniversalが指定された場合は、解析した日時をUTCに補正します。 このため、DateTime.Kindは常にDateTimeKind.Utc、DateTimeOffset.Offsetは00:00:00となります。 文字列中の日時がどのタイムゾーンも表さない場合、時刻の補正は行われず、DateTime.KindにはDateTimeKind.Unspecifiedが設定されます。

一方RoudtripKindが指定された場合は、解析した日時の種類とタイムゾーン情報を維持します。 時刻にZGMTなどのUTCであることを表す文字列が含まれていれば、DateTimeKind.Utcとして扱われます。 このオプションはDateTimeOffsetでは無視されます。

UTC+9の環境での実行結果例
DateTime.ParseExact
2013-04-01T15:00:30.1234567         (None              ) -> 2013/04/01 15:00:30 (Unspecified)
2013-04-01T15:00:30.1234567Z        (None              ) -> 2013/04/02 0:00:30 (Local)
2013-04-01T15:00:30.1234567+00:00   (None              ) -> 2013/04/02 0:00:30 (Local)
2013-04-01T15:00:30.1234567+09:00   (None              ) -> 2013/04/01 15:00:30 (Local)
2013-04-01T15:00:30.1234567-05:00   (None              ) -> 2013/04/02 5:00:30 (Local)

2013-04-01T15:00:30.1234567         (AdjustToUniversal ) -> 2013/04/01 15:00:30 (Unspecified)
2013-04-01T15:00:30.1234567Z        (AdjustToUniversal ) -> 2013/04/01 15:00:30 (Utc)
2013-04-01T15:00:30.1234567+00:00   (AdjustToUniversal ) -> 2013/04/01 15:00:30 (Utc)
2013-04-01T15:00:30.1234567+09:00   (AdjustToUniversal ) -> 2013/04/01 6:00:30 (Utc)
2013-04-01T15:00:30.1234567-05:00   (AdjustToUniversal ) -> 2013/04/01 20:00:30 (Utc)

2013-04-01T15:00:30.1234567         (RoundtripKind     ) -> 2013/04/01 15:00:30 (Unspecified)
2013-04-01T15:00:30.1234567Z        (RoundtripKind     ) -> 2013/04/01 15:00:30 (Utc)
2013-04-01T15:00:30.1234567+00:00   (RoundtripKind     ) -> 2013/04/02 0:00:30 (Local)
2013-04-01T15:00:30.1234567+09:00   (RoundtripKind     ) -> 2013/04/01 15:00:30 (Local)
2013-04-01T15:00:30.1234567-05:00   (RoundtripKind     ) -> 2013/04/02 5:00:30 (Local)


DateTimeOffset.ParseExact
2013-04-01T15:00:30.1234567         (None              ) -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1234567Z        (None              ) -> 2013/04/01 15:00:30 +00:00
2013-04-01T15:00:30.1234567+00:00   (None              ) -> 2013/04/01 15:00:30 +00:00
2013-04-01T15:00:30.1234567+09:00   (None              ) -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1234567-05:00   (None              ) -> 2013/04/01 15:00:30 -05:00

2013-04-01T15:00:30.1234567         (AdjustToUniversal ) -> 2013/04/01 6:00:30 +00:00
2013-04-01T15:00:30.1234567Z        (AdjustToUniversal ) -> 2013/04/01 15:00:30 +00:00
2013-04-01T15:00:30.1234567+00:00   (AdjustToUniversal ) -> 2013/04/01 15:00:30 +00:00
2013-04-01T15:00:30.1234567+09:00   (AdjustToUniversal ) -> 2013/04/01 6:00:30 +00:00
2013-04-01T15:00:30.1234567-05:00   (AdjustToUniversal ) -> 2013/04/01 20:00:30 +00:00

2013-04-01T15:00:30.1234567         (RoundtripKind     ) -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1234567Z        (RoundtripKind     ) -> 2013/04/01 15:00:30 +00:00
2013-04-01T15:00:30.1234567+00:00   (RoundtripKind     ) -> 2013/04/01 15:00:30 +00:00
2013-04-01T15:00:30.1234567+09:00   (RoundtripKind     ) -> 2013/04/01 15:00:30 +09:00
2013-04-01T15:00:30.1234567-05:00   (RoundtripKind     ) -> 2013/04/01 15:00:30 -05:00

DateTime/DateTimeOffset.ParseExactメソッドでDateTimeStylesを指定し、変換した時刻のオフセットを補正または維持する
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "2013-04-01T15:00:30.1234567",        // タイムゾーンの明記無し
      "2013-04-01T15:00:30.1234567Z",       // UTCでの時刻
      "2013-04-01T15:00:30.1234567+00:00",  // UTC+0での時刻
      "2013-04-01T15:00:30.1234567+09:00",  // UTC+9での時刻
      "2013-04-01T15:00:30.1234567-05:00",  // UTC-5での時刻
    };
    var styles = new DateTimeStyles[] {
      DateTimeStyles.None,              // オプション指定なし (デフォルトの動作)
      DateTimeStyles.AdjustToUniversal, // 時刻をUTCに補正する
      DateTimeStyles.RoundtripKind,     // 時刻のタイムゾーン情報を維持する
    };

    // 文字列をDateTimeへ変換
    Console.WriteLine("DateTime.ParseExact");
    foreach (var style in styles) {
      foreach (var input in inputs) {
        var dt = DateTime.ParseExact(input, "o", null, style);

        Console.WriteLine("{0,-35} ({1,-18}) -> {2} ({3})", input, style, dt, dt.Kind);
      }
      Console.WriteLine();
    }
    Console.WriteLine();

    // 文字列をDateTimeOffsetへ変換
    Console.WriteLine("DateTimeOffset.ParseExact");
    foreach (var style in styles) {
      foreach (var input in inputs) {
        try {
          var dto = DateTimeOffset.ParseExact(input, "o", null, style);

          Console.WriteLine("{0,-35} ({1,-18}) -> {2}", input, style, dto);
        }
        catch (FormatException) {
          Console.WriteLine("{0,-35} ({1,-18}) -> (FormatException)", input, style);
        }
      }
      Console.WriteLine();
    }
    Console.WriteLine();
  }
}
DateTime/DateTimeOffset.ParseExactメソッドでDateTimeStylesを指定し、変換した時刻のオフセットを補正または維持する
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "2013-04-01T15:00:30.1234567", _
      "2013-04-01T15:00:30.1234567Z", _
      "2013-04-01T15:00:30.1234567+00:00", _
      "2013-04-01T15:00:30.1234567+09:00", _
      "2013-04-01T15:00:30.1234567-05:00" _
    }
    Dim styles() As DateTimeStyles = New DateTimeStyles() { _
      DateTimeStyles.None, _
      DateTimeStyles.AdjustToUniversal, _
      DateTimeStyles.RoundtripKind _
    }

    ' 文字列をDateTimeへ変換
    Console.WriteLine("DateTime.ParseExact")
    For Each style As DateTimeStyles In styles
      For Each input As String In inputs
        Dim dt As DateTime = DateTime.ParseExact(input, "o", Nothing, style)

        Console.WriteLine("{0,-35} ({1,-18}) -> {2} ({3})", input, style, dt, dt.Kind)
      Next
      Console.WriteLine()
    Next
    Console.WriteLine()

    ' 文字列をDateTimeOffsetへ変換
    Console.WriteLine("DateTimeOffset.ParseExact")
    For Each style As DateTimeStyles In styles
      For Each input As String In inputs
        Try
          Dim dto As DateTimeOffset = DateTimeOffset.ParseExact(input, "o", Nothing, style)

          Console.WriteLine("{0,-35} ({1,-18}) -> {2}", input, style, dto)
        Catch ex As FormatException
          Console.WriteLine("{0,-35} ({1,-18}) -> (FormatException)", input, style)
        End Try
      Next
      Console.WriteLine()
    Next
    Console.WriteLine()
  End Sub
End Class

NoCurrentDateDefault

NoCurrentDateDefaultは、変換しようとする文字列中に日付がない場合、現在の日付ではなく 1年1月1日 (DateTime.Min)を表すものとして変換を行います。 なお、このオプションはDateTimeOffset.Parseでは無効で、指定した場合は例外ArgumentExceptionがスローされます。

なお、日付のみで時刻がない場合は常に00:00:00を表すものとして変換を行います。 これはDateTimeおよびDateTimeOffsetともに同じ動作となります。

実行結果例
2013/04/03 15:00:30  (None                  ) -> 2013/04/03 15:00:30
2013/04/03 15:00:30  (NoCurrentDateDefault  ) -> 2013/04/03 15:00:30

15:00:30             (None                  ) -> 2013/03/02 15:00:30
15:00:30             (NoCurrentDateDefault  ) -> 0001/01/01 15:00:30

15:00                (None                  ) -> 2013/03/02 15:00:00
15:00                (NoCurrentDateDefault  ) -> 0001/01/01 15:00:00
DateTime.ParseメソッドでDateTimeStyles.NoCurrentDateDefaultを指定し、今日ではなく1年1月1日の時刻として変換する
using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    var inputs = new string[] {
      "2013/04/03 15:00:30",  // 日付と時刻
      "15:00:30",             // 時刻のみ
      "15:00",                // 時分のみ
    };
    var styles = new DateTimeStyles[] {
      DateTimeStyles.None,                  // オプション指定なし (デフォルトの動作、今日の日付が設定される)
      DateTimeStyles.NoCurrentDateDefault,  // 日付がない場合、1年1月1日を設定する
    };

    // 文字列をDateTimeへ変換
    foreach (var input in inputs) {
      foreach (var style in styles) {
        var dt = DateTime.Parse(input, null, style);

        Console.WriteLine("{0,-20} ({1,-22}) -> {2}", input, style, dt);
      }
      Console.WriteLine();
    }
  }
}
DateTime.ParseメソッドでDateTimeStyles.NoCurrentDateDefaultを指定し、今日ではなく1年1月1日の時刻として変換する
Imports System
Imports System.Globalization

Class Sample
  Shared Sub Main()
    Dim inputs() As String = New String() { _
      "2013/04/03 15:00:30", _
      "15:00:30", _
      "15:00" _
    }
    Dim styles() As DateTimeStyles = New DateTimeStyles() { _
      DateTimeStyles.None, _
      DateTimeStyles.NoCurrentDateDefault _
    }

    ' 文字列をDateTimeへ変換
    For Each input As String In inputs
      For Each style As DateTimeStyles In styles
        Dim dt As DateTime = DateTime.Parse(input, Nothing, style)

        Console.WriteLine("{0,-20} ({1,-22}) -> {2}", input, style, dt)
      Next
      Console.WriteLine()
    Next
  End Sub
End Class