在日常的 Linux 运维、Shell 脚本编写或日志分析中,处理时间是家常便饭。
尤其是这两个需求,几乎人人都会遇到:
很多人会使用复杂的字符串截取或手动判断月份,其实利用 date -d命令,一行就能优雅解决。
今天我们就来彻底搞懂它,顺便避开一个 99% 的人都会踩的坑。
一、核心知识点:date -d是什么?
date -d(或 --date)允许你显示由字符串描述的时间,而不是直接取当前时间。
它支持非常自然的人类语言式描述,例如 “yesterday”、“next Monday” 或 “-1 month”。
二、实战:如何获取本月和上月的 1 号?
1️⃣ 获取本月 1 号(This Month 1st)
推荐写法(最标准):
date -d "$(date +%Y-%m-01)" "+%Y-%m-%d"
输出示例:
2026-04-01
原理拆解:
$(date +%Y-%m-01):生成当前年月,并拼接 -01(如 2026-04-01)
date -d:解析这个日期
"+%Y-%m-%d":指定最终输出格式
✅ 更口语化的写法:
date -d "this month 1 day" "+%Y-%m-%d"
2️⃣ 获取上个月 1 号(Last Month 1st)
推荐写法(最稳妥):
date -d "$(date +%Y-%m-01) -1 month" "+%Y-%m-%d"
输出示例:
2026-03-01
原理拆解:
先拿到本月 1 号
再减去 1 个月
✅ 简洁写法:
date -d "last month 1 day" "+%Y-%m-%d"
三、⚠️ 避坑指南:千万不要这样写!
很多新手会直接写:
date -d "-1 month" "+%Y-%m-%d"
❌ 为什么这是错的?
如果在 3 月 31 日 执行上述命令:
date -d "2026-03-31 -1 month"
系统会尝试计算 2 月 31 日,但这一天并不存在,于是自动进位,结果可能是:
2026-03-03 # 完全不是你想要的上个月!
✅ 黄金法则
凡是涉及月份的计算,一定要先锚定到当月的 1 号,再进行偏移。
四、Shell 脚本中的经典应用场景
场景 1:按月份切割 Nginx 日志
#!/bin/bash
LAST_MONTH_FIRST=$(date -d "$(date +%Y-%m-01) -1 month" "+%Y%m%d")
THIS_MONTH_FIRST=$(date -d "$(date +%Y-%m-01)" "+%Y%m%d")
LOG_FILE="/data/logs/access_${LAST_MONTH_FIRST}-${THIS_MONTH_FIRST}.log"
mv /data/logs/access.log $LOG_FILE
systemctl reload nginx
场景 2:生成月度报表文件名
REPORT_DATE=$(date -d "$(date +%Y-%m-01) -1 month" "+%Y-%m")
echo "正在生成 ${REPORT_DATE} 月度报表..."
场景 3:统计上个月的数据
START_DATE=$(date -d "$(date +%Y-%m-01) -1 month" "+%Y-%m-%d")
END_DATE=$(date -d "$(date +%Y-%m-01) -1 day" "+%Y-%m-%d")
echo "统计时间范围:${START_DATE} ~ ${END_DATE}"
五、常用格式速查表
需求 | 命令 |
|---|
本月 1 号 | date -d "$(date +%Y-%m-01)" "+%Y-%m-%d"
|
上月 1 号 | date -d "$(date +%Y-%m-01) -1 month" "+%Y-%m-%d"
|
上月最后一天 | date -d "$(date +%Y-%m-01) -1 day" "+%Y-%m-%d"
|
时间戳 | date -d "$(date +%Y-%m-01)" "+%s"
|
六、一句话总结
✅ 本月 1 号:先拼字符串 %Y-%m-01
✅ 上月 1 号:本月 1 号再 -1 month
❌ 避免:直接在“月底日期”上减月份
掌握这个技巧,你的 Shell 脚本将更加健壮、优雅,不再被时间计算折磨!