Christoph discovers that time creates its own alternate universe.
- Continuing our exploration of "literate" time logs
- We want a function to turn the timestamps into minutes.
- Format:
Fri Feb 08 2019 11:30-13:45
- Keep it simple: extract times with a regex and use math
- minutes in day = hours * 60 + minutes
- total minutes = end minutes - starting minutes
- Problem: What happens when we work past midnight? Negative minutes!
- We decided to have only one date, so a time before the starting time must span midnight.
- Format only allows for an activity to be <24 hours.
- "If we end up doing any activity in our life longer than 24 hours, I think we should that we might have other problems."
- Easy Solution: If the end time is before start time, add 24 hours.
- "When I get past any sort of simpler math, I just type it into my connected editor REPL because I know Clojure can do it faster than I can."
- Now we have a function to get minutes, want to add them all up.
- Use
loop
and recur
to iterate through the array and track the sum.
- Oh wait, what about Daylight Savings Time?
- "We all pretend that time is actually in the future."
- If it involves dates and times, we can't just do simple math.
- "If I do this the right way, I now have to open a whole new can of worms."
- Easy way out: write "doesn't support DST" in the release notes and call it "user error"!
- "Any time you have to be careful about something, you're probably doing it wrong."
- Use a time API.
- "The Clojure library is just a wrapper because nobody wants to reinvent this thing."
- Java time is immutable, so that works nicely with functional languages. No
clone
ing!
- Lots of functions in the time API. Which ones do we need?
- Our workflow: try out different expressions in a comment block in our connected editor to figure out the specific time functions we need.
- "Local" dates and times don't have time zones, but "zoned" dates and times do have them.
- Need to create timestamps for accurate math: date + time + zone
- In practice, when we use dates and times, they are implicitly connected to a time zone.
- Your time zone is your alternate universe: it affects the meaning of your dates and times.
- We added support for DST without changing the function signature.
- But, how do we add up other totals? Looks like we're going to need to change even more.
Related episodes:
Clojure in this episode:
nil
re-matches
loop
, recur
Related projects:
Code sample from this episode:
(ns app.time
(:require
[clojure.java.io :as io]
[java-time :as jt]))
(def timestamp-re #"(\w+\s\w+\s\d+\s\d+)\s+(\d{2}:\d{2})-(\d{2}:\d{2})")
(defn localize [dt tm]
(jt/zoned-date-time dt tm (jt/zone-id "America/Los_Angeles")))
(defn parse-time [time-str]
(jt/local-time "HH:mm" time-str))
(defn parse-date [date-str]
(jt/local-date "EEE MMM dd yyyy" date-str))
(defn parse-for-minutes
[line]
(if-let [[whole dt start end] (re-matches timestamp-re line)]
(let [date (parse-date dt)
start (localize date (parse-time start))
end (localize date (parse-time end))]
(if (jt/before? start end)
(jt/time-between start end :minutes)
(jt/time-between start (jt/plus end (jt/days 1)) :minutes)))
0))
(defn total-time
[filename]
(with-open [rdr (io/reader filename)]
(loop [total 0
lines (line-seq rdr)]
(if lines
(recur (+ total (or (some-> (first lines) parse-for-minutes) 0)) (next lines))
total))))