要計算出版日期之間的民用日數,請比較它們的日曆日期,而不要將經過的時數除以 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。套用公式前先確認日期順序。負差值可用來判斷輸入順序是否顛倒;自動取絕對值會失去這項資訊。
同樣的區別也適用於只有一天的範圍。日期差值為零,但包含首尾的計數會包含一個日期。請先決定試算表需要哪種結果,並據此為結果欄命名。
如果輸入資料是時間戳記,該怎麼辦?
捨棄時間之前,必須先將時間戳記轉換為回報時區。例如,使用 America/New_York 時,2025-03-10T00:30:00Z 會對應到 3 月 9 日晚上 8:30,該日期的位移為 -04:00。直接複製時間戳記的前十個字元,選到的會是 UTC 日期,而不是紐約出版日期。
Numbers 文件中 DAYS 的簽章接受兩個日期引數,沒有具名時區引數。請先確定預期日期,再使用此函數。若要在 Mac 上重複計算時間戳記,Foundation 提供所需的日曆 API。
儘管名稱如此,Foundation 的 Date 代表不依附於任何日曆或時區的時間點。Apple 指引使用 Calendar 進行日曆運算。請參閱官方 Date 文件。
對於一般夏令時間轉換,Foundation 的操作順序如下:選取格里曆與回報時區、找出每個端點當地日期的起始時間,然後只使用 day 元件要求計算差值。Apple 記錄了 startOfDay(for:) 和 dateComponents(_:from:to:) 的用法。
在終端機執行完整的 Foundation 範例
此選用方法需要已安裝 Swift 工具鏈。開啟終端機並執行 swift --version 進行確認,方法如 Swift 入門指南所述。如有需要,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 Editor。
時區資料庫:northamerica — IANA。
Date — Apple。
startOfDay(for:) — Apple。
dateComponents(_:from:to:) — Apple。
formatOptions — Apple。
入門指南 — Swift.org。
安裝命令列工具 — Apple。