1. 程式人生 > >python中datetime模組詳解

python中datetime模組詳解

timedelta

timedelta的例項化

一個timedalta物件代表了一個時間差,當兩個datedatetime進行相減操作時會返回一個timedelta物件,或者,我們也可以手動對其進行例項化,其建構函式的原型如下:

class datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]])

其中,所有的引數都是可選的,並且預設為0,一般情況下,我們常用的是其中三個引數dayssecondsmicroseconds,如果我們傳遞了其他的幾個引數值,python會幫助我們自動轉換成上面三個引數,轉換的規則是:

  • 1 millisecond(毫秒) 轉換成 1000 microseconds(微秒)
  • 1 minute 轉換成 60 seconds
  • 1 hour 轉換成 3600 seconds
  • 1 week轉換成 7 days

如果我們在例項化的時候直接傳遞的是上面三個引數值,那麼也要注意下它們的取值範圍:

  • 0 <= microseconds < 1000000
  • 0 <= seconds < 3600*24 (一天的秒數)
  • -999999999 <= days <= 999999999

那麼,如果我們在傳遞這三個引數的時候超出了這個範圍會有什麼問題嗎,答案是不一定,例如:

>>> tmp = datetime.timedelta(seconds=86400)
>>> tmp.days
1
>>> tmp.seconds
0

可以看到,如果超過範圍,python是會幫我們自動轉換的,但是如果days引數超出範圍會有什麼結果呢?

>>> ee = datetime.timedelta(days=1000000000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: days=1000000000; must have magnitude <= 999999999

所以,只有days是不能超出範圍的,否則會丟擲OverflowError異常。

在例項化的時候,引數不僅僅可以是整數,也可以是浮點數、正數或者負數,當引數為負數的時候,要特別注意,因為生成的樣式也許與我們設想的不太一致

>>> tmp = datetime.timedelta(microseconds=-1)
>>> tmp.days, tmp.seconds, tmp.microseconds
(-1, 86399, 999999)

所以,我們傳遞引數的時候,儘量避免傳遞負數的情況,同樣,我們也應該極力避免傳遞的引數為浮點數,我們在使用的時候一般以秒作為單位就能滿足99%的需求了。

timedelta的運算

+ 操作

>>> t1 = datetime.timedelta(seconds=60)
>>> t2 = datetime.timedelta(seconds=30)
>>> t3 = t1 + t2
>>> t3.seconds
90

- 操作

>>> t4 = t1 -t2
>>> t4.seconds
30

* 操作

>>> t5 = t1 * 2
>>> t5
datetime.timedelta(0, 120)
>>> t6 = t1 * 0
>>> t6
datetime.timedelta(0)

///操作

>>> t7 = t1 / 3
>>> t7
datetime.timedelta(0, 20)
>>> t7 = t1 / 7
>>> t7
datetime.timedelta(0, 8, 571428)
>>> t7 = t1 // 7
>>> t7
datetime.timedelta(0, 8, 571428)

注意這裡的被除數不能是0,否則會丟擲ZeroDivisionError

比較操作

>>> t1 > t2
True
>>> t1 < t2
False
>>> t1 == t2
False
>>> t1 == 60
False
>>> t1 != 60
True
>>> t1 > 60
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare datetime.timedelta to int

可以看到,兩個timedelta物件可以直接進行比較操作,而一個timedelta物件與一個非timedelta物件進行==!=操作時總是返回False,而進行><操作則會丟擲TypeError

其他操作

>>> +t1
datetime.timedelta(0, 60)
>>> -t1
datetime.timedelta(-1, 86340)

// 返回的格式為[D day[s], ][H]H:MM:SS[.UUUUUU]
>>> str(t1)
'0:01:00'
>>> t1
datetime.timedelta(0, 60)
>>> str(-t1)
'-1 day, 23:59:00'

>>> repr(t1)
'datetime.timedelta(0, 60)'

其他

在2.7版本後,新增了一個方法timedelta.total_seconds()用於計算秒數,它等價於(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6

>>> t1
datetime.timedelta(0, 60)
>>> t1.total_seconds()
60.0

date

一個date物件代表使用年、月、日表示的時間,我們可以只用年、月、日三個值直接構造一個date物件,且這三個引數缺一不可,它們的取值範圍如下

  • MINYEAR <= year <= MAXYEAR
  • 1 <= month <= 12
  • 1 <= day <= 給定年月的天數

如果我們傳遞的引數超出這個範圍,將會丟擲ValueError異常。除了手動傳入年、月、日來構造date物件外,系統還提供了靜態方法,我們可以使用這些靜態方法來方便的得到一個date物件

  • date.today()
    返回當前的本地時間,等價於date.fromtimestamp(time.time())

  • date.fromtimestamp(timestamp)

當得到date物件後,就可以直接訪問它的年、月、日屬性了

>>> today = date.today()
>>> today
datetime.date(2016, 9, 1)
>>> today.year
2016
>>> today.month
9
>>> today.day
1

運算與比較操作

>>> delta = timedelta(days=7)
>>> future = today + delta
>>> future
datetime.date(2016, 9, 8)
>>> future - delta
datetime.date(2016, 9, 1)
>>> future - today
datetime.timedelta(7)
>>> future > today
True

date可以作為字典的key,並且,所有的date物件都會被認為是True的,也就是 if date 這個判斷永遠是成立的。

例項方法

  • date.replace(year, month, day)
>>> d = date(2002, 12, 31)
>>> d
datetime.date(2002, 12, 31)
>>> d.replace(day=26)
datetime.date(2002, 12, 26)
  • date.weekday()
    返回一週的禮拜幾,用int值表示,從0開始
  • date.isoweekday()
    返回一週的禮拜幾,用int值表示,從1開始
  • date.isoformat()
>>> d.isoformat()
'2002-12-31'

datetime

datetime從字面意思上看是datetime的結合,而實際上也是包含了這兩個物件的全部資訊,我們可以手動構造datetime物件,也可以使用系統提供的靜態方法,當我們手動構造的時候,必須要傳入yearmonthday三個引數,他們的取值範圍與上面講到的date物件一致。

我們在處理時間問題時,始終無法迴避的是時區問題,在python中,使用datetime.tzinfo來表示時區,但這是一個抽象基類,python也並沒幫我們實現任意的時區,因此,我們首先來看下在不涉及時區的時候datetime都有哪些用法,然後我們會建立一個本地時區,也就是東八區,來說明datetime如何與tzinfo結合使用。

datetime物件的建立

我們可以通過指定年月日的形式來手動建立一個datetime例項

>>> cur = datetime(year=2016, month=9, day=2)
>>> cur
datetime.datetime(2016, 9, 2, 0, 0)

也可以直接通過靜態方法來獲的

>>> datetime.today()
datetime.datetime(2016, 9, 2, 17, 50, 21, 810692)
>>> datetime.now()
datetime.datetime(2016, 9, 2, 17, 50, 34, 89763)

這兩個方法都會返回一個本地當前時間,也就是上面的兩個寫法是等效的,但是要注意的是,now()方法可以傳入時區資訊,我們稍後再一起討論。

除了上面的方法,我們還可以通過時間戳來獲取一個物件

>>> datetime.fromtimestamp(time.time())
datetime.datetime(2016, 9, 2, 17, 54, 56, 65907)

我們這裡同樣暫不考慮時區的問題。
除了通過時間戳獲取datetime例項外,我們還可以通過一個格式化的時間字串來獲得例項,這個方法同樣是不帶時區資訊的

>>> datetime.strptime('2016-09-02 18:00:00', '%Y-%m-%d %H:%M:%S')
datetime.datetime(2016, 9, 2, 18, 0)

既然一個datetime物件包含了datetime的所有資訊,那麼能不能通過這兩物件來生成一個datetime例項呢,答案是肯定的

>>> d = date(year=2016, month=9, day=2)
>>> d
datetime.date(2016, 9, 2)
>>> t = time(hour=18, minute=14)
>>> t
datetime.time(18, 14)
>>> datetime.combine(d, t)
datetime.datetime(2016, 9, 2, 18, 14)

現在datetime也有了,我們總要在它身上做點什麼吧,能做什麼呢?

常用方法介紹

有了datetime物件之後,我們就可以獲得對應的datetime物件了

>>> today = datetime.today()
>>> today.date()
datetime.date(2016, 9, 2)
>>> today.time()
datetime.time(18, 30, 10, 304536)

那如果我希望得到下個月的今天,該怎麼做呢?方法有多種,但是最方便的是直接把月份進行加一操作(不考慮跨年和日期超過月份最大值的情況),這個時候replace函式就派上用場了

>>> next_month = today.replace(month=today.month+1)
>>> next_month
datetime.datetime(2016, 10, 2, 18, 30, 10, 304536)

replace()方法允許我們對datetime的任意欄位進行替換,並返回一個新的datetime物件,這個新的物件在其他欄位上與原有物件保持一致。

除此之外,還有一個比較常用的方法strftime(),通過它可以格式化成我們希望的樣式

>>> next_month.strftime('%Y-%m-%d %H:%M:%S')
'2016-10-02 18:30:10'

還有一種情況,是我們希望將一個datetime物件轉成時間戳,很遺憾的是python並沒直接提供這個方法,但是提供了一個timetuple()方法,它返回一個time.struct_time物件,通過它我們可以構造出時間戳了

>>> dd = datetime.today()
>>> tt = time.mktime(dd.timetuple())
>>> print int(tt)
1472816083

時區的定義

python中給我們提供了datetime.tzinfo這一抽象的基類,如果我們想使用時區,則必須繼承這個類來實現自己的時區定義。我們先來看下都有哪些方法可能需要我們來實現

  • tzinto.utcoffset(self, dt)

這個方法返回本地時間與UTC時間的時差,我們知道,我們國家使用的是東八區,也就是比世界協調時間(UTC)/格林尼治時間(GMT)快8小時的時區,因此我們可以如下實現這個方法

def utcoffset(self, dt):
    return timedelta(hours=8)
  • tzinfo.dst(self, dt)

這個方法主要是考慮到一些採用夏令時的國家,在固定月份來調整時間,而我們國家是沒有采用夏令時,所以直接返回0

def dst(self, dt):
    return timedelta(0)

而對於採納了夏令時的國家,則需要把夏令時考慮進去

def dst(self, dt):
    # Code to set dston and dstoff to the time zone's DST
    # transition times based on the input dt.year, and expressed
    # in standard local time.  Then

    if dston <= dt.replace(tzinfo=None) < dstoff:
        return timedelta(hours=1)
    else:
        return timedelta(0)

對於這些國家,我還要修改utcoffset函式,將夏令時的偏移量考慮進去

def utcoffset(self, dt):
    return timedelta(hours=N) + self.dst(dt)
  • tzinfo.tzname(self, dt)

這個方法用來返回時區的名稱,沒有太多可說的

一般情況下,我們只要實現上面三個方法就可以了,例如我們可以這樣定義一個UTC時區和我們所在的東八區

ZERO = timedelta(0)
HOUR = timedelta(hours=1)

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO


class GMT8(tzinfo):
    """東八區""

    def utcoffset(self, dt):
        return HOUR * 8

    def tzname(self, dt):
        return 'GMT-8'

    def dst(self, dt):
        return ZERO

除了我們自己來定義時區外,我們還可以使用pytz這個模組,通過easy_install命令直接安裝即可。

datetime與時區的結合使用

我們上面已經介紹了在不考慮時區因素的時候,datetime的一些簡單用法,接下來看下加入時區後,有哪些不一樣的地方,以及我們需要注意的地方。

我們在上面提到了類方法now,在不考慮時區的時候,它的作用和類方法today基本是一致的,我們也可以在使用now的時候傳遞一個tzinfo,如下的兩種方式是等效的

today = datetime.today()
today = today.replace(tzinfo=GMT8())
// 等效於
today = datetime.now(GMT8())

除了now()方法外,系統還提供了utcnow()方法,這個方法沒有引數,返回的是UTC的時間,但是要注意的是,這個方法建立的datetime物件同樣是不帶時區資訊的。

到目前為止,我們都還不知道時區到底有什麼作用,假設說我們現在有這樣的需求:在資料庫中記錄的時間全部採用UTC時間,而在展示的時候需要轉換成本地時間。我們在查庫後,一些ORM(例如:SQLAlchemy)會自動幫我們將時間值轉化成datetime物件,但這些datetime都是不帶時區資訊的,如果我們想轉化成本地時間則必須要建立本地的時區

today = datetime.utcnow()
today = today.replace(tzinfo=UTC())
print today //2016-09-06 06:47:42.665574+00:00

today = today.astimezone(GMT8())
print today //2016-09-06 14:47:42.665574+08:00

如上所示,系統提供了datetime.astimezone(tz)方法,使用這個方法可以在各個時區之間來回轉換時間。這裡有兩點需要注意:

  • 如果datetime物件本身沒有包含時區資訊,呼叫這個方法會丟擲ValueError,並提示astimezone() cannot be applied to a naive datetime
  • replace(tzinfo=...)方法只會替換tzinfo的值,並不會更改時分秒等時間資訊

最後,還有兩個通過時間戳獲取datetime的類方法,datetime.fromtimestamp(timestamp[, tz])datetime.utcfromtimestamp(timestamp)utcfromtimestamp得到的是一個UTC時間,並且不帶tzinfo資訊,除此之外,兩者並無太大區別。

到這裡,datetime模組的大部分方法就介紹完了,還有一些我們不是很常用的方法,大家可以自行看看文件瞭解下就好。

2368004-a5c3f1608cf87ceb.jpg 這樣學機器學習