Files
olebedev-when/rules/common/timezone.go
T
dualfroz fa0fa5b55b feat(rules): add an opt-in timezone rule (#23)
common.Timezone matches UTC and GMT with an optional offset (UTC+2,
GMT-05:30, GMT+0530) and a small table of upper case abbreviations
(EST, PDT, CET, ...), and sets Context.Location, so "10 pm EST" comes back
as 22:00 in a -05:00 zone. A timezone alone does not count as a match.
It is not added to common.All, so existing parsers behave as before.
2026-10-03 02:49:55 +02:00

85 lines
2.0 KiB
Go

package common
import (
"regexp"
"sort"
"strconv"
"strings"
"time"
"github.com/olebedev/when/rules"
)
/*
- 10 pm EST
- 22:00 UTC
- 9am GMT+2
- 18:30 utc-05:00
- 18:30 GMT+0530
Sets the location of the time found by the other rules. A timezone on its
own is not a time, so the rule never yields a result by itself.
Abbreviations are matched in upper case only, so "est" in a sentence is
not taken for a timezone, and each one is a fixed offset from UTC.
*/
var timezoneOffsets = map[string]time.Duration{
"HST": -10 * time.Hour,
"AKST": -9 * time.Hour,
"AKDT": -8 * time.Hour,
"PST": -8 * time.Hour,
"PDT": -7 * time.Hour,
"MST": -7 * time.Hour,
"MDT": -6 * time.Hour,
"CST": -6 * time.Hour,
"CDT": -5 * time.Hour,
"EST": -5 * time.Hour,
"EDT": -4 * time.Hour,
"BST": 1 * time.Hour,
"CET": 1 * time.Hour,
"CEST": 2 * time.Hour,
"EET": 2 * time.Hour,
"EEST": 3 * time.Hour,
"JST": 9 * time.Hour,
"AEST": 10 * time.Hour,
"AEDT": 11 * time.Hour,
}
func Timezone(s rules.Strategy) rules.Rule {
abbreviations := make([]string, 0, len(timezoneOffsets))
for abbreviation := range timezoneOffsets {
abbreviations = append(abbreviations, abbreviation)
}
sort.Strings(abbreviations)
return &rules.F{
RegExp: regexp.MustCompile("(?:\\W|^)" +
"((?i:utc|gmt)(?:([+-])(\\d{1,2})(?::?(\\d{2}))?)?|" +
strings.Join(abbreviations, "|") + ")" +
"(?:\\W|$)"),
Applier: func(m *rules.Match, c *rules.Context, o *rules.Options, ref time.Time) (bool, error) {
if c.Location != nil && s != rules.Override {
return false, nil
}
name := strings.ToUpper(m.Captures[0])
offset, ok := timezoneOffsets[name]
if !ok {
hours, _ := strconv.Atoi(m.Captures[2])
minutes, _ := strconv.Atoi(m.Captures[3])
if hours > 14 || minutes > 59 {
return false, nil
}
offset = time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute
if m.Captures[1] == "-" {
offset = -offset
}
}
c.Location = time.FixedZone(name, int(offset.Seconds()))
return false, nil
},
}
}