日期与时间
- 简单的日期转化与计算
执行不同时间的转换与计算,通常用到datetime
模块,不可忽略的是其中timedelta
。顺便插播一段,时间的处理在日常工作中经常用到,记得最开始的时候,都是自定义一个方法通过时间戳来进行时间计算,直到最近给男朋友写爬虫时,结合之前了解到的datetime
模块,才发现timedelta
的好处。
>>> a=timedelta(days=2, hours=6)
>>> b = timedelta(hours=4.5)
>>> c=a+b
>>> c.days
2
>>> c.seconds
37800
>>> c.total_seconds()
210600.0
>>>
上述提到的爬虫,需求:有一个网站,需要输入起始时间和结束时间以及其他信息,点击下一页、下一页...,提取出每页的内容。
上述需求暂仅考虑时间部分,为了实现最大自动化,用户可决定下载几天数据。‘几天’就需要用到时间的计算,给出一个当前日期,向前或者向后进行推送。
>>> start_time=datetime(2012, 3, 1)
>>> start_time
datetime.datetime(2012, 3, 1, 0, 0)
>>> next_time=start_time+timedelta(days=1)
>>> next_time
datetime.datetime(2012, 3, 2, 0, 0)
>>>
在进行计算时,datetime 会自动处理闰年
>>> a = datetime(2012, 3, 1)
>>> b = datetime(2012, 2, 28)
>>> a-b
datetime.timedelta(2)
>>> (a-b).days
2
>>> c = datetime(2013, 3, 1)
>>> d = datetime(2013, 2, 28)
>>> (c - d).days
1
>>>
使用timedelta
可以很方便的在日期上做days,hours,seconds等时间计算,如果要计算月份则需要另外的办法。
更加复杂的日期处理,可使用dateutil模块,许多类似的时间计算可以使用 dateutil.relativedelta()
函数代替
- 计算最后一个周五的日期
你需要查找星期中某一天最后出现的日期,比如星期五
示例:求出指定星期几最后出现的日期
先将开始日期和目标日期映射到星期数组的位置上 (星期一索引为 0),然后通过模运算计算出目标日期要经过多少天才能到达开始日期。然后用开始日期减去那个时间差即得到结果日期。
>>> 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
>>> get_previous_byday('Monday')
datetime.datetime(2017, 9, 18, 11, 6, 2, 863482)
>>> get_previous_byday('Tuesday')
datetime.datetime(2017, 9, 12, 11, 6, 24, 398482)
>>>
- 计算当前月份的日期范围
问题:在当前月份中循环每一天,想找到一个计算这个日期范围的高效方法。
解决方案:
在这样的日期上循环并需要事先构造一个包含所有日期的列表。关键是先计算出开始日期和结束日期,然后在你步进的时候使用datetime.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)
>>> first_day, last_day = get_month_range()
>>> first_day
datetime.date(2017, 9, 1)
>>> last_day
datetime.date(2017, 10, 1)
使用 calendar.monthrange() 函数来找出该月的总天数。任何时候只要你想获得日历信息,那么 calendar 模块就非常有用了。 monthrange() 函数会返回包含星期和该月天数的元组。
>>> calendar.monthrange(start_date.year, start_date.month)
(4, 30)
>>>
一旦该月的天数已知了,那么结束日期就可以通过在开始日期上面加上这个天数获得。值得注意的是结束日期并不包含在这个日期范围内 (事实上它是下个月的开始日期)。这个和 Python 的 slice 与 range 操作行为保持一致,同样也不包含结尾。可以创建类似内置range()的函数。
>>> 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
2012-09-01 12:00:00
2012-09-01 18:00:00
2012-09-02 00:00:00
2012-09-02 06:00:00
...
- 字符串转换为日期
执行 datetime 对象转化成日期字符串或者日期字符串转化成datetime对象
datetime模块提供了strptime()和strftime()方法供使用,但是考虑到性能,当涉及到处理大量日期的时候,strptime()不建议使用,因为它是使用纯 Python 实现,并且必须处理所有的系统本地设置,性能比想象中差很多。可自定义一个方法代替,比如:
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))
- 结合时区的日期处理
示例:
>>> d = datetime(2012, 12, 21, 9, 30, 0)
>>> central = timezone('US/Central')
>>> loc_d = central.localize(d)
>>> d
datetime.datetime(2012, 12, 21, 9, 30)
>>> loc_d
datetime.datetime(2012, 12, 21, 9, 30, tzinfo=)
>>>
上述关键是类似US/Central
怎么得到?
为了查找,可以使用 ISO 3166 国家代码作为关键字去查阅字典pytz.country timezones
>>> import pytz
>>> pytz.country_timezones['IN']
['Asia/Kolkata']
>>> pytz.country_timezones['AE']
['Asia/Dubai']
>>>