feat(calendar): parse reminder durations

This commit is contained in:
AJ ONeal 2025-11-30 03:11:37 -07:00
parent 0784b58dba
commit e599539c9c
No known key found for this signature in database

View File

@ -21,13 +21,20 @@ type Rule struct {
FixedDay int // used only when Nth == 0 (e.g. “15” for the 15th)
TimeOfDay string // HH:MM in the events local zone (e.g. "19:00")
Location *time.Location
// Reminders are ignored for now you can add them later.
Reminders []Reminder
}
type DayType int
var dayTypes = []string{"Weekdays", "Federal Workdays", "Bank Workdays", "Business Days"}
func (d DayType) String() string {
return dayTypes[d]
}
const (
FEDERAL DayType = iota
WEEK DayType = iota
FEDERAL
BANK
BUSINESS
)
@ -38,54 +45,54 @@ type Reminder struct {
Days int
DayType DayType
Duration time.Duration
Time Time
Time *time.Time
}
type Time struct {
Hour int
Minute int
Second int
func (r Reminder) String() string {
t := r.Time
if t == nil {
t = &time.Time{}
}
return fmt.Sprintf("%d %s %s %s", r.Days, r.DayType.String(), r.Duration.String(), t.Format("15:04"))
}
type Fields struct {
NAME int
NTH int
WKDAY int
DATE int
TIME int
TZ int
LABEL int
DETAIL int
R1 int
R2 int
R3 int
NAME int
NTH int
WKDAY int
DATE int
TIME int
TZ int
LABEL int
DETAIL int
R_FIRST int
R_LAST int
}
var FIELDS = Fields{
NAME: 0,
NTH: 1,
WKDAY: 2,
DATE: 3,
TIME: 4,
TZ: 5,
LABEL: 6,
DETAIL: 7,
R1: 8,
R2: 9,
R3: 10,
NAME: 0,
NTH: 1,
WKDAY: 2,
DATE: 3,
TIME: 4,
TZ: 5,
LABEL: 6,
DETAIL: 7,
R_FIRST: 8,
R_LAST: 10,
}
// ---------- 2. CSV → []Rule ----------
func LoadRules(rd *csv.Reader) ([]Rule, error) {
func LoadRules(csvr *csv.Reader) ([]Rule, error) {
// first line is the header
// TODO create instance and map header names to column ints
if _, err := rd.Read(); err != nil {
if _, err := csvr.Read(); err != nil {
return nil, fmt.Errorf("read header: %w", err)
}
var rules []Rule
for {
rec, err := rd.Read()
rec, err := csvr.Read()
if err == io.EOF {
break
}
@ -93,6 +100,7 @@ func LoadRules(rd *csv.Reader) ([]Rule, error) {
return nil, fmt.Errorf("read row: %w", err)
}
if len(rec) < 6 {
fmt.Printf("WARN: skip short row %q: %v\n", rec, err)
continue // malformed skip
}
rule, err := parseRule(rec)
@ -113,6 +121,7 @@ func parseRule(rec []string) (Rule, error) {
dateStr := rec[FIELDS.DATE]
timeStr := rec[FIELDS.TIME]
tz := rec[FIELDS.TZ]
reminderList := rec[FIELDS.R_FIRST:]
var r Rule
r.Event = strings.TrimSpace(event)
@ -173,6 +182,89 @@ func parseRule(rec []string) (Rule, error) {
}
r.Location = loc
var reminders []Reminder
for _, rule := range reminderList {
rule = strings.TrimSpace(rule)
if len(rule) == 0 {
continue
}
r, err := parseReminder(rule)
if err != nil {
fmt.Printf("WARN: skip reminder %q: %s: %v\n", rec, rule, err)
continue
}
reminders = append(reminders, r)
}
r.Reminders = reminders
return r, nil
}
func parseReminder(rule string) (Reminder, error) {
var r Reminder
var pm string
parts := strings.Fields(rule)
part := parts[len(parts)-1]
if part == "PM" || part == "AM" {
pm = part
parts = parts[:len(parts)-1]
part = parts[len(parts)-1]
}
var t time.Time
var err error
if pm != "" {
part += pm
}
hhmm := strings.ToLower(part)
t, err = time.Parse("03:04pm", hhmm)
if err != nil {
t, err = time.Parse("3:04pm", hhmm)
if err != nil {
t, err = time.Parse("15:04", hhmm)
}
}
if err == nil {
r.Time = &t
parts = parts[:len(parts)-1]
if len(parts) > 0 {
part = parts[len(parts)-1]
} else {
part = ""
}
}
if bPos := strings.Index(part, "b"); bPos > -1 {
bdays := part[:bPos]
part = part[bPos+1:]
r.DayType = BUSINESS
r.Days, err = strconv.Atoi(bdays)
if err != nil {
return r, err
}
} else if dPos := strings.Index(part, "d"); dPos > -1 {
ddays := part[:dPos]
part = part[dPos+1:]
r.DayType = WEEK
r.Days, err = strconv.Atoi(ddays)
if err != nil {
return r, err
}
}
if len(part) != 0 {
r.Duration, err = time.ParseDuration(part)
if err != nil {
return r, err
}
}
if len(parts) != 0 {
return r, err
}
return r, nil
}
@ -339,6 +431,14 @@ func main() {
fmt.Printf("%s → %s\n", ev.Time.Format(time.RFC3339), ev.Rule.Event)
}
for _, ev := range events {
var rs []string
for _, r := range ev.Rule.Reminders {
rs = append(rs, r.String())
}
fmt.Printf("%v\n", strings.Join(rs, " | "))
}
fmt.Println()
for _, ev := range events {
sleep := time.Until(ev.Time.Add(-2 * time.Hour))