Effect Schema 日期时间与时区 Schema 全解析:DateTimeZoned、TimeZone、TimeZoneOffset 与 TimeZoneNamed 实战指南 Effect Schema 日期时间与时区 Schema 全解析DateTimeZoned、TimeZone、TimeZoneOffset 与 TimeZoneNamed 实战指南【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect在跨时区业务系统如国际化 SaaS、日程调度、日志与审计、API 数据交换中日期时间的建模与序列化始终是绕不开的痛点既要保留 IANA 命名的时区语义如Europe/London又要兼容简单的固定偏移如03:00还要确保这些值能安全地穿越 JSON 边界。本篇文章基于 Effect 仓库中关于 Schema 新增DateTimeZoned、TimeZoneOffset、TimeZoneNamed、TimeZone四个 Schema 的变更记录见 .changeset/pre/add-schema-datetime.md结合 Schema.ts 与 DateTime.ts 的源码实现及 Schema.test.ts 中的测试用例系统讲解这些 Schema 的用途、JSON 编解码行为、Arbitrary 生成规则与选型建议。读完本文你将能熟练地为带时区的日期时间数据设计严谨的 Schema并正确地在字符串、数字与DateTime值之间完成类型安全的转换。变更背景Schema 与 DateTime 模块的正式打通该变更记录changeset声明Schema: addDateTimeZoned,TimeZoneOffset,TimeZoneNamed, andTimeZoneschemas.也就是说Effect 的 Schema 模块effect/Schema正式引入了对DateTime模块核心类型的 Schema 支持。在此之前Schema 只能以通用String/Number处理日期时间无法区分 UTC、命名时区、偏移时区与带时区的完整时刻。本次新增的四个 Schema 与既有的DateTimeUtc系列一起构成了完整的日期时间 Schema 家族其源码统一位于 Schema.ts 的 DateTime schemas 区块。从版本标注看DateTimeUtc、TimeZoneOffset、TimeZoneNamed、TimeZone、DateTimeZoned自3.10.0起提供声明式 Schema用于校验已存在的DateTime值DateTimeUtcFromString、DateTimeUtcFromMillis、TimeZoneNamedFromString、TimeZoneFromString、DateTimeZonedFromString自4.0.0起提供转换式 Schema用于从原始输入解析。在深入四个 Schema 之前先明确DateTime模块中时区TimeZone的两种底层类型这是理解一切的基础。前置知识DateTime 模块中的两种时区类型DateTime.TimeZone是带判别联合类型包含两个分支源码见 DateTime.tsDateTime.TimeZone.Named使用 IANA 时区标识如Europe/London、Asia/Tokyo。它承载完整的 DST夏令时规则能根据具体时刻正确换算。创建方式为DateTime.zoneMakeNamed/DateTime.zoneMakeNamedUnsafe。DateTime.TimeZone.Offset固定偏移量内部以毫秒表示如3 * 60 * 60 * 1000表示 UTC3。创建方式为DateTime.zoneMakeOffset其offset属性即毫秒数可经DateTime.zoneToString序列化为03:00形式。这两个分支通过DateTime.isTimeZoneOffset/DateTime.isTimeZoneNamed判别函数区分——这正是后续各 Schema 声明式declare校验的依据。Schema 逐一详解四个新增 Schema 的建模与编解码Schema.TimeZoneOffset毫秒偏移量 ↔ 数字TimeZoneOffset是declare声明式 Schema类型层面对应DateTime.TimeZone.Offset用于校验已经构造好的偏移时区值。其默认 JSON 序列化行为源码 Schema.ts编码将DateTime.TimeZone.Offset编码为数字毫秒偏移量解码将数字通过DateTime.zoneMakeOffset(n)还原为偏移时区值。底层转换由timeZoneOffsetFromNumber完成Schema.tsdecode调用DateTime.zoneMakeOffset(n)encode直接取tz.offset。toFormatter则使用DateTime.zoneToString输出人类可读的03:00形式。测试用例Schema.test.ts验证了DateTime.zoneMakeOffset(3 * 60 * 60 * 1000)可被正常解码与编码。import * as DateTime from effect/DateTime import * as Schema from effect/Schema const schema Schema.TimeZoneOffset // 解码直接传入已构造的偏移时区值 Schema.decodeSync(schema)(DateTime.zoneMakeOffset(3 * 60 * 60 * 1000)) // 编码得到毫秒偏移量数字 Schema.encodeSync(schema)(DateTime.zoneMakeOffset(3 * 60 * 60 * 1000)) // 10800000Schema.TimeZoneNamedIANA 命名时区 ↔ 字符串TimeZoneNamed同样为声明式 Schema校验DateTime.TimeZone.Named值Schema.ts。默认 JSON 序列化行为编码输出 IANA 时区标识字符串如Europe/London解码要求输入已是DateTime.TimeZone.Named值。其转换底层为timeZoneNamedFromStringSchema.tsencode返回tz.iddecode调用DateTime.zoneMakeNamed(s)若无法识别则产生a valid IANA time zone的InvalidValue失败对应测试 Schema.test.ts 中Europe/London的成功往返。值得注意的差异TimeZoneNamed的默认 JSON 编解码并不接受任意字符串解析它只校验已构造的 Named 值字符串解析由后面的TimeZoneNamedFromString负责但其Arbitrary 生成能力十分明确——从arbitraryNamedTimeZones常量UTC、Europe/London、America/New_York、Asia/Tokyo、Australia/Sydney中随机选取再经DateTime.zoneMakeNamedUnsafe构造见 Schema.ts 与 Schema.ts。Schema.TimeZone时区联合类型的大统一SchemaTimeZone同时覆盖 Named 与 Offset 两种分支是对DateTime.TimeZone整个类型的声明式 SchemaSchema.ts。默认 JSON 序列化行为编码统一输出字符串——命名时区输出 IANA 标识Europe/London偏移时区输出03:00风格解码要求输入已是DateTime.TimeZone值Named 或 Offset 均可。底层转换timeZoneFromStringSchema.ts的decode使用DateTime.zoneFromStringencode使用DateTime.zoneToString。测试Schema.test.ts验证了偏移时区与命名时区都能成功往返。TimeZone的 Arbitrary 生成策略最有代表性Schema.ts它构造一个Union——dateTimeArbitraryInteger(-12h, 14h)毫秒偏移数字与Literals(arbitraryNamedTimeZones)五个 IANA 标识的联合随后decode时根据值的类型数字 →zoneMakeOffset字符串 →zoneMakeNamedUnsafe分别构造对应分支。import * as Schema from effect/Schema const schema Schema.TimeZone // 编码命名时区 → IANA 字符串 Schema.encodeSync(schema)(DateTime.zoneMakeNamedUnsafe(Europe/London)) // Europe/London // 编码偏移时区 → 03:00 字符串 Schema.encodeSync(schema)(DateTime.zoneMakeOffset(3 * 60 * 60 * 1000)) // 03:00Schema.DateTimeZoned保留时区语义的完整时刻DateTimeZoned是对DateTime.Zoned时刻 时区的声明式 Schema是最具实战价值的一个Schema.ts。它的默认 JSON 序列化行为定义了两套格式偏移时区编码为带数字偏移的 ISO 日期时间如YYYY-MM-DDTHH:mm:ss.sssHH:MM命名时区编码为 ISO 日期时间 方括号包裹的 IANA 标识如2024-01-01T00:00:00.00000:00[Europe/London]即DateTime.formatIsoZoned的输出见 DateTime.ts。底层转换dateTimeZonedFromStringSchema.ts的decode使用DateTime.makeZonedFromStringencode使用DateTime.formatIsoZoned。注意DateTimeZoned本身只校验已构造的DateTime.Zoned值对字符串的解析由 4.0.0 的DateTimeZonedFromString承担。DateTimeZoned的 Arbitrary 生成逻辑Schema.ts非常精细时刻通过dateTimeArbitraryBounds约束在±8640000000000000 ms并内缩14harbitraryMinimumZonedDateTimeTimestamp/arbitraryMaximumZonedDateTimeTimestamp以容纳任意时区偏移后仍在合法日期范围内时区复用timeZoneArbitrarySchema()偏移数字与 IANA 标识的 Union最终以{ epochMilliseconds, timeZone }结构经DateTime.makeZonedUnsafe构造出DateTime.Zoned。同时DateTimeZoned还通过toEquivalence: () DateTime.Equivalence提供了基于时刻的等价性判断Schema.ts。import * as DateTime from effect/DateTime import * as Schema from effect/Schema const schema Schema.DateTimeZoned const zoned DateTime.makeZonedUnsafe(2021-01-01T00:00:00.000Z, { timeZone: Europe/London }) // 编码命名时区 → ISO [IANA] Schema.encodeSync(schema)(zoned) // 2021-01-01T00:00:00.00000:00[Europe/London]4.0.0 起可用的 FromString 系列从原始输入安全解析上述四个声明式 Schema 只负责校验已经是 DateTime 值的数据要从字符串/数字原始输入解析出DateTime值需要 4.0.0 引入的转换式变体它们均基于decodeTo实现Schema输入输出底层解析函数TimeZoneNamedFromStringstringIANA 标识DateTime.TimeZone.NamedDateTime.zoneMakeNamedTimeZoneFromStringstringIANA 标识或03:00DateTime.TimeZoneDateTime.zoneFromStringDateTimeZonedFromStringstring...Z[Europe/London]格式DateTime.ZonedDateTime.makeZonedFromStringDateTimeUtcFromStringstring可被DateTime.make接受的日期时间串DateTime.UtcDateTime.maketoUtcDateTimeUtcFromMillisnumberepoch 毫秒DateTime.UtcDateTime.maketoUtc测试用例覆盖了这些转换式 Schema 的失败路径例如Schema.test.tsconst schema Schema.DateTimeZonedFromString // 编码DateTime.Zoned → ISO [IANA] 字符串 Schema.encodeSync(schema)(zoned) // DateTime.formatIsoZoned(zoned) // 解码失败非法字符串 Schema.decodeUnknownSync(schema)(invalid) // ParseError: Expected a valid Zoned DateTime string再如TimeZoneFromString可同时解析Europe/London与03:00而TimeZoneNamedFromString只接受 IANA 标识Schema.test.tsconst schema Schema.TimeZoneFromString Schema.decodeUnknownSync(schema)(Europe/London) // zoneMakeNamedUnsafe(Europe/London) Schema.decodeUnknownSync(schema)(03:00) // zoneMakeOffset(3 * 60 * 60 * 1000)这些转换式 Schema 内部均采用transformEffectSchemaTransformation.transformEffect解析失败时通过SchemaIssue.InvalidValue携带明确的期望信息如a valid Zoned DateTime string、a valid IANA time zone、a valid time zone可无缝融入 Effect 的管道式错误处理流程。实战选型指南何时用哪个 Schema综合源码中的版本划分与语义给出如下选型建议持久化 / API 传输带时区的完整时刻→ 首选DateTimeZoned或解析字符串输入用DateTimeZonedFromString。JSON 格式...00:00[Europe/London]同时保留时刻与 DST 语义比单纯偏移更可靠。只需要 UTC 时刻→ 使用DateTimeUtc/DateTimeUtcFromString/DateTimeUtcFromMillis输出无歧义的 UTC ISO 字符串或 epoch 毫秒体积最小。单独建模时区字段如用户偏好时区列→ 用TimeZoneNamed限制为 IANA 命名时区或TimeZone允许偏移与命名并存如08:00与Asia/Tokyo。需要任意值校验、不需要解析→ 用四个声明式 SchemaTimeZoneOffset/TimeZoneNamed/TimeZone/DateTimeZoned配合Schema.decodeSync使用同时它们各自提供 JSON 序列化行为可用于encode。属性校验与测试数据生成→ 依赖 Arbitrary 生成TimeZoneNamed与TimeZone从五个常见 IANA 区域生成DateTimeZoned在全日期域内联合同步生成时刻与时区可无缝接入 effect/Schema 的 FastCheck 属性测试 流程。在组合场景中这些 Schema 可直接嵌入Schema.Struct例如const UserPreferences Schema.Struct({ createdAt: Schema.DateTimeZoned, // 完整带时区时刻 locale: Schema.String, timeZone: Schema.TimeZoneNamed // IANA 命名时区 })小结本次 changeset 将DateTime模块的时区与带时区时刻类型正式纳入 Schema 体系补齐了日期时间建模的关键一环。四个核心 Schema 各司其职TimeZoneOffset以毫秒数字承载固定偏移、TimeZoneNamed以 IANA 字符串承载命名时区、TimeZone统一覆盖两种分支、DateTimeZoned则完整保留时刻 时区语义并以 ISO [Zone]格式穿越 JSON 边界。配合 4.0.0 的*FromString系列与各 Schema 内置的 Arbitrary 生成规则开发者可以放心地在 HTTP API、数据库持久化与属性测试场景中构建类型安全、时区正确的日期时间管线。相关实现与测试可继续阅读 Schema.ts 与 Schema.test.ts 获取一手依据。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考