【C#】DateTimeと文字列の相互変換まとめ(TryParse / 書式指定 / yyyyMMdd)【現場の備忘録】

プログラム

日付と文字列の変換、とくに和暦変換ってどうやるんだっけってなるので基本と一緒にまとめておく。

例にもれず今回もVisualStudio2022.Net8.0、作成プロジェクトはコンソールアプリ、OSはWindows11で各実装をおこなっている。

文字列 → DateTime(TryParseで安全に)

変換元の文字列が不正だとDateTime.Parseは例外になる。失敗を例外にせず判定したいならTryParseを使う。

string s = "2024/01/15";

// 成功/失敗を bool で受けられる(失敗しても例外にならない)
if (DateTime.TryParse(s, out DateTime dt))
{
    Console.WriteLine(dt); // 2024/01/15 0:00:00
}
else
{
    Console.WriteLine("変換失敗");
}

DateTime → 文字列(ToStringの書式指定)

ToStringに書式を渡すと、好きな形に整形できる。

DateTime dt = new DateTime(2024, 1, 15, 9, 5, 3);

Console.WriteLine(dt.ToString("yyyy/MM/dd"));          // 2024/01/15
Console.WriteLine(dt.ToString("yyyy年M月d日"));         // 2024年1月15日
Console.WriteLine(dt.ToString("yyyyMMdd"));            // 20240115
Console.WriteLine(dt.ToString("yyyy-MM-dd HH:mm:ss")); // 2024-01-15 09:05:03
Console.WriteLine(dt.ToString("HH:mm"));               // 09:05

よく使う書式指定子は、yyyy=西暦4桁、MM=月2桁、dd=日2桁、HH=24時間、mm=分、ss=秒。月は大文字MM、分は小文字mm(ここを間違えると意図しない値になる)。

“20240101”のような区切りなし文字列を扱う

"20240101"のような区切りのない日付文字列は、TryParseだとうまく解釈できないことがある。書式をこちらで教えるParseExact / TryParseExactを使う。

using System.Globalization;

string s = "20240101";

// 「この文字列は yyyyMMdd 形式だよ」と明示して変換
if (DateTime.TryParseExact(
        s, "yyyyMMdd",
        CultureInfo.InvariantCulture,
        DateTimeStyles.None,
        out DateTime dt))
{
    Console.WriteLine(dt.ToString("yyyy/MM/dd")); // 2024/01/01
}

実装例

まとめて動かすとこうなる。Program.csにコピペで確認できる。

Program.cs
using System;
using System.Globalization;

class Program
{
    static void Main()
    {
        // 文字列 → DateTime
        if (DateTime.TryParse("2024/01/15", out DateTime d1))
            Console.WriteLine("TryParse     : " + d1.ToString("yyyy/MM/dd"));

        // DateTime → 文字列(いろいろな書式)
        DateTime dt = new DateTime(2024, 1, 15, 9, 5, 3);
        Console.WriteLine("yyyy/MM/dd   : " + dt.ToString("yyyy/MM/dd"));
        Console.WriteLine("yyyyMMdd     : " + dt.ToString("yyyyMMdd"));
        Console.WriteLine("yyyy-MM-dd HH:mm:ss : " + dt.ToString("yyyy-MM-dd HH:mm:ss"));

        // 区切りなし文字列 → DateTime
        if (DateTime.TryParseExact("20240101", "yyyyMMdd",
                CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime d2))
            Console.WriteLine("TryParseExact: " + d2.ToString("yyyy/MM/dd"));
    }
}
実行結果
TryParse     : 2024/01/15
yyyy/MM/dd   : 2024/01/15
yyyyMMdd     : 20240115
yyyy-MM-dd HH:mm:ss : 2024-01-15 09:05:03
TryParseExact: 2024/01/01

和暦との相互変換

社内向け帳票やお役所提出用の資料だと、令和6年1月15日のような和暦表記を求められることがある。CultureInfoJapaneseCalendarを指定すれば、書式指定子はそのままで和暦の入出力ができる。

DateTime → 和暦文字列

gg(元号)とyy(和暦年数)を組み合わせて、CultureInfoCalendarJapaneseCalendarに差し替えるだけでいい。

using System.Globalization;

var jpCulture = new CultureInfo("ja-JP");
jpCulture.DateTimeFormat.Calendar = new JapaneseCalendar();

DateTime dt = new DateTime(2024, 1, 15);
Console.WriteLine(dt.ToString("ggyy年M月d日", jpCulture)); // 令和6年1月15日

要注意なのが元年の扱い。上記の方法だと改元1年目は「令和1年」と出力され、本来使いたい「令和年」にはならない。役所提出書類など「元年」表記が必須の場面では、自前で置換する必要がある。

// 元年表記にしたい場合のヘルパー
static string ToWarekiString(DateTime dt)
{
    var jpCulture = new CultureInfo("ja-JP");
    jpCulture.DateTimeFormat.Calendar = new JapaneseCalendar();

    string s = dt.ToString("ggyy年M月d日", jpCulture);
    // 「令和1年」→「令和元年」に置換(各元号1年目のみ該当)
    return System.Text.RegularExpressions.Regex.Replace(s, "(?<=\\D)1年", "元年");
}

Console.WriteLine(ToWarekiString(new DateTime(2019, 5, 1)));  // 令和元年5月1日
Console.WriteLine(ToWarekiString(new DateTime(2024, 1, 15))); // 令和6年1月15日

和暦文字列 → DateTime

逆方向も同じjpCultureを使えばTryParseExactで変換できる。ただし「元年」という表記はそのままだと解析できないので、パース前に数字の「1」へ置換しておく。

static bool TryParseWareki(string wareki, out DateTime result)
{
    var jpCulture = new CultureInfo("ja-JP");
    jpCulture.DateTimeFormat.Calendar = new JapaneseCalendar();

    // 「元年」を「1年」に置換してからパースする
    string normalized = wareki.Replace("元年", "1年");

    return DateTime.TryParseExact(
        normalized, "ggyy年M月d日",
        jpCulture, DateTimeStyles.None,
        out result);
}

if (TryParseWareki("令和6年1月15日", out DateTime dt1))
    Console.WriteLine(dt1.ToString("yyyy/MM/dd")); // 2024/01/15

if (TryParseWareki("令和元年5月1日", out DateTime dt2))
    Console.WriteLine(dt2.ToString("yyyy/MM/dd")); // 2019/05/01

元号が変わるたびに.NETやOS側のアップデートが必要になる点も地味にハマりどころ。改元直後はOSやランタイムのバージョンによって新元号が認識されないことがあるので、本番投入前に動作確認しておくと安心。

さいごに

迷ったら読み込みはTryParse(区切りなしはTryParseExact)、書き出しはToStringに書式、と覚えておけば大体足りる。

特に月MMと分mmの大文字小文字だけは間違えやすいので注意。

コメント

タイトルとURLをコピーしました