time与datetime模块
在Python中,通常有这几种方式来表示时间:
- 时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。
- 格式化的时间字符串(Format String)
- 结构化的时间(struct_time --元组):struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)
#导入时间模块 >>>import time #时间戳 >>>time.time() 1500875844.800804 #时间字符串 >>>time.strftime("%Y-%m-%d %X") '2017-07-24 13:54:37' >>>time.strftime("%Y-%m-%d %H-%M-%S") '2017-07-24 13-55-04' #时间元组:localtime将一个时间戳转换为当前时区的struct_time time.localtime() time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=13, tm_min=59, tm_sec=37, tm_wday=0, tm_yday=205, tm_isdst=0) #格式化的时间字符串:'2017-02-15 11:40:53' print(time.strftime("%Y-%m-%d %X")) #本地时区的struct_time print(time.localtime()) #UTC时区的struct_time print(time.gmtime())
小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的
几种格式之间的转换
#时间戳-->结构化时间
#时间戳-->结构化时间 #time.gmtime(时间戳) #UTC时间,与英国伦敦当地时间一致 #time.localtime(时间戳) #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间 >>>time.gmtime(1500000000) time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) >>>time.localtime(1500000000) time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) #结构化时间-->时间戳 #time.mktime(结构化时间) >>>time_tuple = time.localtime(1500000000) >>>time.mktime(time_tuple) 1500000000.0
结构化时间-->字符串时间
#结构化时间-->字符串时间 #time.strftime("格式定义","结构化时间") 结构化时间参数若不传,则现实当前时间 >>>time.strftime("%Y-%m-%d %X") '2017-07-24 14:55:36' >>>time.strftime("%Y-%m-%d",time.localtime(1500000000)) '2017-07-14' #字符串时间-->结构化时间 #time.strptime(时间字符串,字符串对应格式) >>>time.strptime("2017-03-16","%Y-%m-%d") time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1) >>>time.strptime("07/24/2017","%m/%d/%Y") time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)
#结构化时间 --> %a %b %d %H:%M:%S %Y串 #time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串 >>>time.asctime(time.localtime(1500000000)) 'Fri Jul 14 10:40:00 2017' >>>time.asctime() 'Mon Jul 24 15:18:33 2017' #时间戳 --> %a %d %d %H:%M:%S %Y串 #time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串 >>>time.ctime() 'Mon Jul 24 15:19:07 2017' >>>time.ctime(1500000000) 'Fri Jul 14 10:40:00 2017'
#时间加减 import datetime # print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925 #print(datetime.date.fromtimestamp(time.time()) ) # 时间戳直接转成日期格式 2016-08-19 # print(datetime.datetime.now() ) # print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天 # print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天 # print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时 # print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分 # # c_time = datetime.datetime.now() # print(c_time.replace(minute=3,hour=2)) #时间替换
问题
你需要执行简单的时间转换,比如天到秒,小时到分钟等的转换。
可以创建一个timedelta 实例
>>> from datetime import timedelta >>> a = timedelta(days=2, hours=6) >>> b = timedelta(hours=4.5) >>> c = a + b >>> c.days 2 >>> c.seconds 37800 >>> c.seconds / 3600 10.5 >>> c.total_seconds() / 3600 58.5 >>>
表示指定的日期和时间
先创建一个datetime 实例然后使用标准的数学
运算来操作它们。
>>> from datetime import datetime >>> a = datetime.now() >>> a.year 2018 >>> a.day 19 >>> print(a + timedelta(days=10)) 2018-01-29 18:03:18.837018 >>> b = datetime(2012,12,21) >>> d = a-b >>> d datetime.timedelta(1855, 64998, 837018) >>> d.days 1855 >>> now = datetime.today() >>> now datetime.datetime(2018, 1, 19, 18, 6, 7, 306654) >>> print(now) 2018-01-19 18:06:07.306654
注意 :当以datetime 进行获取时间时,获取天数使用 obj.day ,但是当两个日期进行相减时,获取天数使用 obj.days
>>> e = datetime.now() >>> f = datetime.now() >>> e datetime.datetime(2018, 1, 19, 18, 22, 30, 380882) >>> f datetime.datetime(2018, 1, 19, 18, 22, 33, 405055) >>> a = f+e Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for +: 'datetime.dat >>> a = f-e >>> a.seconds 3 >>> a.days 0
>>> now = datetime.today()
>>> print(now)
2012-12-21 14:54:43.094063
>>> print(now + timedelta(minutes=10))
2012-12-21 15:04:43.094063 注意: 两个时间只能相减,只能获取到时间的天与秒 天:obj.days 秒:obj.seconds
在计算的时候,需要注意的是datetime 会自动处理闰年。比如:
>>> a = datetime(2012, 3, 1) >>> b = datetime(2012, 2, 28) >>> a-b datetime.timedelta(2) >>> c = datetime(2013, 3, 1) >>> d = datetime(2013, 2, 28) >>> c-d datetime.timedelta(1)
复杂的日期操作,比如处理时区,模糊时间范围,节假日计算等等,可以考虑使
用dateutil 模块
许多类似的时间计算可以使用dateutil.relativedelta() 函数代替。但是,有一
点需要注意的就是,它会在处理月份(还有它们的天数差距) 的时候填充间隙。看例子
最清楚:
>>> a = datetime(2012, 9, 23) >>> a + timedelta(months=1) Traceback (most recent call last): File "", line 1, in TypeError: 'months' is an invalid keyword argument for this function >>> >>> from dateutil.relativedelta import relativedelta >>> a + relativedelta(months=+1) datetime.datetime(2012, 10, 23, 0, 0) >>> a + relativedelta(months=+4) datetime.datetime(2013, 1, 23, 0, 0) >>> >>> # Time between two dates >>> b = datetime(2012, 12, 21) >>> d = b - a >>> d datetime.timedelta(89) >>> d = relativedelta(b, a) >>> d relativedelta(months=+2, days=+28) >>> d.months 2 >>> d.days 28 >>>
问题
你需要查找星期中某一天最后出现的日期,比如星期五。
from datetime import datetime, timedelta weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] def get_previous_byday(dayname, start_date=None): if start_date is None: start_date = datetime.today() day_num = start_date.weekday() day_num_target = weekdays.index(dayname) days_ago = (7 + day_num - day_num_target) % 7 if days_ago == 0: days_ago = 7 target_date = start_date - timedelta(days=days_ago) return target_date
讨论
上面的算法原理是这样的:先将开始日期和目标日期映射到星期数组的位置上(星
期一索引为0),然后通过模运算计算出目标日期要经过多少天才能到达开始日期。然
后用开始日期减去那个时间差即得到结果日期。
如果你要像这样执行大量的日期计算的话,你最好安装第三方包python-dateutil
来代替。比如,下面是是使用dateutil 模块中的relativedelta() 函数执行同样的计
算:
>>> from datetime import datetime >>> from dateutil.relativedelta import relativedelta >>> from dateutil.rrule import * >>> d = datetime.now() >>> print(d) 2012-12-23 16:31:52.718111 >>> # Next Friday >>> print(d + relativedelta(weekday=FR)) 2012-12-28 16:31:52.718111 >>> >>> # Last Friday >>> print(d + relativedelta(weekday=FR(-1))) 2012-12-21 16:31:52.718111
问题:你的代码需要在当前月份中循环每一天,想找到一个计算这个日期范围的高效方法。
解决方案
在这样的日期上循环并需要事先构造一个包含所有日期的列表。你可以先计算出
开始日期和结束日期,然后在你步进的时候使用datetime.timedelta 对象递增这个日
期变量即可。
下面是一个接受任意datetime 对象并返回一个由当前月份开始日和下个月开始日
组成的元组对象。
from datetime import datetime, date, timedelta import calendar def get_month_range(start_date=None): if start_date is None: start_date = date.today().replace(day=1) _, days_in_month = calendar.monthrange(start_date.year, start_date.month) end_date = start_date + timedelta(days=days_in_month) return (start_date, end_date)
有了这个就可以很容易的在返回的日期范围上面做循环操作了:
>>> a_day = timedelta(days=1) >>> first_day, last_day = get_month_range() >>> while first_day < last_day: ... print(first_day) ... first_day += a_day ... 2012-08-01 2012-08-02 2012-08-03 2012-08-04
讨论
上面的代码先计算出一个对应月份第一天的日期。一个快速的方法就是使用date
或datetime 对象的replace() 方法简单的将days 属性设置成1 即可。replace() 方
法一个好处就是它会创建和你开始传入对象类型相同的对象。所以,如果输入参数是一
个date 实例,那么结果也是一个date 实例。同样的,如果输入是一个datetime 实
例,那么你得到的就是一个datetime 实例。
然后,使用calendar.monthrange() 函数来找出该月的总天数。任何时候只要你
想获得日历信息,那么calendar 模块就非常有用了。monthrange() 函数会返回包含
星期和该月天数的元组。
一旦该月的天数已知了,那么结束日期就可以通过在开始日期上面加上这个天数
获得。有个需要注意的是结束日期并不包含在这个日期范围内(事实上它是下个月的开
始日期)。这个和Python 的slice 与range 操作行为保持一致,同样也不包含结尾。
为了在日期范围上循环,要使用到标准的数学和比较操作。比如,可以利用
timedelta 实例来递增日期,小于号< 用来检查一个日期是否在结束日期之前。
理想情况下,如果能为日期迭代创建一个同内置的range() 函数一样的函数就好
了。幸运的是,可以使用一个生成器来很容易的实现这个目标:
def date_range(start, stop, step): while start < stop: yield start start += step
下面是使用这个生成器的例子:
>>> for d in date_range(datetime(2012, 9, 1), datetime(2012,10,1), timedelta(hours=6)): ... print(d) ... 2012-09-01 00:00:00 2012-09-01 06:00:00
字符串转换为日期
问题
你的应用程序接受字符串格式的输入,但是你想将它们转换为datetime 对象以便
在上面执行非字符串操作。
解决方案
使用Python 的标准模块datetime 可以很容易的解决这个问题。比如:
>>> from datetime import datetime >>> text = '2012-09-20' >>> y = datetime.strptime(text, '%Y-%m-%d') >>> z = datetime.now() >>> diff = z - y >>> diff datetime.timedelta(3, 77824, 177393) >>>
讨论
datetime.strptime() 方法支持很多的格式化代码,比如%Y 代表4 位数年份,%m
代表两位数月份。还有一点值得注意的是这些格式化占位符也可以反过来使用,将日期
输出为指定的格式字符串形式。
比如,假设你的代码中生成了一个datetime 对象,你想将它格式化为漂亮易读形
式后放在自动生成的信件或者报告的顶部:
>>> z datetime.datetime(2012, 9, 23, 21, 37, 4, 177393) >>> nice_z = datetime.strftime(z, '%A %B %d, %Y') >>> nice_z 'Sunday September 23, 2012'
还有一点需要注意的是,strptime() 的性能要比你想象中的差很多,因为它是使
用纯Python 实现,并且必须处理所有的系统本地设置。如果你要在代码中需要解析大
量的日期并且已经知道了日期字符串的确切格式,可以自己实现一套解析方案来获取
更好的性能。比如,如果你已经知道所以日期格式是YYYY-MM-DD ,你可以像下面这样
实现一个解析函数:
from datetime import datetime def parse_ymd(s): year_s, mon_s, day_s = s.split('-') return datetime(int(year_s), int(mon_s), int(day_s))
实际测试中,这个函数比datetime.strptime() 快7 倍多。如果你要处理大量的
涉及到日期的数据的话,那么最好考虑下这个方案!
结合时区的日期操作
问题
你有一个安排在2012 年12 月21 日早上9:30 的电话会议,地点在芝加哥。而你
的朋友在印度的班加罗尔,那么他应该在当地时间几点参加这个会议呢?
解决方案
对几乎所有涉及到时区的问题,你都应该使用pytz 模块。这个包提供了Olson 时
区数据库,它是时区信息的事实上的标准,在很多语言和操作系统里面都可以找到。
pytz 模块一个主要用途是将datetime 库创建的简单日期对象本地化。比如,下
面如何表示一个芝加哥时间的示例:
>>> from datetime import datetime >>> from pytz import timezone >>> d = datetime(2012, 12, 21, 9, 30, 0) >>> print(d) 2012-12-21 09:30:00 >>> central = timezone('US/Central') >>> loc_d = central.localize(d) >>> print(loc_d) 2012-12-21 09:30:00-06:00
一旦日期被本地化了,它就可以转换为其他时区的时间了。为了得到班加罗尔对应
的时间,你可以这样做:
>>> # Convert to Bangalore time >>> bang_d = loc_d.astimezone(timezone('Asia/Kolkata')) >>> print(bang_d) 2012-12-21 21:00:00+05:30
处理本地化日期的通常的策略先将所有日
期转换为UTC 时间,并用它来执行所有的中间存储和操作。比如:
>>> print(loc_d) 2013-03-10 01:45:00-06:00 >>> utc_d = loc_d.astimezone(pytz.utc) >>> print(utc_d) 2013-03-10 07:45:00+00:00
一旦转换为UTC,你就不用去担心跟夏令时相关的问题了。因此,你可以跟之前
一样放心的执行常见的日期计算。当你想将输出变为本地时间的时候,使用合适的时区
去转换下就行了。比如:
>>> later_utc = utc_d + timedelta(minutes=30) >>> print(later_utc.astimezone(central)) 2013-03-10 03:15:00-05:00
当涉及到时区操作的时候,有个问题就是我们如何得到时区的名称。比如,在这个
例子中,我们如何知道“Asia/Kolkata”就是印度对应的时区名呢?为了查找,可以使
用ISO 3166 国家代码作为关键字去查阅字典pytz.country_timezones 。比如:
>>> pytz.country_timezones['IN'] ['Asia/Kolkata']
注:当你阅读到这里的时候,有可能pytz 模块已经不再建议使用了,因为PEP431
提出了更先进的时区支持。但是这里谈到的很多问题还是有参考价值的(比如使用UTC
日期的建议等)。
构建时间对象