「対象年月」をどう持つか、意外と迷う
経費精算や売上の集計のような業務システムでは、「2026年10月分」のように年月の単位でデータを扱うことがよくあります。
年月だけを持ちたいのに、DateTimeだと日や時刻まで付いてきて扱いにくい
C#には、年と月だけを表す型がありません。そこで立替精算システムでは、年月を「202610」のような6桁の文字列(yyyyMM)で持ち、計算するときだけ日付に直す小さな部品を作っています。
本記事では、その部品を使って、月初・月末・先月・○か月後・日付が年月に入っているかを簡単に求める方法を紹介します。
立替精算システムで使ったところ
私が自作した立替精算システムでは、申請を「対象年月」ごとにまとめています。締め処理・仕訳データの出力・集計も、対象年月の単位で行います。

締め処理の画面です。対象年月ごとに申請の件数と合計金額を出し、月ごとに締めます。上に「今月は2026年10月、入力できる月は2026年9月〜2026年10月」と出しているのも、この部品で求めています。

申請の入力画面です。明細の日付が申請の年月に入っていない場合は、印を付けて上長や経理に分かるようにしています。
年月を6桁の文字列で持つ理由
| 持ち方 | よいところ | 困るところ |
|---|---|---|
| “202610”(6桁の文字列) | 並べ替えると年月の順になる。データベースの種類に関係なく同じ形で保存できる。見て分かる | 計算するときは日付に直す必要がある |
| 202610(数値) | 並べ替えられる | そのまま足し算すると 202612+1=202613 になる |
| DateTime・DateOnly(その月の1日) | 日付の計算がそのままできる | 「年月」なのか「その日」なのかが分かりにくい |
立替精算システムはSQLite・PostgreSQL・SQL Serverのどれでも動くように設計している為、データベースごとの日付の型の違いに左右されない、6桁の文字列にしています。
C#で年月(yyyyMM)を扱うサンプルコード
動作確認は Windows 11・.NET 10 です(.NET 8 以降で動きます)。Visual Studio でコンソールアプリを作り、Program.cs に貼り付けて実行してください。.NET 10 なら、ファイルに保存して dotnet run ファイル名.cs でも実行できます。
using System.Globalization;
// 年月(yyyyMM)を扱う
var kyou = new DateOnly(2026, 10, 25);
var tougetsu = Nengetsu.From(kyou);
Console.WriteLine($"今日 :{kyou:yyyy/MM/dd}");
Console.WriteLine($"今月 :{tougetsu}({Nengetsu.ToHyouji(tougetsu)})");
Console.WriteLine($"先月 :{Nengetsu.AddMonths(tougetsu, -1)}");
Console.WriteLine($"3 か月後 :{Nengetsu.AddMonths(tougetsu, 3)}(年をまたぐ)");
Console.WriteLine($"今月の月初 :{Nengetsu.Shonichi(tougetsu):yyyy/MM/dd}");
Console.WriteLine($"今月の月末 :{Nengetsu.Matsujitsu(tougetsu):yyyy/MM/dd}");
Console.WriteLine($"2028年2月の月末 :{Nengetsu.Matsujitsu("202802"):yyyy/MM/dd}(うるう年)");
Console.WriteLine();
// 明細の日付が、申請の年月に入っているか
foreach (var hiduke in new[] { new DateOnly(2026, 10, 1), new DateOnly(2026, 10, 31), new DateOnly(2026, 9, 30) })
{
Console.WriteLine($"{hiduke:yyyy/MM/dd} は {Nengetsu.ToHyouji(tougetsu)} に入る? {Nengetsu.Contains(tougetsu, hiduke)}");
}
Console.WriteLine();
// 入力できる月の範囲(今月から 1 か月さかのぼれる場合)
var kaishi = Nengetsu.AddMonths(tougetsu, -1);
Console.WriteLine($"入力できる月 :{Nengetsu.ToHyouji(kaishi)} 〜 {Nengetsu.ToHyouji(tougetsu)}");
Console.WriteLine();
// 正しい年月か
foreach (var s in new[] { "202610", "202613", "2026/10", "20261" })
{
Console.WriteLine($"\"{s}\" は正しい年月? {Nengetsu.IsValid(s)}");
}
/// <summary>年月(yyyyMM の 6 桁の文字列)を扱う。</summary>
public static class Nengetsu
{
public const string Format = "yyyyMM";
/// <summary>日付から年月を作る。</summary>
public static string From(DateOnly hiduke) =>
hiduke.ToString(Format, CultureInfo.InvariantCulture);
/// <summary>正しい年月か。</summary>
public static bool IsValid(string? nengetsu) =>
nengetsu is { Length: 6 }
&& DateOnly.TryParseExact(
nengetsu + "01", "yyyyMMdd", CultureInfo.InvariantCulture,
DateTimeStyles.None, out _);
/// <summary>その年月の初日。</summary>
public static DateOnly Shonichi(string nengetsu) =>
DateOnly.ParseExact(nengetsu + "01", "yyyyMMdd", CultureInfo.InvariantCulture);
/// <summary>その年月の末日(次の月の初日の前の日)。</summary>
public static DateOnly Matsujitsu(string nengetsu) =>
Shonichi(nengetsu).AddMonths(1).AddDays(-1);
/// <summary>日付がその年月に入っているか。</summary>
public static bool Contains(string nengetsu, DateOnly hiduke) =>
hiduke >= Shonichi(nengetsu) && hiduke <= Matsujitsu(nengetsu);
/// <summary>○か月後(マイナスなら前)の年月。</summary>
public static string AddMonths(string nengetsu, int months) =>
From(Shonichi(nengetsu).AddMonths(months));
/// <summary>画面に出す形(2026年10月)。</summary>
public static string ToHyouji(string nengetsu)
{
if (!IsValid(nengetsu))
{
return nengetsu ?? string.Empty;
}
var shonichi = Shonichi(nengetsu);
return $"{shonichi.Year}年{shonichi.Month}月";
}
}実行結果
今日 :2026/10/25
今月 :202610(2026年10月)
先月 :202609
3 か月後 :202701(年をまたぐ)
今月の月初 :2026/10/01
今月の月末 :2026/10/31
2028年2月の月末 :2028/02/29(うるう年)
2026/10/01 は 2026年10月 に入る? True
2026/10/31 は 2026年10月 に入る? True
2026/09/30 は 2026年10月 に入る? False
入力できる月 :2026年9月 〜 2026年10月
"202610" は正しい年月? True
"202613" は正しい年月? False
"2026/10" は正しい年月? False
"20261" は正しい年月? False12月をまたぐ「3か月後」や、うるう年の2月の月末も正しく求められています。
コードの説明
Shonichiは、年月の後ろに “01” を付けて、その月の1日のDateOnlyに直します。ParseExactで形を決めて読むので、”2026/10″ のような違う形は受け付けません。Matsujitsuは、月末を「次の月の1日の前の日」で求めます。月によって28日・29日・30日・31日と変わる日数を自分で数えずに済み、うるう年も正しく出ます。AddMonthsは、一度日付に直してからAddMonthsで月を進め、また6桁に戻します。12月の次は翌年の1月になります。CultureInfo.InvariantCultureは、PCの地域の設定(カレンダーを和暦にしている場合など)に左右されずに、いつも西暦の “yyyyMM” で読み書きするために指定しています。IsValidは、TryParseExactで読めるかどうかで、”202613″(13月)のような正しくない年月をはじきます。
入力できる月の範囲を求める
立替精算システムでは、「今月から何か月さかのぼって入力できるか」をシステム設定で決められます。範囲は AddMonths を使うだけで求められます。
var tougetsu = Nengetsu.From(clock.Today); // 今月(202610)
var kaishi = Nengetsu.AddMonths(tougetsu, -sakanoboru); // さかのぼれる一番古い月(202609)
// 6 桁の文字列は、そのまま比べても年月の順になる
var hairu = string.CompareOrdinal(nengetsu, kaishi) >= 0
&& string.CompareOrdinal(nengetsu, tougetsu) <= 0;6桁の文字列は、文字として比べても年月の順になります。「申請の年月が範囲に入っているか」は、string.CompareOrdinal で比べるだけで判定できます。今日の日付(clock.Today)の取り方は、日本時間(JST)を取得する記事で紹介しています。
注意点
- 6桁の文字列で持つときは、保存する前に必ず
IsValidで確かめてください。”2026/10″ や “20261” が混ざると、並べ替えや比較が正しくなくなります。 - 文字列の大小を比べるときは
string.CompareOrdinalを使ってください。CompareToはPCの地域の設定によって比べ方が変わることがあります。 - 4月始まりの会計年度などで集計するときは、年月から年度を求める処理を別に用意してください。
最後に
以上が、C#で年月(yyyyMM)を扱う方法です。
月初・月末・○か月後の計算は、DateOnly に直して AddMonths・AddDays を使えば、うるう年や年またぎを気にせずに済みます。小さな部品にまとめておくと、締め処理・集計・入力チェックのどこでも同じ書き方で使えます。
この部品を使っている立替精算システムは、こちらで紹介しています。
Pythonで日付や時刻を指定した形で取得する方法は、こちらで紹介しています。




コメント