【保存版】C#での日付・時刻の加算と比較の方法まとめ|DateTimeとTimeSpanの使い方を完全マスター!

C#

「1週間後の日付を求めたい」
「今と過去の日時を比較したい」

そんなときに活躍するのが、C#のDateTime構造体とTimeSpan構造体です。

本記事では、C#で日付や時刻を加算・減算・比較する方法を、基本的な文法と具体的な例を交えてわかりやすく解説します。

実際の開発でよく使うパターンから、つまずきやすい落とし穴まで、初心者から中級者の方が安心して使えるように詳しく説明していきます。

スポンサーリンク

DateTime と TimeSpan の基本概念

DateTime:特定の日時を表す

// 現在の日時
DateTime now = DateTime.Now;
Console.WriteLine(now);  // 2025/06/03 14:30:45

// 特定の日時を指定
DateTime specificDate = new DateTime(2025, 12, 25, 9, 0, 0);
Console.WriteLine(specificDate);  // 2025/12/25 9:00:00

// 今日の日付のみ(時刻は00:00:00)
DateTime today = DateTime.Today;
Console.WriteLine(today);  // 2025/06/03 0:00:00

TimeSpan:時間の長さを表す

// 様々な時間間隔の作成
TimeSpan oneDay = TimeSpan.FromDays(1);
TimeSpan twoHours = TimeSpan.FromHours(2);
TimeSpan thirtyMinutes = TimeSpan.FromMinutes(30);
TimeSpan fiveSeconds = TimeSpan.FromSeconds(5);

// コンストラクタでの作成(日、時、分、秒)
TimeSpan duration = new TimeSpan(1, 2, 30, 45);  // 1日2時間30分45秒

Console.WriteLine(oneDay);        // 1.00:00:00
Console.WriteLine(twoHours);      // 02:00:00
Console.WriteLine(thirtyMinutes); // 00:30:00

日時の加算・減算:AddメソッドとTimeSpan

基本のAddメソッド

DateTime now = DateTime.Now;
Console.WriteLine($"現在: {now}");

// 各種の加算
DateTime nextYear = now.AddYears(1);
DateTime nextMonth = now.AddMonths(3);
DateTime nextWeek = now.AddDays(7);
DateTime nextHour = now.AddHours(2);
DateTime nextMinute = now.AddMinutes(30);
DateTime nextSecond = now.AddSeconds(45);

Console.WriteLine($"1年後: {nextYear}");
Console.WriteLine($"3ヶ月後: {nextMonth}");
Console.WriteLine($"1週間後: {nextWeek}");
Console.WriteLine($"2時間後: {nextHour}");
Console.WriteLine($"30分後: {nextMinute}");
Console.WriteLine($"45秒後: {nextSecond}");

よく使うAddメソッド一覧

メソッド名説明
AddYears(int)年を加算date.AddYears(2)
AddMonths(int)月を加算date.AddMonths(6)
AddDays(double)日を加算date.AddDays(7.5)
AddHours(double)時間を加算date.AddHours(12)
AddMinutes(double)分を加算date.AddMinutes(90)
AddSeconds(double)秒を加算date.AddSeconds(3600)
AddMilliseconds(double)ミリ秒を加算date.AddMilliseconds(500)

減算もできる(マイナス値を使用)

DateTime now = DateTime.Now;

// 過去の日時を求める
DateTime lastWeek = now.AddDays(-7);     // 1週間前
DateTime lastMonth = now.AddMonths(-1);  // 1ヶ月前
DateTime lastYear = now.AddYears(-1);    // 1年前

Console.WriteLine($"1週間前: {lastWeek}");
Console.WriteLine($"1ヶ月前: {lastMonth}");
Console.WriteLine($"1年前: {lastYear}");

TimeSpanを使った加算・減算

DateTime now = DateTime.Now;

// TimeSpanを作成
TimeSpan oneWeek = TimeSpan.FromDays(7);
TimeSpan twoHours = TimeSpan.FromHours(2);
TimeSpan thirtyMinutes = TimeSpan.FromMinutes(30);

// TimeSpanを使った加算
DateTime futureDate1 = now + oneWeek;
DateTime futureDate2 = now + twoHours;
DateTime futureDate3 = now + thirtyMinutes;

// TimeSpanを使った減算
DateTime pastDate1 = now - oneWeek;
DateTime pastDate2 = now - twoHours;

Console.WriteLine($"1週間後: {futureDate1}");
Console.WriteLine($"2時間後: {futureDate2}");
Console.WriteLine($"30分後: {futureDate3}");
Console.WriteLine($"1週間前: {pastDate1}");
Console.WriteLine($"2時間前: {pastDate2}");

複雑な時間間隔の作成

// コンストラクタを使用(時、分、秒)
TimeSpan time1 = new TimeSpan(2, 30, 45);  // 2時間30分45秒

// コンストラクタを使用(日、時、分、秒)
TimeSpan time2 = new TimeSpan(1, 12, 30, 45);  // 1日12時間30分45秒

// コンストラクタを使用(日、時、分、秒、ミリ秒)
TimeSpan time3 = new TimeSpan(0, 1, 30, 45, 500);  // 1時間30分45秒500ミリ秒

// Addメソッドで複数の時間を組み合わせ
TimeSpan combined = TimeSpan.FromDays(1)
    .Add(TimeSpan.FromHours(6))
    .Add(TimeSpan.FromMinutes(30));

Console.WriteLine($"組み合わせた時間: {combined}");  // 1.06:30:00

実際の業務でよく使う加算パターン

営業日の計算

public static DateTime AddBusinessDays(DateTime startDate, int businessDays)
{
    DateTime result = startDate;
    int addedDays = 0;
    
    while (addedDays < businessDays)
    {
        result = result.AddDays(1);
        
        // 土曜日(6)と日曜日(0)以外を営業日とする
        if (result.DayOfWeek != DayOfWeek.Saturday && 
            result.DayOfWeek != DayOfWeek.Sunday)
        {
            addedDays++;
        }
    }
    
    return result;
}

// 使用例
DateTime today = DateTime.Today;
DateTime fiveBusinessDaysLater = AddBusinessDays(today, 5);
Console.WriteLine($"5営業日後: {fiveBusinessDaysLater:yyyy/MM/dd}");

期限切れチェック

public class Product
{
    public string Name { get; set; }
    public DateTime ExpiryDate { get; set; }
    
    public bool IsExpired()
    {
        return DateTime.Now > ExpiryDate;
    }
    
    public bool WillExpireWithin(TimeSpan timespan)
    {
        return DateTime.Now + timespan > ExpiryDate;
    }
    
    public TimeSpan TimeUntilExpiry()
    {
        return ExpiryDate - DateTime.Now;
    }
}

// 使用例
Product product = new Product 
{ 
    Name = "牛乳", 
    ExpiryDate = DateTime.Now.AddDays(3) 
};

Console.WriteLine($"期限切れ?: {product.IsExpired()}");
Console.WriteLine($"1週間以内に期限切れ?: {product.WillExpireWithin(TimeSpan.FromDays(7))}");
Console.WriteLine($"期限まで: {product.TimeUntilExpiry().Days}日");

スケジュール管理

public class Appointment
{
    public DateTime StartTime { get; set; }
    public TimeSpan Duration { get; set; }
    
    public DateTime EndTime => StartTime + Duration;
    
    public bool OverlapsWith(Appointment other)
    {
        return StartTime < other.EndTime && EndTime > other.StartTime;
    }
}

// 使用例
var meeting1 = new Appointment 
{ 
    StartTime = DateTime.Today.AddHours(9), 
    Duration = TimeSpan.FromHours(1) 
};

var meeting2 = new Appointment 
{ 
    StartTime = DateTime.Today.AddHours(9.5), 
    Duration = TimeSpan.FromMinutes(30) 
};

Console.WriteLine($"会議が重複?: {meeting1.OverlapsWith(meeting2)}");

日時の比較:大小関係と差分の取得

基本的な比較演算子

DateTime date1 = new DateTime(2025, 6, 3, 14, 30, 0);
DateTime date2 = new DateTime(2025, 6, 3, 15, 30, 0);
DateTime date3 = new DateTime(2025, 6, 3, 14, 30, 0);

// 等価比較
bool isEqual = date1 == date3;        // true
bool isNotEqual = date1 != date2;     // true

// 大小比較
bool isBefore = date1 < date2;        // true
bool isAfter = date2 > date1;         // true
bool isBeforeOrEqual = date1 <= date3; // true
bool isAfterOrEqual = date2 >= date1;  // true

Console.WriteLine($"date1 == date3: {isEqual}");
Console.WriteLine($"date1 < date2: {isBefore}");
Console.WriteLine($"date2 > date1: {isAfter}");

CompareToメソッドの使用

DateTime date1 = DateTime.Now;
DateTime date2 = date1.AddHours(1);

int comparison = date1.CompareTo(date2);

if (comparison < 0)
    Console.WriteLine("date1はdate2より前");
else if (comparison > 0)
    Console.WriteLine("date1はdate2より後");
else
    Console.WriteLine("date1とdate2は同じ");

// 結果: "date1はdate2より前"

日時の差分計算

DateTime start = new DateTime(2025, 6, 1, 9, 0, 0);
DateTime end = new DateTime(2025, 6, 3, 17, 30, 0);

// 差分を計算
TimeSpan duration = end - start;
// または
TimeSpan duration2 = end.Subtract(start);

Console.WriteLine($"開始: {start}");
Console.WriteLine($"終了: {end}");
Console.WriteLine($"期間: {duration}");
Console.WriteLine($"総日数: {duration.TotalDays:F2}日");
Console.WriteLine($"総時間: {duration.TotalHours:F2}時間");
Console.WriteLine($"総分: {duration.TotalMinutes:F0}分");

TimeSpanの便利なプロパティ

DateTime start = DateTime.Now;
DateTime end = start.AddDays(2).AddHours(5).AddMinutes(30);
TimeSpan span = end - start;

Console.WriteLine($"差分: {span}");

// 各プロパティの使い分け
Console.WriteLine($"Days: {span.Days}");               // 日数部分のみ(整数)
Console.WriteLine($"TotalDays: {span.TotalDays:F2}");  // 全体を日数で表現(小数)

Console.WriteLine($"Hours: {span.Hours}");             // 時間部分のみ(0-23)
Console.WriteLine($"TotalHours: {span.TotalHours:F2}"); // 全体を時間で表現

Console.WriteLine($"Minutes: {span.Minutes}");         // 分部分のみ(0-59)
Console.WriteLine($"TotalMinutes: {span.TotalMinutes:F0}"); // 全体を分で表現

Console.WriteLine($"Seconds: {span.Seconds}");         // 秒部分のみ(0-59)
Console.WriteLine($"TotalSeconds: {span.TotalSeconds:F0}"); // 全体を秒で表現

より実践的な比較とソート

日時のリストをソート

List<DateTime> dates = new List<DateTime>
{
    new DateTime(2025, 3, 15),
    new DateTime(2025, 1, 20),
    new DateTime(2025, 12, 5),
    new DateTime(2025, 6, 10)
};

// 昇順ソート(古い順)
dates.Sort();
Console.WriteLine("昇順:");
foreach (var date in dates)
{
    Console.WriteLine(date.ToString("yyyy/MM/dd"));
}

// 降順ソート(新しい順)
dates.Sort((x, y) => y.CompareTo(x));
Console.WriteLine("\n降順:");
foreach (var date in dates)
{
    Console.WriteLine(date.ToString("yyyy/MM/dd"));
}

期間内の判定

public static bool IsDateInRange(DateTime date, DateTime start, DateTime end)
{
    return date >= start && date <= end;
}

// 使用例
DateTime targetDate = new DateTime(2025, 6, 15);
DateTime periodStart = new DateTime(2025, 6, 1);
DateTime periodEnd = new DateTime(2025, 6, 30);

bool inRange = IsDateInRange(targetDate, periodStart, periodEnd);
Console.WriteLine($"期間内?: {inRange}");  // true

年齢計算

public static int CalculateAge(DateTime birthDate, DateTime currentDate)
{
    int age = currentDate.Year - birthDate.Year;
    
    // 誕生日前なら1歳引く
    if (currentDate.Month < birthDate.Month || 
        (currentDate.Month == birthDate.Month && currentDate.Day < birthDate.Day))
    {
        age--;
    }
    
    return age;
}

// 使用例
DateTime birthDate = new DateTime(1990, 8, 15);
DateTime today = DateTime.Today;
int age = CalculateAge(birthDate, today);
Console.WriteLine($"年齢: {age}歳");

日時処理の注意点と落とし穴

1. UTCとローカル時刻の混在

問題のあるコード

DateTime utcTime = DateTime.UtcNow;
DateTime localTime = DateTime.Now;

bool areEqual = utcTime == localTime;  // ❌ false(時差があるため)
Console.WriteLine($"UTC: {utcTime}");
Console.WriteLine($"Local: {localTime}");
Console.WriteLine($"同じ?: {areEqual}");

解決方法

DateTime utcTime = DateTime.UtcNow;
DateTime localTime = DateTime.Now;

// どちらかに統一して比較
bool areEqualUtc = utcTime == localTime.ToUniversalTime();
bool areEqualLocal = utcTime.ToLocalTime() == localTime;

Console.WriteLine($"UTC統一で比較: {areEqualUtc}");
Console.WriteLine($"ローカル統一で比較: {areEqualLocal}");

2. ミリ秒の精度による問題

問題のあるコード

DateTime time1 = DateTime.Now;
Thread.Sleep(1);  // 1ミリ秒待機
DateTime time2 = DateTime.Now;

bool areEqual = time1 == time2;  // ❌ false(ミリ秒が異なる)

解決方法:許容範囲を設ける

public static bool AreTimesEqual(DateTime time1, DateTime time2, TimeSpan tolerance)
{
    TimeSpan difference = time1 > time2 ? time1 - time2 : time2 - time1;
    return difference <= tolerance;
}

// 使用例
DateTime time1 = DateTime.Now;
Thread.Sleep(10);
DateTime time2 = DateTime.Now;

bool areClose = AreTimesEqual(time1, time2, TimeSpan.FromSeconds(1));
Console.WriteLine($"1秒以内の差?: {areClose}");  // true

3. Addメソッドでインスタンスが変更されない

問題のあるコード

DateTime now = DateTime.Now;
now.AddDays(1);  // ❌ nowは変更されない
Console.WriteLine(now);  // 元の日時のまま

正しいコード

DateTime now = DateTime.Now;
now = now.AddDays(1);  // ✅ 戻り値を代入する
// または
DateTime tomorrow = now.AddDays(1);  // ✅ 新しい変数に代入

4. 月末日の計算に注意

問題が起こる例

DateTime january31 = new DateTime(2025, 1, 31);
DateTime february = january31.AddMonths(1);
Console.WriteLine(february);  // 2025/02/28(2月は28日まで)

DateTime march = february.AddMonths(1);
Console.WriteLine(march);     // 2025/03/28(元の31日ではない)

月末日を正しく取得する方法

public static DateTime GetLastDayOfMonth(int year, int month)
{
    return new DateTime(year, month, DateTime.DaysInMonth(year, month));
}

// 使用例
DateTime lastDayFeb2025 = GetLastDayOfMonth(2025, 2);
Console.WriteLine(lastDayFeb2025);  // 2025/02/28

DateTime lastDayFeb2024 = GetLastDayOfMonth(2024, 2);  // うるう年
Console.WriteLine(lastDayFeb2024);  // 2024/02/29

5. 時刻を含む日付の比較

問題のあるコード

DateTime today = DateTime.Today;           // 2025/06/03 0:00:00
DateTime userInput = DateTime.Parse("2025/06/03 14:30:00");

bool isSameDay = today == userInput;  // ❌ false(時刻が異なる)

解決方法:日付のみで比較

DateTime today = DateTime.Today;
DateTime userInput = DateTime.Parse("2025/06/03 14:30:00");

bool isSameDay = today.Date == userInput.Date;  // ✅ true
// または
bool isSameDay2 = today == userInput.Date;      // ✅ true

高度な日時処理テクニック

DateTimeOffsetの活用

// タイムゾーンを含む日時
DateTimeOffset tokyoTime = new DateTimeOffset(2025, 6, 3, 14, 30, 0, TimeSpan.FromHours(9));
DateTimeOffset newYorkTime = new DateTimeOffset(2025, 6, 3, 1, 30, 0, TimeSpan.FromHours(-4));

// UTC時刻での比較(正確)
bool areSimultaneous = tokyoTime.UtcDateTime == newYorkTime.UtcDateTime;
Console.WriteLine($"同時刻?: {areSimultaneous}");  // true

カスタムDateTimeヘルパークラス

public static class DateTimeHelper
{
    public static DateTime StartOfDay(this DateTime date)
    {
        return date.Date;
    }
    
    public static DateTime EndOfDay(this DateTime date)
    {
        return date.Date.AddDays(1).AddTicks(-1);
    }
    
    public static DateTime StartOfWeek(this DateTime date, DayOfWeek startOfWeek = DayOfWeek.Monday)
    {
        int diff = (7 + (date.DayOfWeek - startOfWeek)) % 7;
        return date.AddDays(-diff).Date;
    }
    
    public static DateTime EndOfWeek(this DateTime date, DayOfWeek startOfWeek = DayOfWeek.Monday)
    {
        return date.StartOfWeek(startOfWeek).AddDays(7).AddTicks(-1);
    }
    
    public static DateTime StartOfMonth(this DateTime date)
    {
        return new DateTime(date.Year, date.Month, 1);
    }
    
    public static DateTime EndOfMonth(this DateTime date)
    {
        return date.StartOfMonth().AddMonths(1).AddTicks(-1);
    }
}

// 使用例
DateTime now = DateTime.Now;
Console.WriteLine($"今日の開始: {now.StartOfDay()}");
Console.WriteLine($"今日の終了: {now.EndOfDay()}");
Console.WriteLine($"今週の開始: {now.StartOfWeek()}");
Console.WriteLine($"今月の開始: {now.StartOfMonth()}");
Console.WriteLine($"今月の終了: {now.EndOfMonth()}");

まとめ

C#での日時の加算・比較処理は、基本的な操作から複雑なビジネスロジックまで、様々な場面で必要になる重要なスキルです。

重要なポイント

  • 加算・減算AddXXX()メソッドとTimeSpanの使い分け
  • 比較:演算子とCompareToメソッドの活用
  • 精度:ミリ秒やタイムゾーンの違いに注意
  • 不変性:DateTimeは不変なので戻り値の受け取りが必要

ベストプラクティス

  • UTC時刻とローカル時刻を適切に使い分ける
  • 日付のみの比較では.Dateプロパティを使用
  • 許容範囲を設けた時刻比較を実装
  • 月末日や営業日計算では専用メソッドを作成

よく使う場面

  • スケジュール管理システム
  • 期限管理と通知機能
  • 勤怠管理システム
  • ログの時系列分析
  • データの期間フィルタリング

コメント

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