跳至內容

使用者:Xyy23330121/Python/日期與時間

來自維基學院


許多程序需要處理日期與時間。Python 提供了兩個模塊可以用於處理時間。

本頁面的內容太多,所以大部分內容都被改為了超鏈接索引。如果讀者需要詳細了解一些信息,請直接點擊表格中或文章中的超鏈接。

時間的儲存方式

[編輯 | 編輯原始碼]

紀元秒數(UNIX 時間戳)

[編輯 | 編輯原始碼]

許多平台都會使用紀元秒數來存儲時間。紀元秒數是自 1970 年 1 月 1 日 00:00:00 以來,經過的秒數(用浮點數表示)。簡單來講:0.0 代表 1970 年 1 月 1 日 00:00:00,而 1.0 代表 1970 年 1 月 1 日 00:00:01 。

添加閏秒的條件(選讀)
閏秒是 UTC 時間標準的一部分,它用於調節經由原子鐘測量的精確時間與太陽時的誤差。一天是 原子秒(即 秒),但地球自轉有時會不可預知地慢幾十毫秒。當地球自轉 天花費的時間大於 秒時,就應當在第 天加上一秒。
在實際操作時,則是順延到月份的最後一天再插入閏秒。

這一記錄方法通常是不包含閏秒的。閏秒是 UTC 標準下、對滿足一定條件的一天、在其最後最後添加一秒的規則。在所有符合 POSIX 標準的平台上,閏秒都不會被記錄在總秒數中。比如:

  • 2016 年 12 月 31 日有閏秒,使得比如 2016 年 12 月 31 日 23:59:60 的時間是存在的(而非錯誤地沒有進位)。而在 POSIX 標準的平台中,這一秒被直接忽略掉——根本不表示這一秒之間的時間。

ISO 8601 標準

[編輯 | 編輯原始碼]

時間還可以由 ISO 8601 標準給出的的字符串來儲存和進行交流。

Python 儲存時間的方式

[編輯 | 編輯原始碼]

除上述方法以外,Python 還通過幾種方式儲存時間。這裡簡單列出這些方法的特徵,讀者應按照自身需求來選擇對應的方法。

納秒數

[編輯 | 編輯原始碼]

和其它程序語言中,整數在 -32768 ~ 32767 之間不同,Python 的整數可以表示任意整數(只要系統內存能存儲)。所以 Python 可以使用整數來解決紀元秒數中、浮點數精度的問題。

備註:1,000,000,000 納秒(即 1e9 納秒)等於 1 秒。

time.struct_time

[編輯 | 編輯原始碼]

time 模塊中,包含各個時間屬性的對象。除基本的年、月、日、時、分、秒外,該對象還包含:

  1. 在一年中第幾個星期
  2. 是星期幾
  3. 在一年中第幾天
  4. 夏令時信息
  5. 時區信息

除此之外,該對象自帶的輸出方式較為多樣,可以簡單地把該對象按自定義格式轉化為字符串並輸出。

datetime.datetime

[編輯 | 編輯原始碼]

datetime 模塊中,包含各個時間屬性的對象。除基本的年、月、日、時、分、秒外,該對象還包含「微秒數」,從而它能表示比 time.struct_time 更精確的時間。但該方法對時區的支持較為繁瑣。

該對象在時間計算相關的支持較為豐富,比如,它支持直接求兩個時間的間隔。

該對象可以被認為是以下兩類對象的結合:

datetime.date

[編輯 | 編輯原始碼]

datetime 模塊中,表示日期的類型。

datetime.time

[編輯 | 編輯原始碼]

datetime 模塊中,表示時間的類型。

紀元秒數相關方法

[編輯 | 編輯原始碼]

獲取當前時間

[編輯 | 編輯原始碼]

time.time() -> float

[編輯 | 編輯原始碼]

獲得紀元秒數。對閏秒的處理基於具體平台。Windows 和大多數 Unix 系統均忽略閏秒。

time.time_ns() -> int

[編輯 | 編輯原始碼]

獲得紀元「納秒數」,使用它的好處是不會因浮點數捨入而丟失精度。

轉換為字符串

[編輯 | 編輯原始碼]

time.ctime([secs])

[編輯 | 編輯原始碼]

把紀元秒數轉換為形如 "Thu Jan 11 00:00:00 1970" 的內容。

如果不輸入 t,則使用當前時間生成。如果日期僅有 1 位,則會用空格進行縮進,比如:"Thu Jan 1 00:00:00 1970"

time.struct_time 的使用

[編輯 | 編輯原始碼]

time.struct_time 的屬性

[編輯 | 編輯原始碼]

可以通過索引和屬性名來訪問 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 實例。
可以選擇生成零時區的對象或本地時間的對象。
紀元秒數 可以 可以 time.struct_time 和表示紀元秒數的浮點數之間的轉換。
可以選擇轉換為零時區的對象或本地時間的對象。
字符串格式化 可以 可以 「字符串格式化」指的是讀者自定義的輸出方式,
它會輸出形如 "Thu Jan 11 00:00:00 1970" 的字符串。
其使用方法和 printf 式字符串格式化相似。

datetime.datetime 的使用

[編輯 | 編輯原始碼]
dt.datetime 的屬性
屬性
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 的屬性

[編輯 | 編輯原始碼]

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.datetime 的轉化

[編輯 | 編輯原始碼]

以下是 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.datetime dt.date dt.time
屬性 可以 可以 可以 對已有實例輸入屬性和數值,返回屬性被修改後的副本
時區 可以 不可以 不可以 返回表示相同時間,但時區不同的對象

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 對象,保留時區信息。
dt.timedelta 的屬性
屬性名 解釋 範圍
days 天數 -99999999 到 999999999
之間的整數
seconds 秒數 0 到 86399 之間的整數
(一天為 86400 秒)
microseconds 微秒數 0 到 999999 之間的整數

dt.datetime 的加減運算

[編輯 | 編輯原始碼]

dt 模塊為了對日期進行計算,設計了 dt.timedelta 類型來表示時間差。兩個 dt.datetime 實例相減,會輸出對應時間差的 dt.timedelta 對象。除此以外:

  1. dt.datetime 可以加上 dt.timedelta,來輸出時間後移 dt.timedelta 後、對應的 dt.datetime 實例
  2. dt.datetime 可以減去 dt.timedelta,來輸出時間前移 dt.timedelta 後、對應的 dt.datetime 實例。

創建 dt.timedelta

[編輯 | 編輯原始碼]

dt.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

可以用以上函數創建對應的 dt.timedelta 實例。以上的毫秒、分鐘等都會被轉化為相等的屬性值來儲存。

dt.timedelta 的運算

[編輯 | 編輯原始碼]
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 實例之間可以進行比較運算。時間上靠後的實例 > 時間上靠前的實例。

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.time 的運算

[編輯 | 編輯原始碼]

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 是時區信息的抽象基類。有以下方法的,被視為是 dt.tzinfo 的子類型。

  1. utcoffset()
    返回時區與 UTC 零時區之間時間差的 dt.timedelta 對象。
  2. dst()
    如果使用夏令時,返回夏令時信息(比如夏令時調快1小時即為 dt.timedelta(hours = 1)
    如果不使用夏令時,返回dt.timedelta(0)
    如果未知,返回 None
  3. tzname()
    返回時區名稱的字符串。如果未知,返回 None

class dt.timezone(offset, name=None)

[編輯 | 編輯原始碼]

dt 模塊中有一個 dt.tzinfo 抽象基類的簡單子類:dt.timezone。該類型不能處理夏令時。

創建該類型的方法,就是將表示與 UTC 零時區時差的 dt.timedelta 對象和可選的時區名稱傳入 dt.timezone 類型構造器即可。

從 dt.datetime 直接獲取時區信息

[編輯 | 編輯原始碼]

除了調用 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() -> float

[編輯 | 編輯原始碼]

返回一個以秒為單位的時鐘的值,該值不受系統時鐘的影響。

time.monotonic_ns() -> int
[編輯 | 編輯原始碼]

類似 time.monotonic,但單位是納秒,且不會有精度丟失。

time.perf_counter() -> float

[編輯 | 編輯原始碼]

類似 time.monotonic,但使用的時鐘為性能計數器——它用於測量較短的持續時間,且具有最高有效精度。它會包含系統睡眠時間。

time.perf_counter_ns() -> float
[編輯 | 編輯原始碼]

類似 time.perf_counter,但單位是納秒,且不會有精度丟失。

time.process_time() -> float

[編輯 | 編輯原始碼]

返回當前進程的系統 CPU 時間和用戶 CPU 時間的總和。不包括睡眠時間,且只作用於進程範圍。返回值沒有嚴格定義的參考點。

time.process_time_ns() -> int
[編輯 | 編輯原始碼]

類似 time.process_time,但單位是納秒,且不會有精度丟失。

暫停進程

[編輯 | 編輯原始碼]

time.sleep(secs)

[編輯 | 編輯原始碼]

使當前程序暫停 secs 秒。

參考文獻

[編輯 | 編輯原始碼]