
开发工具【免费下载链接】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 库中的TimeSpanHumanizeExtensions扩展类展开系统讲解如何把System.TimeSpan转换为1 day2 hours, 3 seconds这类人类可读的时长文本。你将掌握Humanize()两个重载的全部参数语义precision、countEmptyUnits、maxUnit、minUnit、culture、collectionSeparator、toWords理解 month/year 近似换算的底层常数并了解同一扩展类下HumanizeToSymbols、HumanizeWithCase、HumanizeWithFractionalSeconds、ToAge等进阶能力可直接用于日志、审计界面、年龄展示、本地化时长文本等实战场景。一、这个扩展类解决什么问题在 .NET 中TimeSpan是一段精确到刻度tick的持续时间直接ToString()会输出1.02:03:04这类机器可读格式而业务界面往往需要1 day2 hours, 3 seconds这样直观、可本地化的表述。Humanizer 在 src/Humanizer/TimeSpanHumanizeExtensions.cs 中定义了公开静态类TimeSpanHumanizeExtensions通过一系列this TimeSpan扩展方法把时间跨度人性化Humanizes TimeSpan into human readable form。该类源码中声明的三个换算常量是整个转换体系的基础const int DaysInAWeek 7; const double DaysInAYear 365.2425; // 公历Gregorian calendar平均年长 const double DaysInAMonth DaysInAYear / 12; // ≈ 30.4369 天也就是说当涉及月、年单位时Humanizer 采用近似换算而非严格的日历计算这一点在maxUnit参数文档中有明确说明见后文。二、快速上手一行代码得到人类可读时长最常用的调用方式是不带任何参数此时默认输出最大的一个非零时间单位using Humanizer; TimeSpan.FromDays(14).Humanize(); // 2 weeks TimeSpan.FromDays(1).Humanize(); // 1 day TimeSpan.FromHours(2).Humanize(); // 2 hours TimeSpan.FromMinutes(135).Humanize(); // 2 minutes自动向上取整到分钟 TimeSpan.FromMilliseconds(2500).Humanize();// 2 seconds2500ms 进位到秒 TimeSpan.Zero.Humanize(); // 0 milliseconds以上输出均可在 tests/Humanizer.Tests/TimeSpanHumanizeTests.cs 的Weeks、Days、Hours、Minutes、Seconds、Milliseconds、NoTime等测试用例中找到对应断言。值得注意的一个行为负数 TimeSpan 的输出与正数相同符号被忽略例如TimeSpan.FromDays(-6).Humanize()与TimeSpan.FromDays(6).Humanize()都返回6 days。这在源码的BuildFormatTimePart中通过Math.Abs(amountOfTimeUnits)实现测试Days用例-6 → 6 days亦验证了这一行为。三、核心 API 与参数全解该扩展类在 API 文档website/versioned_docs/version-2.14.1/api/Humanizer.TimeSpanHumanizeExtensions.md中公开了两个Humanize重载。重载一全参数版本public static string Humanize( this TimeSpan timeSpan, int precision, bool countEmptyUnits, CultureInfo culture null, TimeUnit maxUnit TimeUnit.Week, TimeUnit minUnit TimeUnit.Millisecond, string collectionSeparator , , bool toWords false);重载二常用参数版本默认精度为 1public static string Humanize( this TimeSpan timeSpan, int precision 1, CultureInfo culture null, TimeUnit maxUnit TimeUnit.Week, TimeUnit minUnit TimeUnit.Millisecond, string collectionSeparator , , bool toWords false);两个重载都返回System.String即E.g. 1 day形式的人类可读文本。全部参数说明如下参数类型默认值含义timeSpanSystem.TimeSpan必填待格式化的时间跨度扩展方法的接收者precisionint1最多返回多少个时间单位。默认1表示只返回最大的那个单位countEmptyUnitsboolfalse仅全参数重载显式传入是否把空的时间单位计入precision上限。前导空单位永远不计入如 1 小时 20 毫秒中中间的分/秒是否占名额由该参数决定cultureCultureInfonull使用的区域文化。为null时使用当前线程的 UI 文化maxUnitTimeUnitTimeUnit.Week允许输出的最大时间单位。设为Month/Year时对超过 30 天的跨度按近似值换算1 年 ≈ 365.2425 天、1 月 ≈ 30.4369 天minUnitTimeUnitTimeUnit.Millisecond允许输出的最小时间单位collectionSeparatorstring, 拼接多个时间部分的连接符。为null时使用当前文化默认的集合格式化器例如英文的 andtoWordsboolfalse为true时用单词而非数字表示数量例如 one dayTimeUnit枚举定义在 src/Humanizer/Localisation/TimeUnit.cs取值依次为Millisecond、Second、Minute、Hour、Day、Week、Month、Year。四、precision 与 countEmptyUnits控制输出粒度precision直接决定返回多少个时间部分。参考测试TimeSpanWithPrecision同样位于 TimeSpanHumanizeTests.cs以 3,608,020 毫秒1 小时又 20 毫秒为例TimeSpan.FromMilliseconds(3600020).Humanize(precision: 1); // 1 hour TimeSpan.FromMilliseconds(3600020).Humanize(precision: 2); // 1 hour, 20 milliseconds TimeSpan.FromMilliseconds(3600020).Humanize(precision: 3); // 1 hour, 20 milliseconds注意precision: 2与precision: 3输出相同因为中间不存在非零的分钟/秒部分——空单位不占用名额countEmptyUnits为false的默认行为。再看 1,299,630,020 毫秒2 周 1 天 1 小时 30 秒 20 毫秒的逐步细化// precision: 1 → 2 weeks // precision: 2 → 2 weeks, 1 day // precision: 3 → 2 weeks, 1 day, 1 hour // precision: 4 → 2 weeks, 1 day, 1 hour, 30 seconds // precision: 5 → 2 weeks, 1 day, 1 hour, 30 seconds, 20 milliseconds当countEmptyUnits: true时中间的空单位也占用名额输出会被压短。测试TimeSpanWithPrecisionAndCountingEmptyUnits中3,600,020 毫秒在precision: 2, countEmptyUnits: true下输出1 hour原本precision: 2会输出1 hour, 20 milliseconds因为分钟和秒两个空单位依次占用了名额。前导空单位则始终不计数——例如 1 小时 20 毫秒中最前面的小时之前没有更大的非零单位因此转换仍从小时开始。从源码看precision 1 !countEmptyUnits会走HumanizeSinglePart快路径只找第一个非零单位否则进入CreatePrecisionLimitedTimeParts逐单位分解TimeSpanHumanizeExtensions.cs。五、maxUnit 与 minUnit圈定时长单位范围默认maxUnit Week意味着 30 天以上的跨度不会自动换算成年/月而会以周为单位累加例如TimeSpan.FromDays(730).Humanize()输出104 weeks。测试TimeSpanWithMaxTimeUnit展示了显式指定上限的效果TimeSpan.FromMilliseconds(366L * 24 * 60 * 60 * 1000).Humanize(maxUnit: TimeUnit.Month); // 12 months TimeSpan.FromMilliseconds(6L * 7 * 24 * 60 * 60 * 1000).Humanize(maxUnit: TimeUnit.Week); // 6 weeks TimeSpan.FromMilliseconds(7L * 24 * 60 * 60 * 1000).Humanize(maxUnit: TimeUnit.Day); // 7 days TimeSpan.FromMilliseconds(24L * 60 * 60 * 1000).Humanize(maxUnit: TimeUnit.Hour); // 24 hours当maxUnit设为Month或Year时Humanizer 使用365.2425 天/年、30.4369 天/月的近似换算对应源码常量DaysInAYear与DaysInAMonth。这是文档明确提示的近似行为它不是严格日历计算而是为超出 30 天的跨度给出合理估算。相关分解逻辑见GetSpecialCaseYearAsInteger、GetSpecialCaseMonthAsInteger等私有方法测试Years、Months、MonthAndYearRangesIncludeWeeks验证了1 year, 11 months, 4 weeks, 1 day这类含周部分的输出。minUnit则限制最小粒度。测试TimeSpanWithMinTimeUnit中2,500 毫秒TimeSpan.FromMilliseconds(2500).Humanize(minUnit: TimeUnit.Millisecond); // 2 seconds, 500 milliseconds TimeSpan.FromMilliseconds(2500).Humanize(minUnit: TimeUnit.Second); // 2 seconds当minUnit大于实际最大单位时如 10 毫秒但minUnit: Second输出退化为0 seconds若同时开启toWords: true则输出no time。0 值跨度的默认输出是0 milliseconds对应格式化器的TimeSpanHumanize_Zero见 DefaultFormatter.cs。六、culture 与 collectionSeparator本地化与连接词culture参数控制输出语言与数字格式。为null时使用当前线程文化。测试CanSpecifyCultureExplicitly给出了多语言示例TimeSpan.FromMilliseconds(6 * 24 * 60 * 60 * 1000) .Humanize(precision: 1, culture: new CultureInfo(ru-RU)); // 6 дней TimeSpan.FromMilliseconds(11 * 60 * 60 * 1000) .Humanize(precision: 1, culture: new CultureInfo(ar)); // 11 ساعة TimeSpan.FromMilliseconds(3603001) .Humanize(precision: 2, culture: new CultureInfo(it-IT), collectionSeparator: null); // 1 ora e 3 secondicollectionSeparator默认是, 逗号加空格传null时改用当前文化的默认集合格式化器英文下会生成 and 连接词含牛津逗号测试TimeSpanWithPrecisionAndAlternativeCollectionFormatter的断言如下TimeSpan.FromMilliseconds(62000).Humanize(precision: 2, collectionSeparator: null); // 1 minute and 2 seconds TimeSpan.FromMilliseconds(62020).Humanize(precision: 3, collectionSeparator: null); // 1 minute, 2 seconds, and 20 milliseconds TimeSpan.FromMilliseconds(3603001).Humanize(precision: 3, collectionSeparator: null); // 1 hour, 3 seconds, and 1 millisecond底层实现上ConcatenateTimeSpanParts在collectionSeparator null时调用Configurator.CollectionFormatters.ResolveForCulture(culture).Humanize(timeSpanParts)由 Configurator.cs 中的集合格式化器注册表按文化解析默认连接词。七、toWords用英文单词代替数字toWords: true会把数量部分转成单词E.g. one day测试TimeSpanWithNumbersConvertedToWords展示了效果TimeSpan.FromMilliseconds(2500).Humanize(precision: 2, toWords: true); // two seconds, five hundred milliseconds TimeSpan.FromMilliseconds(62020).Humanize(precision: 3, toWords: true); // one minute, two seconds, twenty milliseconds TimeSpan.FromMilliseconds(86401200).Humanize(precision: 2, toWords: true);// one day, one second TimeSpan.Zero.Humanize(toWords: true); // no time该模式同样支持文化本地化测试CanSpecifyCultureExplicitlyToWords中 236 天在阿拉伯语下输出سبعة أشهر, ثلاثة أسابيعmaxUnit: TimeUnit.Year, precision: 2。单词化由格式化器内部调用对应文化的数字转单词能力完成见FormatTimePart与 DefaultFormatter.cs。八、底层实现从扩展方法到格式化器的完整调用链Humanize本身是薄封装真正的工作委托给可配置的策略对象。调用链如下TimeSpanHumanizeExtensions.Humanize(...) → Configurator.TimeSpanHumanizeStrategy.Humanize(...) → DefaultTimeSpanHumanizeStrategy.Humanize(...) → TimeSpanHumanizeExtensions.DefaultHumanize(...) → DefaultHumanizeCore(...) // 单位分解 拼接策略入口Configurator.TimeSpanHumanizeStrategy默认是 DefaultTimeSpanHumanizeStrategy.cs 的实例。这是一个公开可替换属性文档要求在应用启动阶段设置一次避免运行期并发修改Configurator.cs 中带volatile语义注释。单位遍历顺序TimeUnits Enumerable.Reverse(Enum.GetValuesTimeUnit())即从Year到Millisecond从大到小遍历先找最大非零单位。数值分解GetTimeUnitNumericalValue按单位取值当某个单位恰好是maxUnit时使用TotalXxx累计值如TotalMinutes否则取余数部分timespan.Minutes保证1 hour, 2 minutes这类分解正确。负数处理BuildFormatTimePart内部使用Math.Abs因此负数跨度输出不带负号。大数饱和GetNormalCaseTimeAsInteger将计数钳制到int.MaxValue测试LargeTimeSpansSaturateCounts验证TimeSpan.MaxValue.Humanize(maxUnit: Second, minUnit: Second)输出2147483647 seconds。零值回退找不到任何非零部分时若toWords !toSymbols返回TimeSpanHumanize_Zero()英文为 no time否则按minUnit输出0 ...。本地化文案单位短语来自各文化的短语表phrase table缺失时抛出InvalidOperationException异常信息包含缺失的 culture 与单位名。另外TimeSpanHumanizeTests.cs 中的AllTimeSpansMustBeUniqueForASequenceOfDays对 0–100000 天连续跨度以precision: 4, maxUnit: Year逐一Humanize并断言输出互不重复保证了分解算法的单调唯一性。九、同一扩展类下的进阶能力TimeSpanHumanizeExtensions还提供了一系列围绕同一主题的扩展方法1. HumanizeToSymbols本地化单位符号HumanizeToSymbols把单位渲染为短符号而非完整单词适合紧凑 UI。测试CanUseLocalizedSymbolsWithPrecisionvar timeSpan new TimeSpan(8, 1, 2, 3, 4); // 8 天 1 小时 2 分 3 秒 4 毫秒 timeSpan.HumanizeToSymbols(3); // 1week, 1d, 1h timeSpan.HumanizeToSymbols(6); // 1week, 1d, 1h, 2min, 3s, 4ms TimeSpan.Zero.HumanizeToSymbols(minUnit: TimeUnit.Second); // 0s符号同样随文化变化如格鲁吉亚语ka下TimeSpan.FromMinutes(2).HumanizeToSymbols(culture: new(ka))输出2წთ。2. HumanizeWithCase语法格感知的时长HumanizeWithCase按GrammaticalCase如Nominative、Dative、Absolutive等选择本地化短语适用于格语言如德语、巴斯克语、斯拉夫语系的复杂语法场景new TimeSpan(8, 2, 0, 0).HumanizeWithCase( GrammaticalCase.Dative, precision: 3, culture: new CultureInfo(de-DE)); // einer Woche, einem Tag, 2 Stunden该方法要求当前策略实现IGrammaticalCaseTimeSpanHumanizeStrategy接口IGrammaticalCaseTimeSpanHumanizeStrategy.cs否则抛出NotSupportedException非法枚举值抛出ArgumentOutOfRangeException。3. HumanizeWithFractionalSeconds小数秒HumanizeWithFractionalSeconds与HumanizeToSymbolsWithFractionalSeconds支持秒以下的小数位参数maxFractionalDigits范围 0–7roundingMode仅支持MidpointRounding.ToEven与AwayFromZero超出即抛ArgumentOutOfRangeException。实现会先按精度四舍五入RoundToFractionalSecondPrecision再以秒为最小单位输出若当前策略不支持小数秒且结果确实存在小数部分会抛出InvalidOperationException提示实现IFractionalTimeSpanHumanizeStrategy。4. ToAge年龄表达ToAge把跨度包装成多少岁的表达如TimeSpan.FromDays(4).ToAge()→4 days old、TimeSpan.FromDays(367).ToAge(toWords: true)→one year old。它复用Humanize后再套用文化特有的年龄模板TimeSpanHumanize_Age()模板同时兼容{0}与{value}两种占位符见FormatAge与测试AgeFormatterSupportsLegacyAndNamedTemplates。十、总结TimeSpanHumanizeExtensions是 Humanizer 中最常用的时长人性化入口Humanize两个重载提供了从只取最大单位到多单位、计空位、限范围、多语言、单词化的完整控制面HumanizeToSymbols、HumanizeWithCase、HumanizeWithFractionalSeconds、ToAge则覆盖了紧凑符号、语法格、小数秒与年龄等细分场景。整套机制以Configurator.TimeSpanHumanizeStrategy为可替换策略点、以文化短语表为本地化底座源码集中在 src/Humanizer/TimeSpanHumanizeExtensions.cs行为由 tests/Humanizer.Tests/TimeSpanHumanizeTests.cs 与 tests/Humanizer.Tests/FractionalTimeSpanHumanizeTests.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 的 TimeSpanHumanizeExtensions 完全指南将 TimeSpan 转化为人类可读的时长文本Humanizer 的 TimeSpanHumanizeExtensions 完全指南将 TimeSpan 转化为人类可读的时长文本 TimeSpanHuma开发工具Humanizer 的 TimeSpanHumanizeExtensions 完全指南把 TimeSpan 变成自然语言Humanizer 的 TimeSpanHumanizeExtensions 完全指南把 TimeSpan 变成自然语言 导读 Humanizer 是一个面向开发工具Humanizer 的 IGrammaticalCaseTimeSpanHumanizeStrategy让 TimeSpan 时长文本按语法格Grammatical Case本地化Humanizer 的 IGrammaticalCaseTimeSpanHumanizeStrategy让 TimeSpan 时长文本按语法格Grammat开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考