package timeutil import ( "fmt" "regexp" "strconv" "strings" "time" ) const DateLayout = "2006-01-02" const LocalDateTimeLayout = "2006-01-02T15:04" func LoadLocation(name string) (*time.Location, error) { if location, ok := timezoneAliases[name]; ok { return location, nil } if location, ok := parseUTCOffset(name); ok { return location, nil } location, err := time.LoadLocation(name) if err == nil { return location, nil } if strings.Contains(name, "/") { return nil, fmt.Errorf("load timezone %q: %w", name, err) } location, chicagoErr := time.LoadLocation("America/" + name) if chicagoErr == nil { return location, nil } return nil, fmt.Errorf("load timezone %q: %w", name, err) } var timezoneAliases = map[string]*time.Location{ "Chicago": mustLocation("America/Chicago"), "Stl": mustLocation("America/Chicago"), "EST": time.FixedZone("EST", -5*60*60), "EDT": time.FixedZone("EDT", -4*60*60), "CST": time.FixedZone("CST", -6*60*60), "CDT": time.FixedZone("CDT", -5*60*60), "MST": time.FixedZone("MST", -7*60*60), "MDT": time.FixedZone("MDT", -6*60*60), "PST": time.FixedZone("PST", -8*60*60), "PDT": time.FixedZone("PDT", -7*60*60), } func mustLocation(name string) *time.Location { location, err := time.LoadLocation(name) if err != nil { panic(err) } return location } var utcOffsetPattern = regexp.MustCompile(`^([+-])(\d{1,2})(?::?(\d{2}))?$`) func parseUTCOffset(value string) (*time.Location, bool) { matches := utcOffsetPattern.FindStringSubmatch(value) if matches == nil { return nil, false } hours, err := strconv.Atoi(matches[2]) if err != nil || hours > 23 { return nil, false } minutes := 0 if matches[3] != "" { minutes, err = strconv.Atoi(matches[3]) if err != nil || minutes > 59 { return nil, false } } offset := (hours*60 + minutes) * 60 if matches[1] == "-" { offset = -offset } return time.FixedZone(value, offset), true } func LocalDate(now time.Time, location *time.Location) time.Time { local := now.In(location) return time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, location) } func ParseLocalDate(value string, location *time.Location) (time.Time, error) { parsed, err := time.ParseInLocation(DateLayout, value, location) if err != nil { return time.Time{}, fmt.Errorf("parse date %q as YYYY-MM-DD: %w", value, err) } return parsed, nil }