使用 bttf 精通日期时间算术:一款现代 CLI 瑞士军刀

在终端中处理日期和时间是一种众所周知的令人沮丧的体验。对于许多开发者来说,标准的 date 命令是一个使用频率较低的工具,其标志(flags)和特有的行为在两次使用之间往往会被遗忘,从而导致不断重复阅读手册的循环。bttf 应运而生,它是一款用于日期时间算术、解析和格式化的命令行“瑞士军刀”。

由 BurntSushi 创建的 bttf 并非设计为 POSIX date 的直接替代品,而是一个更直观、更强大的替代方案。它的目标是提供一种简洁的方式来处理复杂的时序计算和格式化任务,否则这些任务可能需要繁琐的 shell 脚本或自定义代码。

超越简单的格式化

虽然 bttf 可以执行基本任务,如打印当前时间或通过 strftime 语法格式化日期,但其真正的力量在于处理相对时间和复杂序列的能力。

相对时间和算术

与其纠结于复杂的标志,bttf 允许进行类自然语言的算术运算。你可以轻松地为当前时间添加时长,或者查找过去或未来的特定日期:

# 为当前时间增加一周和 12 小时
$ bttf time add '1 week, 12 hours ago' now

# 查找自特定日期以来的持续时间,并四舍五入到最近的一天
$ bttf span since 2025-01-20T12:00 | bttf span round -l day -s day

强大的序列生成

最突出的功能之一是 seq 命令,它可以取代数十行 Bash 脚本。例如,生成一个列表,显示直到年底的每个月的第二个星期二:

$ bttf time seq monthly -w 2-tue --until $(bttf time end-of year now) | bttf time fmt -f '%a, %F'

架构:Jiff 与“民用时间”问题

在底层,bttf 由 Jiff 提供支持,这是一个 Rust 库,旨在解决许多日期时间 API 中的一个根本缺陷:混淆了“民用时间”(civil time)与“绝对瞬时”(absolute instants)。

正如社区成员所指出的,大多数 API 在处理夏令时(DST)时会默默地选择一边。例如,在特定时区询问“从现在起 30 天后是什么时候”可能会导致 29 天 23 小时或 30 天 0 小时的流逝时间,具体取决于是否发生了 DST 转换。Jiff(受 TC39 为 JavaScript 提出的 Temporal 提案启发)将这些视为不同的类型,从而确保计算是显式且可预测的。

高级工作流:tag 命令

bttf 最具创新性的功能或许是 tag 命令。这允许该工具处理任意数据——不仅仅是日期时间——通过在文本流中识别时间戳,将其封装在 JSON lines 格式中进行处理,然后将其“取消标记”(untagging)回原始文本。

这使得强大的流水线成为可能。例如,作者演示了如何列出 git 仓库中的所有文件,按最后一次提交日期排序,并以本地时间格式化:

$ git ls-files | bttf tag exec git log -n1 --format='%aI' | bttf time in system | bttf time sort | bttf time fmt -f '%a %Y-%m-%d %H:%M:%S' | bttf untag -f '{tag}|t{data}'

在此工作流中,bttf tag exec 为每个文件并行运行 git 命令,随后的 bttf time 命令处理时区转换、排序和格式化,最后通过 untag 恢复原始文件路径以及格式化后的日期。

社区观点:CLI vs. AI

bttf 的发布引发了关于在 LLM 时代使用专门化 CLI 工具实用性的有趣辩论。一些用户认为,一次性的时间戳转换或日期计算现在可以通过询问 AI 来更高效地处理。

然而,其他人指出,对于程序化工作流、自动化以及集成到 AI agent(其中工具可以作为函数被调用)中,一个确定性、快速且精确的 CLI 工具是不可或缺的。正如一位用户所指出的,仅 bttf time seq 就能取代 40 行用于新闻邮件调度计划的自定义 Bash 代码。

安装与使用

对于想要尝试的用户,bttf 提供适用于 Windows、macOS 和 Linux 的预编译二进制文件,或者可以通过 Cargo 为 Rust 开发者提供:

cargo install bttf

注意:Locale 支持默认启用,但可以在安装时禁用(--no-default-features)以减小二进制文件大小并加快编译速度。

Sources