datetime与时间处理

Python进阶编程专题 · Python日期时间处理完整指南

专题:Python进阶编程系统学习

关键词:Python, datetime, date, time, timedelta, 时区, strftime, dateutil, pytz

一、datetime模块概述

Python内置的datetime模块是处理日期和时间的标准库,提供了五个核心类:date(日期)、time(时间)、datetime(日期时间)、timedelta(时间间隔)和timezone(时区)。这些类协同工作,覆盖了绝大多数日期时间处理场景。相比于第三方库,标准库的优势在于无需额外安装,但某些高级功能(如复杂时区数据库、工作日计算)需要借助dateutilzoneinfo

核心概念:在Python中,日期时间对象分为"naive"(朴素,不带时区信息)和"aware"(感知,带时区信息)两种。官方推荐在生产环境中始终使用aware对象以避免时区混淆问题。

类名描述示例
date表示年、月、日date(2026, 5, 5)
time表示时、分、秒、微秒time(14, 30, 0)
datetime组合日期和时间datetime(2026, 5, 5, 14, 30)
timedelta时间间隔/差值timedelta(days=7)
timezone固定偏移时区timezone(timedelta(hours=8))

二、日期时间创建与属性访问

2.1 创建date对象

from datetime import date # 基本创建:年、月、日 d1 = date(2026, 5, 5) print(d1) # 2026-05-05 # 获取当前本地日期 today = date.today() print(today) # 2026-05-05 # 从时间戳创建 d2 = date.fromtimestamp(1774886400) print(d2) # 从ISO格式字符串创建 d3 = date.fromisoformat("2026-05-05") print(d3) # 替换属性生成新对象 d4 = d1.replace(year=2027) print(d4) # 2027-05-05

2.2 创建time对象

from datetime import time # 时、分、秒、微秒、时区信息 t1 = time(14, 30, 45) print(t1) # 14:30:45 t2 = time(14, 30, 45, 123456) print(t2) # 14:30:45.123456 # 属性访问 print(t1.hour, t1.minute, t1.second, t1.microsecond) # 14 30 45 0

2.3 创建datetime对象

from datetime import datetime # 同时指定日期和时间 dt1 = datetime(2026, 5, 5, 14, 30, 0) print(dt1) # 2026-05-05 14:30:00 # 获取当前日期时间 now = datetime.now() # 本地时间 utc_now = datetime.utcnow() # UTC时间(不推荐,返回naive对象) print(now) # 获取当前UTC aware时间(推荐方式) from datetime import timezone utc_aware = datetime.now(timezone.utc) print(utc_aware) # 2026-05-05 06:30:00+00:00 # combine():合并date和time对象 d = date(2026, 5, 5) t = time(14, 30) dt2 = datetime.combine(d, t) print(dt2) # 2026-05-05 14:30:00 # 从字符串解析 dt3 = datetime.strptime("2026-05-05 14:30:00", "%Y-%m-%d %H:%M:%S") print(dt3) # 从ISO格式字符串解析(Python 3.7+) dt4 = datetime.fromisoformat("2026-05-05T14:30:00") print(dt4)

2.4 属性访问

from datetime import datetime dt = datetime(2026, 5, 5, 14, 30, 45, 123456) print(dt.year) # 2026 print(dt.month) # 5 print(dt.day) # 5 print(dt.hour) # 14 print(dt.minute) # 30 print(dt.second) # 45 print(dt.microsecond) # 123456 # 获取周几(Monday=0, Sunday=6) print(dt.weekday()) # 1(周二) # 获取周几(Monday=1, Sunday=7) print(dt.isoweekday()) # 2(周二) # 获取年月日(元组) print(dt.timetuple()) # time.struct_time # 获取ISO日历元组(年、周数、周几) print(dt.isocalendar()) # (2026, 19, 2) # 提取date和time部分 print(dt.date()) # 2026-05-05 print(dt.time()) # 14:30:45.123456

提示:isocalendar()返回的ISO周数在某些业务场景(如财务报表按周统计)中非常有用。ISO标准中,一周从周一开始,每年第一周是包含该年第一个周四的周。

三、timedelta与日期时间运算

3.1 timedelta基本用法

timedelta表示两个日期时间之间的差值,支持天、秒、微秒、毫秒、分钟、小时和周等参数。

from datetime import timedelta, datetime # 创建时间间隔 td1 = timedelta(days=7) # 7天 td2 = timedelta(hours=3, minutes=30) # 3小时30分钟 td3 = timedelta(weeks=2, days=3) # 2周3天 = 17天 # 查看timedelta的内部表示 print(td1.days) # 7 print(td1.seconds) # 0 print(td1.total_seconds()) # 604800.0(7*24*3600)

3.2 日期时间加减

from datetime import datetime, timedelta now = datetime(2026, 5, 5, 14, 30) # 加减时间 future = now + timedelta(days=10) # 10天后 past = now - timedelta(weeks=2) # 2周前 next_hour = now + timedelta(hours=1) # 1小时后 half_day_later = now + timedelta(hours=12) # 12小时后 print(future) # 2026-05-15 14:30:00 print(past) # 2026-04-21 14:30:00 # 连续运算 result = now + timedelta(days=30) - timedelta(hours=5) print(result) # 2026-06-04 09:30:00 # 计算两个日期之间的差值,结果也是timedelta d1 = datetime(2026, 5, 5) d2 = datetime(2026, 1, 1) delta = d1 - d2 print(delta.days) # 124 print(delta.total_seconds()) # 10713600.0

3.3 日期时间比较

from datetime import datetime, date d1 = date(2026, 5, 5) d2 = date(2026, 6, 1) print(d1 < d2) # True print(d1 == d2) # False print(d1 <= d2) # True # 判断日期范围 target = date(2026, 5, 15) start = date(2026, 5, 1) end = date(2026, 5, 31) if start <= target <= end: print("Target is within May 2026") # 查找最接近的日期 dates = [date(2026, 5, 10), date(2026, 5, 20), date(2026, 6, 1)] target = date(2026, 5, 15) closest = min(dates, key=lambda x: abs((x - target).days)) print(closest) # 2026-05-10(最接近5月15日)

最佳实践:比较操作会自动处理秒、微秒级别的精度。在比较时,确保两个对象要么都是naive,要么都是aware(且使用相同时区),否则会引发TypeError

四、格式化与解析

4.1 strftime:日期时间转字符串

from datetime import datetime dt = datetime(2026, 5, 5, 14, 30, 45) # 常用格式 print(dt.strftime("%Y-%m-%d")) # 2026-05-05 print(dt.strftime("%Y-%m-%d %H:%M:%S")) # 2026-05-05 14:30:45 print(dt.strftime("%Y年%m月%d日")) # 2026年05月05日 print(dt.strftime("%A, %B %d, %Y")) # Tuesday, May 05, 2026 print(dt.strftime("%I:%M %p")) # 02:30 PM # 中文环境星期、月份 print(dt.strftime("%A")) # Tuesday print(dt.strftime("%a")) # Tue print(dt.strftime("%B")) # May print(dt.strftime("%b")) # May # 周数和年中的第几天 print(dt.strftime("%W")) # 19(今年第19周,周一为一周开始) print(dt.strftime("%j")) # 125(今年第125天)
指令含义示例
%Y4位年份2026
%y2位年份26
%m2位月份(01-12)05
%d2位日期(01-31)05
%H24小时制(00-23)14
%I12小时制(01-12)02
%M分钟(00-59)30
%S秒(00-59)45
%pAM/PMPM
%A完整星期名称Tuesday
%a缩写星期名称Tue
%B完整月份名称May
%b缩写月份名称May
%j年中的第几天(001-366)125
%W年中的第几周(周一为第一天)19
%w周几的数字表示(0=周日,6=周六)2
%zUTC偏移(±HHMM)+0800
%Z时区名称CST
%%百分号字面量%

4.2 strptime:字符串解析为日期时间

from datetime import datetime # 标准格式解析 dt1 = datetime.strptime("2026-05-05 14:30:00", "%Y-%m-%d %H:%M:%S") print(dt1) # 2026-05-05 14:30:00 # 中文格式解析 dt2 = datetime.strptime("2026年05月05日 14:30:45", "%Y年%m月%d日 %H:%M:%S") print(dt2) # 12小时制解析 dt3 = datetime.strptime("05/05/2026 02:30 PM", "%m/%d/%Y %I:%M %p") print(dt3) # 2026-05-05 14:30:00 # 带时区偏移的解析 dt4 = datetime.strptime("2026-05-05T14:30:00+0800", "%Y-%m-%dT%H:%M:%S%z") print(dt4) # 2026-05-05 14:30:00+08:00 # 容错解析常见日期格式(自定义函数) def parse_date(date_str): formats = [ "%Y-%m-%d", "%Y/%m/%d", "%m/%d/%Y", "%Y年%m月%d日", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S", ] for fmt in formats: try: return datetime.strptime(date_str, fmt) except ValueError: continue raise ValueError(f"无法解析日期字符串: {date_str}") print(parse_date("2026/05/05")) # 2026-05-05 00:00:00 print(parse_date("2026年05月05日")) # 2026-05-05 00:00:00

注意:strptime在格式不匹配时会抛出ValueError。对于未知格式的字符串,建议先尝试多种可能的格式,或者使用dateutil.parser进行智能解析。

五、时区处理

5.1 使用内置timezone

Python 3.2+ 内置的timezone类只能表示固定偏移的时区,不支持夏令时(DST)自动切换。适合简单的UTC偏移场景。

from datetime import datetime, timezone, timedelta # 定义时区 utc = timezone.utc cst = timezone(timedelta(hours=8)) # 中国标准时间 UTC+8 est = timezone(timedelta(hours=-5)) # 美国东部时间 UTC-5 # 为naive datetime添加时区信息 dt_naive = datetime(2026, 5, 5, 14, 30) dt_utc = dt_naive.replace(tzinfo=utc) dt_cst = dt_naive.replace(tzinfo=cst) print(dt_utc) # 2026-05-05 14:30:00+00:00 print(dt_cst) # 2026-05-05 14:30:00+08:00 # 时区转换:从CST转到UTC dt_cst = datetime(2026, 5, 5, 14, 30, tzinfo=cst) dt_utc_converted = dt_cst.astimezone(utc) print(dt_utc_converted) # 2026-05-05 06:30:00+00:00 # 转换为其他时区 dt_est = dt_cst.astimezone(est) print(dt_est) # 2026-05-05 01:30:00-05:00

5.2 使用zoneinfo(Python 3.9+)

zoneinfo是Python 3.9引入的正式时区库,使用IANA时区数据库,支持DST自动切换,是处理时区的推荐方案。

from datetime import datetime from zoneinfo import ZoneInfo # Python 3.9+ # 使用时区名称创建aware datetime shanghai = ZoneInfo("Asia/Shanghai") nyc = ZoneInfo("America/New_York") tokyo = ZoneInfo("Asia/Tokyo") london = ZoneInfo("Europe/London") dt_sh = datetime(2026, 5, 5, 14, 30, tzinfo=shanghai) print(dt_sh) # 2026-05-05 14:30:00+08:00 # 不同时区之间转换 dt_ny = dt_sh.astimezone(nyc) dt_tokyo = dt_sh.astimezone(tokyo) print(dt_ny) # 2026-05-05 02:30:00-04:00 print(dt_tokyo) # 2026-05-05 15:30:00+09:00 # 获取当前时间在指定时区 now_sh = datetime.now(shanghai) now_ny = datetime.now(nyc) now_utc = datetime.now(ZoneInfo("UTC")) print(now_sh) # 2026-05-05 22:48:12.xxxxxx+08:00 print(now_ny) # 2026-05-05 10:48:12.xxxxxx-04:00 # 列出所有可用时区 import zoneinfo zones = zoneinfo.available_timezones() print(len(zones)) # 约600个时区名称 print("Asia/Shanghai" in zones) # True

5.3 使用pytz(Python 3.9之前的方案)

pytz是第三方库,在zoneinfo出现之前被广泛使用。需要注意的是,pytz的使用方式与标准库有所不同,不能直接用tzinfo参数创建datetime对象。

import pytz from datetime import datetime # pytz的正确使用方式 tz_sh = pytz.timezone("Asia/Shanghai") tz_ny = pytz.timezone("America/New_York") # 正确:localize方法 dt_naive = datetime(2026, 5, 5, 14, 30) dt_sh = tz_sh.localize(dt_naive) print(dt_sh) # 2026-05-05 14:30:00+08:00 # 错误:直接使用tzinfo参数(pytz会出错!) # dt_wrong = datetime(2026, 5, 5, 14, 30, tzinfo=tz_sh) # 不推荐 # 时区转换 dt_ny = dt_sh.astimezone(tz_ny) print(dt_ny) # 2026-05-05 02:30:00-04:00 # 处理夏令时 # pytz的normalize方法自动处理DST dt_us = pytz.timezone("US/Eastern").localize( datetime(2026, 3, 8, 2, 30) ) print(dt_us) # 2026-03-08 03:30:00-04:00(DST自动调整)

版本选择建议:Python 3.9+ 用户优先使用zoneinfo(标准库,无需安装);Python 3.8及以下版本需要使用pytz;简单的固定偏移场景使用内置timezone即可。

六、dateutil库高级用法

6.1 relativedelta:更灵活的时间运算

relativedelta弥补了timedelta不支持按"月"或"年"加减的缺陷,还支持按工作日、周几等语义化操作。

from datetime import datetime from dateutil.relativedelta import relativedelta dt = datetime(2026, 5, 5) # 按月加减(timedelta做不到) next_month = dt + relativedelta(months=1) print(next_month) # 2026-06-05 last_year = dt - relativedelta(years=1) print(last_year) # 2025-05-05 # 月末处理(自动处理不同月份天数差异) dt_jan31 = datetime(2026, 1, 31) dt_feb = dt_jan31 + relativedelta(months=1) print(dt_feb) # 2026-02-28(自动调整为2月最后一天,而非越界) # 月末修正策略 dt_feb_last = dt_jan31 + relativedelta(months=1, day=31) print(dt_feb_last) # 2026-02-28 # 工作日计算 # 下周一 next_monday = dt + relativedelta(weekday=0) print(next_monday) # 2026-05-11(如果5月5日是周二) # 本月最后一个周五 last_friday = dt + relativedelta(day=31, weekday=4(-1)) print(last_friday) # 计算两个日期之间的差值(年、月、日) d1 = datetime(2020, 3, 15) d2 = datetime(2026, 5, 5) rd = relativedelta(d2, d1) print(f"{rd.years}年{rd.months}月{rd.days}日") # 6年1月20日

6.2 dateutil.parser:智能字符串解析

from dateutil import parser # 自动识别各种常见日期格式 print(parser.parse("2026-05-05")) # 2026-05-05 00:00:00 print(parser.parse("05/05/2026")) # 2026-05-05 00:00:00 print(parser.parse("May 5, 2026 2:30 PM")) # 2026-05-05 14:30:00 print(parser.parse("2026-05-05T14:30:00+08:00")) # 2026-05-05 14:30:00+08:00 print(parser.parse("5th May 2026")) # 2026-05-05 00:00:00 # 相对日期解析(英文) print(parser.parse("today")) # 2026-05-05 print(parser.parse("yesterday")) # 2026-05-04 print(parser.parse("next Monday")) # 最近的下周一 # 模糊解析(忽略无关文字) print(parser.parse("The meeting is on May 5, 2026 at 2pm", fuzzy=True)) # 2026-05-05 14:00:00

6.3 rrule:重复规则与日期生成

from datetime import datetime from dateutil.rrule import rrule, DAILY, WEEKLY, MONTHLY, MO, TU, WE, TH, FR # 每日重复,生成10天 start = datetime(2026, 5, 1) daily = list(rrule(DAILY, count=5, dtstart=start)) for d in daily: print(d.strftime("%Y-%m-%d %A")) # 2026-05-01 Friday # 2026-05-02 Saturday # 2026-05-03 Sunday # 2026-05-04 Monday # 2026-05-05 Tuesday # 每周一、三、五,持续4周 weekly = list(rrule(WEEKLY, byweekday=(MO, WE, FR), count=12, dtstart=start)) for d in weekly: print(d.strftime("%Y-%m-%d %A")) # 每月第一个周一 first_monday = rrule(MONTHLY, byweekday=MO(1), count=6, dtstart=start) for d in first_monday: print(d.strftime("%Y-%m-%d %A")) # 每月最后一个工作日 last_business = rrule(MONTHLY, byweekday=(MO, TU, WE, TH, FR)(-1), count=6, dtstart=start) for d in last_business: print(d.strftime("%Y-%m-%d %A"))

七、时间戳转换

7.1 时间戳与datetime互转

时间戳(Unix timestamp)是指从1970年1月1日UTC开始的秒数(忽略闰秒),是跨语言通用的时间表示方式。

from datetime import datetime, timezone import time # datetime转时间戳 dt = datetime(2026, 5, 5, 14, 30, tzinfo=timezone.utc) ts = dt.timestamp() print(ts) # 1777552200.0 # 时间戳转datetime(UTC) dt_from_ts = datetime.fromtimestamp(ts, tz=timezone.utc) print(dt_from_ts) # 2026-05-05 14:30:00+00:00 # 时间戳转datetime(本地时区) # 不指定tz时,使用系统本地时区 dt_local = datetime.fromtimestamp(ts) print(dt_local) # 2026-05-05 22:30:00(东八区) # 时间戳转UTC naive时间(已废弃,不推荐) # dt_utcnaive = datetime.utcfromtimestamp(ts) # Python 3.12+ 已废弃 # 使用time模块获取时间戳 current_ts = time.time() print(current_ts) # 当前时间戳 # struct_time转时间戳 t = time.mktime(time.localtime()) print(t) # 毫秒时间戳处理 millis = int(dt.timestamp() * 1000) print(millis) # 1777552200000 millis_to_dt = datetime.fromtimestamp(millis / 1000.0, tz=timezone.utc) print(millis_to_dt) # 恢复为原时间

注意事项:datetime.fromtimestamp()返回的是本地时间的naive对象(如果不传tz参数),而datetime.utcfromtimestamp()在Python 3.12+已被标记为废弃。推荐始终传递tz=timezone.utc以获得明确的aware对象。

7.2 常见时间戳应用场景

from datetime import datetime, timezone import time # 场景1:API响应中的时间戳解析 api_timestamp = "1714896000" dt = datetime.fromtimestamp(int(api_timestamp), tz=timezone.utc) print(dt.strftime("%Y-%m-%d %H:%M:%S UTC")) # 场景2:计算当前时间戳(毫秒)用于日志/数据库 def current_millis(): return int(time.time() * 1000) print(current_millis()) # 场景3:时间戳与人类可读格式互转工具 def ts_to_readable(ts, tz=timezone.utc): return datetime.fromtimestamp(ts, tz=tz).strftime("%Y-%m-%d %H:%M:%S %Z") def readable_to_ts(date_str, fmt="%Y-%m-%d %H:%M:%S", tz=timezone.utc): dt = datetime.strptime(date_str, fmt).replace(tzinfo=tz) return dt.timestamp() print(ts_to_readable(1777552200)) # 2026-05-05 14:30:00 UTC print(readable_to_ts("2026-05-05 14:30:00")) # 1777552200.0

八、工作日计算实用技巧

8.1 自定义工作日历

from datetime import date, timedelta from dateutil.rrule import rrule, DAILY, MO, TU, WE, TH, FR # 计算两个日期之间的工作日数量 def count_business_days(start, end): return len(list(rrule(DAILY, dtstart=start, until=end, byweekday=(MO, TU, WE, TH, FR)))) start = date(2026, 5, 1) end = date(2026, 5, 31) print(count_business_days(start, end)) # 21个工作日(假设5月1日为周五) # 计算从今天起N个工作日后的日期 def add_business_days(start, n): # 简单版本:跳过周末 current = start while n > 0: current += timedelta(days=1) if current.weekday() < 5: # 周一到周五 n -= 1 return current print(add_business_days(date(2026, 5, 1), 10)) # 7个工作日后的日期 # 使用rrule实现更灵活的版本(跳过节假日) def add_business_days_rrule(start, n, holidays=[]): biz_days = rrule(DAILY, byweekday=(MO, TU, WE, TH, FR), dtstart=start + timedelta(days=1)) count = 0 for d in biz_days: if d.date() not in holidays: count += 1 if count == n: return d.date() return None holidays = [date(2026, 5, 4)] # 假设5月4日是法定假日 result = add_business_days_rrule(date(2026, 5, 1), 3, holidays) print(result)

8.2 日期范围生成

from datetime import date, timedelta # 生成某一周的所有日期 def week_dates(year, week_number): # 使用ISO周数计算 jan4 = date(year, 1, 4) # 一定在第一周的日期 start_of_week1 = jan4 - timedelta(days=jan4.isoweekday() - 1) start = start_of_week1 + timedelta(weeks=week_number - 1) return [start + timedelta(days=i) for i in range(7)] print(week_dates(2026, 19)) # [2026-05-04, 2026-05-05, 2026-05-06, 2026-05-07, 2026-05-08, 2026-05-09, 2026-05-10] # 生成指定月份的所有日期 def month_dates(year, month): import calendar _, days = calendar.monthrange(year, month) return [date(year, month, d) for d in range(1, days + 1)] print(month_dates(2026, 5)) # [2026-05-01, ..., 2026-05-31] 共31天 # 日期范围生成器(可指定步长) def date_range(start, end, step=timedelta(days=1)): current = start while current <= end: yield current current += step # 遍历5月每天 for d in date_range(date(2026, 5, 1), date(2026, 5, 10)): print(d.strftime("%Y-%m-%d %A"))

实用技巧:对于需要排除国定假日的复杂工作日计算,建议维护一个节假日列表(可通过开源项目如chinese-calendar获取中国法定假日),然后结合上述rrule方法实现。

九、实际应用场景

9.1 日志时间戳处理

from datetime import datetime, timezone, timedelta import re # 日志解析:提取ISO格式时间戳并按小时聚合 log_lines = [ "[2026-05-05T08:15:30+08:00] INFO 用户登录成功", "[2026-05-05T08:45:12+08:00] WARNING 连接超时", "[2026-05-05T09:01:05+08:00] ERROR 数据库连接失败", "[2026-05-05T09:30:00+08:00] INFO 服务重启完成", ] def parse_log_timestamp(line): match = re.search(r"\[(.*?)\]", line) if match: return datetime.fromisoformat(match.group(1)) # 按小时分组统计 hourly_stats = {} for line in log_lines: dt = parse_log_timestamp(line) if dt: hour_key = dt.strftime("%Y-%m-%d %H:00") hourly_stats[hour_key] = hourly_stats.get(hour_key, 0) + 1 print(hourly_stats) # {'2026-05-05 08:00': 2, '2026-05-05 09:00': 2}

9.2 过期时间判断

from datetime import datetime, timedelta, timezone # Token过期判断 def is_token_expired(expires_at_str, format="%Y-%m-%d %H:%M:%S"): expires = datetime.strptime(expires_at_str, format) expires = expires.replace(tzinfo=timezone.utc) now = datetime.now(timezone.utc) return now > expires print(is_token_expired("2026-05-01 00:00:00")) # True print(is_token_expired("2026-12-31 00:00:00")) # False # Session有效期判断(15分钟过期) def is_session_valid(last_activity, max_minutes=15): now = datetime.now(timezone.utc) elapsed = now - last_activity return elapsed < timedelta(minutes=max_minutes) last_active = datetime.now(timezone.utc) - timedelta(minutes=10) print(is_session_valid(last_active)) # True(10分钟<15分钟)

9.3 跨时区会议时间

from datetime import datetime from zoneinfo import ZoneInfo # 上海团队和纽约团队约定会议时间 # 上海时间14:00开会,纽约同事需要知道本地时间 shanghai_time = datetime(2026, 5, 5, 14, 0, 0, tzinfo=ZoneInfo("Asia/Shanghai")) nyc_time = shanghai_time.astimezone(ZoneInfo("America/New_York")) london_time = shanghai_time.astimezone(ZoneInfo("Europe/London")) tokyo_time = shanghai_time.astimezone(ZoneInfo("Asia/Tokyo")) print(f"上海: {shanghai_time.strftime('%Y-%m-%d %H:%M %Z')}") print(f"纽约: {nyc_time.strftime('%Y-%m-%d %H:%M %Z')}") print(f"伦敦: {london_time.strftime('%Y-%m-%d %H:%M %Z')}") print(f"东京: {tokyo_time.strftime('%Y-%m-%d %H:%M %Z')}") # 上海: 2026-05-05 14:00 CST # 纽约: 2026-05-05 02:00 EDT # 伦敦: 2026-05-05 07:00 BST # 东京: 2026-05-05 15:00 JST # 检查是否在每个人的工作时间(9:00-18:00) def is_work_hours(dt): return 9 <= dt.hour < 18 print(f"上海工作时间: {is_work_hours(shanghai_time)}") # True print(f"纽约工作时间: {is_work_hours(nyc_time)}") # False(凌晨2点) # 结论:14:00 CST对纽约团队不友好,建议调整

十、性能优化与最佳实践

10.1 批量处理优化

from datetime import datetime import time # 避免在循环中重复创建时区对象 # ❌ 不推荐:每次循环都创建新对象 def bad_way(timestamps): from zoneinfo import ZoneInfo results = [] for ts in timestamps: dt = datetime.fromtimestamp(ts, tz=ZoneInfo("Asia/Shanghai")) results.append(dt) return results # ✅ 推荐:复用时区对象 def good_way(timestamps): from zoneinfo import ZoneInfo tz = ZoneInfo("Asia/Shanghai") # 只创建一次 results = [] for ts in timestamps: dt = datetime.fromtimestamp(ts, tz=tz) results.append(dt) return results # 批量strptime优化:使用列表推导 date_strings = ["2026-05-01", "2026-05-02", "2026-05-03"] dates = [datetime.strptime(s, "%Y-%m-%d") for s in date_strings]

10.2 常见误区与陷阱

错误做法

  • 将naive和aware datetime直接比较
  • 使用datetime.utcnow()(返回naive对象)
  • 在pytz中直接使用tzinfo参数
  • timedelta(months=1)(不支持)
  • 假设fromtimestamp()返回UTC时间

正确做法

  • 统一使用aware datetime并转换为相同时区再比较
  • 使用datetime.now(timezone.utc)
  • 使用pytz的localize()方法
  • 使用relativedelta(months=1)
  • 始终指定tz参数

Python 3.12+ 废弃警告:datetime.utcfromtimestamp()datetime.utcnow()已被标记为废弃,将在未来版本中移除。所有新代码应使用带tz=timezone.utcfromtimestamp()now()替代。

十一、核心要点总结

  • 五大核心类:date、time、datetime、timedelta、timezone构成Python时间处理的基础
  • naive vs aware:生产环境始终使用aware datetime(带时区信息),避免时区混淆
  • timedelta局限:不支持按月/年运算,需要借助dateutil.relativedelta
  • 时区方案:Python 3.9+ 用zoneinfo,旧版本用pytz,固定偏移用内置timezone
  • 格式化:strftime用于输出,strptime用于解析,dateutil.parser用于智能解析
  • 日期生成:dateutil.rrule是生成重复日期的最强工具
  • 时间戳:始终使用aware对象的timestamp()fromtimestamp(ts, tz=...)
  • 工作日计算:组合使用rruleweekday()isoweekday()和节假日列表
  • 废弃API:避免使用utcnow()utcfromtimestamp()
  • 性能优化:时区对象和格式字符串尽量复用,避免在循环中重复创建

推荐阅读:Python官方文档中关于datetime模块的PEP 615(zoneinfo的引入)和PEP 495(处理模糊时间)是深入了解Python时间处理的权威资料。