- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
Humanizer 的InDate系列是面向System.DateOnly的流式(Fluent)日期构造 API,其中InDate.Ten静态嵌套类专门封装"从现在起 10 天、10 周、10 个月、10 年之后"的日期取值,以及基于任意基准日期的相对计算。本文以 2.14.1 版本的 Humanizer.InDate.Ten API 文档 为主体,结合仓库源码与测试用例,完整讲解该类 4 个属性、8 个方法的签名、行为、实现原理与实战用法,读完即可在 .NET 6+ 项目中安全使用InDate.Ten进行日期推算。
InDate.Ten 在 Humanizer 流式日期体系中的位置
Humanizer 的 FluentDate 模块位于 src/Humanizer/FluentDate 目录,提供两组平行的流式日期 API:
In类:基于System.DateTime的流式日期(秒、分、时、天、周、月、年),实现于 In.SomeTimeFrom.cs;InDate类:基于System.DateOnly的流式日期(天、周、月、年),实现于 InDate.SomeTimeFrom.cs;- 与之配套的还有月份构造
InDate.January/JanuaryOf(year)等(见 InDate.Months.cs)以及InDate.TheYear(year)(见 InDate.cs)。
InDate.Ten就是InDate中数量为 10 的嵌套静态类,与InDate.One到InDate.Nine组成完整的 1~10 数字序列。需要说明的是,DateOnly自 .NET 6 起才引入,因此整个InDate系列源码都以#if NET6_0_OR_GREATER条件编译保护,仅在兼容的目标框架(.NET 6.0 及以上)上可用;按官方版本说明,DateOnly流式辅助 API 自 2.11.10 版本起提供(见 fluent-dates-and-time-spans 场景指南)。
类型声明与继承关系
InDate.Ten的完整声明如下:
public static class InDate.Ten- 它是一个静态类,所有成员均为静态成员,可直接通过
InDate.Ten.Days等方式调用,无需实例化; - 它继承自
System.Object,没有额外基类; - 它嵌套在
public partial class InDate内部(partial使多个源文件可以共同构成该类)。
属性成员:从现在起 10 个单位之后的日期
InDate.Ten提供 4 个只读静态属性,语义统一为"从现在起 N 个单位之后",全部返回System.DateOnly:
| 属性 | 说明 | 返回类型 |
|---|---|---|
Days | 从现在起 10 天之后 | System.DateOnly |
Weeks | 从现在起 10 周之后 | System.DateOnly |
Months | 从现在起 10 个月之后 | System.DateOnly |
Years | 从现在起 10 年之后 | System.DateOnly |
对应声明(节选自 Humanizer.InDate.Ten.md):
public static System.DateOnly Days { get; } public static System.DateOnly Weeks { get; } public static System.DateOnly Months { get; } public static System.DateOnly Years { get; }属性成员的底层实现
从 InDate.SomeTimeFrom.cs 的Ten类源码可以看到,这些属性并非简单的"今天 + 10":
public static class Ten { // 10 days from now public static DateOnly Days => DateOnly.FromDateTime(DateTime.UtcNow.AddDays(10)); // 10 weeks from now public static DateOnly Weeks => DateOnly.FromDateTime(DateTime.UtcNow.AddDays(70)); // 10 months from now public static DateOnly Months => DateOnly.FromDateTime(DateTime.UtcNow.AddMonths(10)); // 10 years from now public static DateOnly Years => DateOnly.FromDateTime(DateTime.UtcNow.AddYears(10)); }可以总结出三个关键实现事实:
- 基准时间是 UTC:属性基于
DateTime.UtcNow计算,再通过DateOnly.FromDateTime转换为只含年、月、日的DateOnly; - 周是天数的倍数:
Weeks实现为AddDays(70)(10 × 7),而不是单独的"周"单位,周的定义严格等于 7 天; - 月/年走日历运算:
Months与Years分别委托给AddMonths(10)与AddYears(10),属于"日历粒度"的日期运算(见场景指南对 InDate API 中 Calendar arithmetic 的说明),而不是按固定天数估算。
注意:属性取值依赖"现在",不适合确定性代码
由于Days/Weeks/Months/Years内部读取DateTime.UtcNow,每次调用结果都会随当前时间漂移。场景指南的 Pitfall 小节明确提醒:不带From或Of(year)后缀的属性在代码或测试中可能破坏可重复性,需要确定性结果时应改用下面介绍的XxxFrom(date)方法注入基准日期。
方法成员:基于指定基准日期的相对计算
InDate.Ten提供 8 个静态方法,覆盖 4 个时间单位 × 2 种基准日期类型(DateOnly与DateTime),全部返回DateOnly:
| 方法 | 说明 | 参数 | 返回类型 |
|---|---|---|---|
DaysFrom(DateOnly date) | 从指定日期起 10 天之后 | System.DateOnly | System.DateOnly |
DaysFrom(DateTime date) | 从指定日期起 10 天之后 | System.DateTime | System.DateOnly |
WeeksFrom(DateOnly date) | 从指定日期起 10 周之后 | System.DateOnly | System.DateOnly |
WeeksFrom(DateTime date) | 从指定日期起 10 周之后 | System.DateTime | System.DateOnly |
MonthsFrom(DateOnly date) | 从指定日期起 10 个月之后 | System.DateOnly | System.DateOnly |
MonthsFrom(DateTime date) | 从指定日期起 10 个月之后 | System.DateTime | System.DateOnly |
YearsFrom(DateOnly date) | 从指定日期起 10 年之后 | System.DateOnly | System.DateOnly |
YearsFrom(DateTime date) | 从指定日期起 10 年之后 | System.DateTime | System.DateOnly |
对应声明:
public static System.DateOnly DaysFrom(System.DateOnly date); public static System.DateOnly DaysFrom(System.DateTime date); public static System.DateOnly WeeksFrom(System.DateOnly date); public static System.DateOnly WeeksFrom(System.DateTime date); public static System.DateOnly MonthsFrom(System.DateOnly date); public static System.DateOnly MonthsFrom(System.DateTime date); public static System.DateOnly YearsFrom(System.DateOnly date); public static System.DateOnly YearsFrom(System.DateTime date);方法成员的底层实现
源码中每个单位的方法都成对出现——DateOnly重载直接调用DateOnly的加法扩展,DateTime重载则先对DateTime做加法、再DateOnly.FromDateTime裁剪掉时间部分:
// 10 days from the provided date public static DateOnly DaysFrom(DateOnly date) => date.AddDays(10); public static DateOnly DaysFrom(DateTime date) => DateOnly.FromDateTime(date.AddDays(10)); // 10 weeks from the provided date public static DateOnly WeeksFrom(DateOnly date) => date.AddDays(70); public static DateOnly WeeksFrom(DateTime date) => DateOnly.FromDateTime(date.AddDays(70)); // 10 months from the provided date public static DateOnly MonthsFrom(DateOnly date) => date.AddMonths(10); public static DateOnly MonthsFrom(DateTime date) => DateOnly.FromDateTime(date.AddMonths(10)); // 10 years from the provided date public static DateOnly YearsFrom(DateOnly date) => date.AddYears(10); public static DateOnly YearsFrom(DateTime date) => DateOnly.FromDateTime(date.AddYears(10));两点值得注意:
DateTime重载会丢弃时间分量:例如InDate.Ten.DaysFrom(new DateTime(2025, 1, 1, 23, 59, 59))返回2025-01-11的DateOnly,时间部分被FromDateTime舍去;- 月/年的"月末归一化"行为:
MonthsFrom与YearsFrom继承AddMonths/AddYears的归一化规则,例如从 2 月 29 日(闰年)起加 10 个月,目标是非闰年的 12 月时结果会被归一化为 12 月 28 日。场景指南的 Pitfall 对此有明确说明;如果业务需要"保持同月同日、不存在则抛异常"的严格语义,则应使用date.In(year)之类的重构构造方式(属于PrepositionsExtensions的范畴,见 PrepositionsExtensions API)。
这些成员是如何生成出来的:T4 模板
InDate.Ten及One~Nine并非手工逐个编写,而是由一个 T4 模板 InDate.SomeTimeFrom.tt 生成的。模板的核心逻辑为:
for (var i = 1; i <= 10; i++) { var plural = i > 1 ? "s" : ""; // 类名:i.ToWords().Dehumanize() // Day/Week/Month/Year 按单复数命名,如 One.Day 与 Ten.Days }也就是说:
- 循环从 1 到 10,为每个数字生成一个静态嵌套类(
One、Two…Ten); - 数量为 1 时成员名使用单数(如
InDate.One.Day),数量大于 1 时使用复数(如InDate.Ten.Days); - 类名通过
i.ToWords().Dehumanize()生成,把数字转换成英文单词后再反人化(dehumanize)为 PascalCase 标识符; - 每个类都包含"从现在起"属性与
From方法两套成员,与InDate.Ten完全同构。
因此,本文介绍的全部 12 个成员(4 属性 + 8 方法)在InDate.One~InDate.Nine中均有对应形态,理解Ten就等于理解了整个数字序列;In(DateTime版本)由 In.SomeTimeFrom.tt 以同样的方式生成,且In序列还额外包含秒、分、时三个单位(见 In.SomeTimeFrom.cs)。
实战用法与测试佐证
完整可运行的示例
using Humanizer; // 属性:以当前 UTC 时间为基准 DateOnly in10Days = InDate.Ten.Days; // UtcNow + 10 天 DateOnly in10Weeks = InDate.Ten.Weeks; // UtcNow + 70 天 DateOnly in10Months = InDate.Ten.Months; // UtcNow + 10 个月 DateOnly in10Years = InDate.Ten.Years; // UtcNow + 10 年 // 方法:以固定日期为基准(适合确定性代码与测试) DateOnly baseDate = new(2025, 1, 1); DateOnly fromDateOnly = InDate.Ten.DaysFrom(baseDate); // 2025-01-11 DateOnly fromDateTime = InDate.Ten.WeeksFrom(new DateTime(2025, 1, 1)); // 2025-03-12 DateOnly months = InDate.Ten.MonthsFrom(baseDate); // 2025-11-01 DateOnly years = InDate.Ten.YearsFrom(baseDate); // 2035-01-01与月份构造类组合使用
InDate.Ten的方法重载接受DateOnly,因此可以很方便地与InDate的月份构造器链式组合。例如结合 InDate.Months.cs 中的InDate.JanuaryOf(2025):
DateOnly anniversaryPlus10Months = InDate.Ten.MonthsFrom(InDate.JanuaryOf(2025)); // 2025-11-01仓库测试验证
仓库中的 InDateTests.cs 对InDate系列做了单元验证,其中InFiveDays用例展示了方法重载的预期语义:
[Fact] public void InFiveDays() { var baseDate = OnDate.January.The21st; var date = InDate.Five.DaysFrom(baseDate); Assert.Equal(baseDate.AddDays(5), date); }同一测试文件还验证了InDate.January、InDate.JanuaryOf(2009)、InDate.TheYear(2009)等属性/方法的行为。InDate.Ten与InDate.Five由同一 T4 模板生成、实现同构,因此上述断言模式可直接套用于InDate.Ten.DaysFrom(...)。
使用注意事项小结
| 关注点 | 说明 |
|---|---|
| 目标框架 | InDate及InDate.Ten仅存在于#if NET6_0_OR_GREATER编译分支,需 .NET 6.0+ |
| 版本要求 | DateOnly流式辅助自 2.11.10 起提供,本文基于 2.14.1 文档版本 |
| 属性基准时间 | Days/Weeks/Months/Years基于DateTime.UtcNow,随时间漂移,不宜用于确定性代码 |
| 方法基准时间 | XxxFrom(date)接受调用方注入的基准日期,可重复、可测试,推荐优先使用 |
| 周的定义 | Weeks/WeeksFrom实现为 7 天倍数,并非独立单位 |
| 月/年语义 | 委托AddMonths/AddYears,存在月末归一化(如 2 月 29 日 → 2 月 28 日) |
| 时间裁剪 | 所有成员最终都返回DateOnly,DateTime重载会丢弃时分秒 |
进一步阅读
- 类总览:InDate API(2.14.1) 与 In API(2.14.1)
- 同级数字类:
InDate.One至InDate.Nine(API 目录:website/versioned_docs/version-2.14.1/api) - 实战场景:Compose dates and time spans fluently(2.14.1)
- 源码与测试:InDate.SomeTimeFrom.cs、InDate.SomeTimeFrom.tt、InDateTests.cs
- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
相关推荐
Humanizer InDate.Ten 详解:用 DateOnly 流式 API 计算 10 天/周/月/年后的日期
Humanizer InDate.Ten 详解:用 DateOnly 流式 API 计算 10 天/周/月/年后的日期 InDate.Ten 是 Humaniz
开发工具Humanizer 流式日期 API 详解:InDate.Ten 的 10 天 / 周 / 月 / 年日期计算
Humanizer 流式日期 API 详解:InDate.Ten 的 10 天 / 周 / 月 / 年日期计算 本篇技术指南聚焦 Humanizer 流式日期(
开发工具Tesla Dashcam:3 步快速完整合并行车记录视频
Tesla Dashcam:3 步快速完整合并行车记录视频 特斯拉行车记录仪每次记录事件后,会在 U 盘 TeslaCam 文件夹里留下数十个按摄像头、按分钟切
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考