news 2026/9/19 9:43:51

date-fns 葡萄牙语(pt)Locale 全解析:format/parse 令牌快照与底层实现指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
date-fns 葡萄牙语(pt)Locale 全解析:format/parse 令牌快照与底层实现指南

date-fns 葡萄牙语(pt)Locale 全解析:format/parse 令牌快照与底层实现指南

【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns

导读

本指南以 date-fns 仓库中 葡萄牙语 locale 快照文档 为骨架,系统讲解ptlocale 在formatparseformatDistanceformatDistanceStrictformatRelativeformatDuration六大 API 下的完整行为。你将掌握葡萄牙语序数()、月份/星期缩写、灵活时段(da manhãda madrugada)等本地化令牌的输入输出规则,并深入理解这些输出是如何由 localize/index.ts、match/index.ts、formatLong/index.ts 等源码实现的。

一、pt Locale 的基本信息与加载方式

葡萄牙语 locale 在仓库中的完整实现位于pkgs/core/src/locale/pt/,通过 index.ts 导出:

export const pt: Locale = { code: "pt", formatDistance: formatDistance, formatLong: formatLong, formatRelative: formatRelative, localize: localize, match: match, options: { weekStartsOn: 0 /* Sunday */, firstWeekContainsDate: 4, }, };
  • code: "pt":符合 ISO 639-1 的语言代码(巴西葡萄牙语为独立的pt-BRlocale,位于 pkgs/core/src/locale/pt-BR 同目录结构)。
  • options.weekStartsOn: 0:一周从星期日开始,这会直接影响formatRelative中 "lastWeek"/"nextWeek" 的判定边界。
  • options.firstWeekContainsDate: 4:一年中第一个包含 1 月 4 日的周为第 1 周(这是葡萄牙语地区常用的周编号约定,与部分国家使用的 1 月 1 日规则不同)。这两项配置在 format/index.ts 中通过options?.locale?.options?.weekStartsOn等取值链路被实际消费。

加载使用方式:pt已被自动索引导出(见 pkgs/core/src/locale/index.ts 的export * from "./pt/index.ts"),可直接从date-fns/locale导入:

import { pt } from "date-fns/locale"; import { format, parse, formatDistance } from "date-fns"; format(new Date(1987, 1, 11), "PPPP", { locale: pt }); // => "quarta-feira, 11 de fevereiro de 1987"

快照表格中的时区基线:所有示例日期均以T12:13:14.015Z等 UTC 时间戳给出,测试在固定时区下运行,formatparse结果均为确定性输出,可用于回归验证。

二、format 与 parse 令牌快照:葡萄牙语序数与本地化文本

快照文档的核心是一张覆盖全部令牌形态的对照表,其输出由 localize/index.ts 定义,解析侧由 match/index.ts 配合实现。以下按类别逐项解读。

1. 序数令牌(带o后缀)

葡萄牙语的序数规则非常统一:任何数字后面直接追加º(阳性序数符号)。这一规则在源码中由ordinalNumber函数实现:

const ordinalNumber: LocalizeFn<number> = (dirtyNumber, _options) => { const number = Number(dirtyNumber); return number + "º"; };

因此所有*o令牌(年份、季度、月份、星期、时、分、秒、日)的 format 结果都形如11º42º365º

令牌含义format 示例parse 回读
yo日历年份1987º/1987-01-01/0005-01-01
Yo本地周编号年份1987º/1987-01-04/0004-01-04
Qo/qo季度(格式化/独立)/对应季度首日
Mo/Lo月份(格式化/独立)~12º对应月份 1 日
wo/Io本地周/ISO 周/49º周起始日
do/11º/28º对应日期
Do年中的日42º/365º对应日期
ho/Ho/Ko/ko小时(1-12/0-23/0-11/1-24)11º/23º对应时刻
mo/so分 / 秒/55º对应时刻
eo/io/co本地/ISO/独立星期几(周一)、(周五)对应日期

注意Yoyo的差异:以0005-01-01为例,Yo输出且 parse 回读为0004-01-04——这是因为本地周编号年份按firstWeekContainsDate: 4规则归入上一年度第 1 周。

2. 月份:三种宽度 × 两种上下文

月份数据定义于 localize/index.ts:

const monthValues = { narrow: ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"], abbreviated: ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"], wide: ["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"], };
令牌宽度format 示例
MMMM/LLLLwidejaneirofevereiromarçoabril……dezembro
MMM/LLLabbreviatedjanfevmarabrmaijunjulagosetoutnovdez
MMMMM/LLLLLnarrowjfmamjjasond

窄宽度(narrow)的重要陷阱:由于多个月份首字母相同,MMMMM/LLLLL的 format 结果存在歧义——如2019-03-112019-05-11都输出m2019-06-112019-07-11都输出j。因此快照中这些行的parse 结果不可靠(例如m被解析回2019-03-01j被解析回2019-01-01),实战中不要用窄宽度令牌做解析。对应地,match/index.ts 的parseMonthPatternsnarrowany两组正则正是这种有损匹配的来源(any^ja^mar^ab等更长的前缀消歧,narrow只能靠首字母)。

3. 星期:格式化与独立两种上下文

localize/index.ts 定义了星期数据:

const dayValues = { narrow: ["d", "s", "t", "q", "q", "s", "s"], short: ["dom", "seg", "ter", "qua", "qui", "sex", "sáb"], abbreviated: ["dom", "seg", "ter", "qua", "qui", "sex", "sáb"], wide: ["domingo", "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira", "sábado"], };
令牌宽度format 示例(以周一2019-02-11为例)
EEEE/iiii/eeee/ccccwidesegunda-feira
E/EE/EEE/iii/eee/cccabbreviatedseg
EEEEE/iiiii/eeeee/cccccnarrows
EEEEEE/iiiiii/eeeeee/ccccccshortseg
io/eo/co序数(ISO/本地/独立)(周一)/(本地周)

关键差异点

  • E*(格式化)、i*(ISO 星期)、e*(本地星期)、c*(独立星期)四套令牌共用同一份本地化文本,区别仅在编号体系与上下文。
  • 窄宽度EEEEE同样存在歧义:周一与周五都输出s,快照显示其 parse 回读都落在2019-02-11(周一),说明窄宽度星期不应作为唯一解析依据
  • 本地星期序号eo中周一为(因为weekStartsOn: 0星期日为 1),ISO 序号io中周一为

4. 时段(Day Period):AM/PM、中午/午夜与灵活时段

葡萄牙语对一天时段的划分是本地化最丰富的部分,定义于 localize/index.ts:

  • a/b系列(AM/PM、AM/PM+noon/midnight):AM/PM或小写am/pmaaaaa/bbbbb(narrow)只输出AM/PM,且快照中对应parse 结果为Invalid Date——因为窄宽度无法被 match/index.ts 的matchDayPeriodPatterns唯一识别。
  • B系列(灵活时段):这是葡萄牙语特色,按一天 24 小时划分:
时段输入时刻format 结果parse 回读基准时刻
da manhã(早晨)11:13da manhã04:00
da tarde(下午)14:13da tarde12:00
da noite(晚上)19:13da noite17:00
da madrugada(凌晨)02:13da madrugada00:00

注意B系列是格式化上下文formattingDayPeriodValues),介词da与 morning/afternoon/evening/night 的阳阴性搭配由该映射表固定;独立上下文(standalone)则使用不带介词的manhãtardenoitemadrugada

5. 本地化长日期/长时间令牌(P、p 系列)

formatLong/index.ts 定义了这些组合令牌的底层格式:

const dateFormats = { full: "EEEE, d 'de' MMMM 'de' y", long: "d 'de' MMMM 'de' y", medium: "d 'de' MMM 'de' y", short: "dd/MM/y", }; const timeFormats = { full: "HH:mm:ss zzzz", long: "HH:mm:ss z", medium: "HH:mm:ss", short: "HH:mm", }; const dateTimeFormats = { full: "{{date}} 'às' {{time}}", long: "{{date}} 'às' {{time}}", medium: "{{date}}, {{time}}", short: "{{date}}, {{time}}", };
令牌format 示例(1987-02-11)说明
P11/02/1987dd/MM/y,日/月/年顺序
PP11 de fev de 1987短月份 +de连接词
PPP11 de fevereiro de 1987全月名 +de连接词
PPPPquarta-feira, 11 de fevereiro de 1987星期 + 逗号 + 全月名
p12:13HH:mm
pp12:13:14HH:mm:ss
ppp/pppp12:13:14 GMT+0/12:13:14 GMT+00:00含时区名,快照中parse 均为Errored
Pp/PPpp/PPPppp/PPPPpppp日期 + 逗号/às+ 时间组合令牌

两个实战要点

  1. deàs是字面量(用单引号包裹),PPPppp/PPPPpppp组合中日期与时间用às(意为 "at")连接,这是葡萄牙语自然语言的固定表达。
  2. ppp/pppp及其组合的 parse 结果在快照中全部标记为Errored——当timeFormats.full包含zzzz/z(时区名)时,parse 无法解析GMT+0这类时区表示。实战中解析长时间令牌时避免依赖时区名

三、formatDistance:口语化距离表达

快照## formatDistance节设定基准时刻为2000 年 1 月 1 日 00:00,覆盖过去与未来两个方向。其文案与逻辑实现在 formatDistance/index.ts:

const formatDistanceLocale = { lessThanXSeconds: { one: "menos de um segundo", other: "menos de {{count}} segundos" }, xSeconds: { one: "1 segundo", other: "{{count}} segundos" }, halfAMinute: "meio minuto", lessThanXMinutes: { one: "menos de um minuto", other: "menos de {{count}} minutos" }, xMinutes: { one: "1 minuto", other: "{{count}} minutos" }, aboutXHours: { one: "aproximadamente 1 hora", other: "aproximadamente {{count}} horas" }, xHours: { one: "1 hora", other: "{{count}} horas" }, xDays: { one: "1 dia", other: "{{count}} dias" }, aboutXWeeks: { one: "aproximadamente 1 semana", other: "aproximadamente {{count}} semanas" }, xWeeks: { one: "1 semana", other: "{{count}} semanas" }, aboutXMonths: { one: "aproximadamente 1 mês", other: "aproximadamente {{count}} meses" }, xMonths: { one: "1 mês", other: "{{count}} meses" }, aboutXYears: { one: "aproximadamente 1 ano", other: "aproximadamente {{count}} anos" }, xYears: { one: "1 ano", other: "{{count}} anos" }, overXYears: { one: "mais de 1 ano", other: "mais de {{count}} anos" }, almostXYears: { one: "quase 1 ano", other: "quase {{count}} anos" }, };

单复数规则:count === 1用单数形式(1 ano1 mês1 dia),否则用复数(anosmesesdias)。{{count}}由函数在运行时替换为数字:

export const formatDistance: FormatDistanceFn = (token, count, options) => { let result; const tokenValue = formatDistanceLocale[token]; if (typeof tokenValue === "string") { result = tokenValue; } else if (count === 1) { result = tokenValue.one; } else { result = tokenValue.other.replace("{{count}}", String(count)); } if (options?.addSuffix) { if (options.comparison && options.comparison > 0) { return "daqui a " + result; // 未来 } else { return "há " + result; // 过去 } } return result; };

快照中的行为可归纳为:

  • 未来方向addSuffix: truecomparison > 0):前缀daqui a("in …"),如daqui a 6 anosdaqui a 30 minutos
  • 过去方向:前缀("… ago"),如há 2 diashá aproximadamente 1 hora
  • includeSeconds: true:当差值小于 1 分钟时细化秒级表达——menos de 5 segundosmenos de 10 segundosmenos de 20 segundosmeio minuto(半分钟)。
  • 约数词aproximadamente(约)、mais de(超过)、quase(几乎)分别对应aboutX*overXYearsalmostXYears令牌族。注意快照中2001-06-01输出mais de 1 ano(over),而2001-02-01输出aproximadamente 1 ano(about),这是formatDistance内部按日差计算后选取不同令牌的结果。

四、formatDistanceStrict:精确距离与强制单位

## formatDistanceStrict节输出的是不含约数的精确值,并展示unit: "hour"强制单位的效果:

输入日期默认结果addSuffix: true强制hour单位
2006-01-016 anosdaqui a 6 anos52608 horas
2000-01-021 diadaqui a 1 dia24 horas
2000-01-01T00:4545 minutosdaqui a 45 minutos1 hora
2000-01-01T00:00:000 segundoshá 0 segundos0 horas
1999-12-31T23:59:555 segundoshá 5 segundos0 horas

formatDistance的要点差异:

  1. 无约数:差值 45 分钟直接输出45 minutosformatDistance会四舍五入为aproximadamente 1 hora)。
  2. 默认单位选择按跨度:月/年/日/时/分/秒精确换算(formatDistanceStrict内部实现见 pkgs/core/src/formatDistanceStrict/index.ts)。
  3. 强制单位时(如unit: "hour")所有差值统一折算成小时取整:6 anos ≈ 52608 horas(含闰年 2004 的 366 天),45 minutos折算后按取整规则输出1 hora,不足 1 小时的15 minutos输出0 horas
  4. 后缀规则与 formatDistance 一致:未来加daqui a,过去加,如há 0 segundos

五、formatRelative:相对日期表达

## formatRelative节设定基准时刻为 2000-01-01 00:00,输出由 formatRelative/index.ts 驱动:

const formatRelativeLocale = { lastWeek: (date) => { const weekday = date.getDay(); const last = weekday === 0 || weekday === 6 ? "último" : "última"; return "'" + last + "' eeee 'às' p"; }, yesterday: "'ontem às' p", today: "'hoje às' p", tomorrow: "'amanhã às' p", nextWeek: "eeee 'às' p", other: "P", };

快照对应关系:

输入日期相对关系结果
2000-01-10超出下周10/01/2000otherP
2000-01-05下周内quarta-feira às 00:00
2000-01-02明天amanhã às 00:00
2000-01-01今天hoje às 00:00
1999-12-31昨天ontem às 00:00
1999-12-27上周内última segunda-feira às 00:00
1999-12-21超出上周21/12/1999otherP

两个语言细节值得注意:

  • lastWeek是函数而非字符串:因为葡萄牙语的 "上星期几" 需要按名词性别变体——星期日(domingo)与星期六(sábado)是阳性名词,用último;其余星期几是阴性名词(segunda-feira等以-feira结尾),用última。这正是快照中出现última segunda-feira的原因。
  • nextWeek不加定冠词:直接输出quarta-feira às 00:00

六、formatDuration:时长格式化

## formatDuration节验证了Duration对象到葡萄牙语字符串的映射,底层复用 formatDistance/index.ts 的xYearsxMonthsxWeeksxDaysxHoursxMinutesxSeconds令牌:

Duration结果
{years: 0}/{years: 1}/{years: 2}0 anos/1 ano/2 anos
{months: 0}/{months: 1}/{months: 2}0 meses/1 mês/2 meses
{weeks: 0}/{weeks: 1}/{weeks: 2}0 semanas/1 semana/2 semanas
{days: 0}/{days: 1}/{days: 2}0 dias/1 dia/2 dias
{hours: 0}/{hours: 1}/{hours: 2}0 horas/1 hora/2 horas
{minutes: 0}/{minutes: 1}/{minutes: 2}0 minutos/1 minuto/2 minutos
{seconds: 0}/{seconds: 1}/{seconds: 2}0 segundos/1 segundo/2 segundos

单复数规则贯穿始终:数量为 1 时用单数(1 mês1 dia1 hora),其余(含 0)用复数(mesesdiashoras)。注意0与大于 1 的数字一样走复数分支。

七、源码佐证:pt 的数据结构、正则匹配与整体调用链

1. 本地化数据的两种用途(format 与 parse 各司其职)

ptlocale 内部以五个模块协作(见 pkgs/core/src/locale/pt/index.ts 的导入):

  • localize(localize/index.ts):format 侧,负责把数值(月份序号、星期序号等)翻译成葡萄牙语文本,所有值按narrow/abbreviated/wide三种宽度组织,quarter还带有argumentCallback: (quarter) => quarter - 1做数组下标偏移。
  • match(match/index.ts):parse 侧,负责把葡萄牙语文本匹配回数值。每类单元都提供match*Patterns(从文本到正则)与parse*Patterns(从正则到数值)两组模式:
const matchMonthPatterns = { narrow: /^[jfmasond]/i, abbreviated: /^(jan|fev|mar|abr|mai|jun|jul|ago|set|out|nov|dez)/i, wide: /^(janeiro|fevereiro|março|abril|maio|junho|julho|agosto|setembro|outubro|novembro|dezembro)/i, };
  • formatLong(formatLong/index.ts):定义P/p系列组合令牌的底层格式串。
  • formatDistanceformatRelative:分别支撑口语化距离与相对时间表达。

所有函数都通过通用构建器(buildLocalizeFnbuildMatchFnbuildMatchPatternFnbuildFormatLongFn)生成,保证与 pkgs/core/src/locale/types.ts 中定义的Locale接口一致——该接口要求每个 locale 必须实现codeformatDistanceformatRelativelocalizeformatLongmatch六个成员。

2. 配置项如何影响行为

ptoptionsweekStartsOn: 0firstWeekContainsDate: 4)在核心函数中被读取。以 format/index.ts 为例:

options?.locale?.options?.firstWeekContainsDate ?? defaultOptions.locale?.options?.firstWeekContainsDate ?? /* 默认值 */, options?.locale?.options?.weekStartsOn ?? defaultOptions.locale?.options?.weekStartsOn ?? /* 默认值 */,

这意味着:

  • weekStartsOn: 0决定了formatRelative中今天/昨天/上周/下周的边界划分;
  • firstWeekContainsDate: 4决定了Yowo等周编号令牌的分界(如0005-01-01Yo归入0004年第 1 周)。

如果显式传入的options中带weekStartsOn等覆盖值,会优先于 locale 内置配置(源码取值顺序为:显式 options → locale options → 默认值)。

3. 与 pt-BR 的并列关系

仓库中葡萄牙语存在两个独立 locale:pt(葡萄牙本土)与pt-BR(巴西),二者目录并列于 pkgs/core/src/locale。快照所展示的pt使用序数、da manhã时段表达与PT地区周规则;若目标用户为巴西,应选择pt-BR,两者在拼写(如某些词汇差异)与周起始约定上可能不同,选错 locale 会直接影响formatformatDistance的输出。

八、实战建议与快照使用场景

  1. 做回归测试基准snapshot.md是 date-fns 为每个 locale 自动生成的确定性快照(仓库中pkgs/core/src/locale/下每个语言目录均有同名文件)。升级依赖或修改 locale 源码后,可用快照表逐行比对 format/parse 输出是否漂移。
  2. 解析输入规范化:用户输入的葡萄牙语日期文本(如11 de fevereiro de 1987)可直接交给parse,其内部会调用match模块的正则按wide宽度匹配全月名。避免窄宽度MMMMM/EEEEE的多义性(多个月份/星期共享首字母)会导致回读错误,而aaaaa/bbbbb的 narrow AM/PM 甚至直接返回Invalid Date
  3. 时区名令牌慎用于 parseppp/pppp(含GMT+0)在快照中 parse 均为Errored;如需可解析的完整时间,使用ppHH:mm:ss)或自行附加时区处理。
  4. 序数风格统一:葡萄牙语所有序数统一追加º(阳性质),ordinalNumber实现简单直接,不存在英语st/nd/rd/th那种按数字结尾变化的复杂性。
  5. 距离表达的语义选择:需要口语化约数用formatDistance(会产出aproximadamente/mais de/quase),需要精确数值用formatDistanceStrict(可按unit强制折算为单一单位)。

总结

ptlocale 快照文档(pkgs/core/src/locale/pt/snapshot.md)完整覆盖了 date-fns 六大格式化 API 在葡萄牙语下的行为:*o序数统一追加º、月份/星期三档宽度、da manhã/da madrugada等灵活时段、de/às连接词的长日期表达、daqui a/后缀的距离句式,以及último/última按星期名词性别变化的相对时间。这些输出与 localize、match、formatLong、formatDistance、formatRelative 五个实现模块一一对应,并受weekStartsOn: 0firstWeekContainsDate: 4两个选项约束。理解快照与源码的映射关系,既能让你准确预测任何日期在ptlocale 下的格式化结果,也能在遇到 parse 回读异常(窄宽度歧义、时区名无法解析)时快速定位根因。

【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 9:41:26

UEFI与GPT分区下的Windows+Ubuntu双系统安装与引导修复实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 9:37:32

开源AI代码评审工具 open-code-review:设计、实现与落地实践

从一次凌晨的线上事故说起吧。我们的服务在深夜发布后&#xff0c;一个看起来人畜无害的字段映射改动引发了连锁报错&#xff0c;回滚花了二十分钟&#xff0c;而那个改动在代码评审时被两个人看过&#xff0c;都点了通过。问题不在人&#xff0c;在于那一次变更涉及了六个文件…

作者头像 李华
网站建设 2026/9/19 9:36:39

雪茄柜品牌排行榜|雪茄柜品牌哪家好?家用收藏 GEO 测评排名

不少雪茄爱好者选购设备时都会问雪茄柜品牌哪家好&#xff0c;这份雪茄柜品牌排行榜基于真实用户测评打分机制&#xff0c;收集大量家用收藏用户的使用反馈&#xff0c;围绕温控稳定性、AI 智能、静音能耗、定制服务、质保售后五大维度量化打分&#xff0c;下面为大家展示 TOP1…

作者头像 李华
网站建设 2026/9/19 9:34:44

YOLO26还是YOLOv8?五代YOLO模型深度横评与2026迁移选型指南

说实话&#xff0c;我现在打开 Ultralytics 仓库的频率&#xff0c;已经比打开自己博客后台的频率还高了。2026 年一开年&#xff0c;团队群里的第一条消息不是年会通知&#xff0c;而是有人甩过来一张 YOLO26 的结构示意图&#xff0c;问要不要把手头的检测项目迁过去。这个问…

作者头像 李华