使用者:Xyy23330121/Python/日期與時間
許多程序需要處理日期與時間。Python 提供了兩個模塊可以用於處理時間。
本頁面的內容太多,所以大部分內容都被改為了超鏈接索引。如果讀者需要詳細了解一些信息,請直接點擊表格中或文章中的超鏈接。
許多平台都會使用紀元秒數來存儲時間。紀元秒數是自 1970 年 1 月 1 日 00:00:00 以來,經過的秒數(用浮點數表示)。簡單來講:0.0
代表 1970 年 1 月 1 日 00:00:00,而 1.0
代表 1970 年 1 月 1 日 00:00:01 。
在實際操作時,則是順延到月份的最後一天再插入閏秒。
這一記錄方法通常是不包含閏秒的。閏秒是 UTC 標準下、對滿足一定條件的一天、在其最後最後添加一秒的規則。在所有符合 POSIX 標準的平台上,閏秒都不會被記錄在總秒數中。比如:
- 2016 年 12 月 31 日有閏秒,使得比如 2016 年 12 月 31 日 23:59:60 的時間是存在的(而非錯誤地沒有進位)。而在 POSIX 標準的平台中,這一秒被直接忽略掉——根本不表示這一秒之間的時間。
時間還可以由 ISO 8601 標準給出的的字符串來儲存和進行交流。
除上述方法以外,Python 還通過幾種方式儲存時間。這裡簡單列出這些方法的特徵,讀者應按照自身需求來選擇對應的方法。
和其它程序語言中,整數在 -32768 ~ 32767 之間不同,Python 的整數可以表示任意整數(只要系統內存能存儲)。所以 Python 可以使用整數來解決紀元秒數中、浮點數精度的問題。
備註:1,000,000,000 納秒(即 1e9
納秒)等於 1 秒。
time 模塊中,包含各個時間屬性的對象。除基本的年、月、日、時、分、秒外,該對象還包含:
- 在一年中第幾個星期
- 是星期幾
- 在一年中第幾天
- 夏令時信息
- 時區信息
除此之外,該對象自帶的輸出方式較為多樣,可以簡單地把該對象按自定義格式轉化為字符串並輸出。
datetime 模塊中,包含各個時間屬性的對象。除基本的年、月、日、時、分、秒外,該對象還包含「微秒數」,從而它能表示比 time.struct_time 更精確的時間。但該方法對時區的支持較為繁瑣。
該對象在時間計算相關的支持較為豐富,比如,它支持直接求兩個時間的間隔。
該對象可以被認為是以下兩類對象的結合:
datetime 模塊中,表示日期的類型。
datetime 模塊中,表示時間的類型。
獲得紀元秒數。對閏秒的處理基於具體平台。Windows 和大多數 Unix 系統均忽略閏秒。
獲得紀元「納秒數」,使用它的好處是不會因浮點數捨入而丟失精度。
把紀元秒數轉換為形如 "Thu Jan 11 00:00:00 1970"
的內容。
如果不輸入 t
,則使用當前時間生成。如果日期僅有 1 位,則會用空格進行縮進,比如:"Thu Jan 1 00:00:00 1970"
可以通過索引和屬性名來訪問 time.struct_time 實例的內容。這些內容均為只讀,不可修改。
索引 | 屬性 | 值 |
---|---|---|
0 | tm_year | 整數,比如 1970 |
1 | tm_mon | 1 ~ 12 的整數 |
2 | tm_day | 1 ~ 31 的整數 |
3 | tm_hour | 0 ~ 23 的整數 |
4 | tm_min | 0 ~ 59 的整數 |
5 | tm_sec | 0 ~ 61 的整數 60 代表閏秒,而 61 是歷史遺留問題 |
6 | tm_wday | 0 ~ 6 的整數 其中,周一為 0 |
7 | tm_yday | 1 ~ 366 的整數 |
8 | tm_isdst | 夏令時信息: 0 代表不實行夏令時 1 代表實行夏令時 -1 代表未知,會按情況自動判斷 |
N/A | tm_zone | 時區名稱的縮寫,比如「中國標準時間」 |
N/A | tm_gmtoff | 以秒為單位的時區信息,比如 28800。 28800 秒是 8 小時,即 UTC+8 時區。 |
這是 time.struct_time 與各種其它類型或表示方式之間的轉化
類型 | 轉換 | 注釋 | |
---|---|---|---|
轉換為 time.struct_time | 從 time.struct_time 轉換 | ||
當前時間 | 可以 | 沒必要 | 用當前時間生成對應的 time.struct_time 實例。 可以選擇生成零時區的對象或本地時間的對象。 |
紀元秒數 | 可以 | 可以 | time.struct_time 和表示紀元秒數的浮點數之間的轉換。 可以選擇轉換為零時區的對象或本地時間的對象。 |
字符串格式化 | 可以 | 可以 | 「字符串格式化」指的是讀者自定義的輸出方式, 它會輸出形如 "Thu Jan 11 00:00:00 1970" 的字符串。其使用方法和 printf 式字符串格式化相似。 |
屬性 | 值 |
---|---|
year | 1 ~ 9999 之間的整數 |
month | 1 ~ 12 之間的整數 |
day | 對於不同月份,為1 ~ 28 至 1 ~ 31 之間的整數 |
hour | 0 ~ 23 之間的整數 |
minute | 0 ~ 59 之間的整數 |
second | 0 ~ 59 之間的整數 |
microsecond | 0 ~ 999999 之間的整數 |
tzinfo | 時區信息對象 |
fold | 用於夏令時等導致時間重複的情況。 |
本章將把 datetime.datetime、datetime.date、datetime.time 放在一起講。
在 datetime 模塊中有 datetime 類,又有 date 類——而 datetime 類的實例又有 date 方法。datetime.datetime.date 和 datetime.date 是完全不同的兩個東西。在實際閱讀代碼時,如此多的重複文字容易造成混淆。因此,為防止混淆以及簡便起見,本章節以下的內容均使用了 import datetime as dt
。
dt.datetime 的各個屬性也是只讀的。我們可以見右側表格。
tzinfo 是時區信息,時區信息的詳細內容將在下面講述。
fold 屬性是用於處理時間重複的情況的。以美國為例,因夏令時等原因,導致 2024年11月3日13:00 出現兩次的情況,則以 0 標記第一次對應的時間,而以 1 標記第二次對應的時間。
在中國,就算是需要按季節區分作息時間的情況,也有「夏季作息時間表」(而非夏令時)。我們一般不需要考慮這些內容——讓 fold 留空即可。
dt.date 僅有以上屬性中的年月日。而 dt.time 僅有以上屬性中、年月日以外的部分。
除了屬性給出的信息之外,我們還可以獲取以下信息:
信息 | 獲取信息使用的實例方法 | 注釋 | |
---|---|---|---|
dt.datetime | dt.date | ||
星期 | dt.datetime.weekday() | dt.date.weekday() | 返回一個整數:星期一為0,星期天為6。 |
ISO 星期 | dt.datetime.isoweekday() | dt.date.isoweekday() | 返回一個整數:星期一為1,星期天為7 |
以下是 dt 模塊中已經給出的轉化方法。標註「不可以」的是沒有直接給出的轉化方法,必須用多個步驟才能進行轉化。
類型 | 轉化為 dt 中類型的實例 | 由 dt 中類型的實例轉化 | 注釋 | ||||
---|---|---|---|---|---|---|---|
dt.datetime | dt.date | dt.time | dt.datetime | dt.date | dt.time | ||
當前時間 | 可以 | 可以 | 不可以 | 沒必要 | 沒必要 | 沒必要 | 用當前時間生成對應的實例 |
紀元秒數 | 可以 | 可以 | 不可以 | 可以 | 不可以 | 不可以 | |
ISO 8601 字符串 | 可以 | 可以 | 可以 | 可以 | 可以 | 可以 | 特別的,使用str()函數來轉換,對 dt.date 和 dt.time 也會返回相同的結果。對 dt.datetime,則會把日期部分和時間部分之間的分隔符改為空格。 |
屬性 | 可以 | 可以 | 可以 | 直接調用屬性即可 | 輸入各個屬性的數值,生成對應的對象 | ||
天數序號 | 可以 | 可以 | 沒必要 | 可以 | 可以 | 沒必要 | 輸入自公元元年以來的天數,返回對應的 dt.datetime 對象。 |
time.struct_time | 可以 | 可以 | 不可以 | 不可以 | 不可以 | 不可以 | 輸入自公元元年以來的天數,返回對應的 dt.datetime 對象。 |
字符串格式化 | 可以 | 不可以 | 不可以 | 可以 | 可以 | 可以 | 特別的,這些對象除 printf 式方法外,還支持 str.format 方法。 |
ISO 周曆 | 可以 | 可以 | 沒必要 | 可以 | 可以 | 沒必要 | 這裡「ISO 周曆」指的是 ISO 8601 標準下,星期表示法提供的年、周、星期三個數字。 |
類型 | 修改的對象 | 注釋 | ||
---|---|---|---|---|
dt.datetime | dt.date | dt.time | ||
屬性 | 可以 | 可以 | 可以 | 對已有實例輸入屬性和數值,返回屬性被修改後的副本 |
時區 | 可以 | 不可以 | 不可以 | 返回表示相同時間,但時區不同的對象 |
classmethod dt.datetime.combine(date, time, tzinfo=time.tzinfo)
將 dt.date 對象和 dt.time 對象組合成 dt.datetime 對象。組合時,可以額外設置一次時區信息。除此之外,我們還有以下方法。
方法 | 結果 |
---|---|
dt.datetime.date() | 返回對應 dt.datetime 實例的 dt.date 對象。 |
dt.datetime.time() | 返回對應 dt.datetime 實例的 dt.time 對象,時區信息為 None 。
|
dt.datetime.timetz() | 返回對應 dt.datetime 實例的 dt.time 對象,保留時區信息。 |
屬性名 | 解釋 | 範圍 |
---|---|---|
days | 天數 | -99999999 到 999999999 之間的整數 |
seconds | 秒數 | 0 到 86399 之間的整數 (一天為 86400 秒) |
microseconds | 微秒數 | 0 到 999999 之間的整數 |
dt 模塊為了對日期進行計算,設計了 dt.timedelta 類型來表示時間差。兩個 dt.datetime 實例相減,會輸出對應時間差的 dt.timedelta 對象。除此以外:
- dt.datetime 可以加上 dt.timedelta,來輸出時間後移 dt.timedelta 後、對應的 dt.datetime 實例
- dt.datetime 可以減去 dt.timedelta,來輸出時間前移 dt.timedelta 後、對應的 dt.datetime 實例。
dt.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
可以用以上函數創建對應的 dt.timedelta 實例。以上的毫秒、分鐘等都會被轉化為相等的屬性值來儲存。
運算 | 運算符 | 結果 |
---|---|---|
加法 | td1 + td2 | 返回相當於兩者之和的 dt.timedelta 對象。 |
減法 | td1 - td2 | 返回相當於兩者之差的 dt.timedelta 對象。 |
整數乘法 | td * t | 返回相當於 td 重複 t 次的 dt.timedelta 對象。 |
浮點乘法 | td * t | 類似整數乘法,但是結果會被捨入。 如果被捨入的首位是五、按照偶入奇不入規則。 |
數字除法 | td / t | 等同於 td * (1 / t) |
帶餘除法 求商 |
td1 // td2 | |
帶餘除法 求模 |
td1 % td2 | 返回一個 dt.timedelta 對象 |
帶餘除法 | divmod(td1, td2) | 返回一個 (商, 模) 的元組。 |
正 | +td | 返回 td |
負 | -td | 返回 dt.timedelta(-td.days, -td.seconds, -td.microseconds) |
絕對值 | abs(td) | 若 td.days >= 0 返回 td。反之,返回 -td |
str | str(td) | 返回形如 [D day[s], ][H]H:MM:SS[.UUUUUU] 的字符串當 td.days < 0 時,D 也小於零。 |
repr | repr(td) | 若 td.days >= 0 返回 td。反之,返回 -td |
除了和 dt.datetime 進行加減法計算之外,dt.timedelta 還可以進行其它計算。
在使用右側運算時,要注意使計算結果不要超出 dt.timedelta 的限制。
兩個 dt.datetime 實例之間可以進行比較運算。時間上靠後的實例 > 時間上靠前的實例。
dt.datetime 的相等是時間上的相等,並不需要兩者的所有屬性是完全相同的。尤其是時區不同的情況,比如:
import datetime as dt
dt1 = dt.datetime(1970,1,1,8,tzinfo=dt.timezone(
dt.timedelta(hours=8),name="中国标准时间"))
dt2 = dt.datetime(1970,1,1,8,tzinfo=dt.timezone(
dt.timedelta(hours=8)))
dt3 = dt.datetime(1970,1,1,0,tzinfo=dt.timezone(
dt.timedelta(0)))
dt4 = dt.datetime(1970,1,1,0)
dt5 = dt.datetime(1970,1,1,0)
print(dt1 == dt2 == dt3) #输出:True
print(dt1 == dt4) #输出:False
print(dt4 == dt5) #输出:False
要注意,未設置 tzinfo
的對象會被視為「時區未知」,在與「已知時區」的對象進行「等於運算」時,總會輸出 False
。
關於時區信息的設置,參見下面的章節。
dt.date 也支持上面 dt.datetime 的運算。把上面的 dt.datetime 實例全換成 dt.date 實例是不會出錯的。
但有幾點需要注意:
- dt.date 在運算時,被視為是 dt.date 所對應當天的 00:00:00。
- dt.date 和 dt.timedelta 相加/相減時,會輸出「結果所在天數的」dt.date 對象。
- dt.date 不能和 dt.datetime 作上述運算。
而 dt.time 不支持上面的加、減、大於、小於等運算。它只支持等於 / 不等於運算。在運算時,時區名稱以外的部分必須要對應相等,等於運算的結果才為 True
。時區名稱在屬性 tzinfo 中。
dt.tzinfo 是時區信息的抽象基類。有以下方法的,被視為是 dt.tzinfo 的子類型。
- utcoffset()
返回時區與 UTC 零時區之間時間差的 dt.timedelta 對象。 - dst()
如果使用夏令時,返回夏令時信息(比如夏令時調快1小時即為dt.timedelta(hours = 1)
)
如果不使用夏令時,返回dt.timedelta(0)
如果未知,返回None
- tzname()
返回時區名稱的字符串。如果未知,返回None
dt 模塊中有一個 dt.tzinfo 抽象基類的簡單子類:dt.timezone。該類型不能處理夏令時。
創建該類型的方法,就是將表示與 UTC 零時區時差的 dt.timedelta 對象和可選的時區名稱傳入 dt.timezone 類型構造器即可。
除了調用 dt.datetime.tzinfo 屬性的方法之外,我們可以用同名方法,從 dt.datetime 類型中獲取更多信息。
特別的,如果 tzinfo 屬性為 None
,這些方法都會返回 None
類型 | 獲取信息使用的實例方法 | 注釋 | |
---|---|---|---|
dt.datetime | dt.time | ||
UTC時間差 | dt.datetime.utcoffset() | dt.time.utcoffset() | 返回本地時間與零時區時間差的 dt.timedelta 對象 |
夏令時 | dt.datetime.dst() | dt.time.dst() | 返回夏令時信息的 dt.timedelta 對象 |
時區名稱 | dt.datetime.tzname() | dt.time.tzname() | 返回時區名稱 |
除去日期與時間的表示與計算之外,還有許多其它功能會十分實用。
以下函數返回的數值沒有嚴格定義的參考點,所以這些函數的用法都是兩次返回值作減法、從而得到中間經過的時間。
返回一個以秒為單位的時鐘的值,該值不受系統時鐘的影響。
類似 time.monotonic
,但單位是納秒,且不會有精度丟失。
類似 time.monotonic
,但使用的時鐘為性能計數器——它用於測量較短的持續時間,且具有最高有效精度。它會包含系統睡眠時間。
類似 time.perf_counter
,但單位是納秒,且不會有精度丟失。
返回當前進程的系統 CPU 時間和用戶 CPU 時間的總和。不包括睡眠時間,且只作用於進程範圍。返回值沒有嚴格定義的參考點。
類似 time.process_time
,但單位是納秒,且不會有精度丟失。
使當前程序暫停 secs
秒。