Source file elisp_time.ml
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
open! Core
open! Import
module F0 = struct
let float_time = Funcall.Wrap.("float-time" <: Value.Type.value @-> return float)
end
include Value.Make_subtype (struct
let name = "time"
let here = [%here]
let is_in_subtype value =
match F0.float_time value with
| exception _ -> false
| (_ : float) -> true
;;
end)
let t = type_
let format_time_string =
Funcall.Wrap.("format-time-string" <: string @-> t @-> nil_or string @-> return string)
;;
let format ?zone t ~format_string =
format_time_string format_string t (Option.map zone ~f:Time.Zone.name)
;;
let sexp_of_t t = [%sexp (format t ~format_string:"%F %T.%N%z" : string)]
let ( < ) = Funcall.Wrap.("time-less-p" <: t @-> t @-> return bool)
let ( > ) t1 t2 = t2 < t1
let compare t1 t2 = if t1 < t2 then -1 else if t1 > t2 then 1 else 0
let of_time_ns time_ns =
try
let nanos = time_ns |> Time_ns.to_int_ns_since_epoch in
let negate, nanos = if Int.( < ) nanos 0 then true, -nanos else false, nanos in
let sub_micro_nanos = nanos % 1_000 in
let picos = sub_micro_nanos * 1_000 in
let sec_and_micros = (nanos - sub_micro_nanos) / 1_000 in
let micros = sec_and_micros % 1_000_000 in
let sec = (sec_and_micros - micros) / 1_000_000 in
let sec_low = sec land 0xFFFF in
let sec_high = (sec - sec_low) lsr 16 in
(if negate
then [ -sec_high; -sec_low; -micros; -picos ]
else [ sec_high; sec_low; micros; picos ])
|> Value.Type.(list int |> to_value)
|> of_value_exn
with
| exn ->
raise_s
[%message
"[Elisp_time.of_time_ns]" (time_ns : Time_ns.Alternate_sexp.t) ~error:(exn : exn)]
;;
let min_time_ns_value = lazy (of_time_ns Time_ns.min_value_for_1us_rounding)
let max_time_ns_value = lazy (of_time_ns Time_ns.max_value_for_1us_rounding)
let unexpected_time_value value =
raise_s [%message "[Elisp_time] got unexpected time value" (value : Value.t)]
;;
let to_int_ns_since_epoch_exn t =
let min_time_ns_value = force min_time_ns_value in
let max_time_ns_value = force max_time_ns_value in
if t < min_time_ns_value
then
raise_s
[%message
"[Elisp_time.to_int_ns_since_epoch] got too small time"
~_:(t : t)
(min_time_ns_value : t)];
if t > max_time_ns_value
then
raise_s
[%message
"[Elisp_time.to_int_ns_since_epoch] got too large time"
~_:(t : t)
(max_time_ns_value : t)];
let value = t |> to_value in
if Value.is_integer value
then 1_000_000_000 * (value |> Value.to_int_exn)
else if Value.is_float value
then Float.iround_nearest_exn (1e9 *. (value |> Value.to_float_exn))
else (
let sec_high, sec_low, micros, picos =
match value |> Value.Type.(list int |> of_value_exn) with
| exception _ -> unexpected_time_value value
| [ sec_high ] -> sec_high, 0, 0, 0
| [ sec_high; sec_low ] -> sec_high, sec_low, 0, 0
| [ sec_high; sec_low; micros ] -> sec_high, sec_low, micros, 0
| [ sec_high; sec_low; micros; picos ] -> sec_high, sec_low, micros, picos
| _ -> unexpected_time_value value
in
(((sec_high lsl 16) + sec_low) * 1_000_000_000) + (micros * 1_000) + (picos / 1_000))
;;
let to_time_ns_exn t = t |> to_int_ns_since_epoch_exn |> Time_ns.of_int_ns_since_epoch