DateTime の書式設定 - ToString と標準フォーマット

DateTime オブジェクトを文字列に変換するとき、ToString メソッドにフォーマット指定子を渡すことで出力形式を自由にコントロールできる。C# には標準フォーマット指定子とカスタムフォーマット指定子の 2 種類が用意されており、用途に応じて使い分ける。

ToString の基本

DateTime の ToString を引数なしで呼ぶと、実行環境のカルチャに依存した文字列が返る。

DateTime now = DateTime.Now;

Console.WriteLine(now.ToString());
// 例: 2025/02/15 14:30:45(日本語環境の場合)

これは環境によって出力が変わるため、ログやデータ保存には向かない。明示的にフォーマットを指定する習慣をつけておくのが重要だ。

DateTime now = DateTime.Now;

// フォーマット指定子を渡す
Console.WriteLine(now.ToString("yyyy-MM-dd"));
// 出力: 2025-02-15

Console.WriteLine(now.ToString("HH:mm:ss"));
// 出力: 14:30:45

ToString の引数に渡す文字列がフォーマット指定子であり、これが書式設定の中心となる。

標準フォーマット指定子

標準フォーマット指定子は 1 文字のアルファベットで、よく使われる書式があらかじめ定義されている。

指定子意味出力例
d短い日付2025/02/15
D長い日付2025年2月15日
t短い時刻14:30
T長い時刻14:30:45
DateTime now = DateTime.Now;

Console.WriteLine(now.ToString("d")); // 2025/02/15
Console.WriteLine(now.ToString("D")); // 2025年2月15日
Console.WriteLine(now.ToString("t")); // 14:30
Console.WriteLine(now.ToString("T")); // 14:30:45

日付と時刻を組み合わせた指定子もある。

指定子意味出力例
f長い日付 + 短い時刻2025年2月15日 14:30
F長い日付 + 長い時刻2025年2月15日 14:30:45
g短い日付 + 短い時刻2025/02/15 14:30
G短い日付 + 長い時刻2025/02/15 14:30:45

ここで注意すべきなのは、標準フォーマット指定子の出力がカルチャに依存するという点だ。同じ “d” でも、日本語環境では 2025/02/15、英語環境では 2/15/2025 のように変わる。

カルチャを明示する

環境に依存しない出力が必要な場合は、CultureInfo を明示的に渡す。

using System.Globalization;

DateTime now = DateTime.Now;

// 日本のカルチャを指定
var ja = new CultureInfo("ja-JP");
Console.WriteLine(now.ToString("D", ja));
// 出力: 2025年2月15日

// 米国のカルチャを指定
var en = new CultureInfo("en-US");
Console.WriteLine(now.ToString("D", en));
// 出力: Saturday, February 15, 2025
標準フォーマット指定子のみ

実行環境のカルチャに依存する。開発マシンと本番サーバーで結果が異なる可能性がある。

CultureInfo を併用

出力が一貫する。国際化対応やログ出力では必須の手法。

カスタムフォーマット指定子

標準フォーマット指定子で対応できない形式が必要なときは、カスタムフォーマット指定子を組み合わせて自由に書式を定義する。

指定子意味
yyyy4 桁の年2025
MM2 桁の月02
dd2 桁の日15
DateTime now = DateTime.Now;

Console.WriteLine(now.ToString("yyyy/MM/dd"));
// 出力: 2025/02/15

Console.WriteLine(now.ToString("yyyy年M月d日"));
// 出力: 2025年2月15日

Console.WriteLine(now.ToString("yyyyMMdd_HHmmss"));
// 出力: 20250215_143045

M と MM、d と dd の違いは、先頭のゼロ埋めを行うかどうかにある。MM は常に 2 桁(02)になるのに対し、M は不要なゼロを省略する(2)。ファイル名やデータベースのキーには MM/dd のゼロ埋め形式が適しており、ユーザー向け表示には M/d のほうが自然に見える。

時刻のカスタム指定子も見ておこう。

指定子意味
HH24 時間制(2 桁)14
hh12 時間制(2 桁)02
mm分(2 桁)30
ss秒(2 桁)45
DateTime now = DateTime.Now;

Console.WriteLine(now.ToString("HH:mm:ss"));
// 出力: 14:30:45

Console.WriteLine(now.ToString("hh:mm tt"));
// 出力: 02:30 PM(英語環境)

HH が 24 時間制、hh が 12 時間制という区別は見落としやすい。大文字と小文字で意味が変わるのは C# のフォーマット指定子全般に共通する特徴であり、mm(分)と MM(月)の取り違えにも注意が必要だ。

ISO 8601 形式での出力

API 通信やログ出力では、国際標準である ISO 8601 形式が頻繁に使われる。標準フォーマット指定子 “o”(ラウンドトリップ)を使うと、精度を失わずに DateTime を文字列化できる。

DateTime now = DateTime.Now;

Console.WriteLine(now.ToString("o"));
// 出力: 2025-02-15T14:30:45.1234567+09:00

Console.WriteLine(now.ToString("s"));
// 出力: 2025-02-15T14:30:45
o(ラウンドトリップ)

タイムゾーン情報とミリ秒以下の精度を保持する。DateTime.Parse で復元しても情報が失われない。

s(ソータブル)

ISO 8601 の基本形式で、タイムゾーン情報は含まない。文字列としてのソートが可能。

“o” 指定子は DateTime の Kind(Local / Utc / Unspecified)に応じてタイムゾーンオフセットの出力が変わる。Local なら +09:00 のようなオフセットが付き、Utc なら Z が付く。Unspecified の場合はオフセットが省略される。

文字列補間での利用

C# 6 以降の文字列補間($“”)でも、コロンの後にフォーマット指定子を書ける。

DateTime now = DateTime.Now;

string message = $"現在時刻: {now:yyyy-MM-dd HH:mm}";
Console.WriteLine(message);
// 出力: 現在時刻: 2025-02-15 14:30

string log = $"[{now:HH:mm:ss.fff}] 処理開始";
Console.WriteLine(log);
// 出力: [14:30:45.123] 処理開始

ToString を明示的に呼ばなくても、補間式の中でフォーマット指定子を直接記述できるため、コードが簡潔になる。実務では ToString よりもこちらの書き方を目にする機会が多いだろう。

DateTime.Now.ToString("MM") と DateTime.Now.ToString("mm") の違いとして正しいものはどれか。

  • どちらも月を返すが、桁数が異なる
  • MM は月を、mm は分を返す
  • MM は大文字の月名を、mm は小文字の月名を返す
  • 違いはなく、どちらも同じ結果を返す
__RESULT__

MM は月(Month)、mm は分(Minute)を表す。大文字と小文字で意味が完全に変わるため、フォーマット指定子では常にケースに注意する必要がある。