一个timedalta
对象代表了一个时间差,当两个date
或datetime
进行相减操作时会返回一个timedelta
对象,或者,我们也可以手动对其进行实例化,其构造函数的原型如下:
class datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]])
其中,所有的参数都是可选的,并且默认为0,一般情况下,我们常用的是其中三个参数days
、seconds
、microseconds
,如果我们传递了其他的几个参数值,python会帮助我们自动转换成上面三个参数,转换的规则是:
如果我们在实例化的时候直接传递的是上面三个参数值,那么也要注意下它们的取值范围:
那么,如果我们在传递这三个参数的时候超出了这个范围会有什么问题吗,答案是不一定,例如:
>>> tmp = datetime.timedelta(seconds=86400)
>>> tmp.days
1
>>> tmp.seconds
0
可以看到,如果超过范围,python是会帮我们自动转换的,但是如果days
参数超出范围会有什么结果呢?
>>> ee = datetime.timedelta(days=1000000000)
Traceback (most recent call last):
File "", line 1, in
OverflowError: days=1000000000; must have magnitude <= 999999999
所以,只有days
是不能超出范围的,否则会抛出OverflowError
异常。
在实例化的时候,参数不仅仅可以是整数,也可以是浮点数、正数或者负数,当参数为负数的时候,要特别注意,因为生成的样式也许与我们设想的不太一致
>>> tmp = datetime.timedelta(microseconds=-1)
>>> tmp.days, tmp.seconds, tmp.microseconds
(-1, 86399, 999999)
所以,我们传递参数的时候,尽量避免传递负数的情况,同样,我们也应该极力避免传递的参数为浮点数,我们在使用的时候一般以秒作为单位就能满足99%的需求了。
+
操作
>>> 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 "", line 1, in
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
对象,且这三个参数缺一不可,它们的取值范围如下
如果我们传递的参数超出这个范围,将会抛出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 这个判断永远是成立的。
>>> d = date(2002, 12, 31)
>>> d
datetime.date(2002, 12, 31)
>>> d.replace(day=26)
datetime.date(2002, 12, 26)
>>> d.isoformat()
'2002-12-31'
datetime
从字面意思上看是date
和time
的结合,而实际上也是包含了这两个对象的全部信息,我们可以手动构造datetime
对象,也可以使用系统提供的静态方法,当我们手动构造的时候,必须要传入year
、month
和day
三个参数,他们的取值范围与上面讲到的date
对象一致。
我们在处理时间问题时,始终无法回避的是时区问题,在python中,使用datetime.tzinfo
来表示时区,但这是一个抽象基类,python也并没帮我们实现任意的时区,因此,我们首先来看下在不涉及时区的时候datetime
都有哪些用法,然后我们会创建一个本地时区,也就是东八区,来说明datetime
如何与tzinfo
结合使用。
我们可以通过指定年月日的形式来手动创建一个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
对象包含了date
和time
的所有信息,那么能不能通过这两对象来生成一个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
对象之后,我们就可以获得对应的date
和time
对象了
>>> 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
的一些简单用法,接下来看下加入时区后,有哪些不一样的地方,以及我们需要注意的地方。
我们在上面提到了类方法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
模块的大部分方法就介绍完了,还有一些我们不是很常用的方法,大家可以自行看看文档了解下就好。