- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
本篇围绕 Humanizer 的On.March类展开,它是流式日期(Fluent Date)API 家族中专门表达"当前年份三月的某一天"的静态访问器集合。读完本文,你将掌握On.March.The1st~The31st全部 31 个日期属性的准确语义与返回类型、The(int dayNumber)方法的动态取值用法,并理解这些 API 由 T4 模板批量生成的底层原理、与At/AtNoon/AtMidnight等介词扩展的组合套路,以及"读取当前年份"这一行为在可重复测试与确定型代码中的注意事项。
On.March类概览
On.March是嵌套在静态类On内部的公共类,位于Humanizer命名空间下,用于为三月提供流式日期访问器(fluent date accessors):
public class On.March从继承关系看,它是一个直接继承自System.Object的普通类,类内所有成员都是public static,因此使用时不需实例化,直接以On.March.xxx形式调用。该类的完整源码位于 src/Humanizer/FluentDate/On.Days.cs,整个On类在同一文件中按月份嵌套定义了January~December共 12 个类似的月类。
在 Humanizer 的日期 API 体系中,On系列承担"构造日历日期"的职责:On.April.The3rd、On.March.The15th这类调用读起来就是一句自然语言。配套的还有提供DateOnly变体的OnDate类,以及表示"将来时间"的In系列(如In.Two.MonthsFrom(startingPoint)),三者共同构成 website/docs/scenarios/fluent-dates-and-time-spans.mdx 中描述的"可读性日期构造器"。
完整 API 清单:31 个静态日期属性
On.March为三月的每一天(1 日至 31 日)提供对应的静态属性,命名采用序数词后缀(The1st、The2nd、The3rd……),每个属性的返回值都是System.DateTime,语义统一为"当前年份三月的第 N 天"。
属性总表
| 属性名 | 说明 | 返回类型 |
|---|---|---|
On.March.The1st | 当前年份三月的第 1 天 | System.DateTime |
On.March.The2nd | 当前年份三月的第 2 天 | System.DateTime |
On.March.The3rd | 当前年份三月的第 3 天 | System.DateTime |
On.March.The4th | 当前年份三月的第 4 天 | System.DateTime |
On.March.The5th | 当前年份三月的第 5 天 | System.DateTime |
On.March.The6th | 当前年份三月的第 6 天 | System.DateTime |
On.March.The7th | 当前年份三月的第 7 天 | System.DateTime |
On.March.The8th | 当前年份三月的第 8 天 | System.DateTime |
On.March.The9th | 当前年份三月的第 9 天 | System.DateTime |
On.March.The10th | 当前年份三月的第 10 天 | System.DateTime |
On.March.The11th | 当前年份三月的第 11 天 | System.DateTime |
On.March.The12th | 当前年份三月的第 12 天 | System.DateTime |
On.March.The13th | 当前年份三月的第 13 天 | System.DateTime |
On.March.The14th | 当前年份三月的第 14 天 | System.DateTime |
On.March.The15th | 当前年份三月的第 15 天 | System.DateTime |
On.March.The16th | 当前年份三月的第 16 天 | System.DateTime |
On.March.The17th | 当前年份三月的第 17 天 | System.DateTime |
On.March.The18th | 当前年份三月的第 18 天 | System.DateTime |
On.March.The19th | 当前年份三月的第 19 天 | System.DateTime |
On.March.The20th | 当前年份三月的第 20 天 | System.DateTime |
On.March.The21st | 当前年份三月的第 21 天 | System.DateTime |
On.March.The22nd | 当前年份三月的第 22 天 | System.DateTime |
On.March.The23rd | 当前年份三月的第 23 天 | System.DateTime |
On.March.The24th | 当前年份三月的第 24 天 | System.DateTime |
On.March.The25th | 当前年份三月的第 25 天 | System.DateTime |
On.March.The26th | 当前年份三月的第 26 天 | System.DateTime |
On.March.The27th | 当前年份三月的第 27 天 | System.DateTime |
On.March.The28th | 当前年份三月的第 28 天 | System.DateTime |
On.March.The29th | 当前年份三月的第 29 天 | System.DateTime |
On.March.The30th | 当前年份三月的第 30 天 | System.DateTime |
On.March.The31st | 当前年份三月的第 31 天 | System.DateTime |
方法:The(int dayNumber)
除了 31 个命名属性,On.March还提供一个参数化方法,用于动态取得三月的任意一天:
public static System.DateTime The(int dayNumber);- 参数
dayNumber:类型System.Int32,表示目标日期在三月中的日序号(1~31)。 - 返回:
System.DateTime,即"当前年份三月第dayNumber天"。 - 边界行为:从源码
new(DateTime.Now.Year, 3, dayNumber)可以看出,dayNumber会直接传入DateTime构造函数;超出 1~31 范围(例如The(32)或The(0))会抛出ArgumentOutOfRangeException,这是由System.DateTime构造函数本身强制校验的,Humanizer 未做额外包装。
参数化方法与命名属性的取舍很直接:写死业务日期(如营销活动固定为 3 月 14 日)用The14th更自文档化;日期来自变量或计算结果时用The(dayNumber)。
源码实现:一行表达式与 T4 模板批量生成
On.March的每个成员实现都极其精简,本质是对DateTime构造函数的封装。以属性The1st为例,源码为:
public static DateTime The1st => new(DateTime.Now.Year, 3, 1);即"取当前系统时间的年份,固定月份 3,固定日序号",返回值带 0 时 0 分 0 秒,不携带时区信息。The(int dayNumber)同理:
public static DateTime The(int dayNumber) => new(DateTime.Now.Year, 3, dayNumber);从源码结构看,这段代码并非手写 12 个月 × 各自天数的大量样板,而是由一个 T4 文本模板批量生成的。src/Humanizer/FluentDate/On.Days.tt 中,模板以 2012 年(闰年,用于覆盖 2 月 29 日)为基准遍历 12 个月,用firstDayOfMonth.ToString("MMMM")取得英文月份名作为嵌套类名,再用day.Ordinalize()生成The1st、The2nd……这种序数后缀:
for (var day = 1; day <= DateTime.DaysInMonth(leapYear, month); day++) { var ordinalDay = day.Ordinalize(); // 生成 public static DateTime The<ordinalDay> => new(DateTime.Now.Year, <month>, <day>); }Ordinalize()是 Humanizer 自身的序数词扩展(实现位于 src/Humanizer/OrdinalizeExtensions.cs),模板由此保证1 → 1st、2 → 2nd、3 → 3rd、21 → 21st等英文序数后缀的规范性。这也是为什么On.March的 31 个属性名严格遵循英文序数词形。
顺带一提,模板对每个月的天数取自闰年 2012 的DateTime.DaysInMonth,所以二月生成到The29th,而三月固定生成 31 个属性——三月在任何年份都有 31 天,因此On.March不存在闰年下溢问题。
组合用法:从"三月某日"到完整时间点
On.March.TheNth只产出日期(时间为 00:00:00)。要拼出完整的业务时间点,可与 PrepositionsExtensions 提供的扩展方法链式组合:
using Humanizer; // 当前年份 3 月 14 日 14:30:00 var piDay = On.March.The14th.At(14, 30); // 当前年份 3 月 1 日 12:00:00 var noon = On.March.The1st.AtNoon(); // 当前年份 3 月 31 日 00:00:00 var monthEndMidnight = On.March.The31st.AtMidnight(); // 动态日序号:某月的第 n 天 var dynamicDay = On.March.The(dayNumber);At(int hour, int min = 0, int second = 0, int millisecond = 0)允许一次性指定时、分、秒、毫秒;AtNoon()等价于At(12),AtMidnight()等价于At(0)。官方可运行示例 website/docs/_examples/scenarios-fluent-dates/Program.cs 演示了同类用法(如In.AprilOf(2025).AddDays(2).At(14, 30)),可直接参照此模式组织三月日程。
如需将三月日期固定到指定年份(而非"当前年份"),可用PrepositionsExtensions.In(int year):
var marchFirst2026 = On.March.The1st.In(2026); // 2026-03-01 00:00:00若目标框架支持DateOnly(.NET 6+),可改用OnDate.March系列(源码见 src/Humanizer/FluentDate/OnDate.Days.cs),其 API 形状与On.March完全一致,只是返回System.DateOnly。
测试验证
Humanizer 测试套件 tests/Humanizer.Tests/FluentDate/OnTests.cs 直接验证了On系列访问器与"当前年份构造"的等价性:
[Fact] public void OnJanuaryThe23rd() => Assert.Equal(new(DateTime.Now.Year, 1, 23), On.January.The23rd); [Fact] public void OnDecemberThe4th() => Assert.Equal(new(DateTime.Now.Year, 12, 4), On.December.The4th); [Fact] public void OnFebruaryThe() => Assert.Equal(new(DateTime.Now.Year, 2, 11), On.February.The(11));这些断言确认了两点实现事实:命名属性与new DateTime(DateTime.Now.Year, month, day)完全等价;The(dayNumber)方法同样以DateTime.Now.Year为年份基准。On.March的成员遵循同一模板逻辑,行为可由此类推。
使用注意事项与边界
- 依赖"当前年份":
On.March所有成员都读取DateTime.Now.Year,因此结果随调用时刻变化。官方场景文档 website/docs/scenarios/fluent-dates-and-time-spans.mdx 明确建议:在需要可重复执行的代码或测试中,避免依赖"当前时间"的访问器,应注入固定起始日期(例如用In.Two.MonthsFrom(startingPoint)配合显式DateTime),或直接用In(2026)/OnDate固定年份。 - 本地时间而非 UTC:
DateTime.Now是本地时钟时间,跨时区部署时同一代码在不同服务器上会得到不同结果;需要 UTC 语义时应自行用DateTime.UtcNow.Year组合构造。 - 时间部分恒为 0:属性与
The(n)返回的时间均为午夜零点;需要具体时刻务必再接At/AtNoon/AtMidnight。 - 参数范围:
The(dayNumber)的dayNumber越界会抛异常;三月固定 31 天,不存在 2 月 29 日式的闰年特例。 - API 归属:
On.March属于 Humanizer 的Humanizer.FluentDate命名空间文件组(见 src/Humanizer/FluentDate 目录),随主程序集一并分发,引入using Humanizer;即可使用,无需额外安装独立包。
相关 API 与延伸阅读
- 同族 API:[
On类(12 个月完整访问器,源码 src/Humanizer/FluentDate/On.Days.cs)](website/docs/api/Humanizer.On.md)、OnDate类(DateOnly变体) - 配套扩展:
PrepositionsExtensions(At/AtNoon/AtMidnight/In(year)) - 实战场景:流式组合日期与时长 及官方示例 Program.cs
- 测试佐证:OnTests.cs
简而言之,On.March.The14th一类调用把"三月十四日"变成了可读、可组合、类型安全的 C# 表达式——在报表截止日、活动排期、账单日等固定日历场景中,它比手写new DateTime(DateTime.Now.Year, 3, 14)更贴近业务语义,也让代码评审者一眼读懂意图。
- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
相关推荐
Humanizer 流畅日期访问器 On.March 全解析:3 月日期构建 API 的用法与源码实现
Humanizer 流畅日期访问器 On.March 全解析:3 月日期构建 API 的用法与源码实现 导读 On.March 是 Humanizer 流畅日期
开发工具Stable-Baselines3 Logger 完整指南:自定义日志格式与训练指标解读
Stable Baselines3 Logger 完整指南:自定义日志格式与训练指标解读 Stable Baselines3(SB3)内置了一套灵活的日志系统,
开发工具Humanizer 流式日期 API 详解:On.June 六月日期访问器完全指南
Humanizer 流式日期 API 详解:On.June 六月日期访问器完全指南 本篇技术指南围绕 Humanizer 流式日期(Fluent Date)AP
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考