forked from hswaw/hscloud
126 lines
4.0 KiB
Go
126 lines
4.0 KiB
Go
package calendar
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/golang/glog"
|
|
)
|
|
|
|
// UpcomingEvent is a calendar event that will happen in the near future, or is
|
|
// currently happening (relative to same arbitrary timestamp of 'now',
|
|
// depending on the way the UpcomingEvent is crated).
|
|
//
|
|
// It is a best-effort parse of an ICS/iCal event into some event that can be
|
|
// interpreted as a 'community event', to be displayed publicly on a site.
|
|
type UpcomingEvent struct {
|
|
// UID is the unique ICS/iCal ID of this event.
|
|
UID string
|
|
// Summary is the 'title' of the event, usually a short one-liner.
|
|
Summary string
|
|
// Full description of event. Might contain multiple lines of test.
|
|
Description string
|
|
// Start and End of the events, potentially whole-day dates. See EventTime
|
|
// for more information.
|
|
// If Start is WholeDay then so is End, and vice-versa.
|
|
Start *EventTime
|
|
// End of the event, exclusive of the time range (ie. if a timestamp it
|
|
// defines the timestamp at which the next event can start; if it's whole
|
|
// day it defines the first day on which the event does not take place).
|
|
End *EventTime
|
|
// Tentative is whether this event is marked as 'Tentative' in the source
|
|
// calendar.
|
|
Tentative bool
|
|
}
|
|
|
|
// WholeDay returns true if this is a whole-day (or multi-day) event.
|
|
func (u *UpcomingEvent) WholeDay() bool {
|
|
return u.Start.WholeDay
|
|
}
|
|
|
|
var (
|
|
// onceComplainWarsawGone gates throwing a very verbose message about being
|
|
// unable to localize UpcomingEvents into Warsaw local time by WarsawDate.
|
|
onceComplainWarsawGone sync.Once
|
|
)
|
|
|
|
// WarsawDate prints a human-readable timestamp that makes sense within the
|
|
// context of this event taking place in Warsaw, or at least in the same
|
|
// timezone as Warsaw.
|
|
// It will return a time in one of the following formats:
|
|
//
|
|
// YEAR/MONTH/DAY
|
|
// (For one-day events)
|
|
//
|
|
// YEAR/MONTH/DAY - DAY
|
|
// (For multi-day events within the same month)
|
|
//
|
|
// YEAR/MONTH/DAY - YEAR/MONTH/DAY
|
|
// (For multi-day events spanning more than one month)
|
|
//
|
|
// YEAR/MONTH/DAY HH:MM - HH:MM
|
|
// (For timestamped events within the same day)
|
|
//
|
|
// YEAR/MONTH/DAY HH:MM - YEAR/MONTH/DAY HH:MM
|
|
// (For timestamped events spanning more than one day)
|
|
//
|
|
func (u *UpcomingEvent) WarsawDate() string {
|
|
YM := "2006/01"
|
|
D := "02"
|
|
YMD := "2006/01/02"
|
|
HM := "15:04"
|
|
YMDHM := "2006/01/02 15:04"
|
|
|
|
if u.WholeDay() {
|
|
start := u.Start.Time
|
|
// ICS whole-day dates are [start, end), ie. 'end' is exclusive.
|
|
end := u.End.Time.AddDate(0, 0, -1)
|
|
if start == end {
|
|
// Event is one-day.
|
|
return start.Format(YMD)
|
|
}
|
|
if start.Year() == end.Year() && start.Month() == end.Month() {
|
|
// Event starts and ends on the same month, print shortened form.
|
|
return fmt.Sprintf("%s/%s - %s", start.Format(YM), start.Format(D), end.Format(D))
|
|
}
|
|
// Event spans multiple months, print full form.
|
|
return fmt.Sprintf("%s - %s", start.Format(YMD), end.Format(YMD))
|
|
}
|
|
|
|
warsaw, err := time.LoadLocation("Europe/Warsaw")
|
|
if err != nil {
|
|
onceComplainWarsawGone.Do(func() {
|
|
glog.Errorf("Could not load Europe/Warsaw timezone, did the city cease to exist? LoadLoaction: %v", err)
|
|
})
|
|
// Even in the face of a cataclysm, degrade gracefully and assume the
|
|
// users are local to this service's timezone.
|
|
warsaw = time.Local
|
|
}
|
|
|
|
start := u.Start.Time.In(warsaw)
|
|
end := u.End.Time.In(warsaw)
|
|
if start.Year() == end.Year() && start.Month() == end.Month() && start.Day() == end.Day() {
|
|
// Event starts and ends on same day, print shortened form.
|
|
return fmt.Sprintf("%s %s - %s", start.Format(YMD), start.Format(HM), end.Format(HM))
|
|
}
|
|
// Event spans multiple days, print full form.
|
|
return fmt.Sprintf("%s - %s", start.Format(YMDHM), end.Format(YMDHM))
|
|
}
|
|
|
|
func (u *UpcomingEvent) String() string {
|
|
return fmt.Sprintf("%s (%s)", u.Summary, u.WarsawDate())
|
|
}
|
|
|
|
func (e *UpcomingEvent) Elapsed(t time.Time) bool {
|
|
// Event hasn't started yet?
|
|
if e.Start.Time.After(t) {
|
|
return false
|
|
}
|
|
// Event has started, but hasn't ended?
|
|
if e.End.Time.After(t) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|