☰
Humanizer InDate.Six 详解:用流式 API 计算 6 天/6 周/6 月/6 年后的日期
2026/9/27 9:14:29 网站建设 项目流程
  • 开发工具

【免费下载链接】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 2.14.1 文档中的InDate.Six类展开,讲解如何用一行静态属性或方法,直接拿到“6 天后”“6 周后”“6 个月后”“6 年后”的DateOnly日期。读完本文,你将掌握 Humanizer FluentDate 相对日期 API 的属性/方法语义、DateOnly与DateTime两种重载的取舍、其 T4 模板生成机制与测试验证方式,并能在 .NET 6+ 项目中直接落地使用。

InDate.Six 是什么:FluentDate 相对日期体系中的一员

Humanizer 的 FluentDate 模块把"未来某个日期"的表述压缩成了类名与成员名本身。它围绕四个根类型展开:

  • In:返回DateTime的“未来相对时间/月份”API;
  • On:基于DateTime的“某月某日”API;
  • InDate:返回DateOnly的“未来相对日期/月份”API(即本文主角);
  • OnDate:基于DateOnly的“某月某日”API。

InDate是一个用partial拆分的类(src/Humanizer/FluentDate/InDate.cs),内部嵌入了One到Ten十个静态嵌套类,分别对应数量 1 到 10。InDate.Six就是其中数量为 6 的那一个:文档 website/versioned_docs/version-2.14.1/api/Humanizer.InDate.Six.md 显示它继承自System.Object,是一个public static class,全部成员均为public static,无需实例化即可调用。

注意:整个InDate系列(包括InDate.Six)在源码中被#if NET6_0_OR_GREATER条件编译保护(见 src/Humanizer/FluentDate/InDate.SomeTimeFrom.cs),因为DateOnly是 .NET 6 引入的类型。低于 .NET 6 的目标框架中不存在这套 API。

四个核心属性:从“现在”起算的相对日期

InDate.Six提供了 4 个静态属性,语义都是“从现在起 N 个单位后的日期”,返回值统一为System.DateOnly:

属性说明签名
Days6 天后的日期public static System.DateOnly Days { get; }
Weeks6 周后的日期public static System.DateOnly Weeks { get; }
Months6 个月后的日期public static System.DateOnly Months { get; }
Years6 年后的日期public static System.DateOnly Years { get; }

对应源码位于 src/Humanizer/FluentDate/InDate.SomeTimeFrom.cs 的Six类中,实现方式高度一致,例如:

public static DateOnly Days => DateOnly.FromDateTime(DateTime.UtcNow.AddDays(6)); public static DateOnly Weeks => DateOnly.FromDateTime(DateTime.UtcNow.AddDays(42)); // 6 * 7 public static DateOnly Months => DateOnly.FromDateTime(DateTime.UtcNow.AddMonths(6)); public static DateOnly Years => DateOnly.FromDateTime(DateTime.UtcNow.AddYears(6));

有三个容易被忽略的实现细节值得注意:

  1. 基准时间永远是 UTC:属性基于DateTime.UtcNow而非DateTime.Now计算,然后通过DateOnly.FromDateTime截掉时间部分,只保留年月日。因此“6 天后”不会受本地时区设置影响,测试可复现性更好。
  2. 周是天的整数倍:Weeks没有独立的周类型概念,直接等价于AddDays(42)(6 周 × 7 天)。
  3. 月/年遵循 .NET 日历算法:AddMonths(6)、AddYears(6)由DateOnly自身处理大小月与闰年,例如 8 月 31 日加 6 个月会得到次年 2 月的月末(28/29 日),而不是抛异常。

八个 From 方法:从指定日期起算

如果不想基于“现在”,而是基于某个确定的基准日期,InDate.Six还提供了 4 组共 8 个静态方法,每个单位都有DateOnly与DateTime两种参数重载:

方法参数类型返回类型
DaysFrom(DateOnly date)DateOnlyDateOnly
DaysFrom(DateTime date)DateTimeDateOnly
WeeksFrom(DateOnly date)DateOnlyDateOnly
WeeksFrom(DateTime date)DateTimeDateOnly
MonthsFrom(DateOnly date)DateOnlyDateOnly
MonthsFrom(DateTime date)DateTimeDateOnly
YearsFrom(DateOnly date)DateOnlyDateOnly
YearsFrom(DateTime date)DateTimeDateOnly

源码实现同样直观(src/Humanizer/FluentDate/InDate.SomeTimeFrom.cs):

public static DateOnly DaysFrom(DateOnly date) => date.AddDays(6); public static DateOnly DaysFrom(DateTime date) => DateOnly.FromDateTime(date.AddDays(6)); public static DateOnly MonthsFrom(DateOnly date) => date.AddMonths(6); public static DateOnly MonthsFrom(DateTime date) => DateOnly.FromDateTime(date.AddMonths(6));

两组重载的差别在于:DateOnly版本直接在纯日期上做加法,天然无时间分量;DateTime版本则保留输入的时间部分参与运算(例如从某天 14:30 起算 6 天),最终仍通过DateOnly.FromDateTime统一收敛为纯日期返回。无论入参类型如何,所有 From 方法的返回值都固定是DateOnly。

源码视角:T4 模板生成十个兄弟类

InDate.Six不是手写代码,而是由 T4 文本模板批量生成的。模板位于 src/Humanizer/FluentDate/InDate.SomeTimeFrom.tt,核心逻辑是一个循环:

<#for (var i = 1; i <= 10; i++){ var plural = i > 1 ? "s" : ""; // 单复数处理 var day = "Day" + plural; // Day / Days var week = "Week" + plural; // Week / Weeks var month = "Month" + plural; // Month / Months var year = "Year" + plural; // Year / Years #> public static class <#= i.ToWords().Dehumanize() #>

从中可以确认三个设计约定:

  • 类名由i.ToWords().Dehumanize()生成:数字先转英文单词(6→six),再反人类化处理(six→Six),因此得到One、Two……Ten;
  • 属性名按数量自动单复数:数量 1 时是单数Day/Week/Month/Year(见InDate.One),数量 2~10 时是复数Days/Weeks/Months/Years,与文档中InDate.Six.Days等成员一一对应;
  • 生成器把#if NET6_0_OR_GREATER条件编译直接写进模板输出,因此全部生成代码只对 .NET 6+ 生效。

这也解释了为什么文档目录下会同时存在Humanizer.InDate.One.md到Humanizer.InDate.Ten.md十个几乎同构的 API 页面:它们描述的是同一模板产出的十组平行类,本文讨论的InDate.Six只是数量为 6 的那一组。

测试如何保证这些 API 的正确性

仓库对 FluentDate 生成 API 的验证方式是反射遍历:测试不针对单个成员写死断言,而是枚举所有嵌套类与成员,再按类名反推数量、按成员名反推单位,统一断言。相关测试在 tests/Humanizer.Tests/FluentDate/GeneratedFluentDateTests.cs:

  • InDateRelativeDatePropertiesReturnExpectedUtcOffsets:对InDate所有相对日期属性(含Six.Days/Weeks/Months/Years)取值,断言结果落在DateTime.UtcNow加对应数量之后的时间区间内(测试第 L34-L35、L99-L115);
  • InDateRelativeDateOnlyMethodsReturnExpectedOffsetsFromProvidedDate:用固定的基准日期2024-02-29(闰日,专门检验月/年加法边界)调用所有DateOnly参数的重载,逐项断言Assert.Equal(测试第 L38-L39、L117-L138);
  • InDateRelativeDateTimeMethodsReturnExpectedOffsetsFromProvidedDateTime:同样的逻辑覆盖所有DateTime参数重载。

除此之外,tests/Humanizer.Tests/FluentDate/InDateTests.cs 给出了一个非常贴近实际用法的示例:用OnDate.January.The21st构造基准日,再交给InDate.Five.DaysFrom(baseDate),断言结果等于baseDate.AddDays(5)。把Five换成Six即可得到“6 天后”的等效验证,二者行为完全对称。

在项目中的完整使用示例

引入 Humanizer 命名空间后,InDate.Six的用法非常直观:

using Humanizer; // 属性:从"现在"起算 DateOnly inSixDays = InDate.Six.Days; // 今天 + 6 天 DateOnly inSixWeeks = InDate.Six.Weeks; // 今天 + 42 天 DateOnly inSixMonths = InDate.Six.Months; // 今天 + 6 个月 DateOnly inSixYears = InDate.Six.Years; // 今天 + 6 年 // 方法:从指定基准日起算(DateOnly 重载) DateOnly baseDate = new(2026, 9, 26); DateOnly dueDate = InDate.Six.MonthsFrom(baseDate); // 2027-03-26 // 方法:从 DateTime 起算,结果仍是 DateOnly DateOnly auditDate = InDate.Six.YearsFrom(DateTime.UtcNow);

典型应用场景包括:订阅/会员“6 个月后续期提醒日”计算、合同“6 年后到期日”排期、报表“6 周滚动窗口”起点计算等。由于所有成员都是静态且无副作用,可以直接嵌入业务表达式而不需要任何额外配置。

与周边 API 的组合使用

InDate.Six属于相对日期(相对“现在”或相对给定基准日),与InDate家族的绝对日期 API 互补:

  • InDate的月份属性/方法返回“当前年第 X 月 1 日”或“指定年份的第 X 月 1 日”,例如InDate.January、InDate.JuneOf(2030),详见 website/versioned_docs/version-2.14.1/api/Humanizer.InDate.md;
  • InDate.TheYear(int year)返回指定年份的 1 月 1 日,实现于 src/Humanizer/FluentDate/InDate.cs;
  • 兄弟类InDate.One到InDate.Ten提供数量 1~10 的同一套相对日期 API,其中InDate.One使用单数成员名(Day、Week、Month、Year);
  • 需要DateTime形态时,可改用In家族的相对时间 API;需要“某月某日”时使用OnDate(如测试中出现的OnDate.January.The21st)。

使用注意事项小结

  1. 目标框架:InDate.Six全部成员仅存在于 .NET 6 及更高版本(NET6_0_OR_GREATER条件编译),旧框架下不可用;
  2. 属性基于 UTC:Days/Weeks/Months/Years以DateTime.UtcNow为基准,跨时区部署时结果一致;若业务要求本地日界线语义,请自行基于本地时间计算;
  3. 返回值恒为 DateOnly:即使传入DateTime重载,结果也会截断时间分量;
  4. 周的天数换算:Weeks固定按 7 天/周换算(6 周 = 42 天),不涉及“周起始日”概念;
  5. 月/年边界:加月/加年的月末归整由 .NETDateOnly实现保证,闰年场景(如 2024-02-29 加 6 年)行为与AddYears一致,可直接依赖测试 tests/Humanizer.Tests/FluentDate/GeneratedFluentDateTests.cs 作为行为基准。

如果只需要“6 天/周/月/年后”这类常见期限,InDate.Six是比手写DateTime.UtcNow.AddX()更富表达力的选择:成员名即文档,返回类型即约束,无需注释即可自解释。

  • 开发工具

【免费下载链接】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
点击查看免费下载
上一篇:如何在Windows上轻松安装draw.io桌面版:终极离线绘图解决方案
下一篇:Font Awesome 7终极指南:互联网图标库的完整使用手册

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

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

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

立即咨询