找回密码
 会员注册
查看: 29|回复: 0

超详细Pythondatetime(当前日期、时间戳转换、前一天日期等)附:时区原理详解

[复制链接]

2

主题

0

回帖

7

积分

新手上路

积分
7
发表于 2024-9-13 13:10:12 | 显示全部楼层 |阅读模式
文章目录相关文献当前时间前一天日期、后一天日期东八区(北京)时间时间戳转换datetime->strstr->datetimedatetime->timestamp(时间戳)timestamp->datetime获取日期中的年、季度、月、周、日、小时、分、秒等时区原理时区问题复杂性的来源深入理解datetime的坑不同函数时区标准不同timezone时区偏移以西为正,以东为负,和ISO标准相反笔者建议datetime源码解读pytz源码解读作者:小猪快跑基础数学&计算数学,从事优化领域6年+,主要研究方向:MIP求解器、整数规划、随机规划、智能优化算法python3的时区是一个很容易出错的地方。本篇将从原理层面剖析时区概念,让读者真正学懂时区,不踩坑。如有错误,欢迎指正。如有更好的算法,也欢迎交流!!!——@小猪快跑相关文献datetime—Basicdateandtimetypes—Python3.12.4documentation简述时区问题复杂性来源(Python版)-知乎(zhihu.com)当前时间fromdatetimeimportdatetimeprint(datetime.now())#2024-07-1214:39:59.5315251234前一天日期、后一天日期fromdatetimeimportdatetime,timedeltadt=datetime(2024,1,1)dt+timedelta(1)#datetime.datetime(2024,1,2,0,0)dt-timedelta(1)#datetime.datetime(2023,12,31,0,0)123456789东八区(北京)时间importpytzfromdatetimeimportdatetimeprint(datetime(2024,1,1,tzinfo=pytz.timezone('Etc/GMT-8')))#2024-01-0100:00:00+08:0012345时间戳转换datetime->strimportpytzfromdatetimeimportdatetimedt=datetime(2024,1,1,tzinfo=pytz.timezone('Etc/GMT-8'))fmt='%Y-%m-%d%H:%M:%S%z'dt.strftime(fmt)#2024-01-0100:00:00+0800fmt='%a%d%b%Y,%I:%M%p'dt.strftime(fmt)#Mon01Jan2024,12:00AMfmt='%d/%m/%y%H:%M:%S.%f'dt.strftime(fmt)#01/01/2400:00:00.000000'The{1}is{0:%d},the{2}is{0:%B},the{3}is{0:%I:%M%p}.'.format(dt,"day","month","time")#'Thedayis01,themonthisJanuary,thetimeis12:00AM.'12345678910111213141516171819str->datetimefromdatetimeimportdatetimedatetime.strptime("21/11/0616:30","%d/%m/%y%H:%M")#datetime.datetime(2006,11,21,16,30)1234datetime->timestamp(时间戳)fromdatetimeimportdatetimedt=datetime(2024,1,1)dt.timestamp()#1704038400.012345timestamp->datetimefromdatetimeimportdatetimedatetime.fromtimestamp(1704038400)#datetime.datetime(2024,1,1,0,0)1234下面列出了1989年C标准所要求的所有格式代码,这些代码可在所有使用标准C实现的平台上运行。指令含义样例%a工作日作为地方缩写名称。Sun,Mon,…,Sat(en_US);So,Mo,…,Sa(de_DE)%A作为地区全称的工作日。Sunday,Monday,…,Saturday(en_US);Sonntag,Montag,…,Samstag(de_DE)%w以十进制数字表示的工作日,其中0代表周日,6代表周六。0,1,…,6%d以小数点后0位数字表示的月日。01,02,…,31%b月(Month)作为本地语言的缩写名称。Jan,Feb,…,Dec(en_US);Jan,Feb,…,Dez(de_DE)%B以本地全称表示的月份。January,February,…,December(en_US);Januar,Februar,…,Dezember(de_DE)%m以十进制零位表示的月份。01,02,…,12%y以小数点后零位的数字表示不带世纪的年份。00,01,…,99%Y带世纪的年为十进制数。0001,0002,…,2013,2014,…,9998,9999%H小时(24小时制时钟)为零位小数。00,01,…,23%I小时(12小时钟)为零位小数。01,02,…,12%p当地的上午或下午。AM,PM(en_US);am,pm(de_DE)%M分钟,小数点后零位。00,01,…,59%S秒(小数点后零位)。00,01,…,59%f微秒为十进制数,置零后为6位数。000000,000001,…,999999%zUTC偏移量,格式为±HHMM[SS[.fffffff]](如果对象为空字符串)。(empty),+0000,-0400,+1030,+063415,-030712.345216%Z时区名称(如果对象为非正则表达式,则为空字符串)。(empty),UTC,GMT%j以十进制零位形式表示的年日。001,002,…,366%U年份的星期数(星期日为一周的第一天),以十进制零位表示。在新的一年中,第一个星期日之前的所有天数都被视为第0周。00,01,…,53%W年的周号(周一为一周的第一天),小数点后加0。在新的一年中,第一个星期一之前的所有日子都被视为第0周。00,01,…,53%c当地语言的日期和时间表示法。TueAug1621:30:001988(en_US);Di16Aug21:30:001988(de_DE)%x本地语言的日期表示法。08/16/88(None);08/16/1988(en_US);16.08.1988(de_DE)%X当地语言中合适的时间表示法。21:30:00(en_US);21:30:00(de_DE)%%字面"%"字符。%获取日期中的年、季度、月、周、日、小时、分、秒等fromdatetimeimportdatetimedt=datetime.strptime("21/11/0616:30","%d/%m/%y%H:%M")#Usingdatetime.timetuple()togettupleofallattributestt=dt.timetuple()foritintt:print(it)2006#year11#month21#day16#hour30#minute0#second1#weekday(0=Monday)325#numberofdayssince1stJanuary-1#dst-methodtzinfo.dst()returnedNone123456789101112131415161718时区原理时区问题复杂性的来源DST(DaylightSavingTime)夏令时是一种在夏季期间将时钟向前调整一小时的做法,目的是为了在白天较长的季节里更有效地利用自然光照,从而节省能源。在夏令时期间,人们可以享受到更多的日光时间,理论上可以减少照明需求。夏令时的实施通常遵循以下模式:在春季,时钟在特定的周末(通常是三月或四月的某个周日)凌晨2点时向前调整一小时,变为3点。在秋季,时钟则在特定的周末(通常是十月或十一月的某个周日)凌晨2点时向后调整一小时,回到1点。不过,每个国家和地区的具体规则可能有所不同,有些地区可能不实行夏令时。在中国,曾经在1986年至1991年间实行过夏令时,具体的调整时间为:每年从四月中旬第一个星期日的凌晨2时整(北京时间),将时钟拨快一小时,即从2时跳至3时,夏令时开始;到九月中旬第一个星期日的凌晨2时整(北京夏令时),再将时钟拨回一小时,即从2时跳回至1时,夏令时结束。自1992年起,中国暂停实行夏令时,目前中国全境全年使用的是北京时间,即东八区的标准时间,没有进行夏令时调整。ST(StandardTime)冬令时指的是在不实行夏令时(DaylightSavingTime,DST)调整的时期内所采用的标准时间。在那些实行夏令时的地区,冬令时实际上就是全年时间中的“正常”时间,而在夏令时期间,时钟会向前调快一个小时。当夏令时结束,通常在每年的秋季,时钟会被拨回一小时,恢复到冬令时。这一调整意味着日落时间会提前,白天的时间会相应缩短,而晚上则会提早变暗。冬令时的目的是在冬季减少能源消耗,尤其是在北半球,因为冬季的日光时间较短,不需要额外延长日光时间来节约能源。中国在1986年至1991年间曾经实行过夏令时,相应的,在这期间的非夏令时阶段即为所谓的“冬令时”。然而,自1992年起,中国停止实行夏令时,因此也不再有冬令时的概念。目前,中国全境全年使用的是北京时间,即东八区的标准时间(UTC+8),不再进行任何季节性的时间调整。在世界其他实行夏令时的国家和地区,冬令时通常是从每年的秋季持续到次年的春季,具体日期可能因国家而异。例如,美国和加拿大在每年11月的第一个周日开始冬令时,而欧洲大多数国家则在10月的最后一个周日开始。GMT(GreenwichMeanTime)格林尼治标准时间这是以英国格林尼治天文台观测结果得出的时间,这是英国格林尼治当地时间,这个地方的当地时间过去被当成世界标准的时间。UT(UniversalTime)世界时以本初子午线的平子夜起算的平太阳时。又称格林尼治平时或格林尼治时间。各地的地方平时与世界时之差等于该地的地理经度。1960年以前曾作为基本时间计量系统被广泛应用。由于地球自转速度变化的影响,它不是一种均匀的时间系统。后来世界时先后被历书时和原子时所取代,但在日常生活、天文导航、大地测量和宇宙飞行等方面仍属必需;同时,世界时反映地球自转速率的变化,是地球自转参数之一,仍为天文学和地球物理学的基本资料。TAI(InternationalAtomicTime)国际原子时原子时计量的基本单位:原子时秒。由原子钟导出。原子时秒的定义:铯-133原子基态的两个超精细能级间在零磁场下跃迁辐射9,192,631,770周所持续的时间。1967年第十三届国际计量大会(CGPM)决定,把在海平面实现的上述原子时秒,规定为国际单位制中的时间单位。根据原子时秒的定义,任何原子钟在确定起始历元后,都可以提供原子时。由各实验室用足够精确的铯原子钟导出的原子时称为地方原子时。全世界大约有20多个国家的不同实验室分别建立了各自独立的地方原子时。国际时间根据比较、综合世界各地原子钟数据,最后确定的原子时,称为国际原子时,简称TAI。TAI的起点是这样规定的:取1958年1月1日0时0分0秒世界时(UT)的瞬间作为同年同月同日0时0分0秒TAI。(事后发现,在该瞬间原子时与世界时的时刻之差为0.0039秒。这一差值就作为历史事实而保留下来。)在确定原子时起点之后,由于地球自转速度不均匀,世界时与原子时之间的时差便逐年积累。由于世界时存在不均匀性和历书时的测定精度低,自1967年起,原子时已取代历书时作为基本的时间计量系统。UTC(UniversalTimeCoordinated)协调世界时:国际原子时的准确度为每日数纳秒,而世界时的准确度为每日数毫秒。许多应用部门要求时间系统接近世界时UT,对于这种情况,一种称为协调世界时的折中时标于1972年面世。为确保协调世界时与世界时相差不会超过0.9秒,在有需要的情况下会在协调世界时内加上正或负闰秒。因此协调世界时与国际原子时之间会出现若干整数秒的差别,两者之差逐年积累,便采用跳秒(闰秒)的方法使协调时与世界时的时刻相接近,其差不超过1s。它既保持时间尺度的均匀性,又能近似地反映地球自转的变化。[2]按国际无线电咨询委员会(CCIR)通过的关于UTC的修正案,从1972年1月1日起UTC与UT1(在UT中加入极移改正得到)之间的差值最大可以达到±0.9s。位于巴黎的国际地球自转事务中央局负责决定何时加入闰秒。一般会在每年的6月30日、12月31日的最后一秒进行调整。原子时+自转因素(闰秒)=UTCLMT(LocalMeanTime)地方平时地方平时是太阳时改变形式修正后,在指定的经度范围内使用一致时间的地方太阳时,他的一致性仅取决于测量用的钟表准确性。地方平时从19世纪初期开始逐渐被采用,这些地区都不再使用地方太阳时或日晷的时间,直到每个国家都以各种不同的形式将之定为标准时间。标准时间意味着相同的时间使用在一些区域中—通常,不是从格林威治标准时间中抵销就是选择区域内主要区域的地方时间作为标准时间。地方平时和视太阳时之间的差别就是均时差(equationoftime)。地球划分出不同的时区,每个时区都有一个自己的当地时间。比如上海LMT就是UTC+08:06。原子时+自转因素(闰秒)+地理因素=LMTCST时间ChinaStandardTime某个国家统一采用某个时区的时间,比如上海的采用标准时间,就是UTC+8小时。夏令时/冬令时。比如美国会在夏季将时间拨快一个小时。这个时间称之为标准时间。历史因素:比如中国曾在1986年到1991年的内实行过夏令时,以上海为例,他在1988年8月份的标准时间就是UTC+9:00,而在1988年12月份标准时间是UTC+8:00。原子时+自转因素(闰秒)+法律因素(法律选区的特定时区、冬令时、夏令时)+历史因素=当地标准时间以上几个因素是时区问题复杂度的来源,为了解决这个问题,人们成立了时区信息数据库,Linux系统也是采用了该数据库来维护系统时间。深入理解datetime的坑不同函数时区标准不同创建datetime对象使用的是LMTimportpytzfromdatetimeimportdatetime#LMTdatetime(2021,1,1,tzinfo=pytz.timezone('Asia/Shanghai'))#2021-01-0100:00:00+08:06datetime(2021,1,1,tzinfo=pytz.timezone('Asia/Tokyo'))#2021-01-0100:00:00+09:19123456转换时区函数astimezone输出是ST/DST,除非输入时区==输出时区importpytzfromdatetimeimportdatetimetz_sh=datetime(2021,1,1,tzinfo=pytz.timezone('Asia/Shanghai'))#2021-01-0100:00:00+08:06#UTCtz_sh.astimezone(pytz.timezone('Asia/Tokyo'))#2021-01-0100:54:00+09:00#LMTtz_sh.astimezone(pytz.timezone('Asia/Shanghai'))#2021-01-0100:00:00+08:0612345678替换时区函数replace是LMTimportpytzfromdatetimeimportdatetimetz_sh=datetime(2021,1,1,tzinfo=pytz.timezone('Asia/Shanghai'))#2021-01-0100:00:00+08:06tz_sh.replace(tzinfo=pytz.timezone('Asia/Tokyo'))#2021-01-0100:00:00+09:1912345获取当前时间datetime.now是ST/DSTimportpytzfromdatetimeimportdatetime#UTCdatetime.now(tz=pytz.timezone('Asia/Shanghai'))#2024-07-1814:37:33.706649+08:0012345normalizeMT转换成ST/DST(注意1988年中国有夏令时)importpytzfromdatetimeimportdatetimetz=pytz.timezone("Asia/ShangHai")tz.normalize(datetime(2021,1,1,tzinfo=tz))#2020-12-3123:54:00+08:00tz.normalize(datetime(1988,8,1,tzinfo=tz))#1988-08-0100:54:00+09:00123456localize:增加ST/DST(注意1988年中国有夏令时)importpytzfromdatetimeimportdatetimetz=pytz.timezone("Asia/ShangHai")tz.localize(datetime(2021,1,1))#2021-01-0100:00:00+08:00tz.localize(datetime(1988,8,1))#1988-08-0100:00:00+09:00123456timezone时区偏移以西为正,以东为负,和ISO标准相反importpytzfromdatetimeimportdatetime#东八区datetime(2021,1,1,tzinfo=pytz.timezone('Etc/GMT-8'))#2021-01-0100:00:00+08:00#西八区datetime(2021,1,1,tzinfo=pytz.timezone('Etc/GMT+8'))#2021-01-0100:00:00-08:001234567笔者建议如果不存在夏令时类似的情况,为了避免函数时区标准不同的麻烦,可以直接使用'Etc/GMT-8'。如果存在夏令时这些,那么在转换时要注意不同标准带来的误差。尽量使用无时区信息的时间戳进行计算datetime源码解读或许是由于时区非常复杂,datetime时区仅提供了tzinfo的抽象类。可以使用常见的时区库:如pytz。tzinfo两个接口值得注意:classtzinfoabstractmethoddeftzname(self,__dt:datetime|None)->str|None:...@abstractmethoddefutcoffset(self,__dt:datetime|None)->timedelta|None:... #ReturnthetimezoneoffsetastimedeltapositiveeastofUTC(negativewestofUTC). #这是描述了该时区当地平均时间与UTC时间的偏移@abstractmethoddefdst(self,__dt:datetime|None)->timedelta|None:...deffromutc(self,__dt:datetime)->datetime:... #datetimeinUTC->datetimeinlocaltime. #这个接口做的事情是将该UTC时间更改为当地标准时间。#这是一个法律概念上的时间,需要考虑到夏令时,历史等因素。12345678910111213datetime中更换时区的基本过程,通过两个时区相对于utc时间的偏移,计算出两个时区的时间间隔。加上该间隔,然后直接更换时区dt+timedelta).raplace(tz)pytz源码解读pytz是时区信息数据库的python接口,使用了tzif文件来存储、描述时区,与linux相同。pytz.timezone("Asia/ShangHai")读取tzif文件中的信息创建对象。一般我们认为pytz.timezone("Asia/ShangHai")是一个时区,其实这不完全正确,准确来说,pytz.timezone("Asia/ShangHai")应该是一个地方时区库。它存储了三个时区MT+8:06CST+8:00CDT+9:00(已废弃的夏令时)可以通过一下代码进行查看importpytzpytz.timezone("Asia/ShangHai")._tzinfos#{(datetime.timedelta(seconds=29160),datetime.timedelta(0),'LMT'):,(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST'):,(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT'):}1234pytz库之所以被诟病的原因就是这三个时区的转换,让人有些难以把握。除了这些信息,他还记录了上海时区变迁的历史过程。可以使用以下代码查看importpytztz=pytz.timezone("Asia/ShangHai")fora,binzip(tz._utc_transition_times,tz._transition_info):print(a,b)"""0001-01-0100:00:00(datetime.timedelta(seconds=29160),datetime.timedelta(0),'LMT')1901-12-1320:45:52(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1919-04-1216:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1919-09-3015:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1940-05-3116:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1940-10-1215:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1941-03-1416:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1941-11-0115:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1942-01-3016:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1945-09-0115:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1946-05-1416:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1946-09-3015:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1947-04-1416:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1947-10-3115:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1948-04-3016:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1948-09-3015:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1949-04-3016:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1949-05-2715:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1986-05-0318:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1986-09-1317:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1987-04-1118:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1987-09-1217:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1988-04-1618:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1988-09-1017:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1989-04-1518:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1989-09-1617:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1990-04-1418:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1990-09-1517:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')1991-04-1318:00:00(datetime.timedelta(seconds=32400),datetime.timedelta(seconds=3600),'CDT')1991-09-1417:00:00(datetime.timedelta(seconds=28800),datetime.timedelta(0),'CST')"""12345678910111213141516171819202122232425262728293031323334353637
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 12:27 , Processed in 0.495378 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表