みなさんこんにちは!ヒロポンです!
画面の日付表示を直してる途中とか、夜間バッチのログファイル名にタイムスタンプ付けてる最中。ふっと手が止まること、ないですか??
「あれ、yyyy/MM/dd ってどう書くんやったっけ」。
ファイル名に付ける yyyyMMdd_HHmmss の大文字小文字、どっちが分やったっけ。俺もこれ、何回ググったか分からん。
この記事は、C# DateTime フォーマットの書き方を「画面表示」「ファイル名」「ログ」「SQL 渡し」の用途別に、そのままコピペで動く形でまとめた早見表です。
書式指定子そのものの意味も一覧にしてあります。ブックマークして、必要な行だけ持っていってください。
結論: 用途別にこの4行をコピペすればいい
先に答えだけ置いときます。DateTime の変数を now とすると、こう。
using System;
using System.Globalization;
class Program
{
static void Main()
{
var now = new DateTime(2026, 6, 30, 14, 5, 9, 123);
// 画面表示用: 2026/06/30
Console.WriteLine(now.ToString("yyyy/MM/dd"));
// ファイル名用: 20260630_140509
Console.WriteLine(now.ToString("yyyyMMdd_HHmmss"));
// ログ用: 2026-06-30 14:05:09.123
Console.WriteLine(now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
// SQL / API 渡し用 (ISO 8601): 2026-06-30T14:05:09
Console.WriteLine(now.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture));
}
}
出力はこんな感じになります。
2026/06/30
20260630_140509
2026-06-30 14:05:09.123
2026-06-30T14:05:09
実行結果(.NET 9 で実機確認):

とりあえずこの4行で、業務でぶつかる日付整形の8割はカタがつきます。
残りの2割。書式指定子の意味、大文字小文字の罠、環境依存。ここを以下で潰していきます。
なぜ DateTime のフォーマットでこんなに迷うのか
理由はシンプルです。ToString に渡す書式文字列が、大文字と小文字で別物だから。
M は月、m は分。H は24時間、h は12時間。s は秒、f はミリ秒。
英語の頭文字で覚えればええんやけど、ここに落とし穴があって。MM(月)と mm(分)がぱっと見そっくりなんですよね。しかも両方とも「画面に2桁で出したい」場面で使う。だから取り違える。
で、タチが悪いのが、たいていの場合は動いてしまうこと。エラーにならず、ただ値だけ間違って出る。
しくみが分かれば怖くない。まず用途別のコピペ、次に指定子の意味、最後にハマりどころ、の順で見ていきます。
用途別コピペ早見表(画面・ファイル名・ログ・SQL 渡し)
業務でよく使う4用途を、選び方ごとに整理するとこうなります。

選んだら、あとは下のコピペを貼るだけ。
画面表示・帳票(人が読む)
人が見る場所はスラッシュ区切りが読みやすい。
dt.ToString("yyyy/MM/dd"); // 2026/06/30
dt.ToString("yyyy/MM/dd HH:mm"); // 2026/06/30 14:05
dt.ToString("yyyy年M月d日(ddd)"); // 2026年6月30日(火)
ddd で曜日が出ます。納品書とか帳票で「(火)」を添えたい時に便利。
ファイル名・キー(記号が使えない場所)
ファイル名には / や : が使えない。だから区切りを抜いて、アンダースコアでつなぐ。
$"log_{dt:yyyyMMdd_HHmmss}.txt"; // log_20260630_140509.txt
$"backup_{dt:yyyyMMdd}.zip"; // backup_20260630.zip
文字列補間($"...")の中で {dt:書式} と書けるのも覚えとくと楽です。いい感じに短く書けます!!
ログ出力(機械が突合する)
ログは秒・ミリ秒まで欲しい。後でエラーの前後関係を追う時、ミリ秒があるとガチで助かります。
dt.ToString("yyyy-MM-dd HH:mm:ss.fff"); // 2026-06-30 14:05:09.123
ハイフン区切り+ゼロ埋めだと、文字列のままソートしても時系列に並ぶ。これが地味に効くんですよね。
SQL / API 渡し(システム間連携)
別システムやDBに渡すなら、人間の見やすさより機械が一意に解釈できることが正義。ISO 8601 一択です。
// オフセット不要なら DateTime のまま秒まで
dt.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture);
// 2026-06-30T14:05:09
// 時差(オフセット)まで含めたいなら DateTimeOffset を使う
var dto = new DateTimeOffset(2026, 6, 30, 14, 5, 9, TimeSpan.FromHours(9));
dto.ToString("o"); // 2026-06-30T14:05:09.0000000+09:00
"o"(ラウンドトリップ書式)はミリ秒もオフセットも入る形式。
ひとつ注意。DateTime 単体だと Kind 次第で末尾のオフセットが付きません(Unspecified だと付かない)。時差をまたぐ連携なら、DateTimeOffset で持つのが確実です。
書式指定子そのものの早見表
「画面に出したい形が上のどれとも違う」時のために、部品の意味も置いときます。基準は 2026-06-30 14:05:09.123(火曜・JST) です。
カスタム書式指定子(自分で組み立てる用)
| 指定子 | 意味 | 出力例 |
|---|---|---|
yyyy |
年4桁 | 2026 |
yy |
年2桁 | 26 |
MM |
月2桁(ゼロ埋め) | 06 |
M |
月(ゼロ埋めなし) | 6 |
dd |
日2桁(ゼロ埋め) | 30 |
d |
日(ゼロ埋めなし) | 30 |
HH |
時(24時間・2桁) | 14 |
hh |
時(12時間・2桁) | 02 |
mm |
分2桁 | 05 |
ss |
秒2桁 | 09 |
fff |
ミリ秒3桁 | 123 |
tt |
午前/午後 | 午後 |
ddd |
曜日(短縮) | 火 |
dddd |
曜日(フル) | 火曜日 |
zzz |
UTCオフセット | +09:00 |
H(大文字)が24時間、h(小文字)が12時間。12時間表記なら tt で午前/午後を添えないと、14時なのか2時なのか分からなくなります。
標準書式指定子(1文字で済ませる用)
毎回 yyyy/MM/dd と書くのが面倒なら、1文字の標準書式もあります。
| 書式 | 用途 | ja-JP の出力例 |
|---|---|---|
"d" |
短い日付 | 2026/06/30 |
"D" |
長い日付 | 2026年6月30日火曜日 |
"g" |
一般(短) | 2026/06/30 14:05 |
"G" |
一般(長) | 2026/06/30 14:05:09 |
"s" |
ソート可能 | 2026-06-30T14:05:09 |
"o" |
ラウンドトリップ | 2026-06-30T14:05:09.1230000(Local時は末尾に +09:00) |
ただし標準書式は実行環境のカルチャに引っ張られる。これが次のハマりどころに直結します。
ハマりポイント: 知らないと一晩飛ぶやつ
ここからが本題みたいなもんです。コピペで動かしても、この2つを踏むと「なぜか値が変」になります。
① mm と MM の取り違え(分が月になる)
正直に言うと、俺もこれで30分溶かしたことがあります。
ログの時刻がなんかおかしい。そう思って延々コードを追ってたら、書式が HH:MM になってた、というオチ。
mm が分、MM が月。HH:MM って書くと、時のあとに「月」が出ます。
var dt = new DateTime(2026, 6, 30, 14, 5, 9);
Console.WriteLine(dt.ToString("HH:mm")); // 14:05 ← 正しい(時:分)
Console.WriteLine(dt.ToString("HH:MM")); // 14:06 ← 月が出てる(6月→06)
実行結果(HH:MM で分のはずが月の 06 が出る・.NET 9 で再現):

エラーは出ない。14:06 ってそれっぽい値が出るから、テストでも気付きにくい。
6月だからたまたま 06。月初の 00:00 付近だと、「分」と区別すらつかなくなります。これがマジでタチ悪い。
対策はこれだけ。時刻系(HH:mm:ss)は全部小文字、日付系は yyyy/MM/dd で月だけ大文字。口で一度唱えてから書くと踏まなくなります。
② CultureInfo を指定しないと環境で表示が崩れる
これもやられました。
開発機(日本語環境)では 2026/06/30 ってちゃんと出るのに、本番サーバーに載せたら 6/30/2026 になってた、みたいなやつ。
原因は、標準書式("d" とか)や DateTime.Parse が、実行環境のカルチャを見にいくから。
var dt = new DateTime(2026, 6, 30);
// 実行環境のカルチャ次第で形が変わる
Console.WriteLine(dt.ToString("d"));
// ja-JP なら 2026/06/30、en-US なら 6/30/2026
// 環境に依存させたくないなら InvariantCulture を明示する
Console.WriteLine(dt.ToString("yyyy/MM/dd", CultureInfo.InvariantCulture));
// どの環境でも 2026/06/30
サーバーのロケール設定や、和暦が有効な環境だと、"D" で「令和8年」が出ることすらある。
判断はシンプルです。ログ・ファイル名・連携みたいに「いつ・どこで動いても同じ形であってほしい」場面は、必ず CultureInfo.InvariantCulture を渡す。逆に画面表示でユーザーの言語に合わせたい時だけ、あえてカルチャに任せる。
yyyy/MM/dd みたいにカスタム書式で桁まで固定してても、月名や曜日名(ddd)は言語で変わります。連携用途なら InvariantCulture を付けとくのが安全。
もうひとつ落とし穴があって。カスタム書式の中の / や : は、実は「区切り文字」のプレースホルダ扱いなんですよね。これもカルチャで化けます。ドイツ語環境(de-DE)だと yyyy/MM/dd が 2026.06.30 になる。区切りまでガチで固定したいなら、yyyy'/'MM'/'dd とクオートで囲むか、InvariantCulture を渡すのが確実です。
現場メモ: 日付フォーマットで時間を溶かさないために
流通系の基幹システムの保守をやってた頃の話。夜間バッチが吐くログのタイムスタンプがバラバラで、障害調査のたびに「このログとこのログ、どっちが先??」で消耗してました。
原因は単純。書いた人ごとに yyyy/MM/dd HH:mm だったり MM/dd HH:mm:ss だったり、ミリ秒があったりなかったり。
そこからの学びは1個だけ。プロジェクトの頭で「ログは yyyy-MM-dd HH:mm:ss.fff で統一」と決めて、共通メソッドに閉じ込める。
public static class DateFmt
{
// ログ・連携はこれを通す。書式の散らばりを1箇所に集約する。
public static string ForLog(DateTime dt)
=> dt.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
}
こうしとくと、後から「ミリ秒も足したい」となっても1箇所直すだけ。いい感じに保守が楽になります。
ちなみに今回は「日付 → 文字列」の整形の話でした。逆向きの「文字列 → DateTime」、つまりユーザー入力やCSVの日付を読む側でハマる人は、DateTime.TryParse の使い方をまとめた記事のほうが効きます。後半の関連記事に置いときました。
まとめ
要点はこれだけ。
- 画面は
yyyy/MM/dd、ファイル名はyyyyMMdd_HHmmss、ログはyyyy-MM-dd HH:mm:ss.fff、連携は ISO 8601 MMは月、mmは分。時刻系は全部小文字で覚える- 環境で形を変えたくないなら
CultureInfo.InvariantCultureを明示 - 書式は共通メソッドに閉じ込めて散らばらせない
C# の日付・時刻まわりは、標準ライブラリ系の解説書を1冊手元に置いておくと、TimeSpan や DateTimeOffset まで地続きで押さえられて、結局そっちが早いです。
このページ、必要な行だけコピペして使ってください。
よくある質問
Q1. yyyyMMdd と書いたのに区切りが入ってしまうのはなぜ?
書式文字列を二重引用符で正しく囲めていないか、文字列補間の中で {dt:yyyyMMdd} の形になっていない可能性が高いです。dt.ToString("yyyyMMdd") なら 20260630 のように区切りなしで出ます。$"{dt:yyyyMMdd}" でも同じ結果になります。
Q2. HH と hh はどう違いますか?
HH が24時間表記(0〜23)、hh が12時間表記(1〜12)です。hh を使う時は tt(午前/午後)を一緒に付けないと、午後2時なのか午前2時なのか区別できなくなります。業務ログは原則 HH の24時間でそろえるのが無難です。
Q3. ミリ秒は fff と FFF のどちらを使えばいいですか?
固定で3桁出したいなら fff、末尾のゼロを省きたいなら FFF(大文字)です。ログの桁をそろえて読みやすくしたい場面では fff のほうが扱いやすいです。秒の小数を7桁まで出したい場合は fffffff まで指定できます。
Q4. 開発機と本番で日付の表示が違うのを直したいです。
標準書式("d" や "D")や Parse が実行環境のカルチャを見ているのが原因です。dt.ToString("yyyy/MM/dd", CultureInfo.InvariantCulture) のようにカスタム書式とカルチャを両方明示すれば、どの環境でも同じ形で出力できます。
Q5. now と書くだけで現在時刻が入りますか?
DateTime.Now がローカル時刻、DateTime.UtcNow が協定世界時です。サーバーをまたぐ連携では UtcNow で持っておき、画面表示の直前にローカルへ変換するのが事故りにくいやり方です。タイムゾーンの扱いは関連記事のほうで詳しく書いています。
次に読むべき記事





以上!
同じ罠でログの時刻を溶かしてる人いたら、どんどんシェア待ってるぜ!!
執筆者
バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る




