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
type t = Netdate.t
let epoch = Netdate.create 0.0
let now () = Netdate.create (Unix.time ())
let to_string ?(time = true) date =
let timezone = Netdate.format ~fmt:"%z" date in
let tz =
if timezone = "+0000" then "Z"
else
let tz_hour = String.sub timezone 0 3 in
let tz_minute = String.sub timezone 3 2 in
tz_hour ^ ":" ^ tz_minute
in
if time then
let result = Netdate.format ~fmt:"%Y-%m-%dT%H:%M:%.3S" date in
result ^ tz
else Netdate.format ~fmt:"%Y-%m-%d" date
let rfc3339_regexp =
Str.regexp
"^\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)\\(T\\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)\\(\\.\\([0-9]+\\)\\)?\\(Z\\|\\([-+]\\)\\([0-9][0-9]\\):\\([0-9][0-9]\\)\\)\\)?$"
let of_string date_string =
let matched n = Str.matched_group n date_string in
let parse_int n = int_of_string (matched n) in
let matches = Str.string_match rfc3339_regexp date_string 0 in
if matches then
let year = parse_int 1 in
let month = parse_int 2 in
let day = parse_int 3 in
let full_date = { epoch with Netdate.year; month; day; week_day = -1 } in
try
let hour = parse_int 5 in
let minute = parse_int 6 in
let second = parse_int 7 in
let nanos = if matched 8 <> "" then parse_int 9 * 1000000 else 0 in
let timezone = matched 10 in
let tz_sign, tz_hour, tz_minute =
if timezone = "Z" then (1, 0, 0)
else
let sign = matched 11 in
((if sign = "+" then 1 else -1), parse_int 12, parse_int 13)
in
{
full_date with
Netdate.hour;
minute;
second;
nanos;
zone = tz_sign * ((tz_hour * 60) + tz_minute);
week_day = -1;
}
with Not_found -> full_date
else failwith ("Invalid RFC3339 date: " ^ date_string)