要计算出版日期之间相隔的公历日数,应比较它们的日历日期,而不是将经过的小时数除以 24。在 Mac 上,Numbers 可以处理仅含日期的输入;时间戳则需要先按选定的一个时区进行解释。
本指南使用虚构的出版日期和公历。要回答的问题是:“这两份出版物的日期相隔多少个日历日?”默认计算的是带符号的差值:同一天为零,次日为一。
你拥有哪种日期信息?
计算前先确定输入类型。印在出版物上的日期与记录其发布时刻的时间戳,需要不同的预处理。
| 输入 | 含义 | 预处理 |
|---|---|---|
2025-03-09 |
公历日期:年、月、日 | 直接与另一个公历日期比较 |
2025-03-09T05:00:00Z |
标识某个时刻的时间戳 | 转换为所选报告时区中的日期 |
-05:00 |
UTC 偏移量 | 描述某一时刻当地时间与 UTC 的时差 |
America/New_York |
命名时区 | 提供解释当地日期和偏移量变化所需的规则 |
偏移量与命名时区所包含的信息不同。某个时间戳附带的偏移量,并不能说明另一个日期的偏移量。命名时区则提供了两者之间关系的规则,包括夏令时变更。RFC 9557《互联网日期和时间:附加信息的时间戳》明确说明了这一点。
对于出版日期电子表格,请先确定以下规则:已作为公历日期提供的值应予保留;时间戳应使用明确选定的同一个报告时区转换为日期。不要为了相减而给仅含日期的值随意附加午夜时间和偏移量。
日历标准也会区分日期与日期时间。iCalendar 标准定义了一种单独的 DATE 值,其中包含日历组成部分。其序列化形式使用不带分隔符的数字;本指南中带连字符的日期是为了便于阅读。参见 RFC 5545 第 3.3.4 节。
为什么不能将经过的秒数除以 86,400?
公历日期差衡量的是日历上的进度。经过时间差衡量的是两个时刻之间的间隔。跨越夏令时转换时,这两个量不一定对应于每增加一个日期就经过 24 小时。RFC 5545 明确指出,时区偏移发生变化时,名义上的一天重复间隔可能短于或长于 24 小时。参见其中关于重复规则的说明。
来看 America/New_York 时区中的两组虚构出版日期。以下计算使用两个端点各自的当地午夜。偏移量遵循纽约时区及其转换规则,见 IANA 时区数据库。
| 虚构的出版日期区间 | 起始偏移量 | 结束偏移量 | 经过小时数 | 公历日期差 |
|---|---|---|---|---|
| 2025 年 3 月 9 日至 10 日 | -05:00 |
-04:00 |
23 | 1 |
| 2025 年 11 月 2 日至 3 日 | -04:00 |
-05:00 |
25 | 1 |
对于 3 月这组日期,对应的 UTC 时刻是 2025-03-09T05:00:00Z 和 2025-03-10T04:00:00Z:相隔 23 小时。将该间隔除以 24,得到约 0.9583。对于 11 月这组日期,同样计算得到约 1.0417。这两个小数都无法回答关于日历日期的问题。
在这些示例中,四舍五入可能会掩盖错误,但这并不能构成可靠的计数规则。两份出版物分别于连续两天的晚上 11:55 和凌晨 12:05 发布,虽然只相隔十分钟,但公历日期差仍为一。应先提取日期,再计算它们的差值。
如何在 Numbers 中计算出版日期的间隔?
如果你已经有了所需的出版日期,请使用此方法。Apple 将 DAYS(end-date, start-date) 说明为返回其参数之间的天数;如果结束日期早于开始日期,也会返回负值。参见 Numbers DAYS 参考文档。
打开一个包含空表格的 Numbers 电子表格。第 1 行填写描述性标签,然后在第 2 行分别输入以下公式。要输入公式,请选中对应单元格,键入开头的 =,完成表达式后按 Return。这些步骤来自 Apple 记录的 Mac 版 Numbers 公式输入说明。
| 单元格 | 建议的列标签 | 要输入的公式 | 预期值 |
|---|---|---|---|
A2 |
较早的出版日期 | =DATE(2025,3,9) |
2025 年 3 月 9 日 |
B2 |
较晚的出版日期 | =DATE(2025,3,10) |
2025 年 3 月 10 日 |
C2 |
相隔公历日数 | =DAYS(B2,A2) |
1 |
使用 DATE 可根据年、月、日构造输入,而不必依赖系统如何解释含义不明确的文本字符串。Apple 在其日期与时间函数列表中建议使用此函数,以便在不同的日期格式设置下保持日期解释一致。
在第 3 行重复检查:在 A3 中输入 =DATE(2025,11,2),在 B3 中输入 =DATE(2025,11,3),并在 C3 中输入 =DAYS(B3,A3)。答案同样是 1。你比较的是提供的日历日期,因此无需调整经过的小时数。
若要快速计算而不使用辅助单元格,可在任一空单元格中输入 =DAYS(DATE(2025,3,10),DATE(2025,3,9))。将日期放在不同列的做法更易检查,也更方便用于多组出版日期。
你要计算日期间隔,还是包含首尾日期的天数?
标准日期差不会将起始日期计作一个单位。3 月 9 日到 3 月 10 日相隔一天。如果你的任务是计算从 3 月 9 日到 3 月 10 日(含首尾)涵盖多少个日期标签,答案是两个。
对于结束日期不早于开始日期的包含首尾日期范围,可使用 =DAYS(B2,A2)+1。使用该公式前请检查日期顺序。负值有助于发现输入顺序颠倒;自动取绝对值会丢失这条信息。
同样的区别也适用于只包含一天的日期范围。其日期差为零,而包含首尾日期的计数则为一。请确定电子表格需要哪种结果,并相应地命名结果列。
如果输入是时间戳怎么办?
舍弃时间之前,必须先将时间戳转换为报告时区。例如,2025-03-10T00:30:00Z 按该日期的 -04:00 偏移量,对应于 America/New_York 的 3 月 9 日晚上 8:30。若直接复制时间戳的前十个字符,得到的会是 UTC 日期,而不是纽约的出版日期。
有文档说明的 Numbers DAYS 函数签名接受两个日期参数;它不接受命名时区参数。确定所需日期后再使用它。若要在 Mac 上重复计算时间戳,Foundation 提供了所需的日历 API。
尽管名称如此,Foundation 的 Date 表示一个独立于日历或时区的时刻。Apple 指出,日历运算应通过 Calendar 进行。参见官方 Date 文档。
对于常见的夏令时转换,Foundation 的步骤是:选择公历日历和报告时区,找到每个端点当地日期的开始时刻,然后仅使用日分量请求差值。Apple 对 startOfDay(for:) 和 dateComponents(_:from:to:) 均有说明。
在终端中运行完整的 Foundation 示例
此可选方法需要安装 Swift 工具链。按照 Swift 入门指南中的说明,打开终端并运行 swift --version 进行检查。如有需要,Apple 说明了如何使用 xcode-select --install 安装命令行工具;继续之前请完成安装对话框。参见安装命令行工具。
将下面的整个代码块粘贴到终端中。它包含完整程序和虚构输入数据。程序使用明确的偏移量解析时间戳,不含小数秒。Apple 在 ISO8601DateFormatter 选项参考中说明了此时间戳格式对应的 .withInternetDateTime 选项。
swift - <<'SWIFT'
import Foundation
let zoneID = "America/New_York"
guard let zone = TimeZone(identifier: zoneID) else {
fatalError("Unknown reporting zone: \(zoneID)")
}
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = zone
let parser = ISO8601DateFormatter()
parser.formatOptions = [.withInternetDateTime]
let samples = [
("Spring", "2025-03-09T00:00:00-05:00",
"2025-03-10T00:00:00-04:00"),
("Autumn", "2025-11-02T00:00:00-04:00",
"2025-11-03T00:00:00-05:00")
]
for (label, startText, endText) in samples {
guard let start = parser.date(from: startText),
let end = parser.date(from: endText) else {
fatalError("Could not parse timestamps for \(label)")
}
let startDay = calendar.startOfDay(for: start)
let endDay = calendar.startOfDay(for: end)
guard let days = calendar.dateComponents(
[.day], from: startDay, to: endDay
).day else {
fatalError("Could not calculate civil days for \(label)")
}
let hours = end.timeIntervalSince(start) / 3600
print("\(label): \(days) civil day(s), \(hours) elapsed hours")
}
SWIFT
预期输出如下。数值示例已通过日期运算独立核对;此处检查了 Swift 程序,但未在 macOS 上运行。
春季:1 个公历日,经过 23.0 小时
秋季:1 个公历日,经过 25.0 小时
要调整程序,请替换时间戳字符串,并审慎选择报告时区。时间戳中的偏移量用于标识对应时刻;日历的时区决定这些时刻对应的当地日期。若解析器报告失败,程序会停止,而不会用当前日期替代或跳过记录。
此示例接受上述带偏移量的格式。小数秒需要使用不同的解析器选项。它也不支持 RFC 9557 中带方括号的时区注释:请像示例那样单独提供命名时区,不要假设当前解析器配置支持此类注释。
结果看起来不对时应检查什么?
按顺序进行以下检查;每一项针对不同的分歧来源。
- 确认所需的计数方式。 对于日期向前的范围,日期差与包含首尾日期的计数相差一。
- 确认输入值。 对于仅含日期的出版物,请使用实际的年、月、日。不要假设导入的时间戳已经代表所需的当地日期。
- 确认报告时区。 提取日期前,使用同一个选定时区转换两个时刻。
- 确认运算方式。 去除时刻影响后,再计算日历日数。将时刻间隔除以
86,400回答的是经过时长问题。 - 确认方向。 在确定较早和较晚条目是否颠倒之前,保留负值。
对于日历代码,应使用日期开始 API,而不要假设每个当地日期都从唯一的午夜开始。Apple 说明,startOfDay(for:) 在存在两个午夜时会选择较早的那个;若午夜不存在,则会选择当天存在的最早时刻。这样可以处理手动将小时设为零时可能忽略的边界情况。参见 startOfDay(for:)。
Foundation 示例适用于常见的夏令时变更。对于会跳过整个当地日期的历史变更,请先明确任务是计算公历日期标签,还是实际存在过的当地日;两者要求不同。若需要前者,请将仅含日期的输入保留为日历标签。
对于出版日期电子表格,请让最终计算保持简单:确定两个所需的公历日期,保留其顺序,并使用 DAYS 求出间隔。如果输入是时刻,请先明确报告时区再计算。即使两日期之间发生时钟变更,结果仍会对应出版日期。
来源
DAYS — Apple。
在 Mac 版 Numbers 中使用表格单元格中的数据计算数值 — Apple。
按类别列出的函数列表 — Apple。
RFC 9557:互联网日期和时间:附加信息的时间戳 — IETF。
RFC 5545:互联网日历和日程安排核心对象规范(iCalendar) — RFC 编辑部。
时区数据库:northamerica — IANA。
Date — Apple。
startOfDay(for:) — Apple。
dateComponents(_:from:to:) — Apple。
formatOptions — Apple。
入门指南 — Swift.org。
安装命令行工具 — Apple。