哈尔滨队网站网页美工,wordpress.org建站,职业技术学院网站建设项目,西安企业模板网站建设文章目录 相关文献当前时间前一天日期、后一天日期东八区#xff08;北京#xff09;时间时间戳转换datetime - strstr - datetimedatetime - timestamp(时间戳)timestamp - datetime 获取日期中的年、季度、月、周、日、小时、分、秒等时区原理时区问题复杂… 文章目录 相关文献当前时间前一天日期、后一天日期东八区北京时间时间戳转换datetime - strstr - datetimedatetime - timestamp(时间戳)timestamp - datetime 获取日期中的年、季度、月、周、日、小时、分、秒等时区原理时区问题复杂性的来源深入理解 datetime 的坑不同函数时区标准不同timezone 时区偏移以西为正以东为负和ISO标准相反笔者建议 datetime 源码解读pytz 源码解读 作者小猪快跑 基础数学计算数学从事优化领域6年主要研究方向MIP求解器、整数规划、随机规划、智能优化算法
python3 的时区是一个很容易出错的地方。本篇将从原理层面剖析时区概念让读者真正学懂时区不踩坑。
如有错误欢迎指正。如有更好的算法也欢迎交流——小猪快跑
相关文献
datetime — Basic date and time types — Python 3.12.4 documentation简述时区问题复杂性来源Python 版 - 知乎 (zhihu.com)
当前时间
from datetime import datetimeprint(datetime.now())
# 2024-07-12 14:39:59.531525前一天日期、后一天日期
from datetime import datetime, 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)东八区北京时间
import pytz
from datetime import datetimeprint(datetime(2024, 1, 1, tzinfopytz.timezone(Etc/GMT-8)))
# 2024-01-01 00:00:0008:00时间戳转换
datetime - str
import pytz
from datetime import datetimedt datetime(2024, 1, 1, tzinfopytz.timezone(Etc/GMT-8))fmt %Y-%m-%d %H:%M:%S%z
dt.strftime(fmt)
# 2024-01-01 00:00:000800fmt %a %d %b %Y, %I:%M%p
dt.strftime(fmt)
# Mon 01 Jan 2024, 12:00AMfmt %d/%m/%y %H:%M:%S.%f
dt.strftime(fmt)
# 01/01/24 00:00:00.000000The {1} is {0:%d}, the {2} is {0:%B}, the {3} is {0:%I:%M%p}..format(dt, day, month, time)
# The day is 01, the month is January, the time is 12:00AM.str - datetime
from datetime import datetimedatetime.strptime(21/11/06 16:30, %d/%m/%y %H:%M)
# datetime.datetime(2006, 11, 21, 16, 30)datetime - timestamp(时间戳)
from datetime import datetimedt datetime(2024, 1, 1)
dt.timestamp()
# 1704038400.0timestamp - datetime
from datetime import datetimedatetime.fromtimestamp(1704038400)
# datetime.datetime(2024, 1, 1, 0, 0)下面列出了 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当地语言的日期和时间表示法。Tue Aug 16 21:30:00 1988 (en_US);Di 16 Aug 21:30:00 1988 (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)%%字面%字符。%
获取日期中的年、季度、月、周、日、小时、分、秒等
from datetime import datetimedt datetime.strptime(21/11/06 16:30, %d/%m/%y %H:%M)# Using datetime.timetuple() to get tuple of all attributes
tt dt.timetuple()
for it in tt: print(it)2006 # year
11 # month
21 # day
16 # hour
30 # minute
0 # second
1 # weekday (0 Monday)
325 # number of days since 1st January
-1 # dst - method tzinfo.dst() returned None时区原理
时区问题复杂性的来源 DST (Daylight Saving Time) 夏令时 是一种在夏季期间将时钟向前调整一小时的做法目的是为了在白天较长的季节里更有效地利用自然光照从而节省能源。在夏令时期间人们可以享受到更多的日光时间理论上可以减少照明需求。 夏令时的实施通常遵循以下模式 在春季时钟在特定的周末通常是三月或四月的某个周日凌晨2点时向前调整一小时变为3点。 在秋季时钟则在特定的周末通常是十月或十一月的某个周日凌晨2点时向后调整一小时回到1点。 不过每个国家和地区的具体规则可能有所不同有些地区可能不实行夏令时。 在中国曾经在1986年至1991年间实行过夏令时具体的调整时间为 每年从四月中旬第一个星期日的凌晨2时整北京时间将时钟拨快一小时即从2时跳至3时夏令时开始到九月中旬第一个星期日的凌晨2时整北京夏令时再将时钟拨回一小时即从2时跳回至1时夏令时结束。 自1992年起中国暂停实行夏令时目前中国全境全年使用的是北京时间即东八区的标准时间没有进行夏令时调整。 ST(Standard Time) 冬令时 指的是在不实行夏令时Daylight Saving TimeDST调整的时期内所采用的标准时间。在那些实行夏令时的地区冬令时实际上就是全年时间中的“正常”时间而在夏令时期间时钟会向前调快一个小时。当夏令时结束通常在每年的秋季时钟会被拨回一小时恢复到冬令时。这一调整意味着日落时间会提前白天的时间会相应缩短而晚上则会提早变暗。冬令时的目的是在冬季减少能源消耗尤其是在北半球因为冬季的日光时间较短不需要额外延长日光时间来节约能源。中国在1986年至1991年间曾经实行过夏令时相应的在这期间的非夏令时阶段即为所谓的“冬令时”。然而自1992年起中国停止实行夏令时因此也不再有冬令时的概念。目前中国全境全年使用的是北京时间即东八区的标准时间UTC8不再进行任何季节性的时间调整。在世界其他实行夏令时的国家和地区冬令时通常是从每年的秋季持续到次年的春季具体日期可能因国家而异。例如美国和加拿大在每年11月的第一个周日开始冬令时而欧洲大多数国家则在10月的最后一个周日开始。 GMT (Greenwich Mean Time) 格林尼治标准时间 这是以英国格林尼治天文台观测结果得出的时间这是英国格林尼治当地时间这个地方的当地时间过去被当成世界标准的时间。 UT (Universal Time) 世界时 以本初子午线的平子夜起算的平太阳时。又称格林尼治平时或格林尼治时间。各地的地方平时与世界时之差等于该地的地理经度。1960年以前曾作为基本时间计量系统被广泛应用。由于地球自转速度变化的影响它不是一种均匀的时间系统。后来世界时先后被历书时和原子时所取代但在日常生活、天文导航、大地测量和宇宙飞行等方面仍属必需同时世界时反映地球自转速率的变化是地球自转参数之一仍为天文学和地球物理学的基本资料。 TAIInternational Atomic Time国际原子时 原子时计量的基本单位原子时秒。由原子钟导出。原子时秒的定义铯 -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 (Universal Time Coordinated) 协调世界时 国际原子时的准确度为每日数纳秒而世界时的准确度为每日数毫秒。许多应用部门要求时间系统接近世界时UT对于这种情况一种称为协调世界时的折中时标于1972年面世。为确保协调世界时与世界时相差不会超过0.9秒在有需要的情况下会在协调世界时内加上正或负闰秒。因此协调世界时与国际原子时之间会出现若干整数秒的差别两者之差逐年积累便采用跳秒闰秒的方法使协调时与世界时的时刻相接近其差不超过1s。它既保持时间尺度的均匀性又能近似地反映地球自转的变化。 [2]按国际无线电咨询委员会CCIR通过的关于UTC的修正案从1972年1月1日起UTC与UT1在UT中加入极移改正得到之间的差值最大可以达到±0.9s。位于巴黎的国际地球自转事务中央局负责决定何时加入闰秒。一般会在每年的6月30日、12月31日的最后一秒进行调整。原子时 自转因素闰秒 UTC LMT (Local Mean Time) 地方平时 地方平时是太阳时改变形式修正后在指定的经度范围内使用一致时间的地方太阳时他的一致性仅取决于测量用的钟表准确性。地方平时从19世纪初期开始逐渐被采用这些地区都不再使用地方太阳时或日晷的时间直到每个国家都以各种不同的形式将之定为标准时间。标准时间意味着相同的时间使用在一些区域中— 通常不是从格林威治标准时间中抵销就是选择区域内主要区域的地方时间作为标准时间。地方平时和视太阳时之间的差别就是均时差equation of time。地球划分出不同的时区每个时区都有一个自己的当地时间。比如上海 LMT 就是 UTC 08:06。原子时 自转因素闰秒 地理因素 LMT CST时间 China Standard Time 某个国家统一采用某个时区的时间比如上海的采用标准时间就是UTC8小时。夏令时/冬令时。比如美国会在夏季将时间拨快一个小时。这个时间称之为标准时间。历史因素比如中国曾在 1986 年到 1991 年的内实行过夏令时以上海为例他在1988年8月份的标准时间就是 UTC 9:00, 而在 1988年 12 月份标准时间是 UTC 8:00。原子时 自转因素闰秒 法律因素法律选区的特定时区、 冬令时、夏令时 历史因素 当地标准时间
以上几个因素是时区问题复杂度的来源为了解决这个问题人们成立了时区信息数据库Linux 系统也是采用了该数据库来维护系统时间。
深入理解 datetime 的坑
不同函数时区标准不同
创建 datetime 对象使用的是 LMT
import pytz
from datetime import datetime# LMT
datetime(2021, 1, 1, tzinfopytz.timezone(Asia/Shanghai)) # 2021-01-01 00:00:0008:06
datetime(2021, 1, 1, tzinfopytz.timezone(Asia/Tokyo)) # 2021-01-01 00:00:0009:19转换时区函数 astimezone 输出是 ST/DST除非输入时区 输出时区
import pytz
from datetime import datetimetz_sh datetime(2021, 1, 1, tzinfopytz.timezone(Asia/Shanghai)) # 2021-01-01 00:00:0008:06
# UTC
tz_sh.astimezone(pytz.timezone(Asia/Tokyo)) # 2021-01-01 00:54:0009:00
# LMT
tz_sh.astimezone(pytz.timezone(Asia/Shanghai)) # 2021-01-01 00:00:0008:06替换时区函数 replace 是 LMT
import pytz
from datetime import datetimetz_sh datetime(2021, 1, 1, tzinfopytz.timezone(Asia/Shanghai)) # 2021-01-01 00:00:0008:06
tz_sh.replace(tzinfopytz.timezone(Asia/Tokyo)) # 2021-01-01 00:00:0009:19获取当前时间 datetime.now 是 ST/DST
import pytz
from datetime import datetime# UTC
datetime.now(tzpytz.timezone(Asia/Shanghai)) # 2024-07-18 14:37:33.70664908:00normalize: LMT 转换成 ST/DST注意1988年中国有夏令时
import pytz
from datetime import datetimetz pytz.timezone(Asia/ShangHai)
tz.normalize(datetime(2021, 1, 1, tzinfotz)) # 2020-12-31 23:54:0008:00
tz.normalize(datetime(1988, 8, 1, tzinfotz)) # 1988-08-01 00:54:0009:00localize增加 ST/DST注意1988年中国有夏令时
import pytz
from datetime import datetimetz pytz.timezone(Asia/ShangHai)
tz.localize(datetime(2021, 1, 1)) # 2021-01-01 00:00:0008:00
tz.localize(datetime(1988, 8, 1)) # 1988-08-01 00:00:0009:00timezone 时区偏移以西为正以东为负和ISO标准相反
import pytz
from datetime import datetime# 东八区
datetime(2021, 1, 1, tzinfopytz.timezone(Etc/GMT-8)) # 2021-01-01 00:00:0008:00
# 西八区
datetime(2021, 1, 1, tzinfopytz.timezone(Etc/GMT8)) # 2021-01-01 00:00:00-08:00笔者建议
如果不存在夏令时类似的情况为了避免函数时区标准不同的麻烦可以直接使用 Etc/GMT-8。如果存在夏令时这些那么在转换时要注意不同标准带来的误差。尽量使用无时区信息的时间戳进行计算
datetime 源码解读
或许是由于时区非常复杂datetime 时区仅提供了 tzinfo 的抽象类。可以使用常见的时区库如 pytz。
tzinfo 两个接口值得注意
class tzinfo:abstractmethoddef tzname(self, __dt: datetime | None) - str | None: ...abstractmethoddef utcoffset(self, __dt: datetime | None) - timedelta | None: ...# Return the timezone offset as timedelta positive east of UTC (negative west of UTC).# 这是描述了该时区当地平均时间与 UTC 时间的偏移abstractmethoddef dst(self, __dt: datetime | None) - timedelta | None: ...def fromutc(self, __dt: datetime) - datetime: ...# datetime in UTC - datetime in local time.# 这个接口做的事情是将该UTC时间更改为当地标准时间。# 这是一个法律概念上的时间需要考虑到夏令时历史等因素。datetime 中更换时区的基本过程通过两个时区相对于 utc 时间的偏移计算出两个时区的时间间隔。加上该间隔然后直接更换时区:
(dt timedelta).raplace(tz)
pytz 源码解读
pytz 是时区信息数据库的 python 接口使用了 tzif 文件来存储、描述时区与 linux 相同。
pytz.timezone(Asia/ShangHai) 读取 tzif 文件中的信息创建对象。
一般我们认为 pytz.timezone(Asia/ShangHai) 是一个时区其实这不完全正确准确来说pytz.timezone(Asia/ShangHai) 应该是一个地方时区库。
它存储了三个时区:
LMT 8:06CST 8:00CDT 9:00 已废弃的夏令时
可以通过一下代码进行查看
import pytzpytz.timezone(Asia/ShangHai)._tzinfos
# {(datetime.timedelta(seconds29160), datetime.timedelta(0), LMT): DstTzInfo Asia/Shanghai LMT8:06:00 STD, (datetime.timedelta(seconds28800), datetime.timedelta(0), CST): DstTzInfo Asia/Shanghai CST8:00:00 STD, (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT): DstTzInfo Asia/Shanghai CDT9:00:00 DST}pytz 库之所以被诟病的原因就是这三个时区的转换让人有些难以把握。
除了这些信息他还记录了上海时区变迁的历史过程。 可以使用以下代码查看
import pytztz pytz.timezone(Asia/ShangHai)
for a, b in zip(tz._utc_transition_times, tz._transition_info):print(a, b)0001-01-01 00:00:00 (datetime.timedelta(seconds29160), datetime.timedelta(0), LMT)
1901-12-13 20:45:52 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1919-04-12 16:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1919-09-30 15:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1940-05-31 16:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1940-10-12 15:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1941-03-14 16:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1941-11-01 15:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1942-01-30 16:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1945-09-01 15:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1946-05-14 16:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1946-09-30 15:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1947-04-14 16:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1947-10-31 15:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1948-04-30 16:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1948-09-30 15:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1949-04-30 16:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1949-05-27 15:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1986-05-03 18:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1986-09-13 17:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1987-04-11 18:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1987-09-12 17:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1988-04-16 18:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1988-09-10 17:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1989-04-15 18:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1989-09-16 17:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1990-04-14 18:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1990-09-15 17:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)
1991-04-13 18:00:00 (datetime.timedelta(seconds32400), datetime.timedelta(seconds3600), CDT)
1991-09-14 17:00:00 (datetime.timedelta(seconds28800), datetime.timedelta(0), CST)