
本文介绍使用 go 标准库 time 包精准计算两个日期之间所有指定星期几(如每周五、每周日)的日期,避免时间偏移、重复日期和冗余逻辑,并确保输出纯日期格式(无时间部分)。
本文介绍使用 go 标准库 time 包精准计算两个日期之间所有指定星期几(如每周五、每周日)的日期,避免时间偏移、重复日期和冗余逻辑,并确保输出纯日期格式(无时间部分)。
在 Go 开发中,常需按业务规则调度任务或生成周期性数据——例如“从 2015-08-17 到 2015-10-14 之间的所有星期日”,并仅保留日期部分(如 2015-08-23),不带时间戳。原始代码存在多个典型问题:
- 使用 YearDay() 计算年内的天数差,无法跨年且忽略月份长度差异;
- 循环中错误地每次加 7 天却未更新 onDate,导致 z := onDate.AddDate(0, 0, 7) 恒基于初始值,造成重复输出同一日期;
- time.Parse() 在循环内被误用于已为 time.Time 类型的 z,引发 panic;
- 未对结束边界做语义校准(如 2015-10-14 应理解为“包含该日”,即遍历上限为 2015-10-14T23:59:59)。
✅ 正确做法是:以起始时间为基准,向前/向后调整至目标星期几,再以 7 天为步长迭代,严格比较 time.Time 实例而非字符串或年日序号。
以下为健壮、可复用的实现:
package main
import (
"fmt"
"time"
)
// GetWeekdaysInRange 返回指定日期范围内所有目标星期几的日期(仅日期部分,时区为Local)
func GetWeekdaysInRange(start, end time.Time, targetWeekday time.Weekday) []time.Time {
var dates []time.Time
// 将 start 调整为第一个 >= start 的 targetWeekday
for start.Weekday() != targetWeekday {
start = start.AddDate(0, 0, 1)
}
// 迭代:从首个匹配日开始,每次 +7 天,直到超过 end
for !start.After(end) {
// 截断时间部分,只保留日期(00:00:00)
dateOnly := time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, start.Location())
dates = append(dates, dateOnly)
start = start.AddDate(0, 0, 7)
}
return dates
}
func main() {
layout := "2006-01-02"
startDate, _ := time.Parse(layout, "2015-08-17")
endDate, _ := time.Parse(layout, "2015-10-14")
sundays := GetWeekdaysInRange(startDate, endDate, time.Sunday)
for _, d := range sundays {
fmt.Println(d.Format(layout)) // 输出: 2015-08-23, 2015-08-30, ..., 2015-10-11
}
}? 关键要点说明:
- ✅ 边界处理:!start.After(end) 确保包含 end 当日(若其恰好为目标星期几);
- ✅ 纯日期输出:通过 time.Date(..., 0, 0, 0, 0, ...) 显式构造零时刻,再用 Format("2006-01-02") 安全转为字符串,彻底规避时间部分干扰;
- ✅ 时区安全:所有操作基于 start.Location(),适配本地时区或 UTC;
- ⚠️ 注意:若需数据库写入,直接传入 dateOnly(time.Time)即可,ORM 或 SQL 驱动通常能自动处理为 DATE 类型;避免手动拼接字符串日期,以防时区/格式歧义。
? 进阶建议:可封装为支持自定义步长(如每 2 周一次)、排除节假日、或返回 ISO 8601 字符串切片的工具函数。核心原则始终不变——用 time.Time 原生方法做算术与比较,而非字符串解析或年日序号运算。










