OpenClaw多智能体电商自动化实战:比价、库存监控与自动调价闭环
2026/9/25 3:03:13
如果你觉得时间处理很简单——那大概是还没处理过跨时区、夏令时、闰秒的问题。Go的time包设计得相当优秀,但时区转换、时间比较、格式化的"魔术时间"……每个细节都可能在跨国业务中引发事故。本文将彻底解决你的Go时间处理困惑。
typeTimestruct{walluint64// 墙上时钟(纳秒)extint64// 单调时钟或时区偏移loc*Location// 时区信息}// wall的结构:// bit 63 (hasMonotonic): 是否有单调时钟// bit 62-0: 公元元年的纳秒数Go的time.Time是值类型,每次操作返回新的实例,天然线程安全。
// 创建时间now:=time.Now()// 当前时间(含单调时钟)t1:=time.Date(2024,1,15,10,30,0,0,time.UTC)t2,_:=time.Parse("2006-01-02","2024-01-15")// 比较——用Equal而非==// == 比较Location,不同时区的相同时刻会被视为不等t1.Equal(t2)// 推荐// 时间的前后判断t1.Before(t2)// t1 < t2t1.After(t2)// t1 > t2// 时间差duration:=t2.Sub(t1)fmt.Println(duration.Hours())Go使用一个特定的参考时间来进行格式化,这是Go最具特色的设计之一:
// 参考时间:Mon Jan 2 15:04:05 MST 2006// 记忆技巧:1 2 3 4 5 6 7const(layout="2006-01-02 15:04:05"// 2006年1月2日 下午3点4分5秒layout2="2006/01/02 15:04:05.000"// 带毫秒layout3="2006-01-02T15:04:05Z07:00"// RFC3339layout4="Mon, 02 Jan 2006 15:04:05 MST"// RFC1123)// 格式化t:=time.Now()fmt.Println(t.Format(layout))// 解析t,err:=time.Parse(layout,"2024-01-15 10:30:00")// 危险——Local时区不可预测(取决于服务器配置)t:=time.Now()// 使用Local时区t.Format("2006-01-02 15:04:05")// 安全——明确指定UTCt:=time.Now().UTC()// 加载指定时区loc,err:=time.LoadLocation("Asia/Shanghai")iferr!=nil{log.Fatal(err)}t:=time.Now().In(loc)// 时区转换utcTime:=time.Date(2024,1,15,2,30,0,0,time.UTC)beijingTime:=utcTime.In(loc)// 2024-01-15 10:30:00// 推荐:统一使用UTC存储typeModelstruct{CreatedAt time.Time`gorm:"autoCreateTime"`// 数据库存储UTC}// API返回时转换为客户端时区func(u*User)ToResponse(tzstring)UserResponse{loc,_:=time.LoadLocation(tz)returnUserResponse{CreatedAt:u.CreatedAt.In(loc).Format("2006-01-02 15:04:05"),}}// time.After可能导致内存泄漏funcbad(){for{select{case<-time.After(time.Second):// 每次创建新Timer,永不释放doWork()}}}// 正确:重用Timerfuncgood(){timer:=time.NewTimer(time.Second)defertimer.Stop()for{select{case<-timer.C:doWork()timer.Reset(time.Second)}}}// Ticker:周期性任务ticker:=time.NewTicker(5*time.Second)deferticker.Stop()for{select{case<-ticker.C:doPeriodicWork()case<-ctx.Done():return}}packagetimeutil// 获取当天开始时间(指定时区)funcStartOfDay(t time.Time,loc*time.Location)time.Time{year,month,day:=t.In(loc).Date()returntime.Date(year,month,day,0,0,0,0,loc)}// 获取当天结束时间funcEndOfDay(t time.Time,loc*time.Location)time.Time{returnStartOfDay(t,loc).Add(24*time.Hour-time.Nanosecond)}// 获取本周一(周一为一周开始)funcStartOfWeek(t time.Time,loc*time.Location)time.Time{t=t.In(loc)weekday:=t.Weekday()ifweekday==time.Sunday{weekday=7// 将周日视为7}returnStartOfDay(t.Add(-time.Duration(weekday-1)*24*time.Hour),loc)}// 友好的相对时间显示funcFriendlyTime(t time.Time)string{now:=time.Now()diff:=now.Sub(t)switch{casediff<time.Minute:return"刚刚"casediff<time.Hour:returnfmt.Sprintf("%d分钟前",int(diff.Minutes()))casediff<24*time.Hour:returnfmt.Sprintf("%d小时前",int(diff.Hours()))casediff<7*24*time.Hour:returnfmt.Sprintf("%d天前",int(diff.Hours()/24))default:returnt.Format("2006-01-02")}}