ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

t3code 工程中的 Effect DateTime 实践:以可测试、可移植的方式处理日期与时间

t3code 工程中的 Effect DateTime 实践:以可测试、可移植的方式处理日期与时间 t3code 工程中的 Effect DateTime 实践以可测试、可移植的方式处理日期与时间【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code导读在 t3code 这个横跨桌面Electron、移动端React Native与服务端Node的大型 TypeScript 工程中大量 Effect 程序需要处理「当前时间」「日期字符串解析」「时区转换」「ISO 格式化」等需求。本指南基于仓库内.repos/effect-smol的官方 AI 文档与源码系统讲解为何应使用DateTime模块替代原生Date/Date.now并给出可复制、可运行的创建、格式化、时区转换与日历运算完整示例帮助你写出可测试、无副作用、时区正确的日期时间代码。为什么用DateTime而不是Date与Date.now原文核心结论处理日期和时间时请使用DateTime模块而不是Date和Date.now。原生Date的问题在于当前时间获取Date.now/new Date()是隐式的、不可注入的全局副作用它直接绑定宿主时钟字符串解析行为松散且时区语义混乱格式化输出依赖toISOString/toLocaleString等 API在不同运行时Node、浏览器、React Native Hermes表现不一致难以写出稳定断言。DateTime模块把「时间」建模为一等公民的 Effect 值主要解决四类需求见 index.md可测试的当前时间通过 Effect 的Clock服务获取当前时间测试中可用TestClock精确控制时间流逝安全的解析DateTime.make等解析 API 返回Option非法输入不会抛异常而是得到None稳定的 ISO 格式化formatIso系列提供确定性的、跨运行时一致的输出时区转换与日历运算支持 IANA 时区名如Pacific/Auckland与不可变的日历加减。从源码看DateTime.now的类型为Effect.EffectUtcDateTime.tsDateTime.nowInCurrentZone为Effect.EffectZoned, never, CurrentTimeZoneDateTime.ts也就是说时间获取被建模成对Clock/CurrentTimeZone服务的依赖天然支持依赖注入与测试替身。创建与格式化 DateTime 值以下示例完整摘录自.repos/effect-smol/ai-docs/src/07_datetime/10_creating-and-formatting.ts演示了本模块最核心的四个操作。import { DateTime, Effect, Option } from effect Effect.gen(function*() { // 1. 从 Effect 的 Clock 服务获取当前时间UTC // 使用 Clock 服务意味着测试可以用 TestClock 模块控制时间 const now yield* DateTime.now // 2. 用 DateTime.make 安全解析日期输入如用户输入的字符串或 epoch 时间戳 // 返回 Option取决于输入是否合法 const parsedOption: Option.OptionDateTime.Utc DateTime.make(2024-06-15T14:30:00.000Z) // 3. 用 Option API 解包 Option.getOrUndefined(parsedOption) // 4. 日历/日期时间运算返回一个新的 DateTime 值原值不可变 const endsAt now.pipe(DateTime.add({ hours: 2 })) // 5. format* 系列函数将 DateTime 转换为不同格式 yield* Effect.log(ISO string:, DateTime.formatIso(endsAt)) })要点拆解DateTime.now返回EffectUtc不是同步的Date对象。它从Clock服务读取时间因此Effect.gen中必须yield*。测试时可用TestClock把时间拨到任意时刻再断言日志或持久化内容。DateTime.make源码签名export const make: A extends DateTime.Input(input: A) Option.OptionDateTime.PreserveZoneADateTime.ts接受字符串、epoch 毫秒/秒数或日期字段对象等输入返回Option——解析失败得到None而非抛出异常。对于用户输入的日期字符串这类不可信输入这是推荐入口。不可变运算DateTime.add({ hours: 2 })返回新的DateTime值原值不被修改。运算字段除hours外还支持minutes、seconds、days、months、years等日历单位这是Date#setHours这类原地变更 API 无法安全提供的语义。add的完整重载定义见 DateTime.ts。稳定格式化DateTime.formatIsoDateTime.ts输出如2024-06-15T16:30:00.000Z的标准 UTC ISO 字符串适合写入 API payload 或数据库面向用户展示时可配合formatIsoDate、formatIsoTime等变体。处理时区附加 IANA 时区、渲染带时区 ISO 字符串跨时区是日期时间代码最容易出错的场景。以下完整示例摘录自.repos/effect-smol/ai-docs/src/07_datetime/20_time-zones.ts展示了三种把时区附加到 DateTime 的方式以及CurrentTimeZone服务的使用。import { NodeRuntime } from effect/platform-node import { DateTime, Effect, Option } from effect Effect.gen(function*() { // 从 Clock 服务获取当前时间 const now yield* DateTime.now // 方式一附加已知有效的 IANA 时区Unsafe节省一次 Option 判断 const nowInAuckland now.pipe( DateTime.setZoneNamedUnsafe(Pacific/Auckland) ) yield* Effect.log(Now in Auckland:, nowInAuckland) // 方式二附加未知是否有效的 IANA 时区返回 Option const nowInSydneyOption: Option.OptionDateTime.Zoned now.pipe( DateTime.setZoneNamed(Australia/Sydney) ) yield* Effect.log(Now in Sydney:, Option.getOrUndefined(nowInSydneyOption)) // 方式三直接生成位于 CurrentTimeZone 服务的 DateTime.Zoned const nowInNewYork yield* DateTime.nowInCurrentZone yield* Effect.log(Now in New York:, nowInNewYork) // 已知某个日期字符串属于特定 IANA 时区时转换为 Zoned 以保证 instant 正确 const dateInAuckland: DateTime.Zoned DateTime.makeZonedUnsafe(2026-06-05, { timeZone: Pacific/Auckland, // adjustForTimeZone 会将输入调整为给定时区的时间 // 否则输入会被当作 UTC 处理 adjustForTimeZone: true }) yield* Effect.log(Date in Auckland:, dateInAuckland) }).pipe( // 为 CurrentTimeZone 服务提供实现这里固定为纽约时区 Effect.provide(DateTime.layerCurrentZoneNamed(America/New_York)), NodeRuntime.runMain )三种附加时区方式的取舍API签名语义适用场景setZoneNamedUnsafe(zone)已知时区名一定有效时使用直接返回DateTime.ZonedDateTime.ts时区名来自受控配置、白名单setZoneNamed(zone)返回Option.OptionDateTime.Zoned无效时区得到None时区名来自用户输入或不可信配置nowInCurrentZone从CurrentTimeZone服务读取当前时区并生成 Zoned需要跟随工作区/用户时区的代码CurrentTimeZone服务让当前时区可注入DateTime.nowInCurrentZone的效果类型是EffectZoned, never, CurrentTimeZone即它依赖CurrentTimeZone服务。示例代码通过Effect.provide(DateTime.layerCurrentZoneNamed(America/New_York))注入实现layerCurrentZoneNamed的 Layer 定义见 DateTime.ts。这种设计的工程价值在于业务代码不再隐式依赖操作系统时区。在 t3code 这类需要跟随用户/工作区时区的场景如日志时间戳、会话记录、截止时间展示可以将当前时区作为服务注入测试时注入固定时区即可获得确定性输出而不必修改全局process.env.TZ。adjustForTimeZone的陷阱DateTime.makeZonedUnsafe(2026-06-05, { timeZone: Pacific/Auckland, adjustForTimeZone: true })当adjustForTimeZone: true时2026-06-05被解释为奥克兰当地时间的 6 月 5 日零点其底层 UTC instant 会根据奥克兰与 UTC 的偏移换算当省略或为false时输入先被当作 UTC再套用时区外壳。makeZonedUnsafe的完整签名含timeZoneId与adjustForTimeZone选项见 DateTime.ts。选择错误会导致实际 instant 偏移数小时这一点在跨时区业务中必须格外注意。结合源码看DateTime的可靠性与可测试性Option 驱动解析make、setZoneNamed等 API 统一返回Option配合Option.map/Option.getOrUndefined/Option.getOrThrow组合使用保证非法日期在类型层面就被约束不会以运行时异常的形式泄漏。Clock 抽象所有当前时间都经过Clock服务。测试中注入TestClock后可以自由推进时间以模拟两小时后明天零点等边界无需真实等待这也是DateTime.now与Date.now在测试体验上的根本差异。Zoned 类型携带时区DateTime.Zoned的格式化输出形如2024-06-15T15:30:00.00001:00[Europe/London]见 DateTime.ts 的 JSDoc 示例既包含偏移量又包含 IANA 时区名比裸的01:00偏移更精确——偏移会因夏令时而变化IANA 时区名则是稳定的语义标识。跨运行时一致formatIso系列输出纯字符串、无 locale 依赖在 Node、浏览器与 React NativeHermes等 t3code 各端运行时上结果一致便于写跨端快照测试。适用前提与注意事项本指南对应的DateTime模块来自仓库内.repos/effect-smol/packages/effect/src/DateTime.ts属于 Effect V4release candidate形态若项目中锁定的是 Effect V3API 名称与签名可能略有差异请以项目实际依赖版本为准。使用DateTime要求代码运行在 Effect 上下文Effect.gen run 函数如NodeRuntime.runMain中纯同步场景下DateTime.now这类 Effect 值需要被执行后才有实际时间。面向用户展示的本地化格式如2026 年 9 月 14 日仍需要结合 locale 处理DateTime提供的是稳定的机器可读格式与正确的 instant 语义二者分工不同。延伸阅读指南原文DateTime 模块使用说明创建与格式化完整示例10_creating-and-formatting.ts时区处理完整示例20_time-zones.ts模块源码packages/effect/src/DateTime.ts测试用例packages/effect/test/DateTime.test.ts含 DST 切换、闰年等边界场景验证Effect 工程说明与依赖要求.repos/effect-smol/README.md需要 TypeScript 5.9 且开启strict【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表