☰
Humanizer FluentDate 之 In.Two:用「2」做自然流畅的日期偏移计算
2026/9/25 6:09:24 网站建设 项目流程
  • 开发工具

【免费下载链接】Humanizer

Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities

项目地址:https://gitcode.com/gh_mirrors/hu/Humanizer
点击查看免费下载

本篇技术指南围绕 Humanizer 的 FluentDate API 中In.Two静态类展开,它提供「从现在起 2 秒/分钟/小时/天/周/月/年」以及「从指定日期起偏移 2 个时间单位」两类共 14 个成员。读者读完本文将掌握In系列数字类的完整成员清单、底层 T4 模板生成原理、DateTime.UtcNow与AddDays/AddMonths/AddYears的实际调用链,以及如何借助仓库测试用例验证行为,从而在自己的 .NET 项目中写出可读性更高、且可预测的日期偏移代码。

一、FluentDate:让日期偏移像说英语一样自然

Humanizer 的核心目标是让字符串、枚举、日期、时间、数字与数量的处理更贴近人类语言习惯。FluentDate 是其中的一个子模块,它把「两个小时后」「三天后」「两年后」这类自然语言表达,直接映射成强类型、可编译、带智能感知的 C# 代码。

In是一个public partial class(定义于 In.cs),它通过多个 partial 声明聚合了一组嵌套静态类:In.One到In.Ten十个数字类,外加月份类(In.January、In.February……)以及TheYear(int year)等方法。十个数字类并非手写,而是由 T4 文本模板 In.SomeTimeFrom.tt 自动生成源码文件 In.SomeTimeFrom.cs,这也解释了为什么One到Ten的结构高度一致——它们本质上是同一套模板循环 10 次的产物。

In.Two正是这套模板在i = 2时的输出,对应「二」这个数量词。其类型定义为:

public static class Two

它直接嵌套在In内部,因此完整引用路径是Humanizer.In.Two;In本身继承自System.Object,Two作为嵌套静态类同样如此。由于成员均为public static,调用时无需任何实例化或依赖注入,在项目里引用Humanizer程序集后即可直接使用。

二、In.Two 的七个属性:从当前时刻起偏移 2 个单位

In.Two提供 7 个只读属性,全部返回System.DateTime,语义均为「从现在起 2 个时间单位之后」。下表汇总了文档 Humanizer.In.Two.md 中声明的完整属性清单:

属性文档语义返回类型底层实现(见 In.SomeTimeFrom.cs)
Seconds2 seconds from now(2 秒后)System.DateTimeDateTime.UtcNow.AddSeconds(2)
Minutes2 minutes from now(2 分钟后)System.DateTimeDateTime.UtcNow.AddMinutes(2)
Hours2 hours from now(2 小时后)System.DateTimeDateTime.UtcNow.AddHours(2)
Days2 days from now(2 天后)System.DateTimeDateTime.UtcNow.AddDays(2)
Weeks2 weeks from now(2 周后)System.DateTimeDateTime.UtcNow.AddDays(14)
Months2 months from now(2 个月后)System.DateTimeDateTime.UtcNow.AddMonths(2)
Years2 years from now(2 年后)System.DateTimeDateTime.UtcNow.AddYears(2)

一个值得注意的实现细节是Weeks:模板没有调用AddWeeks(.NET 也不存在该方法),而是直接等价为AddDays(2 * 7),即AddDays(14)。这与文档中「2 weeks from now」的语义完全一致,也意味着周是一个固定的 7 天时长概念,不受月份长短或夏令时影响。

示例用法:

using Humanizer; // 从现在起 2 小时后 DateTime reminderTime = In.Two.Hours; // 从现在起 2 周后(即 UtcNow + 14 天) DateTime reviewDue = In.Two.Weeks;

三、In.Two 的七个方法:从指定日期起偏移 2 个单位

除属性外,In.Two还提供 7 个同名加From后缀的方法,每个方法接收一个System.DateTime date参数,返回该日期偏移 2 个时间单位后的System.DateTime,语义为「从给定日期起 2 个时间单位之后」。完整方法清单同样来自 Humanizer.In.Two.md:

方法签名文档语义底层实现(见 In.SomeTimeFrom.cs)
SecondsFrom(System.DateTime date)2 seconds from the provided datedate.AddSeconds(2)
MinutesFrom(System.DateTime date)2 minutes from the provided datedate.AddMinutes(2)
HoursFrom(System.DateTime date)2 hours from the provided datedate.AddHours(2)
DaysFrom(System.DateTime date)2 days from the provided datedate.AddDays(2)
WeeksFrom(System.DateTime date)2 weeks from the provided datedate.AddDays(14)
MonthsFrom(System.DateTime date)2 months from the provided datedate.AddMonths(2)
YearsFrom(System.DateTime date)2 years from the provided datedate.AddYears(2)

与属性版本最大的区别在于基准点:属性以DateTime.UtcNow为起点,而From方法以调用方传入的date为起点,且不会读写任何全局时钟。这意味着From系列天然具有确定性——同样的输入日期必然得到同样的输出,非常适合在测试和需要可复现计算的场景中使用。

using Humanizer; var startDate = new DateTime(2024, 1, 30); // 从指定日期起 2 个月后:2024 年 3 月 30 日 DateTime dueDate = In.Two.MonthsFrom(startDate); // 从指定日期起 2 年后:2026 年 1 月 30 日 DateTime anniversary = In.Two.YearsFrom(startDate);

四、源码级原理:T4 模板如何批量产出 In.One ~ In.Ten

In.Two的代码不是手工维护的,理解其生成机制有助于你把握整个In数字系列的边界。查看 In.SomeTimeFrom.tt 的核心循环:

<#for (var i = 1; i <= 10; i++){ var plural = i > 1 ? "s" : ""; ... #> public static class <#= i.ToWords().Dehumanize() #> { public static DateTime <#= second #> => DateTime.UtcNow.AddSeconds(<#= i #>); ... } <#}#>

其中三个关键机制值得展开:

  1. 类名由i.ToWords().Dehumanize()计算:模板先是调用 Humanizer 自己的ToWords()扩展把数字i转成英文单词(2→two),再用Dehumanize()把单词转成 PascalCase 标识符(two→Two)。这正是 Humanizer「用自身能力生成自身代码」的体现,也保证了One到Ten的类名与数量词严格对应。
  2. 单复数由i > 1决定:模板为每个数字类统一生成Second(s)、Minute(s)、Hour(s)、Day(s)、Week(s)、Month(s)、Year(s)七组属性与方法名。由于Two大于 1,所有成员名均采用复数形式,于是我们看到的是Seconds而非Second,是DaysFrom而非DayFrom(后者是In.One的命名风格)。
  3. 周统一换算为天数:模板中对周的实现是AddDays(<#= i * 7 #>),所以In.Two.Weeks生成的是AddDays(14),与前文表格一致;月、年则直接委托AddMonths(i)、AddYears(i)。

另外,In类还通过 In.cs 补充了TheYear(int year)方法(返回指定年份的 1 月 1 日),以及由其他 T4 模板生成的月份类(In.January、In.February等)与Of(year)系列方法。整个 FluentDate 目录还包含InDate(返回DateOnly的对应版本)和On/OnDate(构造特定日期的版本),共同构成一套完整的自然语言日期构造体系。

五、实践验证:测试如何确认 In 系列行为

仓库测试为In.Two的行为提供了直接证据。在 InTests.cs 中,InFiveDays测试展示了From系列的实际调用模式:

[Fact] public void InFiveDays() { var baseDate = On.January.The21st; var date = In.Five.DaysFrom(baseDate); Assert.Equal(baseDate.AddDays(5), date); }

把数字换成Two即可得到In.Two.DaysFrom(baseDate) == baseDate.AddDays(2)的等价断言——From方法就是对DateTime.AddXxx的直通封装,没有任何额外逻辑。

更系统的验证来自 GeneratedFluentDateTests.cs。该测试文件内置了一张数量映射表RelativeAmounts:

["One"] = 1, ["Two"] = 2, ["Three"] = 3, ... ["Ten"] = 10

并通过反射遍历生成的成员,用Add辅助方法(第 265-287 行)按RelativeDateUnit枚举(Second/Minute/Hour/Day/Week/Month/Year)逐一断言每个属性与方法的结果,其中周同样按AddDays(amount * 7)校验。这意味着In.Two的全部 14 个成员都在生成式测试的覆盖范围内,任何模板改动导致行为偏离都会使测试失败。

六、使用注意事项:UtcNow、日历运算与确定性的取舍

在实战中使用In.Two(乃至整个In数字系列)时,有三点需要留意,这些建议同样来自仓库场景文档 fluent-dates-and-time-spans.mdx:

  1. 属性基于DateTime.UtcNow:In.Two.Days等属性在求值瞬间读取协调世界时,因此它们是「相对当前时刻」的动态值。若代码或测试需要可重复结果,应优先使用From方法注入起始日期,或改用它处提供的固定基准。文档明确建议:"Properties withoutFromorOf(year)readDateTime.NoworUtcNow. Avoid them in deterministic code."
  2. 月、年偏移是日历运算而非时长估算:MonthsFrom/YearsFrom直接继承DateTime.AddMonths/AddYears的规范化语义。例如把 2 月 29 日偏移 2 个月到非闰年月份,会规范化为 4 月末附近的有效日期(AddMonths会做月末截断处理),不会抛异常;这类行为与固定 24 小时一天的TimeSpan运算有本质区别。
  3. 周始终是 7 天:In.Two.Weeks等价于AddDays(14),不受当月天数影响;若你的业务周期依赖自然月(如「下下月的今天」),应使用MonthsFrom而非多次DaysFrom。

结合这些约束,推荐的可预测写法是:业务计算全部使用In.Two.XxxFrom(startDate)这类带基准的方法;只有「从现在起 N 个单位」且可接受动态结果的通知、提醒类场景,才使用In.Two.Xxx属性。

七、小结

In.Two是 Humanizer FluentDate 中数量词In.One~In.Ten家族的标准一员,通过 7 个属性(基于UtcNow)与 7 个From方法(基于入参日期)覆盖秒、分、时、天、周、月、年七种时间单位。它的全部成员由 In.SomeTimeFrom.tt 模板生成,语义透明、零依赖,配合 InTests.cs 与 GeneratedFluentDateTests.cs 的测试保障,是写出「人话级」日期代码的可靠基础。若需要DateOnly版本,可继续查阅同一目录下的 InDate.SomeTimeFrom.cs;若要构造「某个具体的 2 号」而非「2 个时间单位之后」,请转向On/OnDate系列。

  • 开发工具

【免费下载链接】Humanizer

Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities

项目地址:https://gitcode.com/gh_mirrors/hu/Humanizer
点击查看免费下载
上一篇:解决方案:AI编程助手功能解锁 - 突破Cursor试用限制的技术实现
下一篇:Audacity AI音频增强全攻略:零基础掌握效率工具新玩法

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询