mcpField
W
web-utilities-date-fns

web-utilities-date-fns

github.com/agents-inc开发2665 次安装2 天前更新
代码仓库

关于 web-utilities-date-fns

面向使用 date-fns 处理日期的开发者的模式指南,涵盖格式化、解析、运算、比较、区域设置和时区。内容强调纯函数、按需导入和解析后验证,并区分 v3 与 v4 的时区方案。

主要功能

  1. 01按 Unicode TR35 规则格式化
  2. 02解析 ISO 与自定义日期并验证
  3. 03不可变日期运算与周期边界
  4. 04比较日期及处理区间
  5. 05区域化显示与版本对应的时区方案

适用场景

  1. 01格式化多语言界面中的日期
  2. 02验证用户输入或 API 日期
  3. 03计算账期、日历范围和时间间隔
  4. 04处理含时区的日期值

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/agents-inc/skills --skill web-utilities-date-fns

安装后在 Claude Code 中输入/web-utilities-date-fns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.yaml
reference.md
examples/
comparison.md
core.md
i18n.md
relative.md
timezone.md

详细介绍

date-fns 日期工具模式

快速指南: date-fns 是一组可进行摇树优化的纯函数,每个函数都会返回一个新的 Date,不会修改任何内容。ISO 字符串使用 parseISO 解析,自定义格式使用 parse 解析,然后检查 isValid,因为 v4 返回 Invalid Date,而 v3 会抛出错误。格式化令牌遵循 Unicode TR35(yyyy、dd、EEEE),与 Moment 的令牌不同。时区支持由配套软件包提供,软件包名称取决于主版本。

详细资源:


适用的路径

  • v4,且日期带有时区 — @date-fns/tz 提供 TZDate 和 tz(),@date-fns/utc 提供 UTCDate,transpose 则包含在核心软件包中。请参阅 examples/timezone.md。
  • v3.x,且日期带有时区 — date-fns-tz 提供 formatInTimeZone、toZonedTime 和 fromZonedTime。同一文件的后半部分。
  • 时区无关紧要 — 存储并传输 ISO UTC,使用 parseISO 读取,使用 startOfDay 规范化仅含日期的值,并跳过时区文件。

<critical_requirements>

编写 date-fns 代码前

使用 parseISO 解析 ISO 8601 字符串。 new Date(string) 的解析行为取决于引擎,因此 new Date("01/15/2026") 可能成功、失败,也可能因运行时不同而表示另一天。

对解析得到的任何值调用 isValid() 检查。 v4 会返回 Invalid Date 和 NaN,而 v3 会抛出异常,因此 未经检查的解析结果会一直悄无声息,直到后续首次调用 format。

按名称导入每个函数。 import * as dateFns 会破坏 tree-shaking 并引入整个 库;单独导入命名的 format 大约只需 2 KB。

使用 add* / sub* / startOf* 创建新日期,而不是使用 Date 的 setter。 date.setDate(...) 会修改该对象,所有持有该引用的地方都会共享此修改;date-fns 的每个函数都会返回一个新对象。

</critical_requirements>


自动检测: date-fns、format、parseISO、addDays、subMonths、differenceInDays、 formatDistance、formatDistanceToNow、isAfter、isBefore、eachDayOfInterval、startOfWeek、 weekStartsOn、date-fns-tz、@date-fns/tz、@date-fns/utc、TZDate、TZDateMini、UTCDate、UTCDateMini、 tz()、transpose、tzName、tzScan、tzOffset、withTimeZone

适用范围:

  • 格式化日期以供显示,无论是否使用区域设置
  • 解析日期字符串并验证解析结果
  • 日期运算——增加或减少天、月、年和工作日
  • 周期边界,以及为日历生成日期范围
  • 比较、判断是否处于区间内,以及检测区间重叠
  • 相对时间和时长表述(“2 小时前”、“5 个月 14 天”)

其他部分处理:

  • 仅用于显示、不涉及日期运算的格式化——平台自带的 Intl.DateTimeFormat 无需增加任何打包体积即可满足需求;如果运行时提供 Temporal 标准,那么本库的大部分功能都可以省去
  • 重复规则——将重复事件展开为多个实例属于单独的工作;date-fns 处理的是展开后生成的时间点
  • tz 数据库本身——配套包读取运行时的 IANA 数据,而不是自行打包

函数,而非方法。没有可链式调用的包装对象:format(date, "PP") 接收一个 Date 并返回字符串,addDays(date, 7) 接收一个 Date 并返回新的 Date。这让 库可以进行 tree-shaking——打包工具能够移除你未导入的 200 个函数——也让 每项操作都可以单独测试。

有两点值得牢记:组合时从内向外读取(format(startOfMonth(addDays(d, 7)), F)), 而且不存在隐式全局配置,因此只有作为选项传入的区域设置或时区才会影响调用。


核心模式

模式 1:格式化标记遵循 Unicode TR35

标记遵循 Unicode Technical Standard #35,与 Moment 不同:使用 yyyy 而非 YYYY,dd 而非 DD,EEEE 而非 dddd。Moment 标记不会报错,而是会生成不同的值。

import { format } from "date-fns";

const date = new Date(2026, 0, 15, 14, 30, 0);

format(date, "yyyy-MM-dd"); // "2026-01-15"
format(date, "MMMM d, yyyy 'at' h:mm a"); // "January 15, 2026 at 2:30 PM"
format(date, "EEEE"); // "Thursday"

完整标记表:reference.md。完整代码:examples/core.md


模式 2:适应区域设置的格式快捷方式

P、PP、PPP、PPPP(以及用于时间的 p、pp)会按照区域设置的惯例进行格式化,因此同一个 调用在每个地区都能正确显示。硬编码的 MM/dd/yyyy 只适用于美国。

import { format } from "date-fns";
import { enUS, de, ja } from "date-fns/locale";

format(date, "P", { locale: enUS }); // "01/15/2026"
format(date, "P", { locale: de }); // "15.01.2026"
format(date, "P", { locale: ja }); // "2026/01/15"
format(date, "PPPP", { locale: de }); // "Donnerstag, 15. Januar 2026"

完整代码:examples/i18n.md


模式 3:先解析,再验证

使用 parseISO 解析 ISO 8601,使用 parse 解析自定义格式,并将参考日期作为第三个参数传入。 这两者都不会抛出异常——使用前先验证。通过 format 进行往返转换,还可以额外排除解析成功但实际不存在的日期,例如 2 月 30 日。

import { parseISO, parse, isValid, format } from "date-fns";

const fromApi = parseISO("2026-01-15T14:30:00Z");
const fromUser = parse("15/01/2026", "dd/MM/yyyy", new Date());

if (!isValid(fromUser)) return null;
if (format(fromUser, "dd/MM/yyyy") !== "15/01/2026") return null; // Feb 30 etc.

完整代码:examples/core.md


模式 4:日期运算会返回新日期

每个 add* 和 sub* 函数都不会修改输入值,因此存储在状态中或作为 prop 传入的日期都可以安全地用于计算。

import { addDays, addMonths, subDays } from "date-fns";

const trialEnd = addDays(signupDate, 14);
const nextBilling = addMonths(signupDate, 1);
const warnAt = subDays(trialEnd, 7);
// signupDate is unchanged

完整代码:examples/core.md


模式 5:周期边界

startOf* / endOf* 会处理月份天数、闰年和每周起始日约定,并提供 >= start AND <= end 筛选器所需的闭区间。endOfDay 是 23:59:59.999,而不是午夜。

import {
  startOfDay,
  endOfDay,
  startOfWeek,
  startOfMonth,
  endOfMonth,
} from "date-fns";

const today = { start: startOfDay(date), end: endOfDay(date) };
const thisMonth = { start: startOfMonth(date), end: endOfMonth(date) };
const isoWeekStart = startOfWeek(date, { weekStartsOn: 1 }); // Monday

完整代码:examples/core.md


模式 6:使用比较函数,而非时间戳运算

isSameDay、isWithinInterval 等函数能清晰表达意图,并处理手动实现容易出错的单位截断。

import { isAfter, isSameDay, isWithinInterval, isWeekend } from "date-fns";

isAfter(end, start);
isSameDay(a, b); // ignores time-of-day
isWithinInterval(candidate, { start, end }); // endpoints sorted, so order is not checked
isWeekend(date);

完整代码:examples/comparison.md


<red_flags>

注意事项

运行时会出错:

  • 格式化未经验证的解析结果——对无效日期调用 format 会抛出异常,而在 v4 中,解析本身不会发出警告。请先用 isValid 检查。
  • 使用两个参数调用 parse——必须提供参考日期,其年份和月份会补充格式字符串中省略的内容。
  • 对 UTCDate 调用 format(date, "z")——z 令牌需要原生 Date。

行为出乎意料:

  • start 晚于 end 的区间不会被拒绝——从 v3 起,isWithinInterval 和 areIntervalsOverlapping 会对端点排序,而 eachDayOfInterval 会按反向顺序返回日期。因此,用户颠倒顺序的范围会给出看似合理的结果,而不是报错。请自行排列区间端点。
  • 粘贴的 Moment 格式字符串会生成错误的值,而不是报错——这是迁移后的代码库中最常见的缺陷。
  • new Date("2026-01-15") 表示 UTC 午夜,因为仅包含日期的 ISO 字符串被规定为 UTC;其他所有字符串格式的行为均由实现定义,因此相同输入在不同运行时可能相差整整一天。
  • addMonths(jan31, 1) 的结果是 Feb 28,addYears(feb29, 1) 的结果也是 Feb 28——日期会调整为目标月份的最后一天。
  • differenceIn* 会截断:45 天对应的 differenceInMonths 是 1,而不是 1.5。v4 向零截断,而 v3 向下取整,因此负数结果会有所不同。
  • 每周起始日因地区而异,默认值为星期日——面向用户时,请传入 weekStartsOn 或区域设置。
  • daysInYear 等常量应从 date-fns/constants 导入,而不是从包根目录导入。

</red_flags>

评论

支持 Markdown