blob: f742a676c7eeaae5580ee468b5493567739f1cac [file] [log] [blame]
Serge Bazanski8ef457f2021-07-11 14:42:38 +00001package calendar
2
3import (
4 "fmt"
5 "time"
6
7 ics "github.com/arran4/golang-ical"
8)
9
10// EventTime is a timestamp for calendar events. It either represents a real
11// point-in time or a calender day, if it's a whole-day event.
12type EventTime struct {
13 // Time is a timestamp in the timezone originally defined for this event if
14 // WholeDay is true. Otherwise, it's a UTC time from which a year, month
15 // and day can be extracted and treated as the indication of a 'calendar
16 // day' in an unknown timezone.
17 Time time.Time
18 // WholeDay is true if this EventTime represents an entire calendar day.
19 WholeDay bool
20}
21
22func (e *EventTime) String() string {
23 if e.WholeDay {
24 return fmt.Sprintf("%s (whole day)", e.Time.Format("2006/01/02"))
25 } else {
26 return e.Time.String()
27 }
28}
29
30// parseICSTime attempts to parse a given ICS DT{START,END} object into an
31// EventTime, trying to figure out if the given object represents a timestamp
32// or a whole-day event.
33func parseICSTime(p *ics.IANAProperty) (*EventTime, error) {
34 // If this is has a VALUE of DATE, then this is a whole-day time.
35 // Otherwise, it's an actual timestamp.
36 valueList, ok := p.ICalParameters[string(ics.ParameterValue)]
37 if ok {
38 if len(valueList) != 1 || valueList[0] != "DATE" {
39 return nil, fmt.Errorf("unsupported time type: %v", valueList)
40 }
41 ts, err := time.Parse("20060102", p.Value)
42 if err != nil {
43 return nil, fmt.Errorf("could not parse date %q: %w", p.Value, err)
44 }
45 return &EventTime{
46 Time: ts,
47 WholeDay: true,
48 }, nil
49 }
50 // You would expect that nextcloud would emit VALUE == DATE-TIME for
51 // timestamps, but that just doesn't seem to be the case. Maye I should
52 // read the ICS standard...
53
54 tzidList, ok := p.ICalParameters[string(ics.ParameterTzid)]
55 if !ok || len(tzidList) != 1 {
56 return nil, fmt.Errorf("TZID missing")
57 }
58 tzid := tzidList[0]
59 location, err := time.LoadLocation(tzid)
60 if err != nil {
61 return nil, fmt.Errorf("could not parse TZID %q: %w", tzid, err)
62 }
63
64 ts, err := time.ParseInLocation("20060102T150405", p.Value, location)
65 if err != nil {
66 return nil, fmt.Errorf("could not parse time %q: %w", p.Value, err)
67 }
68
69 return &EventTime{
70 Time: ts,
71 WholeDay: false,
72 }, nil
73}