“专业人士笔记”系列目录:
创帆云:Python成为专业人士笔记--强烈建议收藏!每日持续更新!将字符串解析为对应时区的datetime对象
Python 3.2+在将字符串解析为datetime对象时支持%z格式:
import datetime
dt = datetime.datetime.strptime("2016-04-15T08:27:18-0500", "%Y-%m-%dT%H:%M:%S%z")
# 其中时间字符串中最后的 -0500 ,为UTC的偏移量(如果时区是本地的,则是空字符串),其格式一般是+HHMM 或 -HHMM 格式
对于Python2x版本(版本已停止维护),可以使用诸如dateutil这样的外部库,这使得将带时区的字符串解析为datetime对象变得很方便:
注:如python云环境中没有下面的包,请登录SSH并使用命令安装:
python -m pip install python-dateutil
#然后运行代码:
import dateutil.parser
dt = dateutil.parser.parse("2016-04-15T08:27:18-0500")
dt变量现在是一个带有以下值的datetime对象:
datetime.datetime(2016, 4, 15, 8, 27, 18, tzinfo=tzoffset(None, -18000))
从中文对话中提取datetime
注:如python3云环境中没有下面的包,请登录SSH并使用命令安装:
python -m pip install python-dateutil
可以使用dateutil解析器以“模糊”模式从文本中提取日期 , 不被识别为日期一部分的字符串的组件将被忽略
from dateutil.parser import parse
dt = parse("截止2012-12-31日,上午8:21", fuzzy=True)
print(dt)
# 输出2012-12-31 08:21:00
# 其它的可以自己尝试
构建对应时区的datetimes
默认情况下,所有的datetime对象都是本地的,但是对于程序开发尤其面向用户可能包含国际用户时,往往的做法是,在程序数据库里存储标准的世界时区(UTC),然后针对不同地区用户的访问动态根据当地时区生成当地时间。
要使它们强制对应特定的时区,必须附加一个tzinfo对象,该对象提供UTC偏移量和时区的函数
补充:什么是UTC时间,UTC时间和本地时区的区别
世界协调时间(Universal Time Coordinated,UTC),GPS 系统中有两种时间区分,一为UTC,另一为LT(地方时)两者的区别为时区不同,UTC就是0时区的时间,(LocalTime)地方时为本地时间,如北京为早上八点(东八区),UTC时间就为零点,时间比北京时晚八小时,以此计算即可.
—时区偏移的修正
相对UTC有固定偏移量的时区,在Python 3.2+中,datetime模块提供了 timezone 类,即 tzinfo的具体实现,它带有一个timedelta和一个可选的name参数:
from datetime import datetime, timedelta, timezone
JST = timezone(timedelta(hours=+9))
#timedelta函数用做对时间增减,这里相对UTC加了9个小时,即UTC+9的日本时间
dt = datetime(2015, 1, 1, 12, 0, 0, tzinfo=JST)
print(dt)
#tzinfo参数指定时区为日本UTC+9
#输出对象:2015-01-01 12:00:00+09:00
print(dt.tzname())
#输出UTC+09:00
dt = datetime(2015, 1, 1, 12, 0, 0, tzinfo=timezone(timedelta(hours=9), 'JST'))
print(dt.tzname)
#输出'JST'
对于Python 3.2之前的版本,需要使用第三方库,比如dateutil,这里不做详细介绍
计算两个日期的时间差
timedelta模块可以方便地计算时间差:
from datetime import datetime, timedelta
now = datetime.now()
then = datetime(2016, 5, 23)
# 和 datetime.datetime(2016, 05, 23, 0, 0, 0) 效果一样, 在创建新的datetime对象时,指定时间是可选的
delta = now-then
#输出:1436 days, 12:43:25.994771,即时间间隔1436天
#注意:delta这时已经是datetime.timedelta的数据类型
也可以只输出局部:
print(delta.days)
#输出60天
print(delta.seconds)
#输出40826秒
如果要基于当前日期得到n天后、n天前的日期,我们可以使用:
n天之后获取函数:
def get_n_days_after_date(date_format="%d %B %Y", add_days=120):
date_n_days_after = datetime.datetime.now() + timedelta(days=add_days)
return date_n_days_after.strftime(date_format)
n天之前获取函数:
def get_n_days_before_date(self, date_format="%d %B %Y", days_before=120):
date_n_days_ago = datetime.datetime.now() - timedelta(days=days_before)
return date_n_days_ago.strftime(date_format)
datetime对象的基本用法
datetime模块包含三种主要的对象类型:date、time和datetime
import datetime
#Date 对象
today = datetime.date.today()
new_year = datetime.date(2017, 01, 01) #datetime.date(2017, 1, 1)也可以
#Time 对象
noon = datetime.time(12, 0, 0) #datetime.time(12, 0)也可以
#Datetime 对象
millenium_turn = datetime.datetime(2000, 1, 1, 0, 0, 0) #datetime.datetime(2000, 1, 1, 0, 0)也可以
#获取当前时间 datetime
now = datetime.datetime.now()
#注意:这些日期及时间对象的算术操作只在相同的数据类型中受支持,对不同类型的实例执行简单的算术操作将导致数据类型错误
#如用中午减去今天操作:
noon-today
报错:
Traceback (most recent call last):
File "", line 1, in
TypeError: unsupported operand type(s) for -: 'datetime.time' and 'datetime.date'
However, it is straightforward to convert between types.
解析: datetime.time和datime.date两个数据类型不能互减
#像这样处理(两个都是datatime对象了)
datetime.datetime(today.year, today.month, today.day) - millenium_turn)
或者:
datetime.datetime.combine(today, noon) - millenium_turn)
简单的日期计算
日期不是孤立存在的,通常情况下,你需要找出日期之间的间隔时间,或者确定明天的日期,而这些都可以使用timedelta对象来完成
import datetime
today = datetime.date.today()
#print('今天:', today)
yesterday = today - datetime.timedelta(days=1)
#print('昨天:', yesterday)
tomorrow = today + datetime.timedelta(days=1)
#print('明天:', tomorrow)
#print('明天和昨天的时间差是:', tomorrow - yesterday)
#在你的python3云环境中试一下,看是什么效果
打印的结果是:
今天 : 2016-04-15
昨天 : 2016-04-14
明天 : 2016-04-16
明天和昨天的时间差是: 2 days, 0:00:00
从timestamp 时间戳转到datetime
import time
from datetime import datetime
seconds_since_epoch=time.time()
#这是一个时间戳 如,1469182681.709
utc_date=datetime.utcfromtimestamp(seconds_since_epoch) #返回一个datetime对象,datetime.datetime(2016, 7, 22, 10, 18, 1,
709000)
用日历模块精确的增减月份
使用日历模块 calendar 可以很好的处理:
#精确获取下一个月的日期
import calendar
from datetime import date
#定义函数,传入日期及需要增加几个月的参数
def monthdelta(date, delta):
m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
#注:代码使用了内联代码赋值 , 其中 % 为取余数 ,//为相除后取整数
if not m:
m = 12
d = min(date.day, calendar.monthrange(y, m)[1])
return date.replace(day=d,month=m, year=y)
#调用函数获取
next_month = monthdelta(date.today(), 1)
#打印输出下一个月的日期
当然,其实有一种很简单计算下一个月日期的方法,只是不精确:
from datetime import date
next_month =date.today()+datetime.timedelta(days=30)
#这个原理是直接用timedelta将当前日期向后延长30天后日期并返回,只能用于粗略计算场景
使用最少的库解析任意的ISO 8601时间戳
Python只对解析ISO 8601时间戳提供有限的支持,对于strptime,你需要确切地知道它的格式。一个复杂的情况是,datetime转化字符串后是一个ISO 8601时间戳,并用空格作为分隔符和6位分数 ,如下:
str(datetime.datetime(2016, 7, 22, 9, 25, 59, 555555))
#输出'2016-07-22 09:25:59.555555'
但秒后面时间戳如果为0,则没有小数部分输出:
str(datetime.datetime(2016, 7, 22, 9, 25, 59, 0))
#输出'2016-07-22 09:25:59'
这两个时间的解析需要不同的strptime格式,strptime根本不支持解析中包含符号为“ : ”的分钟时区,因此可以解析(2016-07-22 09:25:59+0300),但标准格式(2016-07-22 09:25:59+03:00)则不能。
有一个名为iso8601的专用库,它可以正确地解析ISO 8601时间戳
import iso8601
iso8601.parse_date('2016-07-22 09:25:59')
#datetime.datetime(2016, 7, 22, 9, 25, 59, tzinfo=<iso8601.Utc>)
iso8601.parse_date('2016-07-22 09:25:59+03:00')
#datetime.datetime(2016, 7, 22, 9, 25, 59, tzinfo=<FixedOffset '+03:00' …>)
iso8601.parse_date('2016-07-22 09:25:59Z')
#datetime.datetime(2016, 7, 22, 9, 25, 59, tzinfo=<iso8601.Utc>)
iso8601.parse_date('2016-07-22T09:25:59.000111+03:00')
#datetime.datetime(2016, 7, 22, 9, 25, 59, 111, tzinfo=<FixedOffset '+03:00' …>)
如果没有设置时区,则使用iso8601,解析日期默认为UTC。可以使用默认区域关键字参数更改默认区域。值得注意的是,如果这是None而不是缺省值,那么那些没有显式时区的时间戳将作为原始日期时间返回 :
iso8601.parse_date('2016-07-22T09:25:59', default_timezone=None)
datetime.datetime(2016, 7, 22, 9, 25, 59)
iso8601.parse_date('2016-07-22T09:25:59Z', default_timezone=None)
datetime.datetime(2016, 7, 22, 9, 25, 59, tzinfo=<iso8601.Utc>)
补充: ISO 8601
ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前最新为第三版ISO8601:2004,第一版为ISO8601:1988,第二版为ISO8601:2000。 它规定了日期时间的各种表示方法,它的一般原则:
- 日期和时间值按从最大到最小的时间单位排序:年,月(或周),日,小时,分钟,秒和秒的分数。因此,表示的字典顺序对应于时间顺序,除了涉及负年份的日期表示。这允许日期自然地由例如文件系统排序。
- 每个日期和时间值都有一个固定的位数,必须用前导零填充。
- 日期时间表示只能有数字或少数特殊字符组成(如“ – ”,“:”,“T”,“W”和“Z”),不允许出现地方写法,如“1月”或“星期四”等。
- 表示可以采用两种格式之一完成 – 具有最少数量分隔符的基本格式或添加了分隔符的扩展格式以增强人类可读性。该标准指出“应以纯文本形式避免使用基本格式”。日期值(年,月,周和日)之间使用的分隔符是连字符,而冒号用作时间值(小时,分钟和秒)之间的分隔符。例如,2009年第1个月的第6天可以以扩展格式写为“2009-01-06”,或者以基本格式简称为“20090106”而不含糊不清。
- 为了降低准确度,可以从任何日期和时间表示中删除任意数量的值,但是从最小到最重要的顺序。例如,“2004-05”是有效的ISO 8601日期,表示2004年5月(第5个月)。此格式永远不会代表2004年未指定月份的第5天,也不代表从2004年进入2005年。
- ISO 8601使用24小时制。HH:MM:SS.SSS,HH:MM:SS,HH:MM,HH为合规的时间格式。
- 如果没有指定与UTC关系则假定是本地时间,为安全的跨时区通讯,应制定与UTC关系。若时间是UTC则在时间后面加Z表示,如“09:30UTC”表示为09:30Z”或“0930Z”。其它时区时间则将与UTC的偏移量附加到时间后面,格式为±[hh]:[mm],±[hh] [mm]或±[hh],如“北京时间09:30”表示为”09:30+08:00”或“ 09:30+0800 ” 或“ 09:30+08 ”。
- 用字母T分割日期和时间。如20180703T224426Z或2018-07-03T22:44:26Z 。
获取 ISO 8601 timestamp 时间戳
没有时区,只有微秒
from datetime import datetime
datetime.now().isoformat()
输出: '2016-07-31T23:08:20.886783'
有时区,有微秒
from datetime import datetime
from dateutil.tz import tzlocal
datetime.now(tzlocal()).isoformat()
输出: '2016-07-31T23:09:43.535074-07:00'
有时区,没有微秒
from datetime import datetime
from dateutil.tz import tzlocal
datetime.now(tzlocal()).replace(microsecond=0).isoformat()
输出: '2016-07-31T23:10:30-07:00'
维基百科上有详细的注解:https://en.wikipedia.org/wiki/ISO_8601
遍历日期对象
有时,你希望迭代从开始日期到某个结束日期的日期范围,可以使用datetime库和timedelta对象来实现 :
import datetime
#每次迭代以天为单位
day_delta = datetime.timedelta(days=1)
start_date = datetime.date.today()
end_date = start_date + 7*day_delta
for i in range((end_date - start_date).days):
print(start_date + i*day_delta)
#程序输出:
2016-07-21
2016-07-22
2016-07-23
2016-07-24
2016-07-25
2016-07-26
2016-07-27
今天的分享就到这里,禁止转载,违者必究!