nandi/frqpublic Fork 0
5b5e90e85af0416f173c7c6e7310f01ec8328ba5
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Say when each thing was said dbf3b86 · on 5b5e90e85af0416f173c7c6e7310f01ec8328ba5 · nandi · 20d ago
clock.jolt · 74 lines · 2.9 KBMySQL Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
(ns frq.clock
  "Wall-clock time, in the reader's own zone.

  Messages carry an IRCv3 `time` tag in UTC; a channel is read in local time.
  jolt's time library works in epoch days and nanoseconds-of-day, and the
  offset needs a zone name, which no single call hands over — so the zone is
  found once here and everything else is arithmetic."
  (:require [clojure.string :as str]
            [jolt.host :as host]
            [jolt.time.local :as local]))

(def ^:private zone
  ;; TZ if it is set, otherwise whatever /etc/localtime points at, otherwise
  ;; UTC  which is wrong by hours but never wrong by a day's worth of parsing.
  (delay
    (or (let [tz (host/getenv "TZ")] (when (seq tz) tz))
        (try
          (second (re-find #"/zoneinfo/(.+)$"
                           (str/trim (str (host/sh-out "readlink -f /etc/localtime")))))
          (catch Exception _ nil))
        "UTC")))

(defn now-ms [] (quot (host/wall-nanos) 1000000))

(defn parse-time-tag
  "The `time=` value of an IRCv3 tag string as epoch milliseconds, or nil.

  Fixed format, always UTC: `2026-08-30T07:05:09.000Z`. Read by hand rather
  than through a parser, since this runs once per message of a hundred-message
  backlog."
  [tags]
  (when-let [[_ y mo d h mi s] (re-find #"time=(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})"
                                        (or tags ""))]
    (let [[y mo d h mi s] (map parse-long [y mo d h mi s])
          ;; days from the civil date, by Howard Hinnant's算  the same one
          ;; jolt's own local-date uses in reverse.
          y (if (<= mo 2) (dec y) y)
          era (quot (if (>= y 0) y (- y 399)) 400)
          yoe (- y (* era 400))
          doy (+ (quot (+ (* 153 (+ mo (if (> mo 2) -3 9))) 2) 5) (dec d))
          doe (+ (* yoe 365) (quot yoe 4) (- (quot yoe 100)) doy)
          days (+ (* era 146097) doe -719468)]
      (* 1000 (+ (* days 86400) (* h 3600) (* mi 60) s)))))

(defn- local-parts
  "`[date hour minute]` in the reader's zone, the hour on a 24-clock."
  [ms]
  (let [secs (quot ms 1000)
        secs (+ secs (host/tz-offset-seconds @zone secs))
        text (str (local/local-dt (Math/floorDiv secs 86400)
                                  (* (Math/floorMod secs 86400) 1000000000)))]
    [(subs text 0 10) (parse-long (subs text 11 13)) (subs text 14 16)]))

(defn clock-time
  "A twelve-hour time: `9:05 AM`, `12:30 PM`."
  [ms]
  (let [[_ hour minute] (local-parts ms)
        display (cond (zero? hour) 12
                      (> hour 12) (- hour 12)
                      :else hour)]
    (str display ":" minute " " (if (< hour 12) "AM" "PM"))))

(defn day [ms] (first (local-parts ms)))

(defn day-label
  "The heading for a day's messages: today and yesterday by name, anything
  older by date."
  [ms]
  (let [d (day ms)
        today (day (now-ms))
        yesterday (day (- (now-ms) 86400000))]
    (cond (= d today) "Today"
          (= d yesterday) "Yesterday"
          :else d)))