Python 日期与时间处理全解析
摘要
本文全面解析Python日期时间处理的核心模块datetime
、time
、calendar
,涵盖时区转换(pytz
库)、时间戳操作、定时任务调度、日志时间格式化等场景。通过timedelta
计算、性能优化技巧及10个实战练习题(含完整答案代码),帮助开发者构建完整的时序数据处理知识体系。
一、Python时间处理核心模块对比
1.1 datetime模块
from datetime import datetime, timedelta
# 创建日期对象
now = datetime.now()
print(f"当前时间: {now}") # 2023-08-25 14:30:45.123456
# 时间运算
tomorrow = now + timedelta(days=1)
last_week = now - timedelta(weeks=1)
功能特性:
- 支持日期时间创建、格式化、解析
- 提供
timedelta
进行时间差计算 - 时区基础操作(需搭配
pytz
)
1.2 time模块
import time
# 时间戳操作
timestamp = time.time() # 1692964245.123456
local_time = time.localtime(timestamp)
gm_time = time.gmtime(timestamp)
# 格式化输出
print(time.strftime("%Y-%m-%d %H:%M:%S", local_time)) # 2023-08-25 14:30:45
功能特性:
- 基于C库的时间处理接口
- 高效处理时间戳与时间元组
- 无时区感知能力
1.3 calendar模块
import calendar
# 生成月历
cal = calendar.month(2023, 8)
print(f"2023年8月日历:\n{cal}")
# 闰年判断
print(calendar.isleap(2024)) # True
功能特性:
- 生成文本日历
- 计算周数、月天数等元数据
- 无时间计算功能
二、时区转换与时间戳操作
2.1 pytz时区处理
from datetime import datetime
import pytz
# 创建时区感知对象
utc_time = datetime.now(pytz.utc)
ny_tz = pytz.timezone('America/New_York')
ny_time = utc_time.astimezone(ny_tz)
print(f"UTC时间: {utc_time}")
print(f"纽约时间: {ny_time}")
注意事项:
- 避免直接加减时区偏移量
- 优先使用
pytz
内置时区名称 - 夏令时转换自动处理
2.2 时间戳高级操作
# 获取不同精度时间戳
ts_second = int(time.time()) # 秒级
ts_millis = int(time.time() * 1000) # 毫秒级
# 时间戳互转
dt = datetime.fromtimestamp(ts_second)
ts = dt.timestamp()
三、实战应用场景
3.1 定时任务调度
import schedule
import time
def job():
print(f"任务执行时间: {datetime.now().isoformat()}")
schedule.every(10).minutes.do(job) # 每10分钟执行
while True:
schedule.run_pending()
time.sleep(1)
3.2 日志时间格式化
import logging
logging.basicConfig(
format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%dT%H:%M:%SZ',
level=logging.INFO
)
logging.info("用户登录成功")
# 输出: 2023-08-25T14:30:45Z - 用户登录成功
四、性能优化技巧
4.1 对象复用提升效率
# 低效写法
for _ in range(10000):
d = datetime.now()
# 高效写法(单次获取基准时间)
base_time = datetime.now()
for _ in range(10000):
d = base_time + timedelta(seconds=_)
注:这里仅演示对象复用,以上两个代码块中输出的结果不相同
4.2 时间解析速度对比
from timeit import timeit
def use_strptime():
datetime.strptime("2023-08-25", "%Y-%m-%d")
def use_split():
parts = "2023-08-25".split('-')
datetime(int(parts[0]), int(parts[1]), int(parts[2]))
print("strptime:", timeit(use_strptime, number=10000)) # 约0.15秒
print("split: ", timeit(use_split, number=10000)) # 约0.03秒
五、综合练习题与答案
练习题列表
- 计算两个日期之间的工作日天数
- 生成指定月份的所有周五日期
- 将含闰秒的时间戳转换为datetime
- 实现跨时区会议时间转换工具
- 优化海量日期解析性能
- 检测时间序列中的连续日期段
- 开发秒级精确的倒计时器
- 处理夏令时转换边界问题
- 实现支持多种格式的日期解析器
- 编写时间处理单元测试用例
以下是10个Python时间处理练习题的完整答案代码及解析:
题目1:计算两个日期之间的工作日天数
from datetime import datetime, timedelta
def workdays(start: str, end: str) -> int:
start_dt = datetime.strptime(start, "%Y-%m-%d")
end_dt = datetime.strptime(end, "%Y-%m-%d")
if start_dt > end_dt:
raise ValueError("开始日期不能晚于结束日期")
delta = (end_dt - start_dt).days + 1
weekends = 0
for day in range(delta):
current = start_dt + timedelta(days=day)
if current.weekday() >= 5: # 5=周六,6=周日
weekends += 1
return delta - weekends
print(workdays("2023-08-01", "2023-08-31")) # 输出23
题目2:生成指定月份的所有周五日期
import calendar
from datetime import date
def get_fridays(year: int, month: int):
cal = calendar.monthcalendar(year, month)
fridays = []
for week in cal:
if week[4] != 0: # 第5列是周五
fridays.append(date(year, month, week[4]).strftime("%Y-%m-%d"))
return fridays
print(get_fridays(2023, 8))
# ['2023-08-04', '2023-08-11', '2023-08-18', '2023-08-25']
题目3:将含闰秒的时间戳转换为datetime
from datetime import datetime, timedelta
def handle_leap_second(ts: float):
base_time = datetime(2016, 12, 31, 23, 59, 59)
leap_window_start = datetime(2016, 12, 31, 23, 59, 0).timestamp()
if leap_window_start <= ts < leap_window_start + 3:
return base_time + timedelta(seconds=ts - leap_window_start)
else:
return datetime.fromtimestamp(ts)
print(handle_leap_second(1483228790.5)) # 2016-12-31 23:59:59.500000
题目4:实现跨时区会议时间转换工具
from datetime import datetime
import pytz
def convert_meeting_time(original_time: datetime, from_tz: str, to_tz: str):
from_zone = pytz.timezone(from_tz)
to_zone = pytz.timezone(to_tz)
localized = from_zone.localize(original_time)
return localized.astimezone(to_zone)
origin = datetime(2023, 8, 25, 14, 30)
print(convert_meeting_time(origin, "Asia/Shanghai", "America/New_York"))
# 2023-08-25 02:30:00-04:00
题目5:优化海量日期解析性能
def bulk_parse_dates(date_strings):
parsed = []
for s in date_strings:
parts = s.split('-')
parsed.append(datetime(int(parts[0]), int(parts[1]), int(parts[2])))
return parsed
dates = ["2023-08-01"] * 100000
%timeit bulk_parse_dates(dates) # 比strptime快3倍
题目6:检测时间序列中的连续日期段
from datetime import datetime, timedelta
def find_continuous_periods(dates):
sorted_dates = sorted(datetime.strptime(d, "%Y-%m-%d") for d in dates)
periods = []
current_start = sorted_dates[0]
for i in range(1, len(sorted_dates)):
if (sorted_dates[i] - sorted_dates[i-1]).days != 1:
periods.append((current_start, sorted_dates[i-1]))
current_start = sorted_dates[i]
periods.append((current_start, sorted_dates[-1]))
return periods
dates = ["2023-08-01", "2023-08-02", "2023-08-04"]
print(find_continuous_periods(dates))
# [(2023-08-01, 2023-08-02), (2023-08-04, 2023-08-04)]
题目7:开发秒级精确的倒计时器
import time
def countdown(seconds: int):
end_time = time.time() + seconds
while time.time() < end_time:
remaining = int(end_time - time.time())
print(f"剩余时间: {remaining}秒", end='\r')
time.sleep(1)
print("\n时间到!")
countdown(10)
题目8:处理夏令时转换边界问题
def test_dst_transition():
tz = pytz.timezone('America/New_York')
dt = datetime(2023, 3, 12, 1, 30) # 夏令时开始时间
try:
print(tz.localize(dt, is_dst=None))
except pytz.AmbiguousTimeError:
print("检测到存在歧义时间")
except pytz.NonExistentTimeError:
print("检测到不存在的时间")
test_dst_transition() # 输出不存在的时间异常
题目9:实现支持多种格式的日期解析器
from dateutil.parser import parse
def flexible_parser(date_str):
formats = [
"%Y-%m-%d",
"%Y/%m/%d",
"%d %B %Y",
"%Y%m%d"
]
for fmt in formats:
try:
return datetime.strptime(date_str, fmt)
except ValueError:
continue
return parse(date_str) # 使用dateutil作为后备方案
print(flexible_parser("2023-08-25")) # 标准格式
print(flexible_parser("25 August 2023")) # 备用格式
题目10:编写时间处理单元测试用例
import unittest
from datetime import datetime
class TestDateTime(unittest.TestCase):
def test_timezone_conversion(self):
dt = datetime(2023, 8, 25, 12, 0)
converted = convert_meeting_time(dt, "UTC", "Asia/Shanghai")
self.assertEqual(converted.hour, 20)
def test_leap_year(self):
self.assertTrue(calendar.isleap(2024))
self.assertFalse(calendar.isleap(2023))
def test_workdays(self):
self.assertEqual(workdays("2023-08-01", "2023-08-05"), 5)
if __name__ == '__main__':
unittest.main()
关键知识点总结
- 时区处理:始终使用
pytz
库进行时区转换,避免手动计算偏移量 - 性能优化:批量处理时优先使用数值计算替代对象创建
- 异常处理:特别注意夏令时转换期间的非法时间(2:00-3:00可能不存在)
- 格式兼容:使用
dateutil
库作为多种日期格式解析的备用方案 - 测试要点:必须覆盖闰年、时区转换、夏令时等边界条件
结语
本文从基础操作到高级应用系统梳理了Python时间处理技术栈,针对时区转换、性能优化等难点给出解决方案。通过结合10个典型场景的练习题,读者可快速提升时序数据处理能力。