oficina / calendar Public

Clone
git clone https://amadan.net/oficina/calendar

Plain git — no account needed to clone.

Download

Download this file

1package calendar
2
3import (
4 "context"
5 "time"
6)
7
8// Deciding WHEN a reminder next fires, split from the code that writes
9// the queue row so neither file passes the 300-line cap.
10
11// nextOccurrenceAfter finds the first occurrence whose reminder has not
12// yet come due.
13//
14// It searches from now minus the offset, so a reminder set for an event
15// starting in five minutes still fires — the due time is in the past,
16// and firing late is the correct answer, not skipping.
17func (s *Store) nextOccurrenceAfter(e Event, now int64, offsetMin int) (int64, string, bool) {
18 if e.RRule == "" {
19 if e.StartUTC-int64(offsetMin)*60 <= now && e.StartUTC <= now {
20 return 0, "", false // already started; nothing to warn about
21 }
22 return e.StartUTC, e.StartLocal, true
23 }
24 rule, err := ParseRule(e.RRule)
25 if err != nil {
26 // A foreign rule still has its own first occurrence.
27 if e.StartUTC > now {
28 return e.StartUTC, e.StartLocal, true
29 }
30 return 0, "", false
31 }
32 // A window wide enough to find the next occurrence of anything
33 // realistic — a yearly series needs more than a year.
34 const lookahead = 400 * 24 * 3600
35 occs := Expand(e.StartLocal, e.EndLocal, e.TZ, e.AllDay, rule, e.Exdates, now-3600, now+lookahead)
36 for _, o := range occs {
37 if o.StartUTC > now {
38 return o.StartUTC, o.Local, true
39 }
40 }
41 return 0, "", false
42}
43
44// NextDueAt is what Options.NextDue answers: the earliest moment this
45// instance has work.
46//
47// Zero means nothing is scheduled, and the platform hibernates
48// indefinitely.
49func (s *Store) NextDueAt(ctx context.Context) time.Time {
50 due, _ := s.nextDueAt(ctx)
51 return due
52}
53
54func (s *Store) nextDueAt(ctx context.Context) (time.Time, error) {
55 var at *int64
56 err := s.g.WithContext(ctx).Raw(`SELECT MIN(at) FROM (
57 SELECT MIN(MAX(due_at, COALESCE(retry_at, 0), COALESCE(claimed_at + ?, 0))) AS at
58 FROM reminder_queues WHERE fired_at IS NULL
59 UNION ALL SELECT MIN(hold_expires_at) FROM bookings WHERE state = ?
60 UNION ALL SELECT MIN(due_at) FROM booking_mails WHERE sent_at IS NULL
61 UNION ALL SELECT MIN(ip_forget_at) FROM bookings WHERE ip_fp IS NOT NULL
62 )`, int64(claimLease.Seconds()), BookingHeld).Scan(&at).Error
63 if err != nil || at == nil {
64 return time.Time{}, err
65 }
66 return time.Unix(*at, 0), nil
67}
68