区域设置全解析:从 snapshot 快照看 format、parse 与时长本地化)
date-fns 波斯尼亚语bs区域设置全解析从 snapshot 快照看 format、parse 与时长本地化【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns本文以 date-fns 仓库中的 bs locale 快照文档 为主体系统梳理波斯尼亚语Bosnian, ISO 639-2: bos区域设置在format、parse、formatDistance、formatDistanceStrict、formatRelative、formatDuration六大 API 中的精确输出行为并结合 bs locale 源码 讲解其背后的本地化数据与复数规则实现。读完本文你将掌握 bs locale 的完整令牌映射表、波斯尼亚语复数one/dual/other形态选择逻辑以及如何在实际项目中引入并验证该区域设置。一、snapshot.md 是什么locale 行为的行为快照在 date-fns 的源码结构中每个区域设置目录如pkgs/core/src/locale/bs/都包含一份snapshot.md。它并不是一篇手写文档而是一份自动生成的输出快照以固定的测试日期为输入记录format、parse、formatDistance、formatDistanceStrict、formatRelative、formatDuration在对应 locale 下的全部输出结果充当行为回归基准。这份快照的价值在于精确到行每一个 Unicode 令牌token在给定日期下的格式化结果、解析回填结果都被逐条记录可回归当修改 locale 的本地化数据如复数规则、缩写表时快照能立即暴露输出变化可直接引用开发者在实现波斯尼亚语 UI 时可以把它当作查询手册无需运行代码即可确认某个令牌会输出什么。bs 快照文档分为五大部分formatandparse、formatDistance、formatDistanceStrict、formatRelative、formatDuration。下文逐一展开所有输出以 UTC 时区、默认Date为准。二、bs locale 在 date-fns 中的位置与基本用法bs locale 的入口文件位于 pkgs/core/src/locale/bs/index.ts并通过 pkgs/core/src/locale/index.ts 的export * from ./bs/index.ts统一导出。其Locale对象结构如下export const bs: Locale { code: bs, formatDistance: formatDistance, formatLong: formatLong, formatRelative: formatRelative, localize: localize, match: match, options: { weekStartsOn: 1 /* Monday */, firstWeekContainsDate: 4, }, };关键配置含义| 配置项 | 值 | 含义 | | ------ | -- | ---- | |code|bs| locale 代码遵循 ISO 639 命名 | |weekStartsOn|1| 一周从星期一开始 | |firstWeekContainsDate|4| 一年中第一个含 1 月 4 日的周为第 1 周ISO 8601 风格 |在项目中使用时将bs作为locale选项传入即可例如import { format, formatDistance, formatRelative } from date-fns; import { bs } from ./pkgs/core/src/locale/bs/index.ts; format(new Date(1987-02-11T12:13:14), PPPP, { locale: bs }); // srijeda, 11. februar 1987. formatDistance(new Date(2000-01-01T00:00:00), new Date(2000-03-01T00:00:00), { locale: bs }); // 2 mjeseca formatRelative(new Date(2000-01-02T00:00:00), new Date(2000-01-01T00:00:00), { locale: bs }); // sutra u 00:00Locale类型定义pkgs/core/src/locale/types.ts表明每个 locale 必须提供formatDistance、formatRelative、localize、formatLong、match五个能力模块bs locale 恰好一一实现。三、format 与 parse 令牌对照快照第一部分记录了format/parse在 bs locale 下的完整令牌输出。format列展示令牌格式化结果parse列展示用同一令牌解析该字符串后回填的日期未匹配到宽度的周几等会返回Invalid Date含时区的长格式时间则返回Errored。3.1 年份与季度令牌含义输入日期format结果parse结果yo公历年序数1987-02-111987.1987-01-010005-01-015.0005-01-01Yo本地周编号年序数1987-02-111987.1986-12-290005-01-014.0003-12-29Qo季度formatting序数2019-01-011.2019-01-012019-04-012.2019-04-01QQQ季度缩写2019-01-011. kv.2019-01-012019-04-012. kv.2019-04-01QQQQ季度完整2019-01-011. kvartal2019-01-012019-04-012. kvartal2019-04-01QQQQQ季度窄2019-01-011.Invalid Date2019-04-012.Invalid Dateqo季度stand-alone序数2019-01-011.2019-01-01qqq季度stand-alone缩写2019-01-011. kv.2019-01-01qqqq季度stand-alone完整2019-01-011. kvartal2019-01-01注意两点其一波斯尼亚语序数一律以句点结尾1987.、1.、2.这正是localize.ordinalNumber中return String(number) .;的实现效果见 localize/index.ts其二窄宽度季度QQQQQ的parse返回Invalid Date说明快照中该宽度仅用于展示、未提供解析回填。3.2 月份formatting 与 stand-alone月份令牌区分 formatting 上下文M系用于11. februar 1987这类组合场景与 stand-alone 上下文L系用于单独列举月份。两者在 bs 中词汇一致但语义分离。完整映射如下序数Moformatting与Lostand-alone1. ~ 12.parse 回填当月 1 日例如 2019-02-11 →2.→ 2019-02-01。缩写MMM/LLL月份format月份format1 月jan7 月jul2 月feb8 月avg3 月mar9 月sep4 月apr10 月okt5 月maj11 月nov6 月jun12 月dec完整MMMM/LLLLjanuar, februar, mart, april, maj, juni, juli, avgust, septembar, oktobar, novembar, decembar。窄MMMMM/LLLLL1. ~ 12.数字加点parse 回填 2019-10 至 2019-12 时得到 2019-01-01即窄宽度解析仅识别前导数字。以上数据与源码 localize/index.ts 中的monthValues与formattingMonthValues完全一致——波斯尼亚语中两种上下文的宽/缩写词汇相同但buildLocalizeFn仍通过formattingValues单独维护保证可独立演化。3.3 周与日令牌含义输入日期format结果parse结果wo本地周序数2019-01-011.2018-12-312019-12-0148.2019-11-25IoISO 周序数2019-01-011.2018-12-312019-12-0148.2019-11-25do日序数2019-02-011.2019-02-012019-02-1111.2019-02-112019-02-2828.2019-02-28do MMMM日 完整月2019-02-011. februar2019-02-01Do年中第几天序数2019-02-1142.2019-02-112019-12-31365.2019-12-31星期几令牌E/EE/EEE缩写、EEEE完整、EEEEE窄、EEEEEE短在快照中的parse结果均为Invalid Dateformat输出为缩写/短E~EEE、EEEEEEpon, pet完整EEEEponedjeljak, petak窄EEEEEP波斯尼亚语星期首字母大写ioISO 星期序数2019-02-11周一→1.→ 2019-02-112019-02-15周五→5.→ 2019-02-15。eo本地星期序数输出与io相同。其窄宽度iiiii/eeeee同样输出P。源码 localize/index.ts 的dayValues定义了七个星期的四级形态narrow[N,P,U,S,Č,P,S]、short/abbreviated[ned,pon,uto,sre,čet,pet,sub]、wide[nedjelja,ponedjeljak,utorak,srijeda,četvrtak,petak,subota]。快照中窄宽度显示P是因为解析端对窄宽度的匹配回填为Invalid Dateparse 端matchDayPatterns的 narrow 正则/^[npusčc]/i无法区分同首字母的ponedjeljak与petak。3.4 一天中的时段AM/PM 与 flexible day periodAM/PM 令牌a、aa、aaaa、aaaaa输出大写AM/PMaaa、bbb输出小写am/pm令牌11:1314:1319:1302:13parse 回填a/aa/aaaa/aaaaaAMPMPMAM00:00 / 12:00 / 12:00 / 00:00aaaampmpmam同上Flexible day period 令牌B一天内更细的时段划分这是 bs 本地化最有特色的部分令牌11:1314:1319:1302:13B/BB/BBBujutrupopodneuvečenoćuBBBBujutruposlije podneuvečenoćuBBBBBBujutrupopodneuvečenoću对应源码 localize/index.ts 的dayPeriodValuesmorningujutru、afternoonpopodnewide 形态poslije podne、eveninguveče、nightnoću。parse 侧则通过 match/index.ts 的正则识别pono(ć)、pod(ne)、jutro、poslije podne、uveče、noću。3.5 小时、分钟、秒令牌含义输入formatparse回填ho小时 [1-12]11:13 / 23:1311.11:00 / 23:00Ho小时 [0-23]11:13 / 23:1311. / 23.11:00 / 23:00Ko小时 [0-11]11:13 / 23:1311.11:00 / 23:00ko小时 [1-24]11:13 / 23:1311. / 23.11:00 / 23:00mo分钟12:01 / 12:551. / 55.12:01 / 12:55so秒12:13:01 / 12:13:551. / 55.12:13:01 / 12:13:55所有小时、分钟、秒序数令牌均以句点结尾符合波斯尼亚语书写习惯。3.6 长本地化日期与时间日期P系与时间p系是快照中信息量最大的部分令牌示例输出1987-02-11对应 formatLong 模板P11. 02. 87.dd. MM. yy.PP11. feb 87.d. MMM yy.PPP11. februar 1987.d. MMMM yyyy.PPPPsrijeda, 11. februar 1987.EEEE, d. MMMM yyyy.p12:13HH:mmpp12:13:14HH:mm:ssppp12:13:14 GMT0HH:mm:ss zpppp12:13:14 (GMT00:00)HH:mm:ss (zzzz)上述模板定义于 formatLong/index.ts。其中ppp/pppp的parse列为Errored快照明确提示含时区偏移的长格式时间字符串不能被parse逆向解析这是 date-fns parse 的已知边界。日期 时间组合Pp、PPpp、PPPppp、PPPPpppp令牌示例输出1987-02-11T12:13:14Pp11. 02. 87. 12:13PPpp11. feb 87. 12:13:14PPPppp11. februar 1987. u 12:13:14 GMT0PPPPppppsrijeda, 11. februar 1987. u 12:13:14 (GMT00:00)注意组合格式中的连接词u意为at它来自 formatLong/index.ts 的dateTimeFormats{{date}} u {{time}}模板中用单引号包裹表示字面量。带ppp/pppp的组合同样在parse列为Errored。四、formatDistance人性化相对时间快照第二部分以现在为 2000-01-01 00:00为基准记录formatDistance的全部输出含includeSeconds: true与addSuffix: true两个选项的组合效果。完整结果如下目标日期默认结果includeSeconds: trueaddSuffix: true2006-01-01oko 6 godinaoko 6 godinaza oko 6 godina2005-01-01oko 5 godinaoko 5 godinaza oko 5 godina2004-01-01oko 4 godineoko 4 godineza oko 4 godine2003-01-01oko 3 godineoko 3 godineza oko 3 godine2002-01-01oko 2 godineoko 2 godineza oko 2 godine2001-06-01preko 1 godinupreko 1 godinuza preko 1 godinu2001-02-01oko 1 godinuoko 1 godinuza oko 1 godinu2001-01-01oko 1 godinuoko 1 godinuza oko 1 godinu2000-06-015 mjeseci5 mjeseciza 5 mjeseci2000-03-012 mjeseca2 mjesecaza 2 mjeseca2000-02-01oko 1 mjesecoko 1 mjesecza oko 1 mjesec2000-01-1514 dana14 danaza 14 dana2000-01-021 dan1 danza 1 dan2000-01-01T06:00oko 6 satioko 6 satiza oko 6 sati2000-01-01T01:00oko 1 satoko 1 satza oko 1 sat2000-01-01T00:45oko 1 satoko 1 satza oko 1 sat2000-01-01T00:3030 minuta30 minutaza 30 minuta2000-01-01T00:1515 minuta15 minutaza 15 minuta2000-01-01T00:011 minuta1 minutaza 1 minutu2000-01-01T00:00:25manje od 1 minutepola minuteza manje od 1 minutu2000-01-01T00:00:15manje od 1 minutemanje od 20 sekundiza manje od 1 minutu2000-01-01T00:00:05manje od 1 minutemanje od 10 sekundiza manje od 1 minutu2000-01-01T00:00:00manje od 1 minutemanje od 5 sekundiprije manje od 1 minute1999-12-31T23:59:55manje od 1 minutemanje od 10 sekundiprije manje od 1 minute1999-12-31T23:59:45manje od 1 minutemanje od 20 sekundiprije manje od 1 minute1999-12-31T23:59:35manje od 1 minutepola minuteprije manje od 1 minute1999-12-31T23:59:001 minuta1 minutaprije 1 minute1999-12-31T23:4515 minuta15 minutaprije 15 minuta1999-12-31T23:3030 minuta30 minutaprije 30 minuta1999-12-31T23:15oko 1 satoko 1 satprije oko 1 sat1999-12-31T23:00oko 1 satoko 1 satprije oko 1 sat1999-12-31T18:00oko 6 satioko 6 satiprije oko 6 sati1999-12-302 dana2 danaprije 2 dana1999-12-1517 dana17 danaprije 17 dana1999-12-01oko 1 mjesecoko 1 mjesecprije oko 1 mjesec1999-11-012 mjeseca2 mjesecaprije 2 mjeseca1999-06-017 mjeseci7 mjeseciprije 7 mjeseci1999-01-01oko 1 godinuoko 1 godinuprije oko 1 godinu1998-12-01oko 1 godinuoko 1 godinuprije oko 1 godinu1998-06-01preko 1 godinupreko 1 godinuprije preko 1 godinu1998-01-01oko 2 godineoko 2 godineprije oko 2 godine1997-01-01oko 3 godineoko 3 godineprije oko 3 godine1996-01-01oko 4 godineoko 4 godineprije oko 4 godine1995-01-01oko 5 godinaoko 5 godinaprije oko 5 godina1994-01-01oko 6 godinaoko 6 godinaprije oko 6 godina这张表充分体现了波斯尼亚语斯拉夫式复数系统——同一单位存在one1、dual2~4且倒数第二位非 1、other其他三种形态例如1 godina/2 godine/5 godina1 mjesec/2 mjeseca/5 mjeseci。其判定逻辑在 formatDistance/index.ts 中写得很直白} else if ( count % 10 1 count % 10 5 // 末位数字为 2~4 String(count).substr(-2, 1) ! 1 // 且倒数第二位不是 1 ) { result tokenValue.dual.replace({{count}}, String(count)); } else { result tokenValue.other.replace({{count}}, String(count)); }后缀机制同样在源码中可见formatDistance/index.ts未来时间加zain过去时间加prijeago。注意addSuffix与count 1组合时词汇还会发生格位变化1 minuta→ 未来1 minutu、过去1 minute这正是withPrepositionIn/withPrepositionAgo两个独立词条存在的意义。五、formatDistanceStrict精确时长formatDistanceStrict不进行约整直接输出数值化时长。快照第三部分基准同为 2000-01-01 00:00完整结果如下目标日期默认结果addSuffix: true强制单位hour2006-01-016 godinaza 6 godina52608 sati2005-01-015 godinaza 5 godina43848 sati2004-01-014 godineza 4 godine35064 sata2003-01-013 godineza 3 godine26304 sata2002-01-012 godineza 2 godine17544 sata2001-06-011 godinaza 1 godinu12408 sati2001-02-011 godinaza 1 godinu9528 sati2001-01-011 godinaza 1 godinu8784 sata2000-06-015 mjeseciza 5 mjeseci3648 sati2000-03-012 mjesecaza 2 mjeseca1440 sati2000-02-011 mjesecza 1 mjesec744 sata2000-01-1514 danaza 14 dana336 sati2000-01-021 danza 1 dan24 sata2000-01-01T06:006 satiza 6 sati6 sati2000-01-01T01:001 satza 1 sat1 sat2000-01-01T00:4545 minutaza 45 minuta1 sat2000-01-01T00:3030 minutaza 30 minuta1 sat2000-01-01T00:1515 minutaza 15 minuta0 sati2000-01-01T00:011 minutaza 1 minutu0 sati2000-01-01T00:00:2525 sekundiza 25 sekundi0 sati2000-01-01T00:00:1515 sekundiza 15 sekundi0 sati2000-01-01T00:00:055 sekundiza 5 sekundi0 sati2000-01-01T00:00:000 sekundiprije 0 sekundi0 sati1999-12-31T23:59:555 sekundiprije 5 sekundi0 sati1999-12-31T23:59:4515 sekundiprije 15 sekundi0 sati1999-12-31T23:59:3525 sekundiprije 25 sekundi0 sati1999-12-31T23:59:001 minutaprije 1 minute0 sati1999-12-31T23:4515 minutaprije 15 minuta0 sati1999-12-31T23:3030 minutaprije 30 minuta1 sat1999-12-31T23:1545 minutaprije 45 minuta1 sat1999-12-31T23:001 satprije 1 sat1 sat1999-12-31T18:006 satiprije 6 sati6 sati1999-12-302 danaprije 2 dana48 sati1999-12-1517 danaprije 17 dana408 sati1999-12-011 mjesecprije 1 mjesec744 sata1999-11-012 mjesecaprije 2 mjeseca1464 sata1999-06-017 mjeseciprije 7 mjeseci5136 sati1999-01-011 godinaprije 1 godine8760 sati1998-12-011 godinaprije 1 godine9504 sata1998-06-012 godineprije 2 godine13896 sati1998-01-012 godineprije 2 godine17520 sati1997-01-013 godineprije 3 godine26280 sati1996-01-014 godineprije 4 godine35064 sata1995-01-015 godinaprije 5 godina43824 sati1994-01-016 godinaprije 6 godina52584 sati观察要点强制单位列展示了下取整行为00:15 → 0 sati、00:45 → 1 sat严格模式下同样应用 dual/other 复数规则4 godinevs5 godinaprije 0 sekundi说明时间差为 0 时仍按过去处理。六、formatRelative相对日期快照第四部分以 2000-01-01星期六为基准目标日期结果2000-01-1010. 01. 00.2000-01-05sljedeću srijedu u 00:002000-01-02sutra u 00:002000-01-01danas u 00:001999-12-31juče u 00:001999-12-27prošli ponedjeljak u 00:001999-12-2121. 12. 99.其模板在 formatRelative/index.ts 中定义注意lastWeek与nextWeek是按星期几分派的函数星期日/星期三/星期六有专属形态prošle nedjelje、prošle srijede、prošle subotesljedeće nedjelje、sljedeću srijedu、sljedeću subotu其余星期走通用模板prošli EEEE u p/sljedeći EEEE u p。超出上周/下周范围的日期回退到Pdd. MM. yy.。七、formatDuration时长对象格式化快照第五部分覆盖formatDuration对Duration对象的单单位输出Duration结果Duration结果{years:0}0 godina{days:0}0 dana{years:1}1 godina{days:1}1 dan{years:2}2 godine{days:2}2 dana{months:0}0 mjeseci{hours:0}0 sati{months:1}1 mjesec{hours:1}1 sat{months:2}2 mjeseca{hours:2}2 sata{weeks:0}0 sedmice{minutes:0}0 minuta{weeks:1}1 sedmicu{minutes:1}1 minuta{weeks:2}2 sedmice{minutes:2}2 minute{seconds:0}0 sekundi{seconds:1}1 sekunda{seconds:2}2 sekunde这里formatDistance字典中的aboutXWeeks/xWeeks词条0 sedmice、1 sedmicu、2 sedmice被formatDuration复用且0值同样按复数规则生成对应形态如0 sati、0 minuta这印证了 formatDistance/index.ts 中xWeeks词条的 standalone 形态1 sedmicu与2 sedmice的差异。八、从快照到源码bs locale 的实现骨架快照中的每一行输出都能在源码中找到对应数据形成完整的证据链词汇表localize/index.ts 定义了 era/quarter/month/day/dayPeriod 五类本地化值其中月份同时维护 formatting 与 stand-alone 两套values/formattingValues序数函数统一追加句点1.、1987.。解析正则match/index.ts 的matchMonthPatterns同时兼容主格与属格如januar|januara、septembar|septembramatchDayPeriodPatterns兼容有无变音符号的写法ponoc|ponoć、uvece|uveče、nocu|noću这正是快照中 parse 列能够稳定回填的底层保障。长格式模板formatLong/index.ts 提供P~PPPP与p~pppp共 8 个宽度的模板以及带连接词u的日期时间组合模板。相对时间模板formatRelative/index.ts 按星期几分派上周/下周表述。距离词条formatDistance/index.ts 定义了 16 类距离词条lessThanXSeconds到almostXYears其中单数词条细分standalone/withPrepositionAgo/withPrepositionIn三态配合 one/dual/other 复数分支与za/prije前后缀。九、结语与使用建议bs snapshot 文档本质上是一份波斯尼亚语日期格式化的行为契约。在实际项目中使用 bs locale 时建议直接以快照为验收标准编写单元测试时将快照中的format/parse关键行尤其是序数句点、PPPP的星期完整名、PPPppp的u连接词、复数三态变化作为断言注意解析边界窄宽度QQQQQ、EEEEE与含时区长格式ppp/pppp在parse侧返回Invalid Date/Errored生产环境解析用户输入时应优先使用P~PPPP与p/pp宽度复数一致性波斯尼亚语的dual规则2~4 且非 11~14在formatDistance、formatDistanceStrict、formatDuration中保持一致前端展示时长时可放心跨 API 混用星期起点bs locale 默认weekStartsOn: 1周一、firstWeekContainsDate: 4周相关令牌Yo、wo、Io、eo的计算均以此为基准。如需深入研读实现细节可继续查阅 bs locale 入口、localize 词汇表、formatDistance 复数逻辑 与 match 解析正则快照原文见 pkgs/core/src/locale/bs/snapshot.md。【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考