以下是针对Python datetime
库的全面讲解,包含核心类、方法、高级特性的进阶学习以及实际应用示例等:
date
类:处理日期(年、月、日)from datetime import date
# 创建日期对象
d = date(2023, 10, 5)
print(d) # 输出: 2023-10-05
# 获取当前日期
today = date.today()
print(today) # 输出当前日期,如 2023-10-05
# 属性访问
print(f"Year: {d.year}, Month: {d.month}, Day: {d.day}")
# 日期运算
new_date = d.replace(year=2024) # 替换年份
print(new_date) # 2024-10-05
# 星期和日历计算
print(d.weekday()) # 返回0-6(0为周一)
print(d.isoweekday())# 返回1-7(1为周一)
time
类:处理时间(时、分、秒、微秒)from datetime import time
t = time(14, 30, 15, 500) # 14:30:15.000500
print(t.hour, t.minute, t.second, t.microsecond)
datetime
类:日期与时间的结合from datetime import datetime
# 创建对象
dt = datetime(2023, 10, 5, 14, 30)
print(dt) # 2023-10-05 14:30:00
# 当前时间(本地)
now = datetime.now()
print(now)
# 属性扩展
print(now.date(), now.time())
# 转换为时间戳(秒)
timestamp = dt.timestamp()
print(timestamp)
# 从时间戳恢复
dt_from_ts = datetime.fromtimestamp(1677721600)
timedelta
类:时间间隔计算from datetime import timedelta
delta = timedelta(days=5, hours=3)
print(delta) # 5 days, 3:00:00
# 时间运算
future = datetime.now() + delta
past = datetime.now() - timedelta(weeks=2)
# 计算两个日期间隔
dt1 = datetime(2023, 1, 1)
dt2 = datetime(2023, 12, 31)
diff = dt2 - dt1
print(diff.days) # 364
strftime()
:日期 → 字符串dt = datetime.now()
formatted = dt.strftime("%Y-%m-%d %H:%M:%S")
print(formatted) # 类似 "2023-10-05 14:30:00"
strptime()
:字符串 → 日期date_str = "2023-10-05 14:30"
dt = datetime.strptime(date_str, "%Y-%m-%d %H:%M")
print(dt) # 2023-10-05 14:30:00
常用格式符号:
%Y
:四位年份%m
:两位月份%d
:两位日期%H
:24小时制小时%M
:分钟%S
:秒%A
:完整星期名称from datetime import datetime, timezone, timedelta
# 创建带时区的datetime对象
utc_time = datetime.now(timezone.utc)
print(utc_time) # 2023-10-05 12:30:00+00:00
# 自定义时区
tz = timezone(timedelta(hours=8)) # UTC+8
local_time = datetime.now(tz)
# 将本地时间转换为其他时区
utc_time = local_time.astimezone(timezone.utc)
print(utc_time)
def last_day_of_month(any_day):
next_month = any_day.replace(day=28) + timedelta(days=4)
return next_month - timedelta(days=next_month.day)
print(last_day_of_month(date(2023, 2, 10))) # 2023-02-28
def count_weekdays(start, end):
delta = end - start
count = 0
for day_offset in range(delta.days + 1):
day = start + timedelta(day_offset)
if day.weekday() < 5: # 0-4为周一到周五
count += 1
return count
start_date = date(2023, 10, 1)
end_date = date(2023, 10, 31)
print(count_weekdays(start_date, end_date)) # 输出22(假设没有节假日)
date
, time
, datetime
, timedelta
aware datetime
)timedelta
处理时间间隔calendar
模块Python datetime
支持微秒级精度,但需要特别注意不同系统的时间戳差异:
# 带微秒的时间对象
dt = datetime(2023, 10, 5, 14, 30, 15, 500000) # 500,000微秒 = 0.5秒
print(dt.microsecond) # 500000
# 时间戳转换(包含微秒)
timestamp = dt.timestamp()
print(timestamp) # 1696519815.5
# 从高精度时间戳恢复
dt_high_precision = datetime.fromtimestamp(1696519815.500123)
print(dt_high_precision) # 2023-10-05 14:30:15.500123
pytz
处理复杂时区(需安装 pip install pytz
):import pytz
# 创建带时区的对象(推荐方式)
tz_shanghai = pytz.timezone('Asia/Shanghai')
dt_local = datetime.now(tz_shanghai)
print(dt_local) # 2023-10-05 22:30:00+08:00
# 时区转换(纽约时间)
tz_ny = pytz.timezone('America/New_York')
dt_ny = dt_local.astimezone(tz_ny)
print(dt_ny) # 2023-10-05 10:30:00-04:00
# 夏令时边界示例(伦敦时区)
tz_london = pytz.timezone('Europe/London')
dt_pre = datetime(2023, 3, 26, 1, 59, tzinfo=tz_london)
dt_post = datetime(2023, 3, 26, 2, 30, tzinfo=tz_london) # 无效时间!
# 正确创建方式(使用 localize)
dt_safe = tz_london.localize(datetime(2023, 3, 26, 2, 30))
print(dt_safe) # 自动处理为03:00(夏令时生效)
直接创建可能引发 ValueError
,建议安全创建方法:
def safe_create_date(year, month, day):
try:
return date(year, month, day)
except ValueError as e:
print(f"无效日期: {e}")
return None
print(safe_create_date(2023, 2, 29)) # 无效日期(2023不是闰年)
使用生成器避免内存溢出:
def date_range(start_date, end_date):
delta = (end_date - start_date).days
for n in range(delta + 1):
yield start_date + timedelta(days=n)
for day in date_range(date(2023, 10, 1), date(2023, 10, 5)):
print(day) # 输出10月1日至5日所有日期
import pandas as pd
# datetime → Pandas Timestamp
dt = datetime.now()
pd_time = pd.Timestamp(dt)
print(pd_time.floor('D')) # 当天0点:2023-10-05 00:00:00
# 生成时间范围序列
date_index = pd.date_range('2023-10-01', periods=7, freq='D')
print(date_index.dayofweek.values) # [6, 0, 1, 2, 3, 4, 5]
dateutil
解析复杂格式(需安装 pip install python-dateutil
):from dateutil import parser
dt = parser.parse("October 5th 2023 2:30 PM")
print(dt) # 2023-10-05 14:30:00
# 处理模糊日期
dt_ambiguous = parser.parse("2023-10-05", fuzzy=True) # 忽略非日期部分
# 错误方式:每次循环创建新对象
start = datetime.now()
for _ in range(1000000):
datetime.now()
# 正确方式:预先计算基准时间
base_time = datetime.now()
for _ in range(1000000):
base_time + timedelta(seconds=_)
time
模块处理高频率时间戳import time
# 快速获取时间戳(精度取决于系统)
start = time.time()
time.sleep(1)
print(time.time() - start) # 1.000123秒
# 错误:未指定时区
naive_dt = datetime.now()
utc_dt = naive_dt.astimezone(timezone.utc) # 报错!
# 正确:先添加时区
aware_dt = naive_dt.replace(tzinfo=timezone.utc)
# 直接添加 timedelta 可能导致错误
dt = datetime(2023, 1, 31) + timedelta(days=1)
print(dt) # 2023-02-01(正确)
# 但2月28日加2天:
dt = datetime(2023, 2, 28) + timedelta(days=2)
print(dt) # 2023-03-02(可能不符合业务预期)
工具库 | 用途 | 示例场景 |
---|---|---|
pytz |
完整时区数据库 | 国际会议时间转换 |
dateutil |
复杂日期解析 | 解析用户输入的模糊日期 |
pendulum |
更人性化的API | 2.days_ago() 语法 |
arrow |
简化日期操作 | 快速生成周报表时间范围 |
maya |
自然语言解析 | “next Tuesday at 2pm” |
def calculate_flight_times(departure_local, duration, from_tz, to_tz):
""" 计算到达时间和时差影响 """
# 创建带时区的出发时间
departure = from_tz.localize(departure_local)
# 计算到达时间(本地时区)
arrival = departure + duration
arrival = arrival.astimezone(to_tz)
# 计算总行程时间(考虑时差)
real_duration = arrival - departure
return {
"departure_utc": departure.astimezone(timezone.utc),
"arrival_local": arrival,
"total_duration": real_duration
}
# 示例:纽约→伦敦(飞行时间7小时)
from_tz = pytz.timezone('America/New_York')
to_tz = pytz.timezone('Europe/London')
result = calculate_flight_times(
datetime(2023, 10, 5, 18, 30),
timedelta(hours=7),
from_tz,
to_tz
)
print(f"实际行程时间: {result['total_duration']}") # 可能显示6小时(时差影响)
def time_slot(dt: datetime, interval_minutes=15) -> str:
""" 将时间划分到最近的业务时段 """
minutes = (dt.hour * 60 + dt.minute) // interval_minutes * interval_minutes
slot = f"{minutes//60:02d}:{minutes%60:02d}"
return f"{dt.date()} {slot}"
dt = datetime(2023, 10, 5, 14, 22)
print(time_slot(dt)) # 2023-10-05 14:15
def get_quarter(d: date) -> tuple:
""" 返回(年份, 季度)元组 """
return (d.year, (d.month - 1) // 3 + 1)
print(get_quarter(date(2023, 5, 1))) # (2023, 2)
Python标准库不直接支持闰秒,但可通过自定义逻辑处理:
# 已知闰秒发生时间列表(需手动维护)
leap_seconds = [
datetime(2016, 12, 31, 23, 59, 60),
datetime(2020, 12, 31, 23, 59, 60)
]
def is_leap_second(dt: datetime) -> bool:
return any(ls == dt for ls in leap_seconds)
test_dt = datetime(2016, 12, 31, 23, 59, 60)
print(is_leap_second(test_dt)) # True
def time_equal_ms(dt1: datetime, dt2: datetime) -> bool:
""" 比较到毫秒级精度 """
return abs(dt1 - dt2) <= timedelta(milliseconds=0.5)
dt1 = datetime(2023,10,5,0,0,0,500000)
dt2 = datetime(2023,10,5,0,0,0,500123)
print(time_equal_ms(dt1, dt2)) # True(差异小于0.5ms)
import sys
import ctypes
def set_system_time(new_time: datetime):
if sys.platform == 'win32':
# Windows系统设置时间
class SYSTEMTIME(ctypes.Structure):
_fields_ = [
("wYear", ctypes.c_ushort),
("wMonth", ctypes.c_ushort),
("wDayOfWeek", ctypes.c_ushort),
("wDay", ctypes.c_ushort),
("wHour", ctypes.c_ushort),
("wMinute", ctypes.c_ushort),
("wSecond", ctypes.c_ushort),
("wMilliseconds", ctypes.c_ushort)
]
st = SYSTEMTIME()
st.wYear = new_time.year
st.wMonth = new_time.month
st.wDay = new_time.day
st.wHour = new_time.hour
st.wMinute = new_time.minute
st.wSecond = new_time.second
ctypes.windll.kernel32.SetLocalTime(ctypes.byref(st))
else:
# Linux系统(需调用系统命令)
import subprocess
subprocess.call(f"date -s '{new_time.strftime('%Y%m%d %H:%M:%S')}'", shell=True)
from time import perf_counter
class PrecisionTimer:
def __enter__(self):
self.start = perf_counter()
return self
def __exit__(self, *args):
self.end = perf_counter()
self.elapsed = self.end - self.start
with PrecisionTimer() as timer:
# 执行需要计时的代码
sum(range(10**6))
print(f"耗时: {timer.elapsed:.6f}秒")
def detect_periodic_events(timestamps, expected_period):
""" 检测时间序列是否符合预期周期 """
diffs = [j-i for i,j in zip(timestamps[:-1], timestamps[1:])]
avg_diff = sum(diffs) / len(diffs)
return abs(avg_diff - expected_period) < 0.1 * expected_period
# 示例:检测心跳包(预期1秒间隔)
timestamps = [
datetime(2023,10,5,14,30,i).timestamp()
for i in range(0, 60, 1)
]
print(detect_periodic_events(timestamps, 1.0)) # True
def resample_data(data, freq='5T'):
""" 使用Pandas进行时间重采样 """
import pandas as pd
df = pd.DataFrame(data, columns=['time', 'value'])
df['time'] = pd.to_datetime(df['time'])
df.set_index('time', inplace=True)
return df.resample(freq).mean()
# 示例:将秒级数据转为5分钟均值
data = [
(datetime(2023,10,5,14,i,j).isoformat(), i*10+j)
for i in range(14,15)
for j in range(60)
]
print(resample_data(data))
from unittest.mock import Mock
import datetime as dt
def test_expired():
# 冻结时间为2023-10-01
real_datetime = dt.datetime
dt.datetime = Mock(now=Mock(return_value=real_datetime(2023,10,1)))
# 测试过期判断逻辑
assert not is_license_expired() # 假设许可证有效期到2023-12-31
# 恢复原始datetime
dt.datetime = real_datetime
import pytest
@pytest.mark.parametrize("dt_input, tz, expected", [
# 测试夏令时切换
("2023-03-12 02:30:00", "America/New_York", "2023-03-12 03:30:00"),
("2023-11-05 01:30:00", "America/Chicago", "2023-11-05 01:30:00")
])
def test_dst_transition(dt_input, tz, expected):
import pytz
naive_dt = datetime.strptime(dt_input, "%Y-%m-%d %H:%M:%S")
tz_obj = pytz.timezone(tz)
aware_dt = tz_obj.localize(naive_dt, is_dst=None)
assert str(aware_dt) == expected
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
def plot_time_series(dates, values):
plt.figure(figsize=(10,6))
plt.plot(dates, values)
# 配置时间轴格式
ax = plt.gca()
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax.xaxis.set_major_locator(mdates.DayLocator(interval=2))
plt.xticks(rotation=45)
plt.show()
# 示例数据
dates = [datetime(2023,10,i) for i in range(1,31)]
values = [i**2 for i in range(30)]
plot_time_series(dates, values)
时区规范:
# 数据库存储示例
db_time = datetime.utcnow().replace(tzinfo=timezone.utc)
# 前端展示转换
user_tz = pytz.timezone('Asia/Tokyo')
display_time = db_time.astimezone(user_tz)
性能敏感场景优化:
# 使用整数时间戳进行内部计算
def days_between(start_ts: float, end_ts: float) -> int:
return int((end_ts - start_ts) // 86400)
日志时间标准化:
def get_log_timestamp():
return datetime.utcnow().isoformat(timespec='milliseconds') + 'Z'
# 输出:2023-10-05T08:30:45.123Z
通过掌握这些进阶技术,开发者可以:
提醒:时间处理看似简单,实则充满边界情况和历史包袱,建议在关键系统中进行充分的历史时间测试(如测试2038年问题、时区规则变更等)。
不同数据库与Python类型的对应关系及转换技巧:
# SQLite 示例
import sqlite3
from datetime import datetime
conn = sqlite3.connect(':memory:')
conn.execute('''CREATE TABLE events
(id INT PRIMARY KEY, event_time TIMESTAMP)''')
# 自动转换datetime对象
dt = datetime.now()
conn.execute("INSERT INTO events VALUES (1, ?)", (dt,))
# 读取时自动转换为datetime对象
cursor = conn.execute("SELECT event_time FROM events WHERE id=1")
print(cursor.fetchone()[0]) # 返回datetime对象
# PostgreSQL适配(需psycopg2)
import psycopg2
conn_pg = psycopg2.connect("dbname=test")
conn_pg.cursor().execute("CREATE TABLE pg_events (event_time TIMESTAMPTZ)")
from sqlalchemy import Column, DateTime
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Event(Base):
__tablename__ = 'events'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
modified_at = Column(DateTime(timezone=True), onupdate=datetime.utcnow)
# 自动处理时区(推荐UTC存储)
event = Event()
session.add(event)
session.commit() # created_at和modified_at自动填充
from email.utils import formatdate, parsedate
# 生成HTTP标准日期
http_date = formatdate(timeval=None, localtime=False, usegmt=True)
print(http_date) # 'Thu, 05 Oct 2023 08:30:00 GMT'
# 解析HTTP日期
parsed_tuple = parsedate('Thu, 05 Oct 2023 08:30:00 GMT')
dt = datetime.datetime.fromtimestamp(mktime(parsed_tuple))
import json
from datetime import datetime
class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat() + ('Z' if obj.utcoffset() is None else '')
return super().default(obj)
data = {'time': datetime.now(timezone.utc)}
json_str = json.dumps(data, cls=DateTimeEncoder)
# {"time": "2023-10-05T08:30:00.123456Z"}
# 解码处理
class DateTimeDecoder(json.JSONDecoder):
def __init__(self, *args, **kwargs):
super().__init__(object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, obj):
for key in obj:
if isinstance(obj[key], str):
try:
obj[key] = datetime.fromisoformat(obj[key])
except ValueError:
pass
return obj
import asyncio
async def periodic_task(interval: float):
""" 精确的周期任务执行器 """
while True:
start = asyncio.get_event_loop().time()
await execute_task() # 自定义任务
elapsed = asyncio.get_event_loop().time() - start
await asyncio.sleep(interval - elapsed)
async def execute_task():
# 模拟耗时操作
await asyncio.sleep(0.8)
# 启动任务
asyncio.run(periodic_task(1.0)) # 保证每1秒执行一次(考虑执行时间)
class AsyncTimeout:
def __init__(self, timeout: float):
self.timeout = timeout
async def __aenter__(self):
self.task = asyncio.current_task()
self.start = asyncio.get_event_loop().time()
self.timeout_handle = asyncio.get_event_loop().call_later(
self.timeout, self._cancel_task)
return self
async def __aexit__(self, exc_type, exc, tb):
self.timeout_handle.cancel()
def _cancel_task(self):
self.task.cancel()
# 使用示例
async def fetch_data():
async with AsyncTimeout(2.0):
await asyncio.sleep(3) # 触发超时取消
asyncio.run(fetch_data()) # 抛出asyncio.CancelledError
def verify_jwt_exp(exp_timestamp: float):
""" 安全验证JWT过期时间 """
current_time = time.time()
# 防御时钟偏移攻击(允许1分钟误差)
if current_time > exp_timestamp + 60:
raise SecurityError("Token expired")
# 防御未来时间异常
if exp_timestamp > current_time + 3600*24*365: # 超过1年
raise SecurityError("Invalid expiration")
# 使用时区安全的比较
from hmac import compare_digest
def verify_timing_safe(token: str, actual: str):
""" 时间恒定的安全比较 """
return compare_digest(token.encode(), actual.encode())
def sanitize_date_input(user_input: str):
""" 清洗用户输入的日期参数 """
try:
dt = datetime.strptime(user_input, "%Y-%m-%d")
if dt.year < 1900 or dt.year > 2100:
raise ValueError("Year out of range")
return dt
except ValueError:
raise InvalidInputError("Invalid date format")
def time_bucket_analysis(data, period='hour'):
""" 按时间维度聚合分析 """
import pandas as pd
df = pd.DataFrame(data, columns=['timestamp', 'value'])
df['time_group'] = df['timestamp'].dt.floor(period)
return df.groupby('time_group').agg({
'value': ['sum', 'mean', 'count']
}).reset_index()
# 示例:按15分钟聚合
data = [(datetime(2023,10,5,i//4,i%4*15), i) for i in range(96)]
print(time_bucket_analysis(data, '15T'))
def rolling_window_stats(series, window_size='7D'):
""" 滑动窗口统计 """
return series.rolling(window_size).agg({
'mean': 'mean',
'std': 'std',
'max': 'max'
})
# Python生成JS时间戳(毫秒级)
js_timestamp = int(datetime.now().timestamp() * 1000)
# 解析JavaScript的Date字符串
js_date_str = '2023-10-05T08:30:00.123Z'
py_dt = datetime.fromisoformat(js_date_str.replace('Z', '+00:00'))
# 处理React日期选择器数据
def parse_react_date(react_date):
# 假设收到格式:'2023-10-05T08:30:00.000Z'
return datetime.strptime(react_date, '%Y-%m-%dT%H:%M:%S.%fZ').replace(tzinfo=timezone.utc)
# 处理Go的RFC3339Nano格式
go_time_str = '2023-10-05T08:30:00.123456789Z'
py_dt = datetime.strptime(go_time_str, '%Y-%m-%dT%H:%M:%S.%fZ').replace(microsecond=123456)
def debug_time_info(dt: datetime):
""" 显示时间的完整元数据 """
print(f"Naive: {dt.tzinfo is None}")
if dt.tzinfo:
print(f"UTC Offset: {dt.utcoffset()}")
print(f"Timezone Name: {dt.tzname()}")
print(f"DST: {dt.dst()}")
print(f"ISO Format: {dt.isoformat()}")
print(f"Timestamp: {dt.timestamp()}")
print(f"Fold: {dt.fold}") # 处理重复时间
debug_time_info(datetime.now(pytz.timezone('America/New_York')))
def detect_time_drift(expected_interval=1.0):
""" 检测系统时钟漂移 """
prev = time.monotonic()
while True:
time.sleep(expected_interval)
now = time.monotonic()
actual_interval = now - prev
drift = actual_interval - expected_interval
if abs(drift) > 0.01: # 允许1%误差
print(f"Time drift detected: {drift:.6f}s")
prev = now
# 量子抗性签名的时间戳示例(使用hashlib)
import hashlib
def create_quantum_safe_timestamp(data: bytes):
timestamp = int(time.time())
hash_obj = hashlib.sha3_512(data + str(timestamp).encode())
return (timestamp, hash_obj.hexdigest())
# 验证时间戳
def verify_quantum_safe(timestamp: int, data: bytes, signature: str):
reconstructed = hashlib.sha3_512(data + str(timestamp).encode()).hexdigest()
return compare_digest(reconstructed, signature)
class VectorClock:
def __init__(self):
self.nodes = defaultdict(int)
def increment(self, node_id):
self.nodes[node_id] += 1
def merge(self, other):
for node, time in other.nodes.items():
self.nodes[node] = max(self.nodes[node], time)
def __str__(self):
return json.dumps(self.nodes)
# 使用示例
node1 = VectorClock()
node1.increment('node1')
node2 = VectorClock()
node2.increment('node2')
node1.merge(node2)
print(node1) # {"node1": 1, "node2": 1}
分层时间处理架构:
监控指标:
# Prometheus时间相关指标示例
from prometheus_client import Gauge
TIME_DRIFT = Gauge('system_time_drift', 'Clock drift from NTP')
DB_TIME_DELAY = Gauge('db_replication_delay', 'Database replication latency')
灾难恢复策略: