searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Python strftime全攻略:从格式符到生产级时间处理实战

2026-07-08 14:58:37
0
0

一、strftime到底是什么?

strftime,全称 string format time,即"字符串格式化时间"。它是Python time模块和datetime模块中的一个方法,作用是:把datetime对象转换成你指定格式的字符串。

它的兄弟是strptime(string parse time),作用正好相反:把字符串解析成datetime对象。

python
from datetime import datetime

# strftime:datetime → 字符串(格式化输出)
now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))
# 输出:2026-06-22 10:24:22

# strptime:字符串 → datetime(解析输入)
dt = datetime.strptime("2026-06-22 10:24:22", "%Y-%m-%d %H:%M:%S")
print(dt)
# 输出:2026-06-22 10:24:22

一句话总结:strftime是"往外吐"时间字符串,strptime是"往里吃"时间字符串。 记住这个方向,永远不会搞混。


二、完整格式符速查表

strftime的核心就是那些以%开头的格式符。下面这张表,建议收藏:

格式符 含义 示例输出
%Y 四位年份 2026
%y 两位年份(00-99) 26
%m 月份(01-12) 06
%d 日期(01-31) 22
%H 24小时制小时(00-23) 10
%I 12小时制小时(01-12) 10
%M 分钟(00-59) 24
%S 秒(00-59) 22
%p AM/PM AM
%A 星期全称 Monday
%a 星期缩写 Mon
%B 月份全称 June
%b 月份缩写 Jun
%j 一年中的第几天(001-366) 173
%U 一年中的第几周(周日为起始,00-53) 25
%W 一年中的第几周(周一为起始,00-53) 25
%w 星期几(0=周日,6=周六) 1
%z UTC偏移量 +0800
%Z 时区名称 CST
%% 字面量% %

高频踩坑预警

  • %Y vs %y:日志和数据库存储必须用%Y,两位数年份在2000年前后会产生歧义。
  • %I vs %H%I是12小时制,必须搭配%p使用,否则"10:00"你分不清是上午还是晚上。
  • %w:Python里周日是0,不是1。很多人按ISO标准(周一=1)来用,结果全错。

三、实战代码:五个生产场景全覆盖

光看表格记不住,下面用五个真实场景把知识点串起来。

场景1:日志文件命名(最常用)

生产环境的日志文件必须带时间戳,方便按天归档和排查问题。

python
from datetime import datetime

def get_log_filename():
    """生成带时间戳的日志文件名"""
    now = datetime.now()
    filename = now.strftime("log_%Y%m%d_%H%M%S.log")
    return filename

print(get_log_filename())
# 输出:log_20260622_102422.log

为什么这样写? 文件名用%Y%m%d_%H%M%S格式,排序时天然按时间顺序排列,不需要额外处理。这是我在多个项目中验证过的最佳实践。

场景2:生成标准化报表时间

业务报表要求时间格式统一为"2026年06月22日 星期一 上午10点24分",这种中文格式strftime原生不支持,需要组合处理:

python
from datetime import datetime

def format_chinese_time(dt=None):
    if dt is None:
        dt = datetime.now()
    
    weekday_map = {0: '日', 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六'}
    
    year = dt.strftime("%Y")
    month = dt.strftime("%m").lstrip("0")  # 去掉前导零
    day = dt.strftime("%d").lstrip("0")
    weekday = weekday_map[dt.weekday()]
    hour = dt.strftime("%I").lstrip("0")
    minute = dt.strftime("%M")
    period = dt.strftime("%p")
    
    period_cn = "上午" if period == "AM" else "下午"
    
    return f"{year}{month}{day}日 星期{weekday} {period_cn}{hour}{minute}分"

print(format_chinese_time())
# 输出:2026年6月22日 星期一 上午10点24分

这段代码的关键在于:strftime只负责提取数值,中文拼接由字符串格式化完成。不要试图用一个格式符解决所有问题。

场景3:处理时区时间(跨境业务必看)

strftime本身不处理时区,它只是把datetime对象"拍照"成字符串。如果你的datetime对象是带时区信息的(timezone-aware),%z%Z才会有值。

python
from datetime import datetime, timezone, timedelta

# UTC时间
utc_now = datetime.now(timezone.utc)
print("UTC:", utc_now.strftime("%Y-%m-%d %H:%M:%S %z"))
# 输出:UTC: 2026-06-22 02:24:22 +0000

# 北京时间(UTC+8)
beijing = utc_now + timedelta(hours=8)
print("北京:", beijing.strftime("%Y-%m-%d %H:%M:%S %z"))
# 输出:北京: 2026-06-22 10:24:22 +0800

# 错误示范:naive datetime直接用%z
naive = datetime.now()
print("无时区:", naive.strftime("%Y-%m-%d %H:%M:%S %z"))
# 输出:无时区: 2026-06-22 10:24:22 (空字符串,不是+0800)

关键结论:%z只对timezone-aware的datetime对象有效。 如果你的系统涉及多时区,务必使用pytzzoneinfo模块先转换时区,再调用strftime

场景4:解析用户输入的日期字符串

用户在表单里输入"2026/06/22",你需要把它转成datetime对象存数据库。这时候用strptime

python
from datetime import datetime

def parse_user_date(date_str):
    """解析用户输入的日期,支持多种分隔符"""
    formats = [
        "%Y/%m/%d",
        "%Y-%m-%d",
        "%Y.%m.%d",
        "%Y%m%d"
    ]
    
    for fmt in formats:
        try:
            return datetime.strptime(date_str, fmt)
        except ValueError:
            continue
    
    raise ValueError(f"无法解析日期格式: {date_str}")

# 测试
print(parse_user_date("2026/06/22"))
print(parse_user_date("2026-06-22"))
print(parse_user_date("20260622"))
# 全部输出:2026-06-22 00:00:00

这种"多格式尝试"的写法在Web开发中非常实用。用户输入格式不可控,与其报错,不如多试几种常见格式。

场景5:计算两个时间的差值并格式化输出

运维监控场景经常需要输出"系统已运行 X天 X小时 X分钟":

python
from datetime import datetime, timedelta

def format_uptime(start_time: datetime) -> str:
    """格式化系统运行时长"""
    now = datetime.now()
    delta = now - start_time
    
    days = delta.days
    seconds = delta.seconds
    hours = seconds // 3600
    minutes = (seconds % 3600) // 60
    
    return f"系统已运行 {days}{hours}小时 {minutes}分钟"

# 模拟系统启动时间(7天前)
boot_time = datetime.now() - timedelta(days=7, hours=3, minutes=45)
print(format_uptime(boot_time))
# 输出:系统已运行 7天 3小时 45分钟

这里有个细节:timedelta.seconds只返回当天剩余秒数(不超过86400),总秒数要用delta.total_seconds()。我在代码里用delta.days单独取天数,再用delta.seconds取余数,这是最清晰的写法。


四、常见陷阱与最佳实践

陷阱1:月份和日期的前导零

%m%d默认带前导零(06、09),但很多业务场景不需要。用.lstrip("0")或转成int再转回str都可以:

python
dt = datetime(2026, 6, 9)
print(dt.strftime("%m"))  # 06
print(str(int(dt.strftime("%m"))))  # 6

陷阱2:线程安全问题

time.strftime()是线程安全的,但datetime.strftime()在某些旧版本Python中存在共享状态问题。生产环境建议统一使用datetime模块的方法,并确保Python版本≥3.7。

陷阱3:Windows与Linux的%z差异

Windows上%z的输出格式是+08:00(带冒号),Linux上是+0800(不带冒号)。如果你的字符串要跨平台解析,需要标准化处理:

python
def normalize_timezone(dt):
    tz_str = dt.strftime("%z")
    if ":" in tz_str:
        return tz_str.replace(":", "")
    return tz_str

五、总结:记住这张思维导图

 
strftime
├── 方向:datetime → 字符串(输出/格式化)
├── 兄弟:strptime(字符串 → datetime,输入/解析)
├── 核心:%开头的格式符,大小写敏感
├── 高频组合:
│   ├── "%Y-%m-%d %H:%M:%S" → 数据库标准
│   ├── "%Y%m%d_%H%M%S" → 日志文件名
│   └── "%Y年%m月%d日" → 中文报表
├── 铁律:
│   ├── 年份用%Y不用%y
│   ├── 12小时制必须配%p
│   └── 时区用timezone-aware对象
└── 验证:改完配置先跑一遍,看输出是否符合预期

strftime本身不复杂,复杂的是业务场景对时间格式的千奇百怪的要求。掌握格式符只是第一步,真正的功力在于知道什么场景该用什么格式、怎么处理时区、怎么防御用户的奇葩输入。

把上面五个实战场景吃透,你在时间处理这块就已经能覆盖90%的业务需求了。剩下那10%,交给arrowpendulum这类第三方库就好——但前提是,你得先把strftime的底层逻辑搞明白。

0条评论
作者已关闭评论
窝补药上班啊
1449文章数
7粉丝数
窝补药上班啊
1449 文章 | 7 粉丝
原创

Python strftime全攻略:从格式符到生产级时间处理实战

2026-07-08 14:58:37
0
0

一、strftime到底是什么?

strftime,全称 string format time,即"字符串格式化时间"。它是Python time模块和datetime模块中的一个方法,作用是:把datetime对象转换成你指定格式的字符串。

它的兄弟是strptime(string parse time),作用正好相反:把字符串解析成datetime对象。

python
from datetime import datetime

# strftime:datetime → 字符串(格式化输出)
now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))
# 输出:2026-06-22 10:24:22

# strptime:字符串 → datetime(解析输入)
dt = datetime.strptime("2026-06-22 10:24:22", "%Y-%m-%d %H:%M:%S")
print(dt)
# 输出:2026-06-22 10:24:22

一句话总结:strftime是"往外吐"时间字符串,strptime是"往里吃"时间字符串。 记住这个方向,永远不会搞混。


二、完整格式符速查表

strftime的核心就是那些以%开头的格式符。下面这张表,建议收藏:

格式符 含义 示例输出
%Y 四位年份 2026
%y 两位年份(00-99) 26
%m 月份(01-12) 06
%d 日期(01-31) 22
%H 24小时制小时(00-23) 10
%I 12小时制小时(01-12) 10
%M 分钟(00-59) 24
%S 秒(00-59) 22
%p AM/PM AM
%A 星期全称 Monday
%a 星期缩写 Mon
%B 月份全称 June
%b 月份缩写 Jun
%j 一年中的第几天(001-366) 173
%U 一年中的第几周(周日为起始,00-53) 25
%W 一年中的第几周(周一为起始,00-53) 25
%w 星期几(0=周日,6=周六) 1
%z UTC偏移量 +0800
%Z 时区名称 CST
%% 字面量% %

高频踩坑预警

  • %Y vs %y:日志和数据库存储必须用%Y,两位数年份在2000年前后会产生歧义。
  • %I vs %H%I是12小时制,必须搭配%p使用,否则"10:00"你分不清是上午还是晚上。
  • %w:Python里周日是0,不是1。很多人按ISO标准(周一=1)来用,结果全错。

三、实战代码:五个生产场景全覆盖

光看表格记不住,下面用五个真实场景把知识点串起来。

场景1:日志文件命名(最常用)

生产环境的日志文件必须带时间戳,方便按天归档和排查问题。

python
from datetime import datetime

def get_log_filename():
    """生成带时间戳的日志文件名"""
    now = datetime.now()
    filename = now.strftime("log_%Y%m%d_%H%M%S.log")
    return filename

print(get_log_filename())
# 输出:log_20260622_102422.log

为什么这样写? 文件名用%Y%m%d_%H%M%S格式,排序时天然按时间顺序排列,不需要额外处理。这是我在多个项目中验证过的最佳实践。

场景2:生成标准化报表时间

业务报表要求时间格式统一为"2026年06月22日 星期一 上午10点24分",这种中文格式strftime原生不支持,需要组合处理:

python
from datetime import datetime

def format_chinese_time(dt=None):
    if dt is None:
        dt = datetime.now()
    
    weekday_map = {0: '日', 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六'}
    
    year = dt.strftime("%Y")
    month = dt.strftime("%m").lstrip("0")  # 去掉前导零
    day = dt.strftime("%d").lstrip("0")
    weekday = weekday_map[dt.weekday()]
    hour = dt.strftime("%I").lstrip("0")
    minute = dt.strftime("%M")
    period = dt.strftime("%p")
    
    period_cn = "上午" if period == "AM" else "下午"
    
    return f"{year}{month}{day}日 星期{weekday} {period_cn}{hour}{minute}分"

print(format_chinese_time())
# 输出:2026年6月22日 星期一 上午10点24分

这段代码的关键在于:strftime只负责提取数值,中文拼接由字符串格式化完成。不要试图用一个格式符解决所有问题。

场景3:处理时区时间(跨境业务必看)

strftime本身不处理时区,它只是把datetime对象"拍照"成字符串。如果你的datetime对象是带时区信息的(timezone-aware),%z%Z才会有值。

python
from datetime import datetime, timezone, timedelta

# UTC时间
utc_now = datetime.now(timezone.utc)
print("UTC:", utc_now.strftime("%Y-%m-%d %H:%M:%S %z"))
# 输出:UTC: 2026-06-22 02:24:22 +0000

# 北京时间(UTC+8)
beijing = utc_now + timedelta(hours=8)
print("北京:", beijing.strftime("%Y-%m-%d %H:%M:%S %z"))
# 输出:北京: 2026-06-22 10:24:22 +0800

# 错误示范:naive datetime直接用%z
naive = datetime.now()
print("无时区:", naive.strftime("%Y-%m-%d %H:%M:%S %z"))
# 输出:无时区: 2026-06-22 10:24:22 (空字符串,不是+0800)

关键结论:%z只对timezone-aware的datetime对象有效。 如果你的系统涉及多时区,务必使用pytzzoneinfo模块先转换时区,再调用strftime

场景4:解析用户输入的日期字符串

用户在表单里输入"2026/06/22",你需要把它转成datetime对象存数据库。这时候用strptime

python
from datetime import datetime

def parse_user_date(date_str):
    """解析用户输入的日期,支持多种分隔符"""
    formats = [
        "%Y/%m/%d",
        "%Y-%m-%d",
        "%Y.%m.%d",
        "%Y%m%d"
    ]
    
    for fmt in formats:
        try:
            return datetime.strptime(date_str, fmt)
        except ValueError:
            continue
    
    raise ValueError(f"无法解析日期格式: {date_str}")

# 测试
print(parse_user_date("2026/06/22"))
print(parse_user_date("2026-06-22"))
print(parse_user_date("20260622"))
# 全部输出:2026-06-22 00:00:00

这种"多格式尝试"的写法在Web开发中非常实用。用户输入格式不可控,与其报错,不如多试几种常见格式。

场景5:计算两个时间的差值并格式化输出

运维监控场景经常需要输出"系统已运行 X天 X小时 X分钟":

python
from datetime import datetime, timedelta

def format_uptime(start_time: datetime) -> str:
    """格式化系统运行时长"""
    now = datetime.now()
    delta = now - start_time
    
    days = delta.days
    seconds = delta.seconds
    hours = seconds // 3600
    minutes = (seconds % 3600) // 60
    
    return f"系统已运行 {days}{hours}小时 {minutes}分钟"

# 模拟系统启动时间(7天前)
boot_time = datetime.now() - timedelta(days=7, hours=3, minutes=45)
print(format_uptime(boot_time))
# 输出:系统已运行 7天 3小时 45分钟

这里有个细节:timedelta.seconds只返回当天剩余秒数(不超过86400),总秒数要用delta.total_seconds()。我在代码里用delta.days单独取天数,再用delta.seconds取余数,这是最清晰的写法。


四、常见陷阱与最佳实践

陷阱1:月份和日期的前导零

%m%d默认带前导零(06、09),但很多业务场景不需要。用.lstrip("0")或转成int再转回str都可以:

python
dt = datetime(2026, 6, 9)
print(dt.strftime("%m"))  # 06
print(str(int(dt.strftime("%m"))))  # 6

陷阱2:线程安全问题

time.strftime()是线程安全的,但datetime.strftime()在某些旧版本Python中存在共享状态问题。生产环境建议统一使用datetime模块的方法,并确保Python版本≥3.7。

陷阱3:Windows与Linux的%z差异

Windows上%z的输出格式是+08:00(带冒号),Linux上是+0800(不带冒号)。如果你的字符串要跨平台解析,需要标准化处理:

python
def normalize_timezone(dt):
    tz_str = dt.strftime("%z")
    if ":" in tz_str:
        return tz_str.replace(":", "")
    return tz_str

五、总结:记住这张思维导图

 
strftime
├── 方向:datetime → 字符串(输出/格式化)
├── 兄弟:strptime(字符串 → datetime,输入/解析)
├── 核心:%开头的格式符,大小写敏感
├── 高频组合:
│   ├── "%Y-%m-%d %H:%M:%S" → 数据库标准
│   ├── "%Y%m%d_%H%M%S" → 日志文件名
│   └── "%Y年%m月%d日" → 中文报表
├── 铁律:
│   ├── 年份用%Y不用%y
│   ├── 12小时制必须配%p
│   └── 时区用timezone-aware对象
└── 验证:改完配置先跑一遍,看输出是否符合预期

strftime本身不复杂,复杂的是业务场景对时间格式的千奇百怪的要求。掌握格式符只是第一步,真正的功力在于知道什么场景该用什么格式、怎么处理时区、怎么防御用户的奇葩输入。

把上面五个实战场景吃透,你在时间处理这块就已经能覆盖90%的业务需求了。剩下那10%,交给arrowpendulum这类第三方库就好——但前提是,你得先把strftime的底层逻辑搞明白。

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0