[Issue #2] - Added timezone support with a list of support timezones
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type TimezoneConfig struct {
|
||||
StandardName string
|
||||
StandardOffset int
|
||||
DaylightName string
|
||||
DaylightOffset int
|
||||
ObservesUSDST bool
|
||||
}
|
||||
|
||||
// Map timezone aliases to standard/daylight offsets
|
||||
var timezones = map[string]TimezoneConfig{
|
||||
// Dynamic US zones (switch automatically between Standard and Daylight)
|
||||
"US/Eastern": {"EST", -5 * 3600, "EDT", -4 * 3600, true},
|
||||
"ET": {"EST", -5 * 3600, "EDT", -4 * 3600, true},
|
||||
"US/Central": {"CST", -6 * 3600, "CDT", -5 * 3600, true},
|
||||
"CT": {"CST", -6 * 3600, "CDT", -5 * 3600, true},
|
||||
"US/Mountain": {"MST", -7 * 3600, "MDT", -6 * 3600, true},
|
||||
"MT": {"MST", -7 * 3600, "MDT", -6 * 3600, true},
|
||||
"US/Pacific": {"PST", -8 * 3600, "PDT", -7 * 3600, true},
|
||||
"PT": {"PST", -8 * 3600, "PDT", -7 * 3600, true},
|
||||
|
||||
// Fixed non-DST zones
|
||||
"UTC": {"UTC", 0, "UTC", 0, false},
|
||||
"EST": {"EST", -5 * 3600, "EST", -5 * 3600, false},
|
||||
"EDT": {"EDT", -4 * 3600, "EDT", -4 * 3600, false},
|
||||
"CST": {"CST", -6 * 3600, "CST", -6 * 3600, false},
|
||||
"CDT": {"CDT", -5 * 3600, "CDT", -5 * 3600, false},
|
||||
"MST": {"MST", -7 * 3600, "MST", -7 * 3600, false},
|
||||
"MDT": {"MDT", -6 * 3600, "MDT", -6 * 3600, false},
|
||||
"PST": {"PST", -8 * 3600, "PST", -8 * 3600, false},
|
||||
"PDT": {"PDT", -7 * 3600, "PDT", -7 * 3600, false},
|
||||
}
|
||||
|
||||
// IsUSDST checks if the given UTC time falls in US Daylight Saving Time
|
||||
// (Second Sunday in March at 02:00 local to first Sunday in November at 02:00 local).
|
||||
func IsUSDST(t time.Time, stdOffsetSec int) bool {
|
||||
month := t.Month()
|
||||
if month < time.March || month > time.November {
|
||||
return false // Dec, Jan, Feb
|
||||
}
|
||||
if month > time.March && month < time.November {
|
||||
return true // Apr through Oct
|
||||
}
|
||||
|
||||
day := t.Day()
|
||||
weekday := int(t.Weekday())
|
||||
|
||||
if month == time.March {
|
||||
// Second Sunday in March is between 8th and 14th
|
||||
prevSunday := day - weekday
|
||||
if prevSunday < 8 {
|
||||
return false
|
||||
}
|
||||
if prevSunday >= 15 {
|
||||
return true
|
||||
}
|
||||
// Transition occurs at 2:00 AM standard local time -> (2 - stdOffsetHours) in UTC
|
||||
stdOffsetHours := stdOffsetSec / 3600
|
||||
switchHourUTC := 2 - stdOffsetHours
|
||||
return weekday != int(time.Sunday) || t.Hour() >= switchHourUTC
|
||||
}
|
||||
|
||||
// November: first Sunday is between 1st and 7th
|
||||
prevSunday := day - weekday
|
||||
if prevSunday < 1 {
|
||||
return true // Before first Sunday
|
||||
}
|
||||
// Fall-back occurs at 2:00 AM daylight local time -> (2 - (stdOffsetHours + 1)) in UTC
|
||||
stdOffsetHours := stdOffsetSec / 3600
|
||||
switchHourUTC := 2 - (stdOffsetHours + 1)
|
||||
return weekday == int(time.Sunday) && t.Hour() < switchHourUTC
|
||||
}
|
||||
|
||||
// ResolveTimezone dynamically determines the *time.Location and label
|
||||
// based on whether the zone observes DST at the given UTC time.
|
||||
func ResolveTimezone(name string, utcNow time.Time) (*time.Location, string) {
|
||||
cfg, ok := timezones[name]
|
||||
if !ok {
|
||||
return time.FixedZone("UTC", 0), "UTC"
|
||||
}
|
||||
|
||||
if cfg.ObservesUSDST && IsUSDST(utcNow, cfg.StandardOffset) {
|
||||
return time.FixedZone(cfg.DaylightName, cfg.DaylightOffset), cfg.DaylightName
|
||||
}
|
||||
return time.FixedZone(cfg.StandardName, cfg.StandardOffset), cfg.StandardName
|
||||
}
|
||||
Reference in New Issue
Block a user