说起 python时间,我这心里啊,五味杂陈。你说它简单吧,也就那几个模块,datetime
、time
啥的;可真要用起来,尤其掺和进数据库、前端、不同系统、跨国协作这些破事儿,瞬间就感觉掉坑里了,而且是那种深不见底的坑,全是时区和格式化这些玩意儿挖的。今天就来掰扯掰扯,这 python时间怎么 才能玩得溜一点,少掉点头发。
刚开始学 Python 那会儿,想获取当前时间,多简单啊,import datetime
,然后 datetime.datetime.now()
,嗖!一个时间对象就出来了,美滋滋。但这玩意儿,用久了就发现不对劲。尤其当你处理日志,或者需要精确计算两个事件间隔的时候,会发现,咦,怎么有时候算出来的时间差总是差那么几个小时?或者,存到数据库里的时间和我在本地看到的不一样?问题多半出在 时区 上。
默认的 datetime.now()
啊,它给的是本地时间,但这个“本地”取决于你程序跑在哪台机器上,那机器设的是哪个时区。这在自己电脑上玩玩没啥,可一旦部署到服务器,尤其服务器可能在地球另一边,或者更要命的是,你的应用要服务全球用户,那本地时间就完全不够看了。我血泪的教训告诉我,处理时间,尤其是需要跨系统、跨地域传递或存储的时间,永远、永远、永远 使用 UTC 时间 (世界协调时)。Python 里获取 UTC 时间,用 datetime.datetime.utcnow()
,或者更推荐的方式是使用带时区信息的 datetime.datetime.now(datetime.timezone.utc)
。后者返回的是一个“有感知”的时间对象,它知道自己是哪个时区的,这比前者的“无感知”UTC时间对象好多了。
但光有 UTC 时间还不够啊,有时候你得把它展示给用户看,用户看 UTC 时间多半是懵圈的。这时候就需要 时区转换 了。标准库里有个 zoneinfo
模块(Python 3.9+),之前大家普遍用的是第三方库 pytz
。说实话,pytz
用起来有点怪,比如 pytz.timezone('Asia/Shanghai').localize(naive_datetime)
来把一个“无感知”的本地时间变“有感知”,或者 utc_dt.astimezone(tz_shanghai)
来把 UTC 时间转成上海时间。反正核心思想就是:把时间对象变成带时区信息的,然后在不同时区之间转换。这块儿是新手最容易摔跟头的地方,因为一个“无感知”的时间对象,看着和带时区的 UTC 时间对象可能长得一样,但它们完全是两码事!计算、转换的时候,一旦混用,结果就全乱了。记住,要带上时区信息!
除了 datetime
对象本身,时间戳 (Timestamp) 也是个绕不开的概念。这玩意儿其实就是从某个固定的时间点(Unix 纪元,1970年1月1日 00:00:00 UTC)到现在的秒数,通常是个浮点数。时间戳最大的好处是它是个数字,没有时区、格式这些花里胡哨的问题,在不同系统之间传递时间信息特别方便。Python 里,把 datetime 对象变成时间戳,直接调用 .timestamp()
方法就行。比如 datetime.datetime.now(datetime.timezone.utc).timestamp()
就能得到当前的 UTC 时间戳。反过来,要把时间戳变回 datetime 对象,用 datetime.datetime.fromtimestamp(timestamp)
。注意这里的坑:fromtimestamp
默认是根据你本地时区来解析时间戳的!也就是说,同一个 UTC 时间戳,你在上海的机器上跑 fromtimestamp
,出来的是上海时间;在纽约的机器上跑,出来的是纽约时间。如果你想要 UTC 时间的 datetime 对象,得用 datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
或者 datetime.datetime.utcfromtimestamp(timestamp)
(但 utcfromtimestamp
不推荐,因为返回的是无感知对象)。看吧,处处是陷阱!
实际开发中,我们经常需要把时间对象按特定的格式显示,或者把一个字符串解析成时间对象。这就要用到 strftime 和 strptime 这对好基友了。strftime (string format time) 是把 datetime 对象格式化成字符串,比如 dt.strftime('%Y-%m-%d %H:%M:%S')
会把 2024-07-19 10:30:00
这样的 datetime 对象变成 "2024-07-19 10:30:00"
这个字符串。那些 %Y
、%m
、%d
、%H
、%M
、%S
都是格式代码,代表年、月、日、小时、分钟、秒。还有 %A
代表星期几(Full name),%B
代表月份(Full name)等等,一堆。我就不全列了,用的时候查文档最靠谱。
反过来,strptime (string parse time) 是把一个时间字符串解析成 datetime 对象。比如 datetime.datetime.strptime("2024-07-19 10:30:00", '%Y-%m-%d %H:%M:%S')
就能把字符串 "2024-07-19 10:30:00"
变成对应的 datetime 对象。strptime 的关键 是你提供的格式字符串必须完全匹配你要解析的字符串格式。多一个空格,少一个标点,或者年、月、日顺序不对,都会直接报错。我遇到过最头疼的情况就是,对接方给的时间格式千奇百怪,有的是 YYYYMMDDHHMMSS
这种没分隔符的,有的是带毫秒的,有的是时区信息格式不对的。每次都要对着文档,一个字符一个字符地凑格式字符串,简直要命!所以,如果你有机会定义时间格式,请务必使用标准、清晰的格式,比如 ISO 8601 (YYYY-MM-DDTHH:MM:SS.ffffff+HH:MM
),这能省掉无数麻烦。
除了获取、格式化、解析,python时间 怎么少得了 时间计算 呢?比如我想知道从现在开始三天后是几月几号几点,或者我的程序跑了多久。这时候 timedelta
就登场了。timedelta 对象表示两个时间点之间的差值,可以是天、小时、分钟、秒、甚至微秒。你可以像创建 datetime 对象一样创建它,比如 datetime.timedelta(days=3, hours=5)
表示3天5小时。然后,它可以和 datetime 对象进行加减运算:current_time + datetime.timedelta(days=3)
就能得到三天后的时间。计算两个时间点之间的差值也很简单,直接用减法:end_time - start_time
得到的就是一个 timedelta
对象,你可以访问它的 days
、seconds
(这里秒数是指除了天以外的秒数)等属性来获取具体的时间差。这在计算任务耗时、定时任务调度、或者根据时间间隔生成一系列时间点时特别有用。
话说回来,虽然 datetime
模块功能挺全的,但处理复杂的循环日期、比如每月的最后一个周五,或者计算两个日期之间的工作日数量,用标准库写起来还是有点费劲。这时候 might consider 试试第三方库 dateutil
。它提供了更强大的日期解析能力(比如能解析很多模糊的时间字符串,“明天”、“下周三”),以及更灵活的日期计算规则。但我一般小打小闹还是标准库就够了,毕竟能少引入依赖就少引入。
写到这儿,感觉 python时间怎么 用,好像也没那么神秘了,核心就是那几个对象和方法:datetime
对象代表一个具体的时间点,timedelta
对象代表一段时间间隔,然后用 strftime 和 strptime 在 datetime 对象和字符串之间互相转换,用 .timestamp()
和 fromtimestamp
在 datetime 对象和时间戳之间转换。最关键的,也是最容易被忽视的,是 时区!处理时间,脑子里一定要有“这个时间是哪个时区的?”这根弦。一旦涉及到跨系统、跨地域,或者你需要精确控制时间,请务必使用带时区信息的 datetime 对象,并且强烈推荐以 UTC 时间 作为内部存储和传递的标准。展示给用户的时候再根据用户的时区进行转换。
总之,python时间怎么 用,不是简单的函数调用,它背后涉及到对时间概念、时区、不同表示形式(对象、字符串、时间戳)的理解和正确处理。踩过的坑多了,自然就知道哪里需要小心了。希望我这些碎碎念,能帮你少走点弯路吧。
评论(0)