- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
本篇技术指南深入解析 Humanizer FluentDate 体系中的OnDate.March类:它是面向System.DateOnly的流畅日期访问器,让你以OnDate.March.The14th或OnDate.March.The(14)这种接近自然语言的表达式,直接获取"当前年份 3 月 14 日"这一日期值。读完本文,你将掌握该类的全部 31 个静态属性与The(int)方法的签名、返回值语义、底层实现原理(T4 模板代码生成)、运行环境前提(.NET 6+),以及它与On系列(DateTime版本)的对应关系,并能在自己的 .NET 项目中直接落地使用。
OnDate.March 类是什么
OnDate.March是 Humanizer 中OnDate静态类的一个嵌套类,文档原文将其定义为"Provides fluent date accessors for March"(为三月提供流畅的日期访问器)。其完整声明为:
public class OnDate.March继承关系为System.Object→OnDate.March,同时该类还带有一个无参构造函数March(),用于初始化类的实例(由于成员均为静态,实际使用时无需实例化)。
在 API 文档中,OnDate.March暴露的成员可以分成三类:
- 31 个静态只读属性:
The1st到The31st,分别对应 3 月的 1 日到 31 日; - 1 个静态方法:
The(int dayNumber),按传入的日期数字返回对应的 3 月日期; - 1 个构造函数:
March()。
OnDate.March属于 Humanizer FluentDate 命名空间 中的OnDate体系。与On类(返回System.DateTime)不同,OnDate系列专门为 .NET 6 引入的DateOnly类型设计,因此整个类被#if NET6_0_OR_GREATER条件编译指令保护——只有目标框架为 .NET 6 或更高版本时,这些成员才可用。
全部 31 个日期属性:The1st 到 The31st
OnDate.March为 3 月的每一天都生成了一个静态属性,属性名采用英文序数词风格命名(The1st、The2nd、The3rd……The31st),每个属性都返回一个System.DateOnly值,语义为"当前年份三月的第 N 天"。完整清单如下:
| 属性 | 说明 | 属性 | 说明 |
|---|---|---|---|
The1st | 3 月 1 日 | The17th | 3 月 17 日 |
The2nd | 3 月 2 日 | The18th | 3 月 18 日 |
The3rd | 3 月 3 日 | The19th | 3 月 19 日 |
The4th | 3 月 4 日 | The20th | 3 月 20 日 |
The5th | 3 月 5 日 | The21st | 3 月 21 日 |
The6th | 3 月 6 日 | The22nd | 3 月 22 日 |
The7th | 3 月 7 日 | The23rd | 3 月 23 日 |
The8th | 3 月 8 日 | The24th | 3 月 24 日 |
The9th | 3 月 9 日 | The25th | 3 月 25 日 |
The10th | 3 月 10 日 | The26th | 3 月 26 日 |
The11th | 3 月 11 日 | The27th | 3 月 27 日 |
The12th | 3 月 12 日 | The28th | 3 月 28 日 |
The13th | 3 月 13 日 | The29th | 3 月 29 日 |
The14th | 3 月 14 日 | The30th | 3 月 30 日 |
The15th | 3 月 15 日 | The31st | 3 月 31 日 |
The16th | 3 月 16 日 |
每个属性的签名完全一致,例如:
public static System.DateOnly The14th { get; }API 文档中标注的Property Value类型均为System.DateOnly。由于属性是静态且只读的,访问时直接使用OnDate.March.The14th即可,无需任何实例。
The(int) 方法:按数字取三月的任意一天
除了固定命名的属性,OnDate.March还提供一个通用的The(int dayNumber)方法,用于按日期数字动态获取 3 月的某一天:
public static System.DateOnly The(int dayNumber);- 参数
dayNumber:System.Int32,表示要获取的 3 月日期数字(如 14 表示 3 月 14 日); - 返回值:
System.DateOnly,即当前年份 3 月dayNumber日对应的日期。
实际使用示例:
using Humanizer; var piDay = OnDate.March.The14th; // 3 月 14 日(圆周率日) var sameDay = OnDate.March.The(14); // 与上面等价,动态传参 Console.WriteLine(piDay); // 输出形如:2026-03-14 Console.WriteLine(piDay == sameDay); // True当日期数字需要在运行时计算、无法预先静态写出时(例如来自用户输入或循环遍历),The(int)是比 31 个命名属性更灵活的选择。
底层实现:T4 模板批量生成的源代码
OnDate.March并非手写代码,而是由 T4 文本模板自动生成。模板位于 src/Humanizer/FluentDate/OnDate.Days.tt,其生成逻辑如下:
- 以闰年
2012为基准,遍历 1 到 12 月,通过new DateTime(leapYear, month, 1).ToString("MMMM")得到月份英文名,从而生成January、February、March等 12 个嵌套类; - 对每个月,用
DateTime.DaysInMonth(leapYear, month)确定该月天数(3 月为 31 天),因此March类恰好生成 31 个属性; - 每个日期属性的名字由
day.Ordinalize()生成——这正是 Humanizer 自己的序数词扩展方法(1→1st、2→2nd、3→3rd、21→21st等),从而得到The1st、The2nd、The3rd直到The31st的属性名; - 每个属性与
The(int)方法内部都统一使用new(DateTime.Now.Year, month, day)构造DateOnly。
生成的实体代码位于 src/Humanizer/FluentDate/OnDate.Days.cs。以March类(从第 391 行开始)为例,其成员实现如下:
public class March { /// <summary> /// The nth day of March of the current year /// </summary> public static DateOnly The(int dayNumber) => new(DateTime.Now.Year, 3, dayNumber); /// <summary> /// The 1st day of March of the current year /// </summary> public static DateOnly The1st => new(DateTime.Now.Year, 3, 1); // ... The2nd 到 The31st 依此类推 }从源码结构可以看出两个关键事实:
- 年份取自系统当前时间:所有成员都使用
DateTime.Now.Year作为年份,因此返回的总是"当前年份"的 3 月日期。若在 2026 年运行,OnDate.March.The1st即为2026-03-01; - 跨年自动生效:由于年份是运行时动态取的,应用程序跨年后无需修改任何代码,访问器会自动指向新一年的 3 月。
测试验证:生成结果如何被保障
仓库通过两层测试保证 31 个属性与The(int)方法的正确性:
1. 直接断言测试(tests/Humanizer.Tests/FluentDate/OnDateTests.cs)
该测试文件使用与生成代码相同的DateTime.Now.Year构造期望值,验证访问器与手写构造的结果一致:
[Fact] public void OnJanuaryThe23rd() => Assert.Equal(new(DateTime.Now.Year, 1, 23), OnDate.January.The23rd); [Fact] public void OnFebruaryThe() => Assert.Equal(new(DateTime.Now.Year, 2, 11), OnDate.February.The(11));2. 反射式全覆盖测试(tests/Humanizer.Tests/FluentDate/GeneratedFluentDateTests.cs)
该测试通过反射遍历OnDate下所有月份嵌套类:
OnDateDayPropertiesCoverAllGeneratedDayAccessors断言所有月份嵌套类的静态属性都能被解析为"月 + 日"并返回正确的DateOnly;OnDateTheMethodsCoverAllGeneratedMonthFactories断言每个月份类都存在The(int)方法且调用The(1)返回该月 1 日;- 解析属性名时采用
The前缀 + 序数后缀的规则(TryParseDayProperty),能识别1st/2nd/3rd/4th…全部 31 个属性名。
这两类测试共同确保了"模板生成的 31 个属性数量正确、命名符合序数词规则、返回日期与手写new DateOnly(year, 3, day)完全等价"。
环境前提与限制
使用OnDate.March需要注意以下约束:
- 目标框架必须为 .NET 6 或更高:因为返回值类型是
System.DateOnly,整个OnDate类(连同InDate)都被#if NET6_0_OR_GREATER条件编译包裹(见 src/Humanizer/FluentDate/OnDate.Days.cs 第 1 行)。在 .NET Framework 或 .NET 5 及以下项目中无法使用; - 属性是静态的:直接通过
OnDate.March.The14th访问,不需要实例化March类; The(int)的入参是普通整数:传入 1~31 之间的任意整数即可,若需要特定命名可优先使用属性版本。
与 On.March(DateTime 版)的对应关系
Humanizer 的 FluentDate 提供了两套平行的日期 API:On系列返回DateTime,OnDate系列返回DateOnly。场景文档 fluent-dates-and-time-spans.mdx 明确说明:"InDateandOnDateprovide correspondingDateOnlyvalues on compatible frameworks"——即在支持DateOnly的框架上,OnDate是On的对应版本。
两者的成员结构完全平行:On.March.The14th返回包含时间分量的DateTime(时间为零点),而OnDate.March.The14th返回不含时间的DateOnly。在选择时可以参考以下规则:
- 只需要"日期"语义、不关心时间部分、且目标是 .NET 6+:优先使用
OnDate.March; - 需要
DateTime(例如要与旧 API 互操作、参与时区转换):使用On.March。
此外,InDate系列(如InDate.March的月份访问器与In(5).Days之类的相对偏移)提供了从"当前日期"偏移的DateOnly访问器,与OnDate的"绝对某月某日"语义互补,共同构成完整的 FluentDate 体验。相关 API 参考可见 Humanizer.OnDate.March、Humanizer.OnDate 与 Humanizer.InDate。
实战组合示例
将命名属性、动态方法与DateOnly的既有能力组合起来,可以写出高度可读的业务代码:
using Humanizer; // 1) 静态命名:圆周率日与白色情人节 DateOnly piDay = OnDate.March.The14th; DateOnly whiteDay = OnDate.March.The14th.AddDays(0); // 3 月 14 日 // 2) 动态传参:根据配置项决定日期 int configuredDay = 8; // 假设来自配置 DateOnly womenDay = OnDate.March.The(configuredDay); // 3 月 8 日 // 3) 结合 DateOnly 计算:本月剩余天数 DateOnly today = DateOnly.FromDateTime(DateTime.Now); DateOnly endOfMarch = OnDate.March.The31st; int daysLeftInMarch = endOfMarch.DayNumber - today.DayNumber; Console.WriteLine($"3 月还剩 {daysLeftInMarch} 天");以上代码同时体现了OnDate.March的两大价值:代码自文档化(OnDate.March.The14th比new DateOnly(2026, 3, 14)表达意图更直接)与年份自适应(无需关心具体年份,始终指向当前年份的 3 月)。
小结
OnDate.March是 Humanizer FluentDate 体系中OnDate静态类的 3 月访问器,提供 31 个静态DateOnly属性(The1st–The31st)与一个The(int)动态方法,全部返回"当前年份三月"的对应日期。它由 OnDate.Days.tt 模板借助 Humanizer 自身的Ordinalize()自动生成,实体代码位于 src/Humanizer/FluentDate/OnDate.Days.cs,并通过 OnDateTests.cs 与 GeneratedFluentDateTests.cs 双重验证。使用时需注意 .NET 6+ 的环境前提,并可参照On.March(DateTime版)在两种返回类型之间按需选择。
- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
相关推荐
Humanizer OnDate.March 流式日期访问器详解:用自然语言式 API 构建 DateOnly 三月日期
Humanizer OnDate.March 流式日期访问器详解:用自然语言式 API 构建 DateOnly 三月日期 OnDate.March 是 Huma
开发工具Humanizer OnDate.March 流式日期 API 指南:用一行代码表达"三月第几日"的 DateOnly 访问器
Humanizer OnDate.March 流式日期 API 指南:用一行代码表达"三月第几日"的 DateOnly 访问器 本文以 Humanizer 的
开发工具Humanizer OnDate.September 详解:用流式 API 构建 9 月日期(DateOnly)
Humanizer OnDate.September 详解:用流式 API 构建 9 月日期(DateOnly) 本篇指南聚焦 Humanizer 的 OnDa
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考