date-fns 德语(奥地利)de-AT 语言环境完全指南:format/parse 令牌、相对时间与本地化实现解析
【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns
本文以 date-fns 仓库中pkgs/core/src/locale/de-AT/snapshot.md(德语奥地利区语言环境的官方行为快照)为核心,系统讲解 de-AT 语言环境下format、parse、formatDistance、formatDistanceStrict、formatRelative、formatDuration六大函数的实际输出规则,并对照pkgs/core/src/locale/de-AT/与pkgs/core/src/locale/de/下的真实源码,说明「Jänner」「vorm. / nachm.」「1. Quartal」等奥地利特有写法背后的实现原理与配置方式。读完本文,你将能够准确预测 de-AT 环境下每个令牌的格式化结果,理解本地化数据的组织方式,并能在自己的项目中正确导入与使用deAT语言环境。
一、de-AT 语言环境在 date-fns 中的定位
de-AT(德语–奥地利)是 date-fns 多语言支持体系中的一员。在仓库中,该语言环境由 pkgs/core/src/locale/de-AT/index.ts 定义并导出,同时通过 pkgs/core/src/locale/index.ts#L22 中的export * from "./de-AT/index.ts"被统一汇总到date-fns/locale入口。
从源码结构上看,de-AT 并不是从零编写的一整套独立实现,而是「复用 de 语言环境的通用部分 + 覆写奥地利特有部分」的组合式实现。这一点在 de-AT/index.ts 中体现得非常明显:
import { formatDistance } from "../de/_lib/formatDistance/index.ts"; import { formatLong } from "../de/_lib/formatLong/index.ts"; import { formatRelative } from "../de/_lib/formatRelative/index.ts"; import { match } from "../de/_lib/match/index.ts"; import type { Locale } from "../types.ts"; // difference to 'de' locale import { localize } from "./_lib/localize/index.ts"; export const deAT: Locale = { code: "de-AT", formatDistance: formatDistance, formatLong: formatLong, formatRelative: formatRelative, localize: localize, match: match, options: { weekStartsOn: 1 /* Monday */, firstWeekContainsDate: 4, }, };可以看到,de-AT 与 de 的核心差异集中在localize(本地化词表)上——尤其是 1 月写作Jänner、缩写为Jän.,以及上午/下午使用vorm. / nachm.等奥地利用法。距离、长格式、相对时间与匹配解析则直接复用 de 的实现。options中的weekStartsOn: 1(周一为一周起点)与firstWeekContainsDate: 4(一年第一周须包含 1 月 4 日)与德国本地历法规则一致,与快照中「Local week-numbering year」等计算结果一一对应。
二、snapshot.md 是什么:语言环境行为的「黄金基准」
每个语言环境目录下都有一份snapshot.md(如 de-AT/snapshot.md、de/snapshot.md)。它由脚本对真实函数执行结果快照生成,记录了该语言环境下全部相关函数的预期输出,是校对本地化数据、回归测试与文档参考的权威依据。本文后续所有结果均取自该快照,可直接当作断言来使用。
快照整体结构分为六大部分,覆盖六类函数:
| 快照章节 | 对应函数 | 关注点 |
|---|---|---|
formatandparse | format/parse | 每个令牌的格式化结果与可解析性 |
formatDistance | formatDistance | 模糊距离、includeSeconds、addSuffix |
formatDistanceStrict | formatDistanceStrict | 精确距离、强制单位 |
formatRelative | formatRelative | 相对日期短语 |
formatDuration | formatDuration | 时长对象转文字 |
下文逐一展开。
三、format与parse:de-AT 令牌行为全景
快照第一部分以「令牌字符串 × 输入日期 → format 结果 → parse 结果」的表格形式,逐一给出 de-AT 环境下每个令牌的预期输出。parse列为空串时表示该令牌在解析阶段不可用,Invalid Date表示无法唯一解析(如下文的单字母星期几),Errored表示该长格式包含时区文本而无法解析。
3.1 年份与季度
| 令牌 | 语义 | format 示例 | parse 结果 |
|---|---|---|---|
yo | 日历年份(序数) | 1987 →1987.;0005 →5. | 1987-01-01 / 0005-01-01 |
Yo | 本地周编号年份 | 1987 →1987.;0005 →4. | 1986-12-29 / 0003-12-29 |
Qo | 季度(格式化,序数) | Q1 →1. | 2019-01-01 |
QQQ | 季度(格式化,缩写) | Q1 →Q1 | 2019-01-01 |
QQQQ | 季度(格式化,全称) | Q1 →1. Quartal | 2019-01-01 |
QQQQQ | 季度(格式化,窄式) | Q1 →1 | 2019-01-01 |
qo/qqq/qqqq/qqqqq | 季度(独立式) | 与格式化同形 | 同上 |
注意Yo(本地周编号年份)与yo(日历年份)在跨年边界处结果不同:快照中 0005-01-01 按Yo输出4.并解析回 0003-12-29,这正是firstWeekContainsDate: 4与周一起始共同作用的结果。
3.2 月份:奥地利写法的核心差异区
de-AT 与 de 最大的差异在 1 月:
| 令牌 | 含义 | 1 月输出 | 说明 |
|---|---|---|---|
Mo | 月份(序数) | 1. | 所有月份均为「数字 + 句点」 |
MMM | 月份(缩写,格式化) | Jän. | 奥地利缩写,区别于德国的 Jan. |
MMMM | 月份(全称,格式化) | Jänner | 奥地利全称,区别于德国的 Januar |
MMMMM | 月份(窄式) | J | 仅首字母,无法唯一解析 |
快照中 3 月的缩写输出为März(无句点,保留完整词形),5 月Mai、6 月Juni、7 月Juli在缩写与全称中同形。这些数据的来源即 de-AT/_lib/localize/index.ts 中的monthValues与formattingMonthValues:
const monthValues = { narrow: ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"] as const, abbreviated: ["Jän", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"] as const, wide: ["Jänner", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"] as const, }; // https://st.unicode.org/cldr-apps/v#/de_AT/Gregorian/ const formattingMonthValues = { narrow: monthValues.narrow, abbreviated: ["Jän.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sep.", "Okt.", "Nov.", "Dez."] as const, wide: monthValues.wide, };关键点在于:
monthValues(独立式 standalone)缩写不带句点:Jän、Feb;formattingMonthValues(格式化 context)缩写带句点:Jän.、Feb.,这正是快照中MMM输出Jän.而LLL输出Jän的原因;- 源码注释表明词表参考自 Unicode CLDR 的
de_ATGregorian 数据(仓库中保留了https://st.unicode.org/cldr-apps/v#/de_AT/Gregorian/这一数据来源链接); - 代码注释还特别提醒:德语中星期与月份需要首字母大写,且格式化结果应像处于句子中间那样自然(与西班牙语小写约定相反)。
3.3 星期几
| 令牌 | 语义 | 示例(2019-02-11,周一) | parse 结果 |
|---|---|---|---|
E/EE/EEE | 星期(缩写) | Mo. | 2019-02-11 |
EEEE | 星期(全称) | Montag | 2019-02-11 |
EEEEE | 星期(窄式) | M | Invalid Date(无法唯一解析) |
EEEEEE | 星期(短式) | Mo | 2019-02-11 |
i/iii/iiii/iiiii/iiiiii | ISO 星期(周一=1) | 1./Mo./Montag/M/Mo | 同 E 系列 |
e/eee/... | 本地星期(周一=1) | 同上 | 同上 |
c/ccc/... | 本地星期(独立式) | 同上 | 同上 |
快照中值得注意的细节:单字母窄式EEEEE/iiiii/eeeee/ccccc在解析时对「M」(Montag)返回Invalid Date,因为单字母无法在多个 M 开头星期(Montag、Mittwoch)间唯一确定;而 2019-02-15 的F(Freitag)可解析。这是快照对解析歧义性的如实记录,提醒开发者:窄式令牌更适合格式化而非解析。
星期词表同样定义在 de-AT/_lib/localize/index.ts 的dayValues中(So./Mo./Di....与Sonntag/Montag/...),de-AT 与 de 在此保持一致。
3.4 上午/下午与弹性时段
de-AT 的 AM/PM 使用奥地利特有的缩写:
| 令牌 | 含义 | 上午输出 | 下午输出 |
|---|---|---|---|
a/aa/aaa | AM/PM(缩写) | vorm. | nachm. |
aaaa | AM/PM(全称) | vormittags | nachmittags |
aaaaa | AM/PM(窄式) | vm. | nm. |
b/bb/bbb | AM/PM/正午/午夜(缩写) | vorm. | nachm. |
bbbb | 同上(全称) | vormittags | nachmittags |
bbbbb | 同上(窄式) | vm. | nm. |
弹性时段令牌B系列按一天中的时间段输出:
| 时间 | B/BB/BBB(缩写) | BBBB(全称) |
|---|---|---|
| 02:13 | nachts | nachts |
| 11:13 | morgens | morgens |
| 14:13 | nachmittags | nachmittags |
| 19:13 | abends | abends |
快照同时记录了 parse 侧的边界行为:B缩写系列解析 14:13 时返回Invalid Date,而全称BBBB可解析为当天 12:00——说明弹性时段的缩写解析存在歧义,建议解析场景使用全称。这些词表定义在 de-AT/_lib/localize/index.ts 的dayPeriodValues与formattingDayPeriodValues中(narrow 用vm./nm.,缩写用vorm./nachm.,wide 用vormittags/nachmittags)。
3.5 小时、分钟、秒与日期序号
| 令牌 | 语义 | 示例 |
|---|---|---|
ho | 小时 [1-12](序数) | 11:13 →11.;23:13 →11. |
Ho | 小时 [0-23](序数) | 23:13 →23. |
Ko | 小时 [0-11](序数) | 23:13 →11. |
ko | 小时 [1-24](序数) | 23:13 →23. |
mo | 分钟(序数) | 12:55 →55. |
so | 秒(序数) | 12:13:55 →55. |
do | 日(序数) | 2 月 11 日 →11. |
do MMMM | 日 + 月份全称 | 11. Februar |
Do | 年内第几天 | 2 月 11 日 →42.;12 月 31 日 →365. |
wo/Io | 本地/ISO 周序号 | 2019-01-01 →1. |
de-AT 的序数规则非常统一:所有序数均为「数字 + 句点」,无性别/格变化,这是由 de-AT/_lib/localize/index.ts 中的ordinalNumber实现保证的:
const ordinalNumber: LocalizeFn<number> = (dirtyNumber) => { const number = Number(dirtyNumber); return number + "."; };3.6 长本地化日期P/PP/PPP/PPPP
长日期令牌由 de/_lib/formatLong/index.ts 定义(de-AT 复用),格式遵循 DIN 5008 日期规范(源码注释引用了该标准):
const dateFormats = { full: "EEEE, do MMMM y", // Montag, 7. Januar 2018 long: "do MMMM y", // 7. Januar 2018 medium: "do MMM y", // 7. Jan. 2018 short: "dd.MM.y", // 07.01.2018 };实际输出(结合 de-AT 词表):
| 令牌 | 输出示例(1987-02-11) |
|---|---|
P | 11.02.1987(dd.MM.y) |
PP | 11. Feb. 1987 |
PPP | 11. Februar 1987 |
PPPP | Mittwoch, 11. Februar 1987 |
快照还验证了 1453 年等历史日期(29.05.1453、Sonntag, 29. Mai 1453)的格式化与解析均正常。
3.7 长本地化时间与日期时间组合
时间格式定义在同一文件:
const timeFormats = { full: "HH:mm:ss zzzz", long: "HH:mm:ss z", medium: "HH:mm:ss", short: "HH:mm", }; const dateTimeFormats = { full: "{{date}} 'um' {{time}}", long: "{{date}} 'um' {{time}}", medium: "{{date}} {{time}}", short: "{{date}} {{time}}", };| 令牌 | 输出示例 | parse 结果 |
|---|---|---|
p | 12:13 | 12:13:00 |
pp | 12:13:14 | 12:13:14 |
ppp | 12:13:14 GMT+0 | Errored(含时区文本,无法解析) |
pppp | 12:13:14 GMT+00:00 | Errored |
Pp | 11.02.1987 12:13 | 完整日期时间 |
PPpp | 11. Feb. 1987 12:13:14 | 完整日期时间 |
PPPppp | 11. Februar 1987 um 12:13:14 GMT+0 | Errored |
PPPPpppp | Mittwoch, 11. Februar 1987 um 12:13:14 GMT+00:00 | Errored |
快照明确标注:任何含GMT时区文本的长时间格式(ppp及以上)在parse时一律返回Errored——这是快照文档对解析边界条件的如实记录,提醒开发者格式化时若需后续解析,应避免使用带时区文本的full/long宽度。
四、formatDistance:模糊距离的奥地利德语表达
快照第二节假设「现在是 2000 年 1 月 1 日 00:00」,给出formatDistance的预期输出,覆盖三种组合:默认、includeSeconds: true、addSuffix: true。节选关键行为:
| 日期 | 默认结果 | includeSeconds | addSuffix |
|---|---|---|---|
| 2006-01-01 | etwa 6 Jahre | 同左 | in etwa 6 Jahren |
| 2001-06-01 | mehr als 1 Jahr | 同左 | in mehr als 1 Jahr |
| 2000-01-15 | 14 Tage | 同左 | in 14 Tagen |
| 2000-01-02 | 1 Tag | 同左 | in 1 Tag |
| 2000-01-01T00:45 | etwa 1 Stunde | 同左 | in etwa 1 Stunde |
| 2000-01-01T00:00:25 | weniger als 1 Minute | eine halbe Minute | in weniger als 1 Minute |
| 2000-01-01T00:00:00 | weniger als 1 Minute | weniger als 5 Sekunden | vor weniger als 1 Minute |
| 1999-12-31T23:45 | 15 Minuten | 同左 | vor 15 Minuten |
| 1998-06-01 | mehr als 1 Jahr | 同左 | vor mehr als 1 Jahr |
要点:
- 未来时间加前缀
in,过去时间加前缀vor; includeSeconds仅影响不足 1 分钟时的细分输出(5/10/20 秒、半分钟);- 单数用
1 Jahr、1 Tag、1 Monat,复数用Jahre、Tage、Monate。
实现位于 de/_lib/formatDistance/index.ts,其核心设计是每个距离令牌同时提供standalone(独立)与withPreposition(带介词)两套词形:
xDays: { standalone: { one: "1 Tag", other: "{{count}} Tage" }, withPreposition: { one: "1 Tag", other: "{{count}} Tagen" }, },当addSuffix: true时使用withPreposition(14 Tagen第三格),否则使用standalone(14 Tage)。最终前缀逻辑:
if (options?.addSuffix) { if (options.comparison && options.comparison > 0) { return "in " + result; } else { return "vor " + result; } }comparison由调用方(如formatDistanceToNow)根据目标日期在过去还是未来传入-1/1,从而决定in/vor。
五、formatDistanceStrict:精确距离与强制单位
快照第三节同样以 2000-01-01 00:00 为基准,展示formatDistanceStrict的输出,重点列包含第三列「强制单位(如 hour)」:
| 日期 | 默认结果 | addSuffix | 强制 hour 单位 |
|---|---|---|---|
| 2006-01-01 | 6 Jahre | in 6 Jahren | 52608 Stunden |
| 2000-01-01T00:45 | 45 Minuten | in 45 Minuten | 1 Stunde |
| 2000-01-01T00:15 | 15 Minuten | in 15 Minuten | 0 Stunden |
| 1999-12-31T23:30 | 30 Minuten | vor 30 Minuten | 1 Stunde |
| 1998-01-01 | 2 Jahre | vor 2 Jahren | 17520 Stunden |
与formatDistance不同,formatDistanceStrict不做四舍五入式的模糊化(45 分钟就是45 Minuten),且可通过unit选项强制以某单位表达(45 分钟按小时计为1 Stunde,15 分钟按小时计为0 Stunden)。addSuffix同样使用withPreposition词形。
六、formatRelative:相对日期的地道表达
快照第四节以「现在是 2000-01-01 00:00」为基准:
| 日期 | 结果 |
|---|---|
| 2000-01-10 | 10.01.2000 |
| 2000-01-05 | Mittwoch um 00:00 |
| 2000-01-02 | morgen um 00:00 |
| 2000-01-01 | heute um 00:00 |
| 1999-12-31 | gestern um 00:00 |
| 1999-12-27 | letzten Montag um 00:00 |
| 1999-12-21 | 21.12.1999 |
实现位于 de/_lib/formatRelative/index.ts:
const formatRelativeLocale = { lastWeek: "'letzten' eeee 'um' p", yesterday: "'gestern um' p", today: "'heute um' p", tomorrow: "'morgen um' p", nextWeek: "eeee 'um' p", other: "P", };其中other回退为长日期P(dd.MM.y),所以超出相对窗口的日期显示为10.01.2000、21.12.1999。
七、formatDuration:时长对象的本地化
快照最后一节给出formatDuration对Duration对象的输出:
| Duration | 结果 |
|---|---|
{"years":1} | 1 Jahr |
{"years":2} | 2 Jahre |
{"months":1} | 1 Monat |
{"months":2} | 2 Monate |
{"weeks":1} | 1 Woche |
{"weeks":2} | 2 Wochen |
{"days":1} | 1 Tag |
{"days":2} | 2 Tage |
{"hours":1} | 1 Stunde |
{"hours":2} | 2 Stunden |
{"minutes":1} | 1 Minute |
{"minutes":2} | 2 Minuten |
{"seconds":1} | 1 Sekunde |
{"seconds":2} | 2 Sekunden |
同时{"years":0}等零值输出为0 Jahre、0 Monate、0 Wochen、0 Tage、0 Stunden、0 Minuten、0 Sekunden(零值默认不省略)。formatDuration的本地化同样依赖formatDistance词表中的xYears/xMonths等令牌,因此单复数变化(Jahr/Jahre、Monat/Monate、Tag/Tage、Stunde/Stunden)与第四、五节完全一致。
八、在项目中导入并使用 de-AT 语言环境
de-AT 通过 pkgs/core/src/locale/index.ts#L22 的export * from "./de-AT/index.ts"暴露为deAT。使用方式与 i18n.md 描述的通用流程一致——显式导入 locale 并作为locale选项传入:
import { format, formatDistance, parse } from "date-fns"; import { deAT } from "date-fns/locale"; // 格式化:奥地利德语月份写法 format(new Date(2019, 0, 11), "do MMMM y", { locale: deAT }); //=> '11. Jänner 2019' // 相对时间:带 in/vor 前缀 formatDistance(new Date(2000, 5, 1), new Date(2000, 0, 1), { locale: deAT }); //=> '5 Monate' // 解析:奥地利缩写可被正确识别(match 复用 de 实现,兼容 Jän./Jan. 变体) parse("11. Jänner 2019", "do MMMM y", new Date(), { locale: deAT });从 de/_lib/match/index.ts 的正则可以看到,月份匹配对j[aä]n(同时接受 Jän/Jän. 与 Jan/Jan.)、mär[z]?(März/Mär)等做了兼容处理,因此解析时奥地利与德国写法均可被识别;星期匹配(son?|mon?|die?|mit?|don?|fre?|sam?)\.?同样容忍带/不带句点的形式。
九、快照的工程价值:如何验证与扩展
snapshot.md 在 date-fns 的语言环境开发流程中承担「行为基准」角色:
- 回归基准:修改
localize、match等词表后,重跑快照即可比对输出是否发生变化,防止破坏既有格式化结果; - 文档参考:所有令牌的组合行为(含
Invalid Date、Errored等边界)都以真实运行结果为据,无需阅读源码即可确定预期; - 解析能力清单:
parse列直接展示哪些格式可以双向转换、哪些仅能格式化,是选择令牌时的实用指南。
对想要在 de-AT 基础上定制(例如新建瑞士德语de-CH或其他变体)的开发者,仓库中的组合式结构提供了清晰的范本:复用相邻语言环境的formatDistance/formatLong/formatRelative/match,只需按 de-AT/_lib/localize/index.ts 的方式重写localize词表,并在 locale 类型定义(Locale接口含code、formatDistance、formatRelative、localize、formatLong、match、options)的约束下组装导出即可。语言环境选项(weekStartsOn、firstWeekContainsDate)则决定了星期与周编号类令牌的计算基准,与快照中Yo、wo、Io的输出直接相关。
十、快速参考:de-AT 关键差异速查表
| 维度 | de(德国) | de-AT(奥地利) |
|---|---|---|
| 一月全称(MMMM/LLLL) | Januar | Jänner |
| 一月缩写(MMM/LLL) | Jan. / Jan | Jän. / Jän |
| 三月缩写 | März | März(同形) |
| 上午/下午缩写 | vorm. / nachm. | vorm. / nachm.(同形) |
| 序数形式 | 数字 + 句点 | 数字 + 句点(同形) |
| 周起始 / 首周规则 | 周一 / 含 1 月 4 日 | 周一 / 含 1 月 4 日 |
综上,de-AT/snapshot.md 是一份可运行的「行为契约」:它既完整记录了 de-AT 环境下六类日期函数的全部输出规则,又与 de-AT/index.ts、de-AT/_lib/localize/index.ts 等源码相互印证。开发者在实现奥地利德语界面时,可将其作为格式化与解析的权威参照,并据此快速定位「Jänner」「vorm./nachm.」「1. Quartal」等本地化细节的代码出处。
【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考