sql server 2012+推荐用eomonth获取月末、datefromparts获取月初,语义清晰、类型安全、跨版本一致;老版本需用dateadd+datediff组合,易因时区、类型隐式转换出错。

SQL Server 2012 及以后版本,直接用 EOMONTH 配合 DATEFROMPARTS 或 DATEADD 最稳妥;老版本必须靠 DATEADD + DATEDIFF 组合推算,且容易因时区或数据类型隐式转换出错。
用 EOMONTH 拿本月最后一天最省事
它专为这个场景设计,语义清晰、性能好、支持 date / datetime / 字符串输入(自动转换),还能跨月计算:
-
EOMONTH(GETDATE())→ 当前月最后一天(date类型,无时间部分) -
EOMONTH(GETDATE(), -1)→ 上个月最后一天 -
EOMONTH('2026-07-15')→ 输入任意日期,返回其所在月最后一天
注意:EOMONTH 不接受 smalldatetime;若传入含毫秒的 datetime,结果仍为当天 00:00:00.000,但返回类型是 date(除非显式 cast)。
获取本月第一天:优先用 DATEFROMPARTS(SQL Server 2012+)
比传统 DATEADD(dd, -DAY(GETDATE()) + 1, GETDATE()) 更直观、无歧义:
-
DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1)→ 返回date类型的当月1日 - 它不依赖当前时间的“日”值,避免了
DAY()在月末跨月时的边界问题(比如 31 号执行-DAY()+1算出来是 1 号,但若某月只有 30 天,逻辑上没问题,可读性差) - 如果必须兼容 SQL Server 2008,才退回到
DATEADD(MM, DATEDIFF(MM, 0, GETDATE()), 0)—— 这个写法本质是“把当前时间归零到月份起点”,但要注意:参数0被解释为1900-01-01,在极老环境可能有隐式转换开销
别踩这些坑
常见错误不是语法错,而是语义和类型错:
- 混用
GETDATE()和GETUTCDATE():如果业务按本地时区算月结,却用了 UTC 时间,会导致跨日偏差(比如北京时间 8 月 1 日 00:00 对应 UTC 是 7 月 31 日 16:00) - 返回值类型不明确:老写法如
DATEADD(dd, -DAY(GETDATE()) + 1, GETDATE())返回的是datetime,带时间部分(如2026-08-01 14:22:33.123),做范围查询时容易漏掉当天数据;而DATEFROMPARTS和EOMONTH默认返回date,更干净 - 忽略
date类型精度:它只存日期,没有时间。如果你需要“8 月 1 日 00:00:00”这种精确时间点用于BETWEEN,得显式 cast 成datetime2或拼接字符串
真正麻烦的从来不是“怎么写出来”,而是“写出来的结果是否在所有边界日期(如 1 月 1 日、2 月 29 日、跨年)和不同 SQL Server 版本下行为一致”。EOMONTH 和 DATEFROMPARTS 把这事封死了,老方法则得靠测试兜底。










