Humanizer 相对时间人性化:DefaultDateTimeHumanizeStrategy 默认策略源码级解析
开发工具【免费下载链接】HumanizerHumanizer 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 的DefaultDateTimeHumanizeStrategy是 .NET 平台下将两个DateTime之间的时间距离转换为自然语言如 2 hours ago、tomorrow、one month from now的默认计算策略。本文以该策略的 API 参考文档为核心结合 src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs 及配套算法源码、配置入口和测试用例完整讲解它的类结构、方法签名、底层分级算法、时态判断与本地化机制并给出可直接运行的实战示例。读完本文你将能掌握DateTime.Humanize()的默认输出规则、替换为自定义策略的方法以及通过测试数据验证输出结果的具体方式。类定位Humanizer 日期人性化的策略模式入口在 Humanizer 中人性化时间距离distance of time in words并不是硬编码在扩展方法里的而是通过策略模式实现DefaultDateTimeHumanizeStrategy就是这一体系的默认实现。public class DefaultDateTimeHumanizeStrategy : Humanizer.DateTimeHumanizeStrategy.IDateTimeHumanizeStrategy类声明要点命名空间Humanizer.DateTimeHumanizeStrategy继承链System.Object→DefaultDateTimeHumanizeStrategy实现接口IDateTimeHumanizeStrategy该接口只声明了一个方法string Humanize(DateTime input, DateTime comparisonBase, CultureInfo? culture)。接口的设计目标见 IDateTimeHumanizeStrategy.cs是允许开发者实现自己的策略并通过Configurator.DateTimeHumanizeStrategy把它挂接到DateTime.Humanize上。也就是说DefaultDateTimeHumanizeStrategy只是 Humanizer 提供给开箱即用的默认答案整个计算链路是开放的、可替换的。策略被挂接的位置在 Configurator.cs默认值正是本类public static IDateTimeHumanizeStrategy DateTimeHumanizeStrategy { get; set; } new DefaultDateTimeHumanizeStrategy();该属性属于Humanizer.Configuration.Configurator静态类因此在实际应用中可以通过一行代码全局替换人性化策略例如Configurator.DateTimeHumanizeStrategy new PrecisionDateTimeHumanizeStrategy(0.75);Humanize 方法签名、参数与返回值文档核心 API 是Humanize(DateTime, DateTime, CultureInfo)方法官方定义为计算两个给定日期之间的时间距离用文字表达。public string Humanize(System.DateTime input, System.DateTime comparisonBase, System.Globalization.CultureInfo culture);参数说明参数类型含义inputSystem.DateTime要被人性化描述的日期即目标时间点comparisonBaseSystem.DateTime比较基准日期即当前时间点cultureSystem.Globalization.CultureInfo用于本地化输出的区域性信息例如en-US、zh-CN、ru-RU返回值System.String一段本地化的自然语言描述例如one year ago、in 2 weeks。实现一行委托给核心算法DefaultDateTimeHumanizeStrategy的源码极其简洁它本身不包含任何计算逻辑而是把工作委托给 DateTimeHumanizeAlgorithms.DefaultHumanizepublic string Humanize(DateTime input, DateTime comparisonBase, CultureInfo? culture) DateTimeHumanizeAlgorithms.DefaultHumanize(input, comparisonBase, culture);真正的时间距离换算、时态判定、单位取舍全部发生在DateTimeHumanizeAlgorithms这个静态算法类中。完整调用链从扩展方法到最终输出在实际使用中开发者通常不会直接调用策略的Humanize而是通过扩展方法触发。整条调用链如下入口DateHumanizeExtensions.cs 中的DateTime.Humanize()扩展方法public static string Humanize(this DateTime input, bool? utcDate null, DateTime? dateToCompareAgainst null, CultureInfo? culture null)它负责确定comparisonBase默认取DateTime.UtcNow并根据utcDate参数将基准统一为 UTC 或本地时间然后调用Configurator.DateTimeHumanizeStrategy.Humanize(input, comparisonBase, culture)策略分发Configurator.DateTimeHumanizeStrategy当前指向DefaultDateTimeHumanizeStrategy算法执行DefaultDateTimeHumanizeStrategy.Humanize委托给DateTimeHumanizeAlgorithms.DefaultHumanize本地化输出算法内部通过Configurator.GetFormatter(culture)取得 IFormatter 实例调用formatter.DateHumanize(TimeUnit, Tense, int)拼出最终文本。值得注意的是扩展方法还提供了两个便捷重载见 DateHumanizeExtensions.csDateTime?可空重载输入为null时返回本地化的 never由formatter.DateHumanize_Never()提供显式传入dateToCompareAgainst和culture可在不依赖系统时钟的情况下做确定性测试测试代码正是这样做的。默认算法逐级拆解阈值、时态与单位选择DateTimeHumanizeAlgorithms.DefaultHumanize是整篇文章的核心。它的第一步是计算时态与时间跨度var tense input comparisonBase ? Tense.Future : Tense.Past; var ts new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks));时态input晚于comparisonBase判定为Tense.Future未来否则为Tense.Past过去。两个枚举值的语义见 Tense.csFuture 输出类似 in 2 daysPast 输出类似 2 days ago时间差通过Ticks差取绝对值构造TimeSpan因此算法只关心距离的大小不关心正负方向月份特殊判定sameMonth用于判断input与comparisonBase是否恰好相差一个月考虑未来/过去方向这影响 28~30 天区间内的输出归属。随后进入由小到大、逐级匹配的阈值判断见 DateTimeHumanizeAlgorithms.cs。下表汇总了完整阈值与对应输出单位区间条件输出单位与数量典型输出en-USPastts.TotalMilliseconds 500Millisecond0nowts.TotalSeconds 60Secondts.Seconds10 seconds agots.TotalSeconds 120Minute1a minute agots.TotalMinutes 60Minutets.Minutes44 minutes agots.TotalMinutes 90Hour1an hour agots.TotalHours 24Hourts.Hours10 hours agots.TotalHours 48Daydaysyesterday跨日或 2 days agots.TotalDays 7Dayts.Days6 days agots.TotalDays 28Weekts.Days / 7one week ago、2 weeks ago28 ≤ TotalDays 30若sameMonth为 Month,1否则 Dayone month ago 或按天TotalDays 345Monthfloor(TotalDays / 29.5)10 months ago其余Yearfloor(TotalDays / 365)至少 1one year ago几个值得注意的算法细节一小时边界90 分钟以内一律输出 an hour 而非 1 hour这与英语本地化习惯一致昨天/明天判定TotalHours 48分支使用的是days Math.Abs((input.Date - comparisonBase.Date).Days)即按日历日差值而非按小时数计算所以跨天 1 天输出 yesterday/tomorrow而不是 24 hours ago28~30 天的月份边界sameMonth成立时输出 one month否则输出 N days。测试 TwentyEightDaysUsesCalendarMonth 专门验证了这一点从 2023-03-01 往前 28 天输出 one month ago从 2023-02-01 往后 28 天输出 one month from now年度下限TotalDays / 365计算出的years为 0 时强制置 1避免出现 0 years。最终输出统一调用formatter.DateHumanize(TimeUnit.Year, tense, years);其中TimeUnit枚举Millisecond / Second / Minute / Hour / Day / Week / Month / Year见 TimeUnit.cs数量词的复数形式one second vs 2 seconds由各语言 formatter 内部处理。本地化culture 如何影响最终文案culture参数在算法中并不参与数值计算而是决定选用哪个语言的 formatter。算法内部通过Configurator.GetFormatter(culture)见 Configurator.cs从FormatterRegistry解析出对应文化的IFormatter再由它把(TimeUnit, Tense, count)三元组渲染成本地语言的自然语句。这一机制可以从测试得到直观印证。CanSpecifyCultureExplicitly 展示了同一组数值在不同文化下的输出[InlineData(1, TimeUnit.Year, Tense.Future, en-US, one year from now)] [InlineData(40, TimeUnit.Second, Tense.Past, ru-RU, 40 секунд назад)] [InlineData(2, TimeUnit.Day, Tense.Past, sv-SE, för 2 dagar sedan)] [InlineData(2, TimeUnit.Week, Tense.Future, de-DE, in 2 Wochen)]如果调用时不传culture传nullHumanizer 将使用当前线程的CurrentCulture。完整的本地化文案由各语言的.yml语言资源文件维护见 src/Humanizer/Locales 下的en.yml、ru.yml、de.yml等并由 SourceGenerator 在编译期生成对应的 Formatter 类型。与 PrecisionDateTimeHumanizeStrategy 的对比理解默认策略的最佳参照系是同接口的另一个实现 PrecisionDateTimeHumanizeStrategypublic class PrecisionDateTimeHumanizeStrategy(double precision .75) : IDateTimeHumanizeStrategy { public string Humanize(DateTime input, DateTime comparisonBase, CultureInfo? culture) DateTimeHumanizeAlgorithms.PrecisionHumanize(input, comparisonBase, precision, culture); }两者差异集中在两点默认策略本主题用一系列固定阈值直接截断到最合适的单位简单直观、输出稳定是Configurator的默认选择精度策略接受一个precision默认 0.75参数在单位进位时做近似舍入如毫秒数 999 * precision才进位到秒适合需要微调边界行为的场景。测试辅助类 DateHumanize.cs 会按precision是否传入在这两个策略之间切换正是两者在测试体系中可互换的证明。在.NET 6目标框架下算法类还为DateOnly、TimeOnly提供了对应重载见 DateTimeHumanizeAlgorithms.cs行为与DateTime版本保持一致。实战使用与预期输出速查引入命名空间using Humanizer;后即可直接调用using Humanizer; var now DateTime.UtcNow; now.AddSeconds(-10).Humanize(); // 10 seconds ago now.AddMinutes(45).Humanize(); // 45 minutes from now now.AddHours(-23).Humanize(); // 23 hours ago now.AddDays(-1).Humanize(); // yesterday now.AddDays(1).Humanize(); // tomorrow now.AddDays(-13).Humanize(); // one week ago now.AddDays(-32).Humanize(); // one month ago now.AddDays(-400).Humanize(); // one year ago // 可空日期与显式文化 DateTime? never null; never.Humanize(); // never now.AddMonths(-10).Humanize(culture: new System.Globalization.CultureInfo(ru-RU));以上英文输出均可在 DateHumanizeDefaultStrategyTests.cs 的 Theory 数据中找到对应断言例如SecondsAgo60 秒 → a minute agoL4-L10HoursAgo24 小时 → yesterdayL42-L48DaysAgo7/13 天 → one week ago32 天 → one month agoL70-L79MonthsAgo12 个月 → one year agoL102-L108Now0 差值 → nowL130-L132。总结DefaultDateTimeHumanizeStrategy是 Humanizer 日期人性化的标准答案它实现IDateTimeHumanizeStrategy接口把全部计算委托给DateTimeHumanizeAlgorithms.DefaultHumanize通过一套由毫秒到年的分级阈值把TimeSpan距离映射为最合适的TimeUnit再借助文化相关的IFormatter输出本地化文案。理解它等于理解了DateTime.Humanize()的全部默认行为——包括 yesterday/tomorrow 的日历日判定、28~30 天的月份边界特判、一周内的周单位折算以及通过Configurator.DateTimeHumanizeStrategy替换为自定义或PrecisionDateTimeHumanizeStrategy的扩展路径。若需进一步研究接口契约可参考 IDateTimeHumanizeStrategy 文档或直接阅读 DateTimeHumanizeAlgorithms.cs 中的完整实现。赞分享开发工具【免费下载链接】HumanizerHumanizer 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 中 DefaultDateTimeOffsetHumanizeStrategy 源码解析DateTimeOffset 相对时间人性化默认策略Humanizer 中 DefaultDateTimeOffsetHumanizeStrategy 源码解析DateTimeOffset 相对时间人性化默认策开发工具Humanizer 中 DefaultTimeOnlyHumanizeStrategy 源码解读TimeOnly 相对时间人性化的默认策略实现Humanizer 中 DefaultTimeOnlyHumanizeStrategy 源码解读TimeOnly 相对时间人性化的默认策略实现 本文围绕开发工具Humanizer 的 DefaultTimeOnlyHumanizeStrategy 详解TimeOnly 相对时间人文化的默认策略与源码剖析Humanizer 的 DefaultTimeOnlyHumanizeStrategy 详解TimeOnly 相对时间人文化的默认策略与源码剖析 导读 本文围开发工具上一篇NiGui未来路线图即将到来的macOS支持与新特性预览下一篇Awesome MCP Servers 项目教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考