1. time模块解析
1.1 time模块核心函数剖析
time()函数
def time() -> float
功能
返回当前时间戳(1970纪元后经过的浮点秒数)
底层原理
调用系统级time()函数,精度通常达到微秒级
性能对比
import timeit
print(timeit.timeit('time.time()', setup='import time', number=1000000))
# 典型结果:???秒(百万次调用)
sleep()函数精度测试
def test_sleep_precision():
durations = [0.001, 0.01, 0.1, 1] # 测试不同休眠时长
for d in durations:
start = time.time()
time.sleep(d)
actual = time.time() - start
print(f"请求休眠 {d:.3f}s, 实际休眠 {actual:.6f}s, 误差 {abs(actual-d)*1000:.3f}ms")
1.2 时间格式化高级技巧
strftime()格式符号扩展表
多语言时间格式化
import locale
import time
defmultilingual_time():
locales = ['en_US', 'zh_CN', 'ja_JP', 'de_DE']
for loc in locales:
try:
locale.setlocale(locale.LC_TIME, loc)
print(f"{loc}: {time.strftime('%c')}")
except locale.Error:
print(f"Locale {loc} not available")
# 输出示例:
# en_US: Fri Jul 15 15:30:45 2023
# zh_CN: 2023年07月15日 15时30分45秒
2. datetime模块高阶应用
2.1 时区处理深度解析
pytz时区陷阱与解决方案
from datetime import datetime
import pytz
# 错误示范(直接附加时区)
dt = datetime(2023,7,15,12,0)
tz = pytz.timezone('Asia/Shanghai')
try:
dt.replace(tzinfo=tz) # 会得到错误时区偏移
except Exception as e:
print(f"错误:{e}")
# 正确做法(使用localize)
proper_dt = tz.localize(datetime(2023,7,15,12,0))
print(f"正确时区时间:{proper_dt}")
# 夏令时处理示例
ny_tz = pytz.timezone('America/New_York')
dt_summer = ny_tz.localize(datetime(2023,7,15,12,0))
dt_winter = ny_tz.localize(datetime(2023,12,15,12,0))
print(f"纽约夏令时:{dt_summer} (UTC偏移:{dt_summer.utcoffset()})")
print(f"纽约冬令时:{dt_winter} (UTC偏移:{dt_winter.utcoffset()})")
2.2 高性能时间运算
datetime与numpy.datetime64性能对比
import numpy as np
from datetime import datetime, timedelta
import timeit
# 创建测试数据
n = 1000000
py_dates = [datetime(2023,1,1) + timedelta(days=i) for i inrange(n)]
np_dates = np.arange('2023-01-01', periods=n, dtype='datetime64[D]')
# 时间增量运算测试
defpy_test():
return [d + timedelta(days=30) for d in py_dates]
defnp_test():
return np_dates + np.timedelta64(30, 'D')
# 性能测试
print("Python datetime:")
%timeit py_test()
print("\nNumPy datetime64:")
%timeit np_test()
# 输出示例:
# Python datetime: 500-800ms
# NumPy datetime64: 5-20ms
3. 时间处理设计模式
3.1 时间工厂模式
from abc import ABC, abstractmethod
from datetime import datetime
import pytz
classTimeFactory(ABC):
@abstractmethod
defnow(self):
pass
classUTCTimeFactory(TimeFactory):
defnow(self):
return datetime.now(pytz.UTC)
classLocalTimeFactory(TimeFactory):
def__init__(self, timezone='Asia/Shanghai'):
self.tz = pytz.timezone(timezone)
defnow(self):
return datetime.now(self.tz)
# 使用示例
deflog_event(factory: TimeFactory, message: str):
print(f"[{factory.now().isoformat()}] {message}")
# 测试不同工厂
log_event(UTCTimeFactory(), "系统事件1")
log_event(LocalTimeFactory(), "本地事件1")
log_event(LocalTimeFactory('America/New_York'), "纽约事件1")
3.2 时间范围处理模式
from dataclasses import dataclass
from datetime import datetime, timedelta
@dataclass
classTimeRange:
start: datetime
end: datetime
def__post_init__(self):
ifself.start > self.end:
raise ValueError("开始时间不能晚于结束时间")
defcontains(self, dt: datetime) -> bool:
returnself.start <= dt <= self.end
defoverlap(self, other: 'TimeRange') -> bool:
return (self.contains(other.start) or
self.contains(other.end) or
other.contains(self.start))
defsplit(self, delta: timedelta) -> list['TimeRange']:
ranges = []
current = self.start
while current < self.end:
next_dt = min(current + delta, self.end)
ranges.append(TimeRange(current, next_dt))
current = next_dt
return ranges
# 使用示例
tr = TimeRange(
datetime(2023,7,1),
datetime(2023,7,31)
)
print("7月包含15号:", tr.contains(datetime(2023,7,15)))
print("按周拆分:", tr.split(timedelta(weeks=1)))
4. 金融时间处理专题
4.1 交易日历处理
import pandas as pd
from pandas.tseries.holiday import AbstractHolidayCalendar, Holiday
from pandas.tseries.offsets import CustomBusinessDay
classChinaTradingCalendar(AbstractHolidayCalendar):
rules = [
Holiday('元旦', month=1, day=1),
Holiday('春节', month=1, day=22), # 示例日期
Holiday('清明节', month=4, day=5),
Holiday('劳动节', month=5, day=1),
Holiday('端午节', month=6, day=22),
Holiday('中秋节', month=9, day=29),
Holiday('国庆节', month=10, day=1),
]
defget_trading_days(start, end):
cal = ChinaTradingCalendar()
return pd.bdate_range(start, end, freq=CustomBusinessDay(calendar=cal))
# 使用示例
trading_days = get_trading_days('2023-01-01', '2023-12-31')
print(f"2023年交易日数量: {len(trading_days)}")
print("前5个交易日:", trading_days[:5])
4.2 时间序列重采样技巧
import pandas as pd
import numpy as np
# 创建高频交易数据
np.random.seed(42)
date_rng = pd.date_range('2023-07-15 09:30', '2023-07-15 15:00', freq='30s')
price = np.cumsum(np.random.randn(len(date_rng)) * 0.1 + 100)
volume = np.random.randint(100, 1000, len(date_rng))
df = pd.DataFrame({'price': price, 'volume': volume}, index=date_rng)
# 复杂重采样
defresample_tick_data(df):
# 1分钟K线
ohlc = df['price'].resample('1T').ohlc()
vol = df['volume'].resample('1T').sum()
# 添加VWAP(成交量加权平均价)
df['price_vol'] = df['price'] * df['volume']
vwap = df['price_vol'].resample('1T').sum() / vol
result = pd.concat([ohlc, vol, vwap], axis=1)
result.columns = ['open', 'high', 'low', 'close', 'volume', 'vwap']
return result.dropna()
# 应用重采样
kline_data = resample_tick_data(df)
print(kline_data.head())
5. 时间处理性能优化
5.1 Cython加速时间计算
# 文件:datetime_cy.pyx
import cython
from datetime import datetime, timedelta
@cython.boundscheck(False)
@cython.wraparound(False)
defprocess_dates(dates_list):
cdef int i, n = len(dates_list)
cdef list result = []
cdef datetime dt, new_dt
for i inrange(n):
dt = dates_list[i]
new_dt = dt + timedelta(days=30)
result.append(new_dt)
return result
# 编译后使用对比
import timeit
from datetime import datetime, timedelta
# Python原生实现
defpy_process(dates_list):
return [d + timedelta(days=30) for d in dates_list]
# 测试数据
dates = [datetime(2023,1,1) + timedelta(days=i) for i inrange(100000)]
# 性能对比
print("Python:", timeit.timeit('py_process(dates)', globals=globals(), number=100))
print("Cython:", timeit.timeit('process_dates(dates)', globals=globals(), number=100))
5.2 避免常见性能陷阱
陷阱1:频繁创建datetime对象
# 错误做法
defslow_function(count):
result = []
for i inrange(count):
result.append(datetime.now()) # 每次循环都创建新对象
return result
# 优化方案
deffast_function(count):
now = datetime.now # 先获取函数引用
return [now() for _ inrange(count)] # 减少属性查找开销
陷阱2:不必要的时区转换
# 错误做法
defconvert_many_times(dt):
tz_sh = pytz.timezone('Asia/Shanghai')
tz_ny = pytz.timezone('America/New_York')
for _ inrange(1000):
sh_dt = dt.astimezone(tz_sh)
ny_dt = dt.astimezone(tz_ny)
# ...
# 优化方案
defconvert_once(dt):
tz_sh = pytz.timezone('Asia/Shanghai')
tz_ny = pytz.timezone('America/New_York')
sh_dt = dt.astimezone(tz_sh)
ny_dt = dt.astimezone(tz_ny)
for _ inrange(1000):
# 使用已转换的时间
pass
6. 时间处理测试策略
6.1 时间模拟测试框架
import time
from datetime import datetime, timedelta
from unittest import TestCase, mock
classTimeMachine:
def__init__(self, initial_time=None):
self._now = initial_time or datetime.now()
deftravel(self, days=0, seconds=0):
self._now += timedelta(days=days, seconds=seconds)
defnow(self):
returnself._now
classTestTimeSensitive(TestCase):
defsetUp(self):
self.tm = TimeMachine(datetime(2023,7,15))
# 模拟datetime.now
self.datetime_patcher = mock.patch('datetime.datetime')
mock_datetime = self.datetime_patcher.start()
mock_datetime.now.side_effect = self.tm.now
# 模拟time.time
self.time_patcher = mock.patch('time.time')
mock_time = self.time_patcher.start()
mock_time.side_effect = lambda: self.tm.now().timestamp()
deftearDown(self):
self.datetime_patcher.stop()
self.time_patcher.stop()
deftest_time_based_logic(self):
from myapp import check_deadline # 被测函数
# 测试截止日期前
self.assertTrue(check_deadline())
# 时间旅行到截止日期后
self.tm.travel(days=1)
self.assertFalse(check_deadline())
6.2 时间模糊测试
import random
from datetime import datetime, timedelta
from hypothesis import given, strategies as st
# 定义时间生成策略
defdatetime_strategy():
return st.datetimes(
min_value=datetime(1970,1,1),
max_value=datetime(2100,1,1),
timezones=st.timezones()
)
@given(dt1=datetime_strategy(), dt2=datetime_strategy())
deftest_datetime_arithmetic(dt1, dt2):
# 测试时间差计算
delta = dt2 - dt1
assert dt1 + delta == dt2
# 测试时间比较
if dt1 < dt2:
assert dt1 + timedelta(seconds=1) <= dt2
# 运行测试
if __name__ == "__main__":
test_datetime_arithmetic()
7. 时间处理实践总结
时区黄金法则:
存储:始终使用UTC时间存储
转换:只在显示时转换为本地时间
处理:使用aware datetime对象(带时区)
性能关键点:
批量操作使用numpy.datetime64
避免在循环中重复创建时间对象
缓存频繁使用的时间计算结果
代码健壮性:
处理闰秒、夏令时等边界情况
验证时间范围有效性(如2月30日)
使用try/except处理时区转换异常
测试策略:
使用freezegun或模拟对象测试时间敏感代码
包含跨时区、跨夏令时的测试用例
对时间计算进行模糊测试
架构设计:
采用时间工厂模式统一时间获取
定义明确的时间范围处理类
隔离时间相关代码以便测试和维护
通过掌握这些技巧,我们将能够处理各种复杂的时间日期场景,构建出健壮、高效且易于维护的时间相关代码。
更新日期:2025-05-11
交流讨论:欢迎在评论区留言!
重要提示:本文主要是记录自己的学习与实践过程,所提内容或者观点仅代表个人意见,只是我以为的,不代表完全正确,不喜请勿关注。
领取专属 10元无门槛券
私享最新 技术干货