datetime 模块

Python datetime 模块的完整指南,包含日期时间创建、格式化、计算和时区处理

分类: stdlib 难度: 中级 更新: 2024-01-01
日期时间 时区 格式化 计算 标准库

datetime 模块

📝 概述

datetime 模块是 Python 中处理日期和时间的核心模块,提供了比 time 模块更高级、更面向对象的日期时间处理功能。它包含了 date、time、datetime、timedelta、timezone 等类,支持日期时间的创建、格式化、计算和时区处理。

🎯 学习目标

  • 掌握 datetime 模块的核心类和方法
  • 学会日期时间的创建和格式化
  • 了解时间差计算和日期运算
  • 掌握时区处理和转换
  • 学会日期时间的比较和排序

📋 前置知识

  • Python 基本语法和面向对象概念
  • 了解时区和 UTC 概念
  • 基本的日期时间概念

🔍 详细内容

核心类概述

类名 功能 示例
date 日期(年月日) 2024-01-01
time 时间(时分秒微秒) 12:30:45.123456
datetime 日期时间 2024-01-01 12:30:45
timedelta 时间差 7 天 3 小时
timezone 时区 UTC+8

date 类 - 日期处理

创建日期对象

from datetime import date, datetime

## 创建日期对象的几种方式
today = date.today()  # 今天的日期
print(f"今天: {today}")

## 指定日期
specific_date = date(2024, 1, 1)
print(f"指定日期: {specific_date}")

## 从时间戳创建
import time
timestamp = time.time()
date_from_timestamp = date.fromtimestamp(timestamp)
print(f"从时间戳创建: {date_from_timestamp}")

## 从序数创建(从公元 1 年 1 月 1 日开始的天数)
ordinal_date = date.fromordinal(738521)  # 2024 年 1 月 1 日
print(f"从序数创建: {ordinal_date}")

## 从 ISO 格式字符串创建
iso_date = date.fromisoformat('2024-01-01')
print(f"从 ISO 格式创建: {iso_date}")

日期属性和方法

today = date.today()

## 获取日期组件
print(f"年份: {today.year}")
print(f"月份: {today.month}")
print(f"日期: {today.day}")

## 星期相关
print(f"星期几: {today.weekday()}")  # 0=周一, 6=周日
print(f"ISO 星期几: {today.isoweekday()}")  # 1=周一, 7=周日

## 格式化
print(f"ISO 格式: {today.isoformat()}")
print(f"字符串格式: {today.strftime('%Y 年%m 月%d 日')}")

## 其他方法
print(f"序数: {today.toordinal()}")
print(f"时间元组: {today.timetuple()}")
print(f"ISO 日历: {today.isocalendar()}")  # (年, 周数, 星期)

time 类 - 时间处理

创建时间对象

from datetime import time

## 创建时间对象
simple_time = time(14, 30, 0)  # 14:30:00
print(f"简单时间: {simple_time}")

## 包含微秒的时间
detailed_time = time(14, 30, 45, 123456)  # 14:30:45.123456
print(f"详细时间: {detailed_time}")

## 包含时区的时间
from datetime import timezone, timedelta
tz = timezone(timedelta(hours=8))  # UTC+8
time_with_tz = time(14, 30, 0, tzinfo=tz)
print(f"带时区时间: {time_with_tz}")

## 从 ISO 格式创建
iso_time = time.fromisoformat('14:30:45')
print(f"从 ISO 格式创建: {iso_time}")

时间属性和操作

t = time(14, 30, 45, 123456)

## 获取时间组件
print(f"小时: {t.hour}")
print(f"分钟: {t.minute}")
print(f"秒: {t.second}")
print(f"微秒: {t.microsecond}")
print(f"时区: {t.tzinfo}")

## 格式化
print(f"ISO 格式: {t.isoformat()}")
print(f"自定义格式: {t.strftime('%H 时%M 分%S 秒')}")

## 替换时间组件
new_time = t.replace(hour=16, minute=0)
print(f"替换后: {new_time}")

datetime 类 - 日期时间处理

创建 datetime 对象

from datetime import datetime, timezone, timedelta

## 获取当前时间
now = datetime.now()  # 本地时间
print(f"当前本地时间: {now}")

utc_now = datetime.utcnow()  # UTC 时间(已弃用,建议使用 timezone.utc)
print(f"当前 UTC 时间: {utc_now}")

## 推荐的 UTC 时间获取方式
utc_now_new = datetime.now(timezone.utc)
print(f"当前 UTC 时间(新): {utc_now_new}")

## 指定日期时间
specific_dt = datetime(2024, 1, 1, 12, 30, 45)
print(f"指定日期时间: {specific_dt}")

## 从字符串解析
dt_from_str = datetime.strptime('2024-01-01 12:30:45', '%Y-%m-%d %H:%M:%S')
print(f"从字符串解析: {dt_from_str}")

## 从 ISO 格式创建
iso_dt = datetime.fromisoformat('2024-01-01T12:30:45')
print(f"从 ISO 格式创建: {iso_dt}")

## 从时间戳创建
timestamp = 1704110445.0
dt_from_timestamp = datetime.fromtimestamp(timestamp)
print(f"从时间戳创建: {dt_from_timestamp}")

datetime 属性和方法

dt = datetime.now()

## 日期时间组件
print(f"年: {dt.year}, 月: {dt.month}, 日: {dt.day}")
print(f"时: {dt.hour}, 分: {dt.minute}, 秒: {dt.second}")
print(f"微秒: {dt.microsecond}")
print(f"时区: {dt.tzinfo}")

## 获取日期和时间部分
print(f"日期部分: {dt.date()}")
print(f"时间部分: {dt.time()}")

## 星期相关
print(f"星期几: {dt.weekday()}")
print(f"ISO 星期几: {dt.isoweekday()}")

## 格式化
print(f"ISO 格式: {dt.isoformat()}")
print(f"时间戳: {dt.timestamp()}")
print(f"自定义格式: {dt.strftime('%Y 年%m 月%d 日 %H:%M:%S')}")

## 替换组件
new_dt = dt.replace(year=2025, hour=0, minute=0, second=0, microsecond=0)
print(f"替换后: {new_dt}")

timedelta 类 - 时间差计算

创建时间差对象

from datetime import timedelta

## 创建时间差
week = timedelta(weeks=1)
day = timedelta(days=1)
hour = timedelta(hours=1)
minute = timedelta(minutes=30)
second = timedelta(seconds=45)
microsecond = timedelta(microseconds=123456)

## 组合时间差
complex_delta = timedelta(days=7, hours=3, minutes=30, seconds=45)
print(f"复合时间差: {complex_delta}")

## 从总秒数创建
total_seconds = 3661  # 1 小时 1 分 1 秒
delta_from_seconds = timedelta(seconds=total_seconds)
print(f"从秒数创建: {delta_from_seconds}")

时间差运算

from datetime import datetime, timedelta

now = datetime.now()
print(f"当前时间: {now}")

## 时间加减
tomorrow = now + timedelta(days=1)
yesterday = now - timedelta(days=1)
next_week = now + timedelta(weeks=1)
next_hour = now + timedelta(hours=1)

print(f"明天: {tomorrow}")
print(f"昨天: {yesterday}")
print(f"下周: {next_week}")
print(f"一小时后: {next_hour}")

## 计算时间差
start_time = datetime(2024, 1, 1, 9, 0, 0)
end_time = datetime(2024, 1, 1, 17, 30, 0)
work_duration = end_time - start_time
print(f"工作时长: {work_duration}")
print(f"工作小时数: {work_duration.total_seconds() / 3600}")

## 时间差属性
delta = timedelta(days=5, hours=3, minutes=30, seconds=45)
print(f"天数: {delta.days}")
print(f"秒数: {delta.seconds}")
print(f"微秒: {delta.microseconds}")
print(f"总秒数: {delta.total_seconds()}")

时区处理

timezone 类

from datetime import datetime, timezone, timedelta

## 创建时区对象
utc = timezone.utc
beijing = timezone(timedelta(hours=8))
tokyo = timezone(timedelta(hours=9))
newyork = timezone(timedelta(hours=-5))

print(f"UTC 时区: {utc}")
print(f"北京时区: {beijing}")
print(f"东京时区: {tokyo}")
print(f"纽约时区: {newyork}")

## 创建带时区的 datetime
utc_time = datetime.now(utc)
beijing_time = datetime.now(beijing)

print(f"UTC 时间: {utc_time}")
print(f"北京时间: {beijing_time}")

## 时区转换
utc_dt = datetime(2024, 1, 1, 12, 0, 0, tzinfo=utc)
beijing_dt = utc_dt.astimezone(beijing)
tokyo_dt = utc_dt.astimezone(tokyo)

print(f"UTC: {utc_dt}")
print(f"北京: {beijing_dt}")
print(f"东京: {tokyo_dt}")

时区转换示例

## 本地时间转 UTC
local_dt = datetime.now()
print(f"本地时间: {local_dt}")

## 为本地时间添加时区信息(假设是北京时间)
local_with_tz = local_dt.replace(tzinfo=beijing)
utc_converted = local_with_tz.astimezone(utc)
print(f"转换为 UTC: {utc_converted}")

## UTC 转其他时区
utc_dt = datetime.now(utc)
print(f"UTC 时间: {utc_dt}")

timezones = {
    '北京': timezone(timedelta(hours=8)),
    '东京': timezone(timedelta(hours=9)),
    '伦敦': timezone(timedelta(hours=0)),
    '纽约': timezone(timedelta(hours=-5)),
    '洛杉矶': timezone(timedelta(hours=-8))
}

print("\n 世界时间:")
for city, tz in timezones.items():
    local_time = utc_dt.astimezone(tz)
    print(f"{city}: {local_time.strftime('%Y-%m-%d %H:%M:%S %Z')}")

💡 实际应用

基础用法

def format_datetime_examples():
    """日期时间格式化示例"""
    now = datetime.now()
    
    formats = {
        '标准格式': '%Y-%m-%d %H:%M:%S',
        '中文格式': '%Y 年%m 月%d 日 %H 时%M 分%S 秒',
        '美式格式': '%m/%d/%Y %I:%M:%S %p',
        '欧式格式': '%d/%m/%Y %H:%M:%S',
        '简短格式': '%y%m%d_%H%M%S',
        'ISO 格式': '%Y-%m-%dT%H:%M:%S',
        '文件名格式': '%Y%m%d_%H%M%S',
        '日志格式': '[%Y-%m-%d %H:%M:%S]'
    }
    
    print("日期时间格式化示例:")
    for name, fmt in formats.items():
        formatted = now.strftime(fmt)
        print(f"{name}: {formatted}")
    
    return formats

## 使用示例
format_datetime_examples()

高级用法

class DateTimeHelper:
    """日期时间助手类"""
    
    @staticmethod
    def get_age(birth_date):
        """计算年龄"""
        today = date.today()
        age = today.year - birth_date.year
        
#        # 检查是否还没过生日
        if today.month < birth_date.month or \
           (today.month == birth_date.month and today.day < birth_date.day):
            age -= 1
        
        return age
    
    @staticmethod
    def get_workdays(start_date, end_date, holidays=None):
        """计算工作日数量(排除周末和节假日)"""
        if holidays is None:
            holidays = set()
        
        workdays = 0
        current = start_date
        
        while current <= end_date:
#            # 检查是否为工作日(周一到周五)
            if current.weekday() < 5 and current not in holidays:
                workdays += 1
            current += timedelta(days=1)
        
        return workdays
    
    @staticmethod
    def get_next_weekday(start_date, weekday):
        """获取下一个指定星期几的日期"""
        days_ahead = weekday - start_date.weekday()
        if days_ahead <= 0:  # 目标日期已过,获取下周的
            days_ahead += 7
        return start_date + timedelta(days=days_ahead)
    
    @staticmethod
    def get_month_range(year, month):
        """获取指定月份的日期范围"""
        first_day = date(year, month, 1)
        
#        # 计算下个月的第一天,然后减一天得到本月最后一天
        if month == 12:
            next_month = date(year + 1, 1, 1)
        else:
            next_month = date(year, month + 1, 1)
        
        last_day = next_month - timedelta(days=1)
        
        return first_day, last_day
    
    @staticmethod
    def format_duration(td):
        """格式化时间差为可读字符串"""
        total_seconds = int(td.total_seconds())
        
        days = total_seconds // 86400
        hours = (total_seconds % 86400) // 3600
        minutes = (total_seconds % 3600) // 60
        seconds = total_seconds % 60
        
        parts = []
        if days:
            parts.append(f"{days}天")
        if hours:
            parts.append(f"{hours}小时")
        if minutes:
            parts.append(f"{minutes}分钟")
        if seconds or not parts:
            parts.append(f"{seconds}秒")
        
        return "".join(parts)

## 使用示例
helper = DateTimeHelper()

## 计算年龄
birth_date = date(1990, 5, 15)
age = helper.get_age(birth_date)
print(f"年龄: {age}岁")

## 计算工作日
start = date(2024, 1, 1)
end = date(2024, 1, 31)
holidays = {date(2024, 1, 1)}  # 元旦
workdays = helper.get_workdays(start, end, holidays)
print(f"2024 年 1 月工作日: {workdays}天")

## 获取下一个周五
today = date.today()
next_friday = helper.get_next_weekday(today, 4)  # 4 表示周五
print(f"下一个周五: {next_friday}")

## 获取月份范围
first, last = helper.get_month_range(2024, 2)
print(f"2024 年 2 月: {first}{last}")

## 格式化时间差
duration = timedelta(days=5, hours=3, minutes=30, seconds=45)
formatted = helper.format_duration(duration)
print(f"时间差: {formatted}")

实际案例

from datetime import datetime, timedelta, timezone
import json

class EventScheduler:
    """事件调度器"""
    
    def __init__(self):
        self.events = []
        self.timezone = timezone(timedelta(hours=8))  # 默认北京时间
    
    def add_event(self, title, start_time, duration_minutes, description=""):
        """添加事件"""
        if isinstance(start_time, str):
            start_time = datetime.fromisoformat(start_time)
        
#        # 确保时间有时区信息
        if start_time.tzinfo is None:
            start_time = start_time.replace(tzinfo=self.timezone)
        
        end_time = start_time + timedelta(minutes=duration_minutes)
        
        event = {
            'id': len(self.events) + 1,
            'title': title,
            'start_time': start_time,
            'end_time': end_time,
            'duration': timedelta(minutes=duration_minutes),
            'description': description,
            'created_at': datetime.now(self.timezone)
        }
        
        self.events.append(event)
        return event['id']
    
    def get_events_by_date(self, target_date):
        """获取指定日期的事件"""
        if isinstance(target_date, str):
            target_date = datetime.fromisoformat(target_date).date()
        elif isinstance(target_date, datetime):
            target_date = target_date.date()
        
        day_events = []
        for event in self.events:
            if event['start_time'].date() == target_date:
                day_events.append(event)
        
#        # 按开始时间排序
        day_events.sort(key=lambda x: x['start_time'])
        return day_events
    
    def check_conflicts(self, start_time, end_time):
        """检查时间冲突"""
        conflicts = []
        
        for event in self.events:
#            # 检查时间重叠
            if (start_time < event['end_time'] and end_time > event['start_time']):
                conflicts.append(event)
        
        return conflicts
    
    def get_free_time(self, date, work_start=9, work_end=18):
        """获取指定日期的空闲时间段"""
        if isinstance(date, str):
            date = datetime.fromisoformat(date).date()
        
#        # 工作时间范围
        work_start_dt = datetime.combine(date, datetime.min.time().replace(hour=work_start))
        work_end_dt = datetime.combine(date, datetime.min.time().replace(hour=work_end))
        
#        # 添加时区信息
        work_start_dt = work_start_dt.replace(tzinfo=self.timezone)
        work_end_dt = work_end_dt.replace(tzinfo=self.timezone)
        
#        # 获取当天的事件
        day_events = self.get_events_by_date(date)
        
        free_periods = []
        current_time = work_start_dt
        
        for event in day_events:
#            # 如果事件开始时间在工作时间内
            if event['start_time'] >= work_start_dt and event['start_time'] <= work_end_dt:
                if current_time < event['start_time']:
#                    # 添加空闲时间段
                    free_periods.append({
                        'start': current_time,
                        'end': event['start_time'],
                        'duration': event['start_time'] - current_time
                    })
                
#                # 更新当前时间到事件结束时间
                current_time = max(current_time, event['end_time'])
        
#        # 检查最后一个事件到工作结束时间的空闲时间
        if current_time < work_end_dt:
            free_periods.append({
                'start': current_time,
                'end': work_end_dt,
                'duration': work_end_dt - current_time
            })
        
        return free_periods
    
    def generate_daily_schedule(self, date):
        """生成日程表"""
        events = self.get_events_by_date(date)
        free_times = self.get_free_time(date)
        
        schedule = f"\n{date} 日程安排\n"
        schedule += "=" * 30 + "\n"
        
        if not events:
            schedule += "今天没有安排事件\n"
        else:
            schedule += "已安排事件:\n"
            for event in events:
                start_str = event['start_time'].strftime('%H:%M')
                end_str = event['end_time'].strftime('%H:%M')
                duration_str = DateTimeHelper.format_duration(event['duration'])
                
                schedule += f"  {start_str}-{end_str} {event['title']} ({duration_str})\n"
                if event['description']:
                    schedule += f"    描述: {event['description']}\n"
        
        if free_times:
            schedule += "\n 空闲时间:\n"
            for free in free_times:
                start_str = free['start'].strftime('%H:%M')
                end_str = free['end'].strftime('%H:%M')
                duration_str = DateTimeHelper.format_duration(free['duration'])
                schedule += f"  {start_str}-{end_str} (空闲 {duration_str})\n"
        
        return schedule
    
    def export_events(self, filename):
        """导出事件到 JSON 文件"""
        export_data = []
        
        for event in self.events:
            export_event = {
                'id': event['id'],
                'title': event['title'],
                'start_time': event['start_time'].isoformat(),
                'end_time': event['end_time'].isoformat(),
                'duration_minutes': int(event['duration'].total_seconds() / 60),
                'description': event['description'],
                'created_at': event['created_at'].isoformat()
            }
            export_data.append(export_event)
        
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(export_data, f, ensure_ascii=False, indent=2)
        
        print(f"事件已导出到 {filename}")

## 使用示例
scheduler = EventScheduler()

## 添加一些事件
scheduler.add_event("晨会", "2024-01-15T09:00:00", 30, "团队日常晨会")
scheduler.add_event("项目评审", "2024-01-15T10:30:00", 90, "新项目方案评审")
scheduler.add_event("午餐", "2024-01-15T12:00:00", 60, "团队聚餐")
scheduler.add_event("客户会议", "2024-01-15T14:00:00", 120, "与客户讨论需求")
scheduler.add_event("代码审查", "2024-01-15T16:30:00", 60, "代码质量检查")

## 生成日程表
schedule = scheduler.generate_daily_schedule(date(2024, 1, 15))
print(schedule)

## 检查冲突
conflicts = scheduler.check_conflicts(
    datetime(2024, 1, 15, 10, 0, tzinfo=scheduler.timezone),
    datetime(2024, 1, 15, 11, 0, tzinfo=scheduler.timezone)
)

if conflicts:
    print(f"\n 时间冲突检测:")
    for conflict in conflicts:
        print(f"  与 '{conflict['title']}' 冲突")
else:
    print("\n 没有时间冲突")

## 导出事件
scheduler.export_events('events.json')

⚠️ 注意事项

  • 时区处理: 始终明确指定时区,避免时区混乱
  • 夏令时: 在处理时区转换时要考虑夏令时的影响
  • 精度: datetime 的精度为微秒,对于更高精度需求考虑其他方案
  • 性能: 大量日期时间计算时注意性能优化
  • 本地化: 月份和星期名称的本地化需要额外处理
  • 闰秒: datetime 不处理闰秒,如需要考虑使用专门的库

🔗 相关内容

📚 扩展阅读

🏷️ 标签

日期时间 时区 格式化 计算 时间差 标准库


最后更新: 2024-01-01
作者: Python 文档团队
版本: 1.0

作者: Python 文档团队

版本: 1.0

讨论与反馈

欢迎在下方留言讨论,分享你的学习心得或提出问题。评论基于GitHub Issues,需要GitHub账号。