date-fns 泰卢固语(Telugu/te)locale 全解析:format、parse 与 formatDistance 快照深度指南
【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns
本文以 pkgs/core/src/locale/te/snapshot.md 为骨架,完整解析 date-fns 中泰卢固语(
te)区域设置在format、parse、formatDistance、formatDistanceStrict、formatRelative、formatDuration六大 API 下的真实行为快照,并结合 te locale 源码 逐层拆解其实现原理。读完本文,你将掌握:Telugu locale 的完整 token 映射与输出规则、序数词(వ后缀)的生成逻辑、addSuffix与介词屈折的处理方式、weekStartsOn 等选项对解析结果的影响,以及如何在自己的项目中使用并验证该 locale。
一、快照文档是什么:locale 行为的"黄金测试基准"
snapshot.md是 date-fns 每个 locale 目录下都会维护的一份行为快照(snapshot),它不是普通文档,而是用真实 API 跑出来的输入-输出对照表。它同时验证两条链路:
format链路:给定一个 UTC 日期与 token 字符串,输出本地化文本;parse链路:把本地化文本反向解析回日期。
快照表中的Invalid Date与Errored两列值尤其关键——它们不是笔误,而是已知的、被测试接受的行为边界(例如 telugu 简写月份సెప్టెం、నవం、డిసెం无法被parse识别,详见下文匹配正则分析)。因此,这份快照既是使用者查手册的依据,也是贡献者改词条后必须回归验证的标尺。
1.1 te locale 的组成结构
在 pkgs/core/src/locale/te/index.ts 中,te被定义为标准的Locale对象:
export const te: Locale = { code: "te", formatDistance: formatDistance, formatLong: formatLong, formatRelative: formatRelative, localize: localize, match: match, options: { weekStartsOn: 0 /* Sunday */, firstWeekContainsDate: 1, }, };五个功能模块分别位于te/_lib/下,与Locale类型一一对应:
| 模块 | 文件 | 职责 |
|---|---|---|
localize | _lib/localize/index.ts | 将数字/枚举值转为 Telugu 文本(月份、星期、纪元、时段) |
match | _lib/match/index.ts | 提供反向解析用的正则模式 |
formatDistance | _lib/formatDistance/index.ts | 相对时间距离的本地化 |
formatLong | _lib/formatLong/index.ts | 长格式模板(P/PP/PPP/PPPP 等) |
formatRelative | _lib/formatRelative/index.ts | 相对日期(今天/昨天/下周等)模板 |
options中的两个参数直接影响快照结果:weekStartsOn: 0表示一周从星期日开始,firstWeekContainsDate: 1表示一周内包含 1 月 1 日的那周算第一周——这解释了快照中Yo(本地周年份)与wo(本地周序号)的解析结果会落到上一年 12 月的原因。
二、format与parse快照:token 全映射
快照第一部分覆盖 24 类 token。下表保留每一类的关键示例(完整矩阵请查阅 snapshot.md,所有测试基准时间均为12:13:14.015Z):
2.1 年份、季度与月份
| 类别 | Token | 示例输入 | format 输出 | parse 结果 |
|---|---|---|---|---|
| Calendar year | yo | 1987-02-11 | 1987వ | 1987-01-01 |
| Local week-numbering year | Yo | 1987-02-11 | 1987వ | 1986-12-28(周起始于周日) |
| Quarter (formatting) | Qo | 2019-04-01 | 2వ | 2019-04-01 |
QQQ | 2019-01-01 | త్రై1 | 2019-01-01 | |
QQQQ | 2019-01-01 | 1వ త్రైమాసికం | 2019-01-01 | |
QQQQQ | 2019-01-01 | 1 | 2019-01-01 | |
| Quarter (stand-alone) | qo/qqq/qqqq | 2019-04-01 | 2వ/త్రై2/2వ త్రైమాసికం | 2019-04-01 |
| Month (formatting) | Mo | 2019-01-11 | 1వ | 2019-01-01 |
MMM | 2019-01-11 | జన | 2019-01-01 | |
MMMM | 2019-09-10 | సెప్టెంబర్ | 2019-09-01 | |
MMMMM | 2019-06-10 | జూ | 2019-01-01(窄格式二义性) | |
| Month (stand-alone) | Lo/LLL/LLLL/LLLLL | 同上 | 与 formatting 宽度相同 | 同上 |
源码依据在 _lib/localize/index.ts:月份三套宽度直接取自 CLDR 数据(జన/ఫిబ్ర/… 为 abbreviated,జనవరి/ఫిబ్రవరి/… 为 wide,జ/ఫి/… 为 narrow)。
值得注意的行为:MMMMM(窄格式)在 6 月输出జూ、7 月输出జు,但parse两个都解析回1 月——因为match中窄月份正则/^జూ/i与/^జు/i无法区分,且回退到首月。这是快照中"看似错误实则已知"的典型案例。
同样,MMM简写月份的 9 月(సెప్టెం)、11 月(నవం)、12 月(డిసెం)在parse时返回Invalid Date,因为 _lib/match/index.ts 的简写正则写作/^(జన|ఫిబ్ర|మార్చి|ఏప్రి|మే|జూన్|జులై|ఆగ|సెప్|అక్టో|నవ|డిసె)/i——其中 9 月匹配前缀是సెప్而非సెప్టెం,11/12 月同理,导致这三个格式化输出无法被反向解析。
2.2 周、日与星期
| 类别 | Token | 示例输入 | format 输出 | parse 结果 |
|---|---|---|---|---|
| Local week of year | wo | 2019-01-01 | 1వ | 2018-12-30(周日为一周之始) |
| ISO week of year | Io | 2019-01-01 | 1వ | 2018-12-31 |
| Day of month | do | 2019-02-11 | 11వ | 2019-02-11 |
do MMMM | 2019-02-11 | 11వ ఫిబ్రవరి | 2019-02-11 | |
| Day of year | Do | 2019-12-31 | 365వ | 2019-12-31 |
| Day of week (formatting) | E/EE/EEE | 2019-02-11 | సోమ | 2019-02-11 |
EEEE | 2019-02-15 | శుక్రవారం | 2019-02-15 | |
EEEEE | 2019-02-11 | సో | 2019-02-11 | |
EEEEEE | 2019-02-11 | సోమ | 2019-02-11 | |
| ISO day of week | io | 2019-02-11 | 1వ(周一=1) | 2019-02-11 |
iii/iiii/iiiii/iiiiii | 同 E 系列 | 同 E 系列 | 同 E 系列 | |
| Local day of week | eo | 2019-02-11 | 2వ(周日=1) | 2019-02-11 |
eee…eeeeee | 同 E 系列 | 同 E 系列 | 同 E 系列 | |
| Local day of week (stand-alone) | co/ccc…cccccc | 同 E 系列 | 同 E 系列 | 同 E 系列 |
注意io(ISO 周几)与eo/co(本地周几)的序数差异:2019-02-11 是周一,ISO 序数为1వ,而 Telugu 本地(周日起始)序数为2వ。星期文本在 _lib/localize/index.ts 中定义,short与abbreviated宽度相同(ఆది/సోమ/…),narrow用首字符(ఆ/సో/…),wide为完整形式(ఆదివారం/సోమవారం/…)。
2.3 时段(AM/PM)与弹性时段
| 类别 | Token | 输入时间 | format 输出 | parse 结果 |
|---|---|---|---|---|
| AM, PM | a/aa/aaa/aaaa/aaaaa | 11:13 | పూర్వాహ్నం | 当天 00:00 |
| 同上 | 14:13 / 19:13 | అపరాహ్నం | 当天 12:00 | |
| AM, PM, noon, midnight | b…bbbbb | 11:13 | పూర్వాహ్నం | 00:00 |
| 同上 | 14:13 | అపరాహ్నం | 12:00 | |
| Flexible day period | B…BBBBB | 11:13 | ఉదయం(上午) | 04:00 |
| 同上 | 14:13 | మధ్యాహ్నం(下午) | 12:00 | |
| 同上 | 19:13 | సాయంత్రం(傍晚) | 17:00 | |
| 同上 | 02:13 | రాత్రి(夜间) | 00:00 |
Telugu 的时段体系比英文丰富:పూర్వాహ్నం(AM)、అపరాహ్నం(PM)、ఉదయం(清晨/上午)、మధ్యాహ్నం(正午/下午)、సాయంత్రం(傍晚)、రాత్రి(夜间)、అర్ధరాత్రి(午夜)、మిట్టమధ్యాహ్నం(正午)。这些值在 _lib/localize/index.ts 中定义,注意其narrow / abbreviated / wide 三档文本完全相同(Telugu 无缩略习惯),且formattingDayPeriodValues与dayPeriodValues一致。
parse对弹性时段的锚点值(04:00 / 12:00 / 17:00 / 00:00)由 _lib/match/index.ts 中的parseDayPeriodPatterns决定:మధ్యాహ్నం前缀మధ్య匹配 afternoon、సాయంత్రం匹配 evening,日期框架(date-fns 的 parse 框架)再映射为固定时刻。
2.4 小时、分钟、秒
| 类别 | Token | 输入 | format 输出 | parse 结果 |
|---|---|---|---|---|
| Hour [1-12] | ho | 23:13 | 11వ | 23:00 |
| Hour [0-23] | Ho | 23:13 | 23వ | 23:00 |
| Hour [0-11] | Ko | 23:13 | 11వ | 23:00 |
| Hour [1-24] | ko | 23:13 | 23వ | 23:00 |
| Minute | mo | 12:55 | 55వ | 12:55 |
| Second | so | 12:13:55 | 55వ | 12:13:55 |
所有数值型 token 的序数后缀వ由 ordinalNumber 统一生成:
const ordinalNumber: LocalizeFn<number> = (dirtyNumber, _options) => { const number = Number(dirtyNumber); return number + "వ"; };即 Telugu 序数词规则为数字直接拼接వ(如1వ、42వ、365వ),无性别/数范畴变化,相比英语的 st/nd/rd/th 规则更简单。
2.5 长格式组合(P/PP/PPP/PPPP 与 p/pp/ppp/pppp)
快照中长格式部分由 _lib/formatLong/index.ts 的模板驱动:
const dateFormats = { full: "d, MMMM y, EEEE", long: "d MMMM, y", medium: "d MMM, y", short: "dd-MM-yy", }; const timeFormats = { full: "h:mm:ss a zzzz", long: "h:mm:ss a z", medium: "h:mm:ss a", short: "h:mm a", }; const dateTimeFormats = { full: "{{date}} {{time}}'కి'", long: "{{date}} {{time}}'కి'", medium: "{{date}} {{time}}", short: "{{date}} {{time}}", };| Token | 输入 | format 输出 | parse 结果 |
|---|---|---|---|
P(short date) | 1987-02-11 | 11-02-87 | 1987-02-11 |
PP(medium date) | 1987-02-11 | 11 ఫిబ్ర, 1987 | 1987-02-11 |
PPP(long date) | 1987-02-11 | 11 ఫిబ్రవరి, 1987 | 1987-02-11 |
PPPP(full date) | 1987-02-11 | 11, ఫిబ్రవరి 1987, బుధవారం | 1987-02-11 |
p(short time) | 1987-01-11 | 12:13 అపరాహ్నం | 12:13 |
pp(medium time) | 1987-01-11 | 12:13:14 అపరాహ్నం | 12:13:14 |
ppp(long time) | 1987-01-11 | 12:13:14 అపరాహ్నం GMT+0 | Errored |
pppp(full time) | 1987-01-11 | 12:13:14 అపరాహ్నం GMT+00:00 | Errored |
Pp | 1987-01-11 | 11-01-87 12:13 అపరాహ్నం | 12:13 |
PPpp | 1987-01-11 | 11 జన, 1987 12:13:14 అపరాహ్నం | 12:13:14 |
PPPppp/PPPPpppp | 1987-01-11 | 含GMT+0కి/GMT+00:00కి后缀 | Errored |
两个要点:
కి后置词:full/long 的 dateTime 模板中'కి'(泰卢固语"在…时"的格助词)作为字面量拼接,因此PPPppp输出… అపరాహ్నం GMT+0కి。Errored是有意的:带时区(z/zzzz)的 ppp/pppp 及组合 token 在parse下会抛错(框架不支持该 parse 组合),快照明确记录了这一行为,属于"测试承认的受限能力"而非缺陷。
三、formatDistance快照:相对时间与介词屈折
快照前提是now = 2000-01-01 00:00,三列分别为默认输出、includeSeconds: true、addSuffix: true:
| 日期 | 默认 | includeSeconds | addSuffix |
|---|---|---|---|
| 2006-01-01 | సుమారు 6 సంవత్సరాలు | 同左 | సుమారు 6 సంవత్సరాలలో |
| 2001-06-01 | ఒక సంవత్సరం పైగా | 同左 | ఒక సంవత్సరంలో |
| 2000-06-01 | 5 నెలలు | 同左 | 5 నెలలలో |
| 2000-01-02 | ఒక రోజు | 同左 | ఒక రోజులో |
| 2000-01-01T00:00:25 | ఒక నిమిషం కన్నా తక్కువ | అర నిమిషం | ఒక నిమిషంలో |
| 2000-01-01T00:00:05 | ఒక నిమిషం కన్నా తక్కువ | 10 సెకన్ల కన్నా తక్కువ | ఒక నిమిషంలో |
| 1999-12-31T23:59:55 | ఒక నిమిషం కన్నా తక్కువ | 10 సెకన్ల కన్నా తక్కువ | ఒక నిమిషం క్రితం |
| 1999-12-31T23:45 | 15 నిమిషాలు | 同左 | 15 నిమిషాల క్రితం |
| 1999-12-31T23:00 | సుమారు ఒక గంట | 同左 | సుమారు ఒక గంట క్రితం |
| 1999-12-30 | 2 రోజులు | 同左 | 2 రోజుల క్రితం |
| 1998-01-01 | సుమారు 2 సంవత్సరాలు | 同左 | సుమారు 2 సంవత్సరాల క్రితం |
3.1 addSuffix 与"独立/介词"双形态的实现
这是 Telugu locale 相对时间最值得注意的语言学特性。实现位于 _lib/formatDistance/index.ts:每个 token 的词条都同时定义standalone(独立形态)与withPreposition(介词后形态)两套,且各自含one/other两种数形:
xDays: { standalone: { one: "ఒక రోజు", other: "{{count}} రోజులు" }, withPreposition: { one: "ఒక రోజు", other: "{{count}} రోజుల" }, },而 formatDistance 函数本体 负责组装:
const tokenValue = options?.addSuffix ? formatDistanceLocale[token].withPreposition : formatDistanceLocale[token].standalone;后缀规则为:未来时间(comparison > 0)追加లో("之后"),过去时间追加క్రితం("之前"):
if (options?.addSuffix) { if (options.comparison && options.comparison > 0) { return result + "లో"; } else { return result + " క్రితం"; } }对比快照可见:5 నెలలు(独立)→5 నెలలలో(未来,介词形నెలల+లో);15 నిమిషాలు→15 నిమిషాల క్రితం(过去)。泰卢固语要求名词在格助词前发生词尾变化,这正是standalone/withPreposition双形态存在的意义,也是该 locale 区别于英语(仅拼接 "ago"/"in")的核心实现。
四、formatDistanceStrict快照:强制单位行为
| 日期 | 结果 | addSuffix | 强制unit: "hour" |
|---|---|---|---|
| 2006-01-01 | 6 సంవత్సరాలు | 6 సంవత్సరాలలో | 52608 గంటలు |
| 2001-01-01 | ఒక సంవత్సరం | ఒక సంవత్సరంలో | 8784 గంటలు |
| 2000-01-15 | 14 రోజులు | 14 రోజులలో | 336 గంటలు |
| 2000-01-01T00:45 | 45 నిమిషాలు | 45 నిమిషాలలో | ఒక గంట(四舍五入) |
| 2000-01-01T00:00:05 | 5 సెకన్ల | 5 సెకన్లలో | 0 గంటలు |
| 1999-12-31T23:59:55 | 5 సెకన్ల | 5 సెకన్ల క్రితం | 0 గంటలు |
| 1999-12-01 | ఒక నెల | ఒక నెల క్రితం | 744 గంటలు |
| 1994-01-01 | 6 సంవత్సరాలు | 6 సంవత్సరాల క్రితం | 52584 గంటలు |
formatDistanceStrict与formatDistance的差异在于:前者不做"约/超过"的近似,直接输出精确计数(如25 సెకన్ల、45 నిమిషాలు);当强制unit: "hour"时,所有间隔被换算成小时并遵循取整规则(如 45 分钟 →ఒక గంట,15 分钟 →0 గంటలు)。由于复用同一个formatDistanceLocale词表,序数/复数规则与formatDistance完全一致。
五、formatRelative快照:相对日期的模板展开
| 日期 | 结果 |
|---|---|
| 2000-01-10 | 10-01-00(超出窗口,回退 P 格式) |
| 2000-01-05 | తదుపరి బుధవారం 12:00 పూర్వాహ్నం(下周三) |
| 2000-01-02 | రేపు 12:00 పూర్వాహ్నం(明天) |
| 2000-01-01 | ఈ రోజు 12:00 పూర్వాహ్నం(今天) |
| 1999-12-31 | నిన్న 12:00 పూర్వాహ్నం(昨天) |
| 1999-12-27 | గత సోమవారం 12:00 పూర్వాహ్నం(上周一) |
| 1999-12-21 | 21-12-99(超出窗口) |
模板定义在 _lib/formatRelative/index.ts:
const formatRelativeLocale = { lastWeek: "'గత' eeee p", yesterday: "'నిన్న' p", today: "'ఈ రోజు' p", tomorrow: "'రేపు' p", nextWeek: "'తదుపరి' eeee p", other: "P", };规律清晰:lastWeek用గత("过去的")+eeee(完整星期名)+p(本地短时间);nextWeek用తదుపరి("接下来的");超出上周/下周窗口的日期回退到P(短日期dd-MM-yy)。p展开为h:mm a,所以时间部分统一输出为12:00 పూర్వాహ్నం这样的 12 小时制。
六、formatDuration快照:时长组件本地化
| Duration 输入 | 结果 |
|---|---|
{"years":0} | 0 సంవత్సరాలు |
{"years":1} | ఒక సంవత్సరం |
{"years":2} | 2 సంవత్సరాలు |
{"months":1} | ఒక నెల |
{"weeks":2} | 2 వారాలు |
{"days":1} | ఒక రోజు |
{"hours":2} | 2 గంటలు |
{"minutes":1} | ఒక నిమిషం |
{"seconds":2} | 2 సెకన్ల |
formatDuration复用formatDistanceLocale词表中的复数规则,0走other形(如0 సంవత్సరాలు),1走one形(ఒక సంవత్సరం)。注意 Telugu 的复数形态:2 వారాలు(周)、2 నెలలు(月)、2 రోజులు(天)、2 సంవత్సరాలు(年),与英语的 "2 weeks/months/days/years" 一一对应,但单数用独立词ఒక("一")。
七、如何在项目中使用与验证 te locale
7.1 引入方式
从源码结构看,所有 locale 统一从 pkgs/core/src/locale/index.ts 导出(该文件由脚本自动生成)。在你的应用中:
import { te } from "date-fns/locale"; import { format, parse, formatDistance } from "date-fns"; // format:按 Telugu 习惯输出 format(new Date(2019, 1, 11, 14, 13), "d MMMM y, EEEE 'గం'", { locale: te }); // => "11 ఫిబ్రవరి 2019, సోమవారం గం" // parse:反向解析 Telugu 文本 parse("11 ఫిబ్రవరి, 1987", "PPP", new Date(), { locale: te }); // formatDistance:相对时间 formatDistance(new Date(2000, 0, 1, 6), new Date(2000, 0, 1), { locale: te }); // => "సుమారు 6 గంటలు"7.2 把快照当回归测试使用
snapshot.md的每一行都可以直接转化为断言。例如验证"简写月份解析失败"这一已知行为:
import { parse } from "date-fns"; import { te } from "date-fns/locale"; // 已知行为:MMM 简写中 9/11/12 月 parse 失败 const d = parse("సెప్టెం", "MMM", new Date(2019, 8, 10), { locale: te }); console.log(isValid(d)); // false在修改或贡献telocale 时(例如修正 _lib/match/index.ts 中సెప్→సెప్టెం的匹配前缀),必须同步更新snapshot.md并运行 locale 测试,确保快照与实现始终一致。
7.3 常见坑位小结
- 窄月份二义性:
MMMMM的 6 月(జూ)与 7 月(జు)parse 均回退到 1 月; - 简写月份 9/11/12 月:format 可输出
సెప్టెం/నవం/డిసెం,但 parse 返回Invalid Date; - 带时区长格式:
ppp/pppp/PPPppp/PPPPpppp在 parse 下报错(Errored),应避免用 parse 解析含z的 Telugu 长时间文本; - 周起始:
weekStartsOn: 0(周日)导致本地周/周序号 token(Yo/wo/eo)的结果可能落在相邻年份/月份。
八、延伸阅读
- 快照原始文件:pkgs/core/src/locale/te/snapshot.md
- locale 入口与选项:pkgs/core/src/locale/te/index.ts
- 文本/序数本地化:pkgs/core/src/locale/te/_lib/localize/index.ts
- 解析正则:pkgs/core/src/locale/te/_lib/match/index.ts
- 相对时间双形态词表:pkgs/core/src/locale/te/_lib/formatDistance/index.ts
- 长格式模板:pkgs/core/src/locale/te/_lib/formatLong/index.ts
- 相对日期模板:pkgs/core/src/locale/te/_lib/formatRelative/index.ts
- locale 通用构造器(
buildLocalizeFn等):pkgs/core/src/locale/_lib/buildLocalizeFn/index.ts
【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考