はじめに 「時刻丸め」は“時間の解像度を落とすテクニック”
「打刻は5分単位にそろえたい」「グラフは1分刻みで表示したい」「ログを10秒単位でまとめたい」
こういう“細かすぎる時刻を、キリのいい時刻にそろえる”処理が「時刻丸め」です。
C# には専用メソッドはありませんが、DateTime と TimeSpan を組み合わせれば、
「切り捨て」「切り上げ」「四捨五入」を自由な単位(1分、5分、15分、10秒…)で実装できます。
ここでは、まず「時・分・秒を直接いじる基本形」から入り、
そのあと「任意の間隔に丸める汎用ユーティリティ」、
最後に「実務でハマりやすいポイント」まで、順にかみ砕いて説明します。
基本形:時刻の一部を0にそろえる(切り捨て)
分・秒を0にして「時間単位」にそろえる
まずは一番イメージしやすい、「分・秒を0にして時間単位にそろえる」例です。
public static DateTime TruncateToHour(DateTime dt)
{
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0, dt.Kind);
}
C#使い方の例です。
DateTime dt = new DateTime(2026, 2, 18, 20, 45, 30);
DateTime truncated = TruncateToHour(dt);
Console.WriteLine(dt); // 2026/02/18 20:45:30
Console.WriteLine(truncated); // 2026/02/18 20:00:00
C#やっていることは、「年・月・日・時だけを残し、分・秒を0にする」です。
これは「1時間単位への切り捨て」と考えてよいです。
同じように、「秒以下を0にして“分単位”にそろえる」こともできます。
public static DateTime TruncateToMinute(DateTime dt)
{
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
}
C#ここでの重要ポイントは、「new DateTime(...) で“残したい粒度”だけを指定し、残りを0にする」というパターンです。
この形が、時刻丸め(切り捨て)の基本になります。
汎用形:任意の間隔に丸める(TimeSpan を使う)
考え方のコアは「Ticks を割って、掛け直す」
「5分単位」「15分単位」「10秒単位」など、自由な間隔に丸めたいときは、TimeSpan を使って「割る→掛ける」という考え方をします。
まずは「切り捨て(floor)」から。
public static DateTime Floor(DateTime dt, TimeSpan interval)
{
if (interval <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(interval));
long ticks = dt.Ticks / interval.Ticks * interval.Ticks;
return new DateTime(ticks, dt.Kind);
}
C#使い方の例(5分単位に切り捨て)。
DateTime dt = new DateTime(2026, 2, 18, 20, 07, 30); // 20:07:30
DateTime floored = Floor(dt, TimeSpan.FromMinutes(5));
Console.WriteLine(floored); // 2026/02/18 20:05:00
C#やっていることを言葉で整理すると、
「元の時刻の Ticks を interval(ここでは5分)の Ticks で割り、
小数点以下を捨ててから、もう一度 interval.Ticks を掛けて“キリのいい時刻”を作る」
という流れです。
切り上げと四捨五入も同じパターンで書ける
切り上げ(ceiling)の実装
切り上げは、「少しでもはみ出していたら次の区切りに進める」丸め方です。
public static DateTime Ceiling(DateTime dt, TimeSpan interval)
{
if (interval <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(interval));
long ticks = (dt.Ticks + interval.Ticks - 1) / interval.Ticks * interval.Ticks;
return new DateTime(ticks, dt.Kind);
}
C#使い方の例(5分単位に切り上げ)。
DateTime dt = new DateTime(2026, 2, 18, 20, 07, 30); // 20:07:30
DateTime ceiled = Ceiling(dt, TimeSpan.FromMinutes(5));
Console.WriteLine(ceiled); // 2026/02/18 20:10:00
C#+ interval.Ticks - 1 してから割ることで、
「ちょっとでもはみ出していたら1つ上の区切りに行く」という動きになります。
四捨五入(round)の実装
四捨五入は、「区切りの真ん中より前なら下、後なら上」に丸める方法です。
public static DateTime Round(DateTime dt, TimeSpan interval)
{
if (interval <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(interval));
long halfTicks = (interval.Ticks + 1) / 2;
long ticks = (dt.Ticks + halfTicks) / interval.Ticks * interval.Ticks;
return new DateTime(ticks, dt.Kind);
}
C#使い方の例(5分単位に四捨五入)。
DateTime dt1 = new DateTime(2026, 2, 18, 20, 07, 29); // 20:07:29 → 20:05
DateTime dt2 = new DateTime(2026, 2, 18, 20, 07, 31); // 20:07:31 → 20:10
Console.WriteLine(Round(dt1, TimeSpan.FromMinutes(5)));
Console.WriteLine(Round(dt2, TimeSpan.FromMinutes(5)));
C#ここでの重要ポイントは、「丸めの本質は“Ticks を interval で割って掛け直す”」ということです。
切り捨て・切り上げ・四捨五入の違いは、「割る前に Ticks に何を足すか」に現れます。
「時刻だけ」を扱いたいときの考え方
DateTime か TimeSpan か
「日付はどうでもよくて、1日の中の時刻だけを丸めたい」
というケースもよくあります(例:営業時間の管理、タイムテーブルなど)。
この場合、2つのアプローチがあります。
ひとつは、今まで通り DateTime を使い、日付は適当な同じ日(例えば DateTime.Today)にそろえてしまう方法です。
もうひとつは、「0:00 からの経過時間」として TimeSpan で扱う方法です。
TimeSpan で扱う場合も、考え方はまったく同じです。
public static TimeSpan Floor(TimeSpan time, TimeSpan interval)
{
if (interval <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(interval));
long ticks = time.Ticks / interval.Ticks * interval.Ticks;
return new TimeSpan(ticks);
}
C#使い方の例(1日の中の時刻を5分単位に切り捨て)。
TimeSpan t = new TimeSpan(10, 07, 30); // 10:07:30
TimeSpan floored = Floor(t, TimeSpan.FromMinutes(5));
Console.WriteLine(floored); // 10:05:00
C#「日付をまたがない前提の“時刻だけ”」なら TimeSpan のほうが意図が明確で、
日付との混同も起きにくくなります。
実務での注意点:丸め方の違いと Kind の扱い
切り捨て・切り上げ・四捨五入を“なんとなく”選ばない
同じ「5分単位」でも、丸め方によって結果は変わります。
20:07 を切り捨て → 20:05
20:07 を切り上げ → 20:10
20:07 を四捨五入 → 20:05
20:08 を切り捨て → 20:05
20:08 を切り上げ → 20:10
20:08 を四捨五入 → 20:10
勤怠、料金計算、課金などでは、
どの丸め方を採用するかでお金が変わることもあります。
「何のために丸めるのか」を決めてから、Floor / Ceiling / Round のどれを使うかを選ぶことが重要です。
DateTimeKind を必ず引き継ぐ
DateTime には Kind(Unspecified / Local / Utc)があります。
丸めるときに new DateTime(ticks) のように書くと、Kind が Unspecified になってしまいます。
ここまでのコードでは、すべて new DateTime(ticks, dt.Kind) のように、
元の Kind を引き継ぐようにしていました。
return new DateTime(ticks, dt.Kind);
C#これを忘れると、
「UTC のつもりだった値が Kind 不明になり、後で ToLocalTime したときにおかしくなる」
といった事故につながります。
丸めユーティリティを書くときは、
「Kind を必ず引き継ぐ」というルールを自分の中で固定しておくと安全です。
まとめ 「時刻丸めユーティリティ」は“時間の粒度をコントロールする道具」
時刻丸めは、見た目は小さなテクニックですが、
ログ集計、グラフ表示、勤怠、料金計算など、
「時間をどう区切るか」が重要な場面で必ず効いてきます。
押さえておきたいポイントをまとめると、次のようなイメージです。
時・分・秒を直接指定して0にするのが、最も素直な「切り捨て」の形。
任意の間隔への丸めは、「Ticks を interval で割って掛け直す」パターンで、切り捨て・切り上げ・四捨五入を実装できる。
「日付+時刻」で扱うなら DateTime、「1日の中の時刻だけ」なら TimeSpan で扱うと意図が明確になる。
実務では「どの丸め方を使うか」と「DateTimeKind を壊さないこと」が特に重要になる。
ここまで理解できれば、
「なんとなく時刻をいじっている」段階から抜け出して、
“ビジネスロジックに合わせて時間の粒度をきちんとコントロールできる、実務で使える時刻丸めユーティリティ”を
自分の C# コードの中に自然に組み込めるようになります。

