对于datetime类型的对象,Python内置提供了time.mktime(p_tuple)
和datetime类的timestamp()
来返回自January 1, 1970, 00:00:00 GMT
以来的秒数,但并没有提供直接获取毫秒数的方法,那么如何获取datetime对象从格林威治纪元时间起始的毫秒数呢?time.mktime(p_tuple)
和datetime类的timestamp()
有什么区别呢?
网络上很容易可以找到这样的写法:
1 2 3 4 5 6 7 def get_ts_in_ms (t ): """ 获取时间t表示的从格林威治时间1970-01-01 00:00:00开始的毫秒数 :param t: 为datetime类型 :return: """ return int(time.mktime(t.timetuple())) * 1000 + int(t.microsecond / 1000 )
但,这样的写法有时并不是万能的或者说有时并非我们真正想要的。
为了方便验证,我们定义了如下方法,用来打印datetime对象的一些信息:
1 2 3 4 5 6 7 8 9 10 def print_time (t ): print(t) print("t.tzname: {}" .format(t.tzname())) print("t.year, t.month, t.day, t.hour: {} {} {} {}" .format(t.year, t.month, t.day, t.hour)) print("t.timetuple() : {}" .format(t.timetuple())) print("t.utctimetuple(): {}" .format(t.utctimetuple())) print("time.mktime(t.timetuple()) : {}" .format(time.mktime(t.timetuple()))) print("time.mktime(t.utctimetuple()): {}" .format(time.mktime(t.utctimetuple()))) print("t.timestamp() : {}" .format(t.timestamp())) print("--------------------------\n" )
1 2 3 4 5 6 7 8 9 10 11 12 13 tzCST = timezone(timedelta(hours=8 )) tzUTC = timezone(timedelta(hours=0 )) local = datetime(2020 , 12 , 17 ) cst = datetime(2020 , 12 , 17 , tzinfo=tzCST) utc = datetime(2020 , 12 , 17 , tzinfo=tzUTC) print_time(local) print_time(cst) print_time(utc)
case1:系统时区为CST时区下,执行以上程序 print_time(local)输出:
1 2 3 4 5 6 7 8 2020-12-17 00:00:00 t.tzname: None t.year, t.month, t.day, t.hour: 2020 12 17 0 t.timetuple() : time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=-1) t.utctimetuple(): time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=0) time.mktime(t.timetuple()) : 1608134400.0 (北京时间2020-12-17 00:00:00) time.mktime(t.utctimetuple()): 1608134400.0 (北京时间2020-12-17 00:00:00) t.timestamp() : 1608134400.0 (北京时间2020-12-17 00:00:00)
print_time(cst)输出:
1 2 3 4 5 6 7 8 2020-12-17 00:00:00+08:00 t.tzname: UTC+08:00 t.year, t.month, t.day, t.hour: 2020 12 17 0 t.timetuple() : time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=-1) t.utctimetuple(): time.struct_time(tm_year=2020, tm_mon=12, tm_mday=16, tm_hour=16, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=351, tm_isdst=0) time.mktime(t.timetuple()) : 1608134400.0 (北京时间2020-12-17 00:00:00) time.mktime(t.utctimetuple()): 1608105600.0 (北京时间2020-12-16 16:00:00) t.timestamp() : 1608134400.0 (北京时间2020-12-17 00:00:00)
print_time(utc)输出:
1 2 3 4 5 6 7 8 2020-12-17 00:00:00+00:00 t.tzname: UTC t.year, t.month, t.day, t.hour: 2020 12 17 0 t.timetuple() : time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=-1) t.utctimetuple(): time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=0) time.mktime(t.timetuple()) : 1608134400.0(北京时间2020-12-17 00:00:00) time.mktime(t.utctimetuple()): 1608134400.0(北京时间2020-12-17 00:00:00) t.timestamp() : 1608163200.0(北京时间2020-12-17 08:00:00)
case2:系统时区为UTC时区下,执行以上程序 print_time(local)输出:
1 2 3 4 5 6 7 8 2020-12-17 00:00:00 t.tzname: None t.year, t.month, t.day, t.hour: 2020 12 17 0 t.timetuple() : time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=-1) t.utctimetuple(): time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=0) time.mktime(t.timetuple()) : 1608163200.0(北京时间2020-12-17 08:00:00) time.mktime(t.utctimetuple()): 1608163200.0(北京时间2020-12-17 08:00:00) t.timestamp() : 1608163200.0(北京时间2020-12-17 08:00:00)
print_time(cst)输出:
1 2 3 4 5 6 7 8 2020-12-17 00:00:00+08:00 t.tzname: UTC+08:00 t.year, t.month, t.day, t.hour: 2020 12 17 0 t.timetuple() : time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=-1) t.utctimetuple(): time.struct_time(tm_year=2020, tm_mon=12, tm_mday=16, tm_hour=16, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=351, tm_isdst=0) time.mktime(t.timetuple()) : 1608163200.0(北京时间2020-12-17 08:00:00) time.mktime(t.utctimetuple()): 1608134400.0(北京时间2020-12-17 00:00:00) t.timestamp() : 1608134400.0(北京时间2020-12-17 00:00:00)
print_time(utc)输出:
1 2 3 4 5 6 7 8 2020-12-17 00:00:00+00:00 t.tzname: UTC t.year, t.month, t.day, t.hour: 2020 12 17 0 t.timetuple() : time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=-1) t.utctimetuple(): time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, tm_isdst=0) time.mktime(t.timetuple()) : 1608163200.0(北京时间2020-12-17 08:00:00) time.mktime(t.utctimetuple()): 1608163200.0(北京时间2020-12-17 08:00:00) t.timestamp() : 1608163200.0(北京时间2020-12-17 08:00:00)
APIs剖析 timetuple()和utctimetuple() 以下为timetuple()
的源码,它使用了datetime实例的year、month、day、hour、minute、second来直接构建元组结构。
1 2 3 4 5 6 7 8 9 10 11 12 def timetuple (self ): "Return local time tuple compatible with time.localtime()." dst = self.dst() if dst is None : dst = -1 elif dst: dst = 1 else : dst = 0 return _build_struct_time(self.year, self.month, self.day, self.hour, self.minute, self.second, dst)
以下为utctimetuple()
源码,它使用了utcoffset()
方法根据当前datetime实例是否定义了时区来计算UTC偏移量;如果datetime实例有时区信息,那么utctimetuple()
将减/加(东区减
,西区加
)该偏移量后得到的year、month、day、hour、minute、second进行构建元组结构;否则直接使用datetime实例的year、month、day、hour、minute、second构建元组结构,从语义上讲,该方法是转换
的语义,在转换UTC元组时,它使用了时间对象的时区信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def utctimetuple (self ): "Return UTC time tuple compatible with time.gmtime()." offset = self.utcoffset() if offset: self -= offset y, m, d = self.year, self.month, self.day hh, mm, ss = self.hour, self.minute, self.second return _build_struct_time(y, m, d, hh, mm, ss, 0 ) def utcoffset (self ): """Return the timezone offset as timedelta positive east of UTC (negative west of UTC).""" if self._tzinfo is None : return None offset = self._tzinfo.utcoffset(self) _check_utc_offset("utcoffset" , offset) return offset
所以,
case1中print_time(local):对于没有时区信息的local
变量,t.timetuple()
和t.utctimetuple()
返回的元组信息除了tm_isdst(夏令时)以外都是一致的;
case1中print_time(cst):cst
变量的实例的时区偏移量为8小时,t.utctimetuple()
返回的元组信息中要晚于t.timetuple()
8个小时;
case1中print_time(utc):utc
变量的实例的时区偏移量为0小时,所以t.timetuple()
和t.utctimetuple()
返回的元组信息除了tm_isdst(夏令时)以外都是一致的;
另外,print_time(local)、print_time(cst)、print_time(utc)对于t.timetuple()
和t.utctimetuple()
的输出在case1和case2中是一致的,与操作系统设置的时区并无关系;
time.mktime(p_tuple) 以下为mktime()
的说明,它的语义是对时间元组(注意,元组结构不具有时区信息)所代表的本地时间计算秒数。
1 2 3 4 5 6 7 8 9 10 def mktime (p_tuple ): """ mktime(tuple) -> floating point number Convert a time tuple in local time to seconds since the Epoch. Note that mktime(gmtime(0)) will not generally return zero for most time zones; instead the returned value will either be equal to that of the timezone or altzone attributes on the time module. """ return 0.0
所以,
print_time(local),在case1和case2不同时区下,t.timetuple()
和t.utctimetuple()
表示的元组都是time.struct_time(tm_year=2020, tm_mon=12, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=352, …)
case1中,该元组被mktime()
认为是CST时间2020-12-17 00:00:00,则结果都为1608134400.0;
case2中,该元组被mktime()
认为是UTC时间2020-12-17 00:00:00,则结果都为1608163200.0;
同理,可以分析print_time(cst)、print_time(utc)
timestamp() 以下为timestamp()
的源码,该方法在没有时区信息时,使用_mktime()
方法计算秒数,_mktime()
内部使用了本地时区信息;在有时区信息时,直接减去_EPOCH
值。
1 2 3 4 5 6 7 def timestamp (self ): "Return POSIX timestamp as float" if self._tzinfo is None : s = self._mktime() return s + self.microsecond / 1e6 else : return (self - _EPOCH).total_seconds()
所以,
local
变量没有定义时区,对于print_time(local),case1北京时区下输出1608134400.0 (北京时间2020-12-17 00:00:00),case2UTC时区下输出1608163200.0(北京时间2020-12-17 08:00:00);
cst
变量带有时区信息,对于print_time(cst),case1北京时区和case2UTC时区下都输出了同样的结果1608134400.0 (北京时间2020-12-17 00:00:00);
结论 time.mktime(t.timetuple())
适用于构建本地时区时间(即,LocalTime)的场景,借助timestamp()
来获取秒数更适用于多时区编程的场景:
1 2 3 4 5 6 7 def get_ts_in_ms (t ): """ 获取时间t表示的从格林威治时间1970-01-01 00:00:00开始的毫秒数 :param t: 为datetime类型 :return: """ return int(t.timestamp()) * 1000 + int(t.microsecond / 1000 )
几种时区设置方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 tzCST = timezone(timedelta(hours=8 )) tzUTC = timezone(timedelta(hours=0 )) local = datetime.now() cst = datetime.now(tzCST) utc = datetime.now(tzUTC) local = datetime(2020 , 12 , 17 ) cst = datetime(2020 , 12 , 17 , tzinfo=tzCST) utc = datetime(2020 , 12 , 17 , tzinfo=tzUTC) local = datetime.strptime("20201217" , '%Y%m%d' ) cst = local.replace(tzinfo=tzCST) utc = local.replace(tzinfo=tzUTC)