- 文档
- 教程
- 知识库
【免费下载链接】til
:memo: Today I Learned
本文围绕仓库中的 elixir/create-a-date-with-the-date-sigil.md 展开,系统讲解 Elixir 1.3 引入的
~D日期 sigil:它的语法、与Date.new/3的等价关系、Date结构体的字段访问方式,以及在模块代码、IEx 交互环境中的实际用法。读完本文,你将能熟练地用~D在代码中直接书写日期字面量,并理解它与~T、~N、~U等姊妹 sigil 的关系,为日常的日期处理代码打下基础。
背景:Elixir 1.3 的日期与时间革命
在 Elixir 1.3 之前,处理日期和时间通常需要借助 Erlang 原语或引入 Timex 记录了那个时代的典型做法——直接调用:erlang.time拿到{时, 分, 秒}元组再手动拼接字符串。
Elixir 1.3 改变了这一切:标准库正式引入了基于Calendar模块的整套日期与时间类型,包括Date、Time、NaiveDateTime、DateTime,并为它们配套提供了专用的sigil(字符选择器)。elixir/create-a-date-with-the-date-sigil.md 记录的正是其中用于创建日期的~Dsigil。
~D的工作方式与Date模块的new/3函数完全一致:解析传入的日期字符串,生成一个包含各日期组成部分(年、月、日)的Date结构体。不同之处只在于语法——sigil 提供了一种更简洁、更接近"字面量"的书写方式。
~D 的基本语法与 IEx 中的第一手体验
~D的完整语法由三部分组成:
~D:sigil 名称;- 定界符
[...]:包裹日期内容(也支持~D{...}、~D{...}等其他定界符); - 日期字符串
YYYY-MM-DD:严格按照 ISO 8601 的年-月-日格式书写。
仓库文档给出的最基础示例可以直接在 IEx 中运行:
> ~D[2016-01-01] ~D[2016-01-01] > ~D[2016-01-01].year 2016 > ~D[2016-01-01].month 1 > ~D[2016-01-01].day 1第一行输出了一个合法的Date结构体,其 IEx 渲染形式恰好就是~D[2016-01-01],方便复制回代码里继续使用。后三行则展示了字段访问:.year返回2016、.month返回1、.day返回1——这三个字段共同构成了一个日期。
值得注意的是,~D要求传入的必须是合法日期。与 JavaScript 中new Date(...)的宽松行为不同,非法输入(例如~D[2016-02-30]这种不存在的 2 月 30 日)会直接抛出ArgumentError,从而把日期校验提前到"写下代码的那一刻",避免脏数据悄悄流入业务逻辑。
与 Date.new/3 的等价关系
原文档明确指出,~D的作用与Date.new/3函数相同。所谓Date.new/3,即Date.new(year, month, day),它接收三个整数并返回带标签的元组:
> Date.new(2016, 1, 1) {:ok, ~D[2016-01-01]} > Date.new(2016, 13, 1) {:error, :invalid_date}两者的对比如下:
| 创建方式 | 示例 | 返回类型 | 适用场景 |
|---|---|---|---|
~Dsigil | ~D[2016-01-01] | 直接返回Date结构体 | 写死日期字面量(如生日、固定节假日、测试夹具) |
Date.new/3 | Date.new(2016, 1, 1) | 返回{:ok, date}或{:error, reason} | 运行时动态构造日期,需要处理非法输入 |
一个关键差异在于返回值形态:~D在写下的瞬间就完成了校验并直接给出结构体;而Date.new/3返回的是 tagged tuple,要求调用方显式匹配{:ok, date}分支。这决定了它们的使用场景:硬编码日期优先用~D,由用户输入或外部数据动态组装日期时则用Date.new/3配合模式匹配处理错误分支。
访问 Date 结构体的字段
Date是一个普通的 Elixir 结构体(struct),定义于 Elixir 标准库的Date模块。除文档中演示的year、month、day之外,它还有一个calendar字段,默认指向Calendar.ISO。结构体的字面形式如下:
%Date{ calendar: Calendar.ISO, day: 1, month: 1, year: 2016 }正因为它是普通结构体,你可以用点语法读取任意字段,也可以把它当作模式匹配的载体:
iex> ~D[2016-01-01] = %Date{calendar: Calendar.ISO, day: 1, month: 1, year: 2016} ~D[2016-01-01] # 也可以只匹配关心的字段 iex> %Date{year: y, month: m} = ~D[2016-01-01] ~D[2016-01-01] iex> y 2016 iex> m 1如果后续需要把日期输出成字符串(例如写入日志或返回给前端),可以用Date.to_string/1:
> Date.to_string(~D[2016-01-01]) "2016-01-01"而结构体级别的相等性比较直接用==即可;当需要判断两个日期谁先谁后时,则用Date.compare/2,它会返回:lt、:gt或:eq。
把 ~D 写进模块代码:函数封装与编译期常量
~D在模块内书写时属于 sigil 语法糖,会在编译期被展开求值,因此它实际上是一个编译期常量。这意味着你可以放心地把这类日期写进模块属性和函数默认值里,不会有任何运行时解析开销:
defmodule User do @default_birthday ~D[1990-06-15] def default_birthday, do: @default_birthday end也可以封装成具名函数,让语义更清晰:
defmodule Holiday do def new_year, do: ~D[2016-01-01] def independence_day, do: ~D[2016-07-04] end在测试代码中,~D尤其好用——它让测试夹具(fixture)里的日期一目了然,且不依赖系统时间,天然具备确定性。例如配合 Ecto 的 schema 做数据断言时,可以直接写出assert user.birthday == ~D[1990-06-15]这样的可读性极佳的比较。
与 ~T、~N、~U 姊妹 sigil 的关系
~D并不是孤立的,Elixir 1.3 起标准库还提供了一套配套的时间类 sigil,覆盖"日期之外的时刻":
| Sigil | 生成结构体 | 示例 | 说明 |
|---|---|---|---|
~D[...] | Date | ~D[2016-01-01] | 仅日期,无时间与时区 |
~T[...] | Time | ~T[13:26:08] | 仅时间,无日期与时区 |
~N[...] | NaiveDateTime | ~N[2016-05-24 13:26:08.003] | 日期 + 时间,无时区信息 |
~U[...] | DateTime | ~U[2016-05-24 13:26:08Z] | 日期 + 时间 + UTC 时区 |
其中~N和~U经常需要转换成DateTime后使用,仓库中的两个 Elixir 笔记正好演示了这一组合:
- elixir/comparing-datetime-structs.md 中通过
DateTime.from_naive(~N[2016-05-24 13:26:08.003], "Etc/UTC")把 naive datetime 挂上时区,再调用DateTime.compare/2比较先后; - elixir/check-list-membership.md 同样先构造
~N[2016-05-24 13:26:08.003],再配合DateTime.compare/2与in操作符判断某个时间是否落在"过去或现在"区间内。
可以看到,~N、~U这类带完整时刻的 sigil 通常只是数据入口,真正的逻辑处理还是要交给NaiveDateTime/DateTime模块的函数;而~D因为只关心年月日,往往可以直接参与比较和运算,是最"独立"的一个。
小结
~D是 Elixir 1.3 提供的最直观的日期构造方式。回顾仓库笔记的核心要点:
~D[2016-01-01]按 ISO 8601 格式直接生成Date结构体,等价于Date.new(2016, 1, 1);- 生成后的结构体可通过
.year、.month、.day等字段直接访问各部分; - 非法日期会在构造时抛出
ArgumentError,将校验提前到写代码阶段; - 在模块中使用时它是编译期常量,适合做日期字面量、测试夹具和函数默认值;
- 与
~T、~N、~U共同构成 Elixir 标准库的日期时间 sigil 家族,各司其职。
如果你的项目仍停留在"调:erlang.time或引入重量级库"的阶段,不妨像 elixir/do-you-have-the-time-part-2.md 建议的那样,先用标准库的 sigil 和模块函数把逻辑写简洁——~D正是这个思路下最基础、最常用的一块积木。
- 文档
- 教程
- 知识库
【免费下载链接】til
:memo: Today I Learned
相关推荐
Delve 后端测试健康报告全解读:各平台跳过测试统计、原因分类与生成机制
Delve 后端测试健康报告全解读:各平台跳过测试统计、原因分类与生成机制 Documentation/backend_test_health.md 是 Del
文档教程知识库JavaScript 日期创建实战:用 `new Date` 构造指定日期(Modern JavaScript Tutorial 习题精解)
JavaScript 日期创建实战:用 new Date 构造指定日期(Modern JavaScript Tutorial 习题精解) 导读 在 JavaSc
文档/教程前端TIL 仓库笔记:ack 命令行工具的隐藏彩蛋 --bar 选项
TIL 仓库笔记:ack 命令行工具的隐藏彩蛋 bar 选项 本篇技术笔记围绕本仓库 ack 分类 https://link.gitcode.com/i/2a6
文档教程知识库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考