- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
本篇技术指南聚焦 Humanizer(.NET 字符串、枚举、日期与数量处理库)中In.Eight这一流式日期(FluentDate)API:它用一组静态属性和方法,以"8 秒/分钟/小时/天/周/月/年后"的自然语义计算相对日期。读完本文,你将掌握In.Eight全部 7 个属性与 7 个方法的确切行为、UTC 基准、DateOnly变体,以及它在 In.SomeTimeFrom.cs 中由 T4 模板代码生成并受测试套件验证的底层实现。
概述:In.Eight是什么
In.Eight是 HumanizerIn静态类中一个嵌套的静态类,属于 FluentDate 目录 提供的"流式日期"(Fluent Date)API 家族。它的设计意图是让调用代码读起来像英文句子:
var reminder = In.Eight.Hours; // 从现在起 8 小时后 var deadline = In.Eight.DaysFrom(today); // 从给定日期起 8 天后在 Humanizer v3.0.10 的 API 参考中,In.Eight的完整签名为public static class In.Eight(继承链为System.Object→Eight),全部成员均为public static,且不依赖任何配置即可直接使用。它和兄弟类In.One~In.Ten(In.SomeTimeFrom.cs)一起,覆盖了 1 到 10 的常见相对日期场景。
七个只读属性:从"现在"起算的 8 个单位
In.Eight提供 7 个属性,每个都返回一个System.DateTime,语义均为"从现在起 8 个 X 之后"。下表是 API 参考中的完整清单:
| 属性 | 返回类型 | 语义 | 底层实现(UTC) |
|---|---|---|---|
Seconds | DateTime | 8 秒后 | DateTime.UtcNow.AddSeconds(8) |
Minutes | DateTime | 8 分钟后 | DateTime.UtcNow.AddMinutes(8) |
Hours | DateTime | 8 小时后 | DateTime.UtcNow.AddHours(8) |
Days | DateTime | 8 天后 | DateTime.UtcNow.AddDays(8) |
Weeks | DateTime | 8 周后 | DateTime.UtcNow.AddDays(56) |
Months | DateTime | 8 个月后 | DateTime.UtcNow.AddMonths(8) |
Years | DateTime | 8 年后 | DateTime.UtcNow.AddYears(8) |
实现要点:UTC 基准与周的单位换算
查看 In.SomeTimeFrom.cs 中Eight类的源码可以发现两个关键细节:
- 属性基于
DateTime.UtcNow计算,返回的是 UTC 时刻的DateTime。如果需要本地时间展示,应在其后调用.ToLocalTime()。 - 周(Week)被换算为天(Day):
Weeks => DateTime.UtcNow.AddDays(56),而不是使用AddWeeks——因为System.DateTime本身没有AddWeeks方法,Humanizer 以8 * 7 = 56天的方式实现,这与In.Three.Weeks => AddDays(21)、In.Ten.Weeks => AddDays(70)等兄弟类的实现完全一致。
七个 From 方法:从指定日期起算
In.Eight还提供 7 个静态方法,接受一个System.DateTime date参数并返回System.DateTime,语义为"从传入日期起 8 个 X 之后":
| 方法签名 | 语义 | 底层实现 |
|---|---|---|
SecondsFrom(DateTime date) | 从 date 起 8 秒后 | date.AddSeconds(8) |
MinutesFrom(DateTime date) | 从 date 起 8 分钟后 | date.AddMinutes(8) |
HoursFrom(DateTime date) | 从 date 起 8 小时后 | date.AddHours(8) |
DaysFrom(DateTime date) | 从 date 起 8 天后 | date.AddDays(8) |
WeeksFrom(DateTime date) | 从 date 起 8 周后 | date.AddDays(56) |
MonthsFrom(DateTime date) | 从 date 起 8 个月后 | date.AddMonths(8) |
YearsFrom(DateTime date) | 从 date 起 8 年后 | date.AddYears(8) |
与属性不同,方法不做任何时区换算,直接基于传入的date值做偏移(In.SomeTimeFrom.cs)。这意味着传入值的Kind(Utc/Local/Unspecified)会被原样保留,适合在既有时间线上追加偏移量:
var shipDate = new DateTime(2026, 9, 20, 10, 0, 0, DateTimeKind.Utc); var dueDate = In.Eight.DaysFrom(shipDate); // 2026-09-28 10:00:00 UTC var reviewDate = In.Eight.MonthsFrom(shipDate); // 2027-05-20 10:00:00 UTC源码级原理解析:T4 模板生成与 partial 类结构
类名由 T4 模板批量生成
In.One~In.Ten十个兄弟类并非手工编写,而是由 T4 文本模板 In.SomeTimeFrom.tt 生成:模板用一个for (var i = 1; i <= 10; i++)循环,先通过i.ToWords().Dehumanize()把数字转成单词并去人化得到类名(如8→"Eight"),再对每个类生成 7 组属性/方法对。Eight正是该循环第 8 次迭代的产物,因此它的命名、签名与One~Ten完全同构——看到In.Eight的 API,就等于看到了整个In数字系列的 API 形态。
In是一个 partial 类家族
In类本身由多个 partial 文件拼合而成,共同构成完整的流式日期能力:
- In.cs:提供
In.TheYear(int year),返回指定年份的 1 月 1 日; - In.SomeTimeFrom.cs:提供
In.One~In.Ten各单位的相对日期属性与方法; - In.Months.cs:提供
In.January、In.February……In.December以及In.JanuaryOf(int year)等月份访问器; - 同目录下的 On.Days.cs、On.Days.tt 等则负责"某月某日"的另一套流式 API。
也就是说,In.Eight.Hours只是 Humanizer FluentDate 体系的一个切片,它与月份、年份访问器互补:前者回答"8 小时后是哪一刻",后者回答"明年的 3 月是哪一天"。
面向 .NET 6+ 的DateOnly变体:InDate.Eight
由于System.DateOnly(.NET 6 引入)不含秒、分、时概念,Humanizer 在#if NET6_0_OR_GREATER条件下额外提供InDate类族。在 InDate.SomeTimeFrom.cs 中,InDate.Eight提供Days、Weeks、Months、Years四个属性及对应的DaysFrom、WeeksFrom、MonthsFrom、YearsFrom方法,返回类型为DateOnly,且每个 From 方法都有DateOnly与DateTime两个重载(后者内部通过DateOnly.FromDateTime(...)转换)。适合只需日历日期、不关心时刻的业务场景,例如"8 个月后的账单日"。
测试验证:相对日期语义有据可依
In.Eight的行为由测试套件严格把关,主要依据有两处:
- GeneratedFluentDateTests.cs:通过反射遍历
In的所有嵌套类型(One~Ten,含Eight),对每个静态属性先记录DateTime.UtcNow的前后快照,再用Assert.InRange(actual, Add(before, amount, unit), Add(after, amount, unit))验证属性落在"预期偏移区间"内;对每个*From方法则用固定的基准日期(如2024-02-29 10:20:30 UTC)断言Assert.Equal(Add(date, amount, unit), actual)。其中amount = 8、unit由成员名解析,Add的switch表达式与源码实现一一对应(Week => date.AddDays(amount * 7))。 - InTests.cs:给出流式组合的集成示例——
var baseDate = On.January.The21st; var date = In.Five.DaysFrom(baseDate); Assert.Equal(baseDate.AddDays(5), date);。把In.Five换成In.Eight,即可验证 8 天偏移的等价行为。
这两个测试共同确认:In.Eight的属性是"基于当前 UTC 时刻的动态值",而 From 方法是"基于入参的纯函数式偏移",二者都精确遵循AddSeconds/AddMinutes/AddHours/AddDays/AddMonths/AddYears(周=天×7)的语义。
实战用法与注意事项
典型场景一:定时任务调度提示
// 用属性表达"相对当前时刻" var retryAt = In.Eight.Minutes; // UTC 时刻 var reportDue = In.Eight.Weeks; // 8 周后的 UTC 时刻 // 用方法表达"相对业务时间点" var expiresAt = In.Eight.MonthsFrom(subscription.StartDate);典型场景二:与其它 Humanizer API 组合
In.Eight返回的DateTime可以继续交给 Humanizer 的Humanize()扩展(DateHumanizeExtensions.cs)生成自然语言描述,形成"计算 + 展示"的完整链路:
var future = In.Eight.Hours; var text = future.Humanize(); // 例如 "8 hours from now"注意事项
- 属性是"动态快照":每次访问
In.Eight.Seconds都会重新读取DateTime.UtcNow并计算,多次访问可能得到略有差异的毫秒级结果;需要稳定值时请先赋值给局部变量。 - 时区:属性产物为 UTC;跨时区场景请配合
.ToLocalTime()或基于本地DateTime的 From 方法。 - 命名约定:
One类使用单数成员名(Second、Minute……),Eight等 2~10 类使用复数成员名(Seconds、Minutes……),这是 T4 模板中var plural = i > 1 ? "s" : "";决定的,引用时注意拼写。 DateOnly需求:目标框架为 .NET 6 及以上且只需要日期时,优先使用 InDate.Eight,避免引入无关的时间分量。
总结
In.Eight是 Humanizer FluentDate API 中"以 8 为量、以秒/分/时/天/周/月/年为单位的相对日期计算"的完整入口:7 个基于 UTC 的属性回答"从现在起 8 个单位后",7 个 From 方法回答"从指定时刻起 8 个单位后",另有 .NET 6+ 的InDate.Eight提供DateOnly变体。其实现由 T4 模板统一生成、以DateTime.Add*为基础、由反射测试精确校验,属于可以直接放心嵌入业务代码的成熟 API。更多流式日期能力可继续阅读 On.Days.cs(某月某日)与 In.Months.cs(月份与年份)等兄弟模块。
- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
相关推荐
Humanizer 流式日期 API 详解:InDate.Two 相对日期计算与源码实现
Humanizer 流式日期 API 详解:InDate.Two 相对日期计算与源码实现 InDate.Two 是 Humanizer 流式日期(FluentD
开发工具Humanizer FluentDate 实战:In.Eight 相对日期 API 完整指南
Humanizer FluentDate 实战:In.Eight 相对日期 API 完整指南 In.Eight 是 Humanizer FluentDate 子
开发工具Humanizer `In.Eight` 详解:用流式 API 表达"8 个时间单位之后"的日期
Humanizer In.Eight 详解:用流式 API 表达"8 个时间单位之后"的日期 导读 Humanizer.In.Eight 是 Humanizer
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考