- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
Humanizer 的OnDate.November是一个面向 11 月的流式(Fluent)日期访问类:它把"当前年份 11 月的某一天"封装成 30 个静态只读属性(The1st~The30th)与一个通用方法The(int dayNumber),全部返回 .NET 6+ 的System.DateOnly类型。本文以 3.0.1 版本文档为基础,结合源码与测试,完整讲解该类 30 个属性、The(int)方法的签名与语义、T4 模板生成机制、与On.November(返回DateTime)的差异,以及可直接运行的实战示例,帮助你告别new DateTime(...)式的样板代码,用一行表达式写出可读性极强的日期字面量。
类概览:位置、命名空间与编译条件
OnDate.November是Humanizer.OnDate类的内嵌(嵌套)公共类,声明如下:
public class OnDate.November- 命名空间:
Humanizer,因此引入using Humanizer;即可直接使用; - 继承关系:
System.Object→November,本身不继承任何自定义基类; - 成员性质:所有成员均为
public static,类为无状态工具类,无需实例化; - 编译条件:整个
OnDate类型族被#if NET6_0_OR_GREATER包裹(见 OnDate.Days.cs),因为其成员返回System.DateOnly——这是 .NET 6 起引入的"只含日期、不含时间"的值类型。如果你的目标框架低于 .NET 6,该类不可用,此时应改用返回DateTime的 On.November。
该类解决的问题很具体:构造"当前年份 + 11 月 + 指定日"的日期值。无论今天是哪一年,OnDate.November.The10th总是返回当年 11 月 10 日的DateOnly,年份由DateTime.Now.Year在运行时动态取用。
30 个静态属性:The1st到The30th完整清单
OnDate.November为 11 月的每一天(1 日到 30 日)都预置了一个静态只读属性。完整清单如下(签名统一为public static System.DateOnly,语义均为"当前年份 11 月的对应日期"):
| 属性 | 语义 | 属性 | 语义 |
|---|---|---|---|
The1st | 当前年 11 月 1 日 | The16th | 当前年 11 月 16 日 |
The2nd | 当前年 11 月 2 日 | The17th | 当前年 11 月 17 日 |
The3rd | 当前年 11 月 3 日 | The18th | 当前年 11 月 18 日 |
The4th | 当前年 11 月 4 日 | The19th | 当前年 11 月 19 日 |
The5th | 当前年 11 月 5 日 | The20th | 当前年 11 月 20 日 |
The6th | 当前年 11 月 6 日 | The21st | 当前年 11 月 21 日 |
The7th | 当前年 11 月 7 日 | The22nd | 当前年 11 月 22 日 |
The8th | 当前年 11 月 8 日 | The23rd | 当前年 11 月 23 日 |
The9th | 当前年 11 月 9 日 | The24th | 当前年 11 月 24 日 |
The10th | 当前年 11 月 10 日 | The25th | 当前年 11 月 25 日 |
The11th | 当前年 11 月 11 日 | The26th | 当前年 11 月 26 日 |
The12th | 当前年 11 月 12 日 | The27th | 当前年 11 月 27 日 |
The13th | 当前年 11 月 13 日 | The28th | 当前年 11 月 28 日 |
The14th | 当前年 11 月 14 日 | The29th | 当前年 11 月 29 日 |
The15th | 当前年 11 月 15 日 | The30th | 当前年 11 月 30 日 |
例如,OnDate.November.The10th的完整声明(摘自源码,属性实现为表达式体):
/// <summary> /// The 10th day of November of the current year /// </summary> public static DateOnly The10th => new(DateTime.Now.Year, 11, 10);可以看到,属性名中的1st / 2nd / 3rd / 21st / 22nd / 23rd等后缀使用了英文序数词(ordinal)而非简单数字,这与 Humanizer 整体的"自然语言优先"设计一致——代码读起来就像一句英文句子:"On November, the 10th"。
通用方法:The(int dayNumber)
当需要以变量形式指定日期(例如来自用户输入或循环遍历)时,使用通用方法:
public static System.DateOnly The(int dayNumber);- 参数:
dayNumber,类型System.Int32,表示 11 月中的第几天; - 返回值:
System.DateOnly; - 实现:
new(DateTime.Now.Year, 11, dayNumber)(见 OnDate.Days.cs)。
典型用法:
using Humanizer; int day = 17; DateOnly meetingDay = OnDate.November.The(day); // 当前年 11 月 17 日需要注意,The(int)与 30 个固定属性在语义上完全等价:OnDate.November.The(10)与OnDate.November.The10th返回相同的DateOnly值。dayNumber的取值范围应落在 1~30 之间(11 月有 30 天);传入超出范围的值时,DateOnly构造器会抛出ArgumentOutOfRangeException,这与直接new DateOnly(year, 11, day)的行为一致。
源码级原理:T4 模板自动生成整个OnDate月份族
OnDate.November并不是手写的 30 个重复属性,而是由 T4 文本模板 OnDate.Days.tt 在构建时生成到 OnDate.Days.cs 的。模板的核心逻辑(节选):
const int leapYear = 2012; for (var month = 1; month <= 12; month++) { var firstDayOfMonth = new DateTime(leapYear, month, 1); var monthName = firstDayOfMonth.ToString("MMMM"); // 为每个月生成一个嵌套类,类名即月份英文名 public class <#= monthName #> { public static DateOnly The(int dayNumber) => new(DateTime.Now.Year, <#= month #>, dayNumber); // 遍历该月每一天(天数取自闰年 2012,保证 2 月有 29 天) for (var day = 1; day <= DateTime.DaysInMonth(leapYear, month); day++) { var ordinalDay = day.Ordinalize(); // 1 -> 1st, 2 -> 2nd, 3 -> 3rd ... public static DateOnly The<#= ordinalDay #> => new(DateTime.Now.Year, <#= month #>, <#= day #>); } } }从中可以提炼三个关键实现事实:
- 月份名来自
ToString("MMMM"):November这个类名由 2012 年 11 月的MMMM格式输出生成,因此 12 个嵌套类(January~December)的命名规则完全一致; - 属性名使用
Ordinalize():属性后缀(1st、2nd、3rd……)由 Humanizer 自己的序数化扩展(OrdinalizeExtensions.cs)生成,这也是整个 FluentDate 系列 API 名称风格的来源; - 以闰年 2012 为模板计算每月天数:
DateTime.DaysInMonth(2012, 11)得到 30,因此 11 月的生成产物恰好是The1st~The30th共 30 个属性,与文档中的 API 清单一一对应。
从源码结构可以推断,OnDate系列共包含 12 个月份嵌套类,每个类都遵循同一套"30 天左右固定属性 + 1 个通用The(int)方法"的模式,本文讨论的November只是其中之一。
OnDate.November与On.November的区别
Humanizer 的 FluentDate 里还有一对容易混淆的姊妹类:On.November(位于 On.Days.cs)与OnDate.November。两者表面签名一致,但返回值类型不同:
| 对比项 | On.November | OnDate.November |
|---|---|---|
| 返回类型 | System.DateTime | System.DateOnly |
| 典型成员 | The(int)、The1st~The30th | The(int)、The1st~The30th |
| 框架要求 | 无特殊要求 | 仅NET6_0_OR_GREATER |
| 适用场景 | 需要同时携带时间部分、或与DateTimeAPI 互操作 | 只关心日期本身、需要纯净的日期语义 |
选择建议:在 .NET 6+ 且业务上"只关心年月日"(如节日、账单日、纪念日)时优先使用OnDate.November,能避免DateTime携带的00:00:00时间分量带来的比较与格式化歧义;需要保留时间或兼容旧框架时再使用On.November。
实战示例:与At()组合构造精确时间点
FluentDate 家族不止提供"哪一天",还通过PrepositionsExtensions提供.At(hour, minute)扩展,可以把DateOnly/DateTime与时刻组合起来。结合 scenarios-fluent-dates 示例 的用法风格,一个完整的"11 月会议安排"示例可以这样写:
using Humanizer; // 当前年 11 月 10 日(DateOnly) DateOnly auditDay = OnDate.November.The10th; // 当前年 11 月第 17 天(变量形式) DateOnly reviewDay = OnDate.November.The(17); // 直接打印 Console.WriteLine($"Audit: {auditDay:yyyy-MM-dd}"); // 如 2026-11-10 Console.WriteLine($"Review: {reviewDay:yyyy-MM-dd}"); // 如 2026-11-17如果项目目标是 .NET 6+,还可以把DateOnly与DateTime统一到同一套流式表达中——例如用In.AprilOf(2025).AddDays(2).At(14, 30)表达"2025 年 4 月 2 日 14:30",与OnDate.November的语义互补。
测试佐证:行为已被单元测试锁定
Humanizer 为 FluentDate 系列编写了专门的测试 OnDateTests.cs,其中针对"固定属性"与"通用方法"两类 API 都有断言:
[Fact] public void OnJanuaryThe23rd() => Assert.Equal(new(DateTime.Now.Year, 1, 23), OnDate.January.The23rd); [Fact] public void OnDecemberThe4th() => Assert.Equal(new(DateTime.Now.Year, 12, 4), OnDate.December.The4th); [Fact] public void OnFebruaryThe() => Assert.Equal(new(DateTime.Now.Year, 2, 11), OnDate.February.The(11));从这些测试可以确认三条行为约定,同样适用于OnDate.November:
- 年份始终取
DateTime.Now.Year,即"当前自然年",测试期望值也是用DateTime.Now.Year构造的; - 固定属性与
The(int)方法结果一致,内部都走new DateOnly(year, month, day)这一条路径; - 测试文件同样以
#if NET6_0_OR_GREATER条件编译,与 API 的框架约束保持一致。
使用注意事项小结
- 框架前提:
OnDate.November仅在 .NET 6 及以上目标框架可用(DateOnly类型依赖),项目文件需满足该条件; - 年份语义:返回的是当前运行年份的 11 月日期,而不是指定年份——如果需要指定年份,应改用
In.AprilOf(year)这类接受年份参数的方法族,或自行new DateOnly(year, 11, day); - 取值范围:11 月固定 30 天,
The(int)的参数建议在 1~30 之间,越界会抛出参数异常; - 与
DateTime的互操作:若需要把结果转成DateTime,可直接使用DateOnly的ToDateTime(TimeOnly)实例方法补上时刻分量。
总而言之,OnDate.November用 31 个静态成员(30 个属性 + 1 个方法)把"当前年 11 月任意一天"这一常见日期构造需求收敛为纯流式、零样板代码的表达式,配合 Humanizer 其他 FluentDate 类(On、OnDate、In、InDate),可以组成一套完整的、可读性极强的日期 DSL。
- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
相关推荐
Humanizer OnDate.December 完全指南:用 Fluent API 构造当前年份十二月的 DateOnly 日期
Humanizer OnDate.December 完全指南:用 Fluent API 构造当前年份十二月的 DateOnly 日期 本篇技术指南聚焦 Huma
开发工具Humanizer `OnDate.January` 流式 API 详解:用一行代码构造当年 1 月的任意 `DateOnly` 日期
Humanizer OnDate.January 流式 API 详解:用一行代码构造当年 1 月的任意 DateOnly 日期 Humanizer 的 Flue
开发工具Humanizer 流式日期 API 详解:用 OnDate.November 在 .NET 中链式构造 11 月 DateOnly 日期
Humanizer 流式日期 API 详解:用 OnDate.November 在 .NET 中链式构造 11 月 DateOnly 日期 本篇技术指南聚焦 H
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考