首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 報表查詢利器:DateTime 預設日期範圍一鍵生成(本週/月/季/年及相對週期)

     編輯:關於C語言

核心設計原則

  • 以「今天 00:00:00」為基準: 使用 DateTime.Today 避免時間部分干擾邊界判斷。
  • 先對齊大週期起點,再微調: 如「本月第一天 → 加 1 月 → 減 1 天 = 本月最後一天」。
  • 統一回傳 DateTime 型別: 由呼叫端決定 .ToString("yyyy-MM-dd") 或傳給參數化查詢,避免字串拼接 SQL 風險。

一站式範圍生成器


public static class DateRangePresets
{
    // 基準日 (可注入測試)
    private static DateTime Today => DateTime.Today; 

    // ========== 週範圍 (週日為週起始,符合 .NET DayOfWeek) ==========
    public static (DateTime Start, DateTime End) ThisWeek() 
    {
        var start = Today.AddDays(-(int)Today.DayOfWeek);
        return (start, start.AddDays(6));
    }
    public static (DateTime Start, DateTime End) LastWeek() => ThisWeek().AddDays(-7);
    public static (DateTime Start, DateTime End) NextWeek() => ThisWeek().AddDays(7);

    // 擴充方法簡化寫法
    private static (DateTime Start, DateTime End) AddDays(this (DateTime, DateTime) r, int days) 
        => (r.Item1.AddDays(days), r.Item2.AddDays(days));

    // ========== 月範圍 ==========
    public static (DateTime Start, DateTime End) ThisMonth()
    {
        var start = new DateTime(Today.Year, Today.Month, 1);
        return (start, start.AddMonths(1).AddDays(-1));
    }
    public static (DateTime Start, DateTime End) LastMonth() => ThisMonth().AddMonths(-1);
    public static (DateTime Start, DateTime End) NextMonth() => ThisMonth().AddMonths(1);

    private static (DateTime Start, DateTime End) AddMonths(this (DateTime, DateTime) r, int months) 
        => (r.Item1.AddMonths(months), r.Item2.AddMonths(months));

    // ========== 季範圍 ==========
    public static (DateTime Start, DateTime End) ThisQuarter()
    {
        int q = (Today.Month - 1) / 3; // 0,1,2,3
        var start = new DateTime(Today.Year, q * 3 + 1, 1);
        return (start, start.AddMonths(3).AddDays(-1));
    }
    public static (DateTime Start, DateTime End) LastQuarter() => ThisQuarter().AddMonths(-3);
    public static (DateTime Start, DateTime End) NextQuarter() => ThisQuarter().AddMonths(3);

    private static (DateTime Start, DateTime End) AddMonths(this (DateTime, DateTime) r, int months) 
        => (r.Item1.AddMonths(months), r.Item2.AddMonths(months));

    // ========== 年範圍 ==========
    public static (DateTime Start, DateTime End) ThisYear() 
        => (new DateTime(Today.Year, 1, 1), new DateTime(Today.Year, 12, 31));
    public static (DateTime Start, DateTime End) LastYear() => ThisYear().AddYears(-1);
    public static (DateTime Start, DateTime End) NextYear() => ThisYear().AddYears(1);

    private static (DateTime Start, DateTime End) AddYears(this (DateTime, DateTime) r, int years) 
        => (r.Item1.AddYears(years), r.Item2.AddYears(years));

    // ========== 相對天數 ==========
    public static (DateTime Start, DateTime End) LastNDays(int n) 
        => (Today.AddDays(-n), Today);
    public static (DateTime Start, DateTime End) NextNDays(int n) 
        => (Today, Today.AddDays(n));
}

Controller / Service 使用範例


public class ReportController : Controller
{
    public IActionResult Sales(string range = "thisMonth")
    {
        (DateTime start, DateTime end) = range switch
        {
            "today"      => (DateTime.Today, DateTime.Today),
            "yesterday"  => (DateTime.Today.AddDays(-1), DateTime.Today.AddDays(-1)),
            "thisWeek"   => DateRangePresets.ThisWeek(),
            "lastWeek"   => DateRangePresets.LastWeek(),
            "thisMonth"  => DateRangePresets.ThisMonth(),
            "lastMonth"  => DateRangePresets.LastMonth(),
            "thisQuarter"=> DateRangePresets.ThisQuarter(),
            "lastQuarter"=> DateRangePresets.LastQuarter(),
            "thisYear"   => DateRangePresets.ThisYear(),
            "lastYear"   => DateRangePresets.LastYear(),
            "last7d"     => DateRangePresets.LastNDays(7),
            "last30d"    => DateRangePresets.LastNDays(30),
            _            => DateRangePresets.ThisMonth()
        };

        // 參數化查詢,完全避免 SQL 注入與格式問題
        var data = _db.Sales
            .Where(s => s.Date >= start && s.Date <= end)
            .GroupBy(s => s.ProductId)
            .Select(g => new { Product = g.Key, Total = g.Sum(x => x.Amount) })
            .ToList();

        ViewBag.Range = $"{start:yyyy-MM-dd} ~ {end:yyyy-MM-dd}";
        return View(data);
    }
}

前端下拉選單綁定 (Vue/React/jQuery 通用)



ToString 格式化技巧:字串直得邊界

若需直接產生字串 (如檔名、URL 參數),可用格式化捷徑:


// 月首日
string monthStartStr = DateTime.Today.ToString("yyyy-MM-01"); // "2024-03-01"
// 月末日
string monthEndStr = DateTime.Parse(monthStartStr).AddMonths(1).AddDays(-1).ToString("yyyy-MM-dd");
// 年首日
string yearStartStr = DateTime.Today.ToString("yyyy-01-01");
// 季首日 (需計算)
int q = (DateTime.Today.Month - 1) / 3;
string quarterStartStr = new DateTime(DateTime.Today.Year, q * 3 + 1, 1).ToString("yyyy-MM-dd");

日期比較陷阱與正確姿勢

錯誤做法問題正確做法
strDate1 > strDate2字串字典序非時間序DateTime.Parse(str1) > DateTime.Parse(str2)
new DateTime(y, m, d+3)月末溢位拋例外date.AddDays(3)
忽略時區比較跨時區用戶見不同結果統一用 UTC 或 DateOnly
DateTime.Compare(d1, d2)含時間部分干擾d1.Date.CompareTo(d2.Date)

實務案例:標記「最新 3 天」資料


public bool IsRecent(DateTime createTime)
{
    // 僅比較日期部分,忽略建立時間的時分秒
    return DateTime.Today <= createTime.Date.AddDays(3);
}

// LINQ to Entities 相容寫法 (EF Core 轉譯為 SQL)
var recentLogs = db.Logs.Where(l => l.CreateTime >= DateTime.Today.AddDays(-3)).ToList();

.NET 6+ 現代化:DateOnly 更精準表達「純日期」


DateOnly today = DateOnly.FromDateTime(DateTime.Today);
DateOnly weekStart = today.AddDays(-(int)today.DayOfWeek);
DateOnly monthStart = new DateOnly(today.Year, today.Month, 1);
DateOnly quarterStart = new DateOnly(today.Year, (today.Month - 1) / 3 * 3 + 1, 1);

// 轉回 DateTime 給舊 API
DateTime dt = weekStart.ToDateTime(TimeOnly.MinValue);

總結

將日期範圍邏輯封裝為 靜態工具類 + 擴充方法,配合 參數化查詢 與 前端枚舉值對應,即可實現報表查詢「零硬編碼、零格式錯誤、零時區坑」。記住三句口訣:用 Today 不用 Now、用 AddDays 不用算天數、用 DateOnly 表達純日期。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved