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
75
76
|
(use date/native)
# you generally start with this
# `time` returns the time in UTC
# note that it's an *opaque* type
# you should not try to interpret it except with comparisons
(def now (time))
# you can convert a `time` object into a `tm` object which represents a date
# using :localtime or :gmtime, where gmtime interprets the time as UTC
(def loc (:localtime now))
(def gmt (:gmtime now))
# you can modify `tm` objects like an associative array
# this means that put, update, keys, values, etc all work
# let's make another `tm` object, but remove one second from it
(def loc* (:localtime now))
(def gmt* (:gmtime now))
(update loc* :sec dec)
(update gmt* :sec dec)
# if the second count was at 0, it would now be at -1, which is out of range
# that's not a problem though, you can actually already perform comparisons
# WARN: you can ONLY compare `tm` objects of the same type (localtime vs gmtime)
(assert (< loc* loc))
(assert (< gmt* gmt))
# :mktime[!] and :timegm[!] "renormalize" the object
# :mktime and :timegm return a `time` object
# :mktime! and :timegm! do that, and normalizes the `tm` object in place by mutating it
(assert (= (:mktime loc) now))
(assert (< (:mktime loc*) now))
(assert (= (:timegm gmt) now))
(assert (< (:timegm gmt*) now))
# you must not call :mktime or :mktime! on the output of a :gmtime and vice versa
# the data *will* be wrong, as mktime (as per the spec) presumes :localtime
(assert (not= (:mktime gmt) now))
(assert (not= (:timegm loc) now))
(assert (= (:mktime loc) now))
(assert (= (:timegm gmt) now))
# as such, the correct approach is to keep things in `time` format as much as
# possible, only converting to :localtime or :gmtime for mutation and formatting
# this is roughly what the janet wrapper does
(defn- put-date
[ds key val]
(when val # allow null val for update-time
(if (function? val)
(update ds key val)
(put ds key val))))
(defn update-time
[time &keys {:sec seconds
:min minutes
:hour hours
:mday month-day
:mon month
:year year}]
(assert (= (type time) :date/time))
(let [g (:gmtime time)
p (partial put-date g)]
(p :sec seconds)
(p :min minutes)
(p :hour hours)
(p :mday month-day)
(p :mon month)
(p :year year)
(:timegm! g))) # timegm! is slightly more efficient and we're going to GC l anyway
# all supported keys can be given a function
(assert (< now (update-time now :sec inc)))
(assert (> now (update-time now :sec dec)))
# or a value to set them to
(assert (< now (update-time now :sec 61)))
(assert (> now (update-time now :sec -1)))
|