forked from cisco/ChezScheme
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdate.ss
445 lines (386 loc) · 15.1 KB
/
date.ss
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
"date.ss"
;;; date.ss
;;; Copyright 1984-2016 Cisco Systems, Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; disagreements with SRFI 19:
;;; - nanoseconds are limited to 999,999,999 (SRFI 19: 9,999,999)
;;; - seconds are limited to 61 (SRFI 19: 60)
;;; - days range from 1 to 31, includsive (SRFI 19: 0 to 31, inclusive)
;;; - years range from 1901 to about 2038, inclusive (SRFI 19: not clear)
;;; - years start at 1970 under Windows
;;; - current-date tz-offset defaults to local offset (SRFI 19: not specified)
;;; questions about SRFI 19:
;;; - must tai times be supported?
;;; can't read past copyright notice in srfi 19 reference implementation.
;;; is it really as restrictive as it appears?
;;; suck in srfi 19 tests, which seem only to be covered by license in
;;; srfi 19 description.
;;; won't be implemented from SRFI 19 except as add-on:
;;; - constants time-duration, time-monotonic, time-process, time-tai,
;;; time-thread, and time-utc (violates no non-procedure value policy)
;;; not yet implemented from SRFI 19:
;;; - time procedures
;;; time-resolution [ts-time]
;;; time-monotonic->time-utc ; may be impossible unless we roll our own
;;; time-monotonic->time-utc! ; monotonic (= tai) based on utc plus leap
;;; time-utc->time-monotonic ; seconds. yuck.
;;; time-utc->time-monotonic!
;;; - date procedures
;;; date-week-number
;;; date->time-monotonic
;;; time-monotonic->date
;;; date->string
;;; string->date
;;; - julian dates
;;; current-julian-day
;;; current-modified-julian-day
;;; date->julian-day
;;; date->modified-julian-day
;;; julian-day->date
;;; julian-day->time-monotonic
;;; julian-day->time-utc
;;; modified-julian-day->date
;;; modified-julian-day->time-monotonic
;;; modified-julian-day->time-utc
;;; time-monotonic->julian-day
;;; time-monotonic->modified-julian-day
;;; time-utc->julian-day
;;; time-utc->modified-julian-day
;;; - tai times
;;; ts-type 'time-tai
;;; date->time-tai
;;; time-monotonic->time-tai
;;; time-monotonic->time-tai!
;;; time-tai->date
;;; time-tai->time-monotonic
;;; time-tai->time-monotonic!
;;; time-tai->time-utc
;;; time-tai->time-utc!
;;; time-utc->time-tai
;;; time-utc->time-tai!
;;; julian-day->time-tai
;;; modified-julian-day->time-tai
;;; time-tai->julian-day
;;; time-tai->modified-julian-day
(let ()
(define $clock-gettime ; clock_id -> tspair
(foreign-procedure "(cs)clock_gettime"
(integer-32)
scheme-object))
(define $gmtime ; #f|tzoff X #f|tspair -> dtvec (returns #f on error)
(foreign-procedure "(cs)gmtime"
(scheme-object scheme-object)
scheme-object))
(define $asctime ; #f | dtvec -> string (returns #f on error)
(foreign-procedure "(cs)asctime"
(scheme-object)
scheme-object))
(define $mktime ; dtvec -> tspair (returns #f on error)
(foreign-procedure "(cs)mktime"
(scheme-object)
scheme-object))
(define-record-type ts
(fields (mutable typeno) (immutable pair))
(nongenerative #{ts a5dq4nztnmq6xlgp-a})
(sealed #t))
(define ts-type->typeno
(lambda (who type)
(case type
[(time-process) (constant time-process)]
[(time-thread) (constant time-thread)]
[(time-duration) (constant time-duration)]
[(time-monotonic) (constant time-monotonic)]
[(time-utc) (constant time-utc)]
[(time-collector-cpu) (constant time-collector-cpu)]
[(time-collector-real) (constant time-collector-real)]
[else ($oops who "unrecognized time type ~s" type)])))
(define ts-typeno->type
(lambda (typeno)
(cond
[(eq? typeno (constant time-process)) 'time-process]
[(eq? typeno (constant time-thread)) 'time-thread]
[(eq? typeno (constant time-duration)) 'time-duration]
[(eq? typeno (constant time-monotonic)) 'time-monotonic]
[(eq? typeno (constant time-utc)) 'time-utc]
[(eq? typeno (constant time-collector-cpu)) 'time-collector-cpu]
[(eq? typeno (constant time-collector-real)) 'time-collector-real]
[else ($oops 'time-internal "unexpected typeno ~s" typeno)])))
(define ts-sec (lambda (ts) (car (ts-pair ts))))
(define ts-nsec (lambda (ts) (cdr (ts-pair ts))))
(define set-ts-sec! (lambda (ts n) (set-car! (ts-pair ts) n)))
(define set-ts-nsec! (lambda (ts n) (set-cdr! (ts-pair ts) n)))
(define (check-ts who ts)
(unless (ts? ts)
($oops who "~s is not a time record" ts)))
(define (check-ts-sec who sec)
(unless (or (fixnum? sec) (bignum? sec))
($oops who "invalid number of seconds ~s" sec)))
(define (check-same-type who t1 t2)
(unless (fx= (ts-typeno t1) (ts-typeno t2))
($oops who "types of ~s and ~s differ" t1 t2)))
(define (check-type-duration who t)
(unless (fx= (ts-typeno t) (constant time-duration))
($oops who "~s does not have type time-duration" t)))
(define-record-type dt
(fields (immutable vec))
(nongenerative #{dt a5jhglnb7tr8ubed-a})
(sealed #t))
(define (check-dt who dt)
(unless (dt? dt)
($oops who "~s is not a date record" dt)))
(define (check-nsec who nsec)
(unless (and (or (fixnum? nsec) (bignum? nsec)) (<= 0 nsec 999999999))
($oops who "invalid nanosecond ~s" nsec)))
(define (check-sec who sec)
(unless (and (fixnum? sec) (fx<= 0 sec 61))
($oops who "invalid second ~s" sec)))
(define (check-min who min)
(unless (and (fixnum? min) (fx<= 0 min 59))
($oops who "invalid minute ~s" min)))
(define (check-hour who hour)
(unless (and (fixnum? hour) (fx<= 0 hour 23))
($oops who "invalid hour ~s" hour)))
(define (check-day who day)
(unless (and (fixnum? day) (fx<= 1 day 31))
($oops who "invalid day ~s" day)))
(define (check-mon who mon)
(unless (and (fixnum? mon) (fx<= 1 mon 12))
($oops who "invalid month ~s" mon)))
(define (check-year who year)
(unless (and (fixnum? year) (fx>= year 1901))
($oops who "invalid year ~s" year)))
(define (check-tz who tz)
(unless (and (fixnum? tz)
; being generous here...
(fx<= (* -24 60 60) tz (* 24 60 60)))
($oops who "invalid time-zone offset ~s" tz)))
(define $copy-time
(lambda (t)
(let ([p (ts-pair t)])
(make-ts (ts-typeno t) (cons (car p) (cdr p))))))
(record-writer (type-descriptor ts)
(lambda (x p wr)
(let ([type (ts-typeno->type (ts-typeno x))] [sec (ts-sec x)] [nsec (ts-nsec x)])
(if (and (< sec 0) (> nsec 0))
(fprintf p "#<~s -~d.~9,'0d>" type (- -1 sec) (- 1000000000 nsec))
(fprintf p "#<~s ~d.~9,'0d>" type sec nsec)))))
(record-writer (type-descriptor dt)
(lambda (x p wr)
(fprintf p "#<date~@[ ~a~]>"
($asctime (dt-vec x)))))
(set! make-time
(lambda (type nsec sec)
(let ([typeno (ts-type->typeno 'make-time type)])
(check-nsec 'make-time nsec)
(check-ts-sec 'make-time sec)
(make-ts typeno (cons sec nsec)))))
(set! time? (lambda (x) (ts? x)))
(set! time-type
(lambda (ts)
(check-ts 'time-type ts)
(ts-typeno->type (ts-typeno ts))))
(set! time-second
(lambda (ts)
(check-ts 'time-second ts)
(ts-sec ts)))
(set! time-nanosecond
(lambda (ts)
(check-ts 'time-nanosecond ts)
(ts-nsec ts)))
(set! set-time-type!
(lambda (ts type)
(check-ts 'set-time-type! ts)
(ts-typeno-set! ts (ts-type->typeno 'set-time-type! type))))
(set! set-time-second!
(lambda (ts sec)
(check-ts 'set-time-second! ts)
(check-ts-sec 'set-time-second! sec)
(set-ts-sec! ts sec)))
(set! set-time-nanosecond!
(lambda (ts nsec)
(check-ts 'set-time-nanosecond! ts)
(check-nsec 'set-time-nanosecond! nsec)
(set-ts-nsec! ts nsec)))
(set! time=?
(lambda (t1 t2)
(check-ts 'time=? t1)
(check-ts 'time=? t2)
(check-same-type 'time=? t1 t2)
(and (= (ts-sec t1) (ts-sec t2))
(= (ts-nsec t1) (ts-nsec t2)))))
(set! time<?
(lambda (t1 t2)
(check-ts 'time<? t1)
(check-ts 'time<? t2)
(check-same-type 'time<? t1 t2)
(or (< (ts-sec t1) (ts-sec t2))
(and (= (ts-sec t1) (ts-sec t2))
(< (ts-nsec t1) (ts-nsec t2))))))
(set! time<=?
(lambda (t1 t2)
(check-ts 'time<=? t1)
(check-ts 'time<=? t2)
(check-same-type 'time<=? t1 t2)
(or (< (ts-sec t1) (ts-sec t2))
(and (= (ts-sec t1) (ts-sec t2))
(<= (ts-nsec t1) (ts-nsec t2))))))
(set! time>=?
(lambda (t1 t2)
(check-ts 'time>=? t1)
(check-ts 'time>=? t2)
(check-same-type 'time>=? t1 t2)
(or (> (ts-sec t1) (ts-sec t2))
(and (= (ts-sec t1) (ts-sec t2))
(>= (ts-nsec t1) (ts-nsec t2))))))
(set! time>?
(lambda (t1 t2)
(check-ts 'time>? t1)
(check-ts 'time>? t2)
(check-same-type 'time>? t1 t2)
(or (> (ts-sec t1) (ts-sec t2))
(and (= (ts-sec t1) (ts-sec t2))
(> (ts-nsec t1) (ts-nsec t2))))))
(let ([f (lambda (t1 t2 who)
(check-ts who t1)
(check-ts who t2)
(check-same-type who t1 t2)
(let-values ([(sec nsec)
(let ([sec (- (ts-sec t1) (ts-sec t2))]
[nsec (- (ts-nsec t1) (ts-nsec t2))])
(if (< nsec 0) (values (- sec 1) (+ nsec 1000000000)) (values sec nsec)))])
(make-ts (constant time-duration) (cons sec nsec))))])
(set-who! time-difference (lambda (t1 t2) (f t1 t2 who)))
(set-who! time-difference! (lambda (t1 t2) (f t1 t2 who))))
(let ([f (lambda (t1 t2 who)
(check-ts who t1)
(check-ts who t2)
(check-type-duration who t2)
(let-values ([(sec nsec)
(let ([sec (- (ts-sec t1) (ts-sec t2))]
[nsec (- (ts-nsec t1) (ts-nsec t2))])
(if (< nsec 0) (values (- sec 1) (+ nsec 1000000000)) (values sec nsec)))])
(make-ts (ts-typeno t1) (cons sec nsec))))])
(set-who! subtract-duration (lambda (t1 t2) (f t1 t2 who)))
(set-who! subtract-duration! (lambda (t1 t2) (f t1 t2 who))))
(let ([f (lambda (t1 t2 who)
(check-ts who t1)
(check-ts who t2)
(check-type-duration who t2)
(let-values ([(sec nsec)
(let ([sec (+ (time-second t1) (time-second t2))]
[nsec (+ (time-nanosecond t1) (time-nanosecond t2))])
(if (>= nsec 1000000000) (values (+ sec 1) (- nsec 1000000000)) (values sec nsec)))])
(make-ts (ts-typeno t1) (cons sec nsec))))])
(set-who! add-duration (lambda (t1 t2) (f t1 t2 who)))
(set-who! add-duration! (lambda (t1 t2) (f t1 t2 who))))
(set-who! copy-time
(lambda (t)
(check-ts who t)
($copy-time t)))
(set-who! current-time
(case-lambda
[() (let ([typeno (constant time-utc)])
(make-ts typeno ($clock-gettime typeno)))]
[(type)
(case type
[(time-collector-cpu) ($copy-time ($gc-cpu-time))]
[(time-collector-real) ($copy-time ($gc-real-time))]
[else (let ([typeno (ts-type->typeno who type)])
(make-ts typeno ($clock-gettime typeno)))])]))
(set! current-date
(case-lambda
[()
(let ([dtvec ($gmtime #f #f)])
(unless dtvec ($oops 'current-date "failed"))
(make-dt dtvec))]
[(tz)
(check-tz 'current-date tz)
(let ([dtvec ($gmtime tz #f)])
(unless dtvec ($oops 'current-date "failed"))
(make-dt dtvec))]))
(set! date-and-time ; ptime|#f -> string
(case-lambda
[() (or ($asctime #f) ($oops 'date-and-time "failed"))]
[(dt)
(check-dt 'date-and-time dt)
(or ($asctime (dt-vec dt))
($oops 'date-and-time "failed for date record ~s" dt))]))
(set! make-date
(lambda (nsec sec min hour day mon year tz)
(check-nsec 'make-date nsec)
(check-sec 'make-date sec)
(check-min 'make-date min)
(check-hour 'make-date hour)
; need more accurate check for day based on year and month
(check-day 'make-date day)
(check-mon 'make-date mon)
(check-year 'make-date year)
(check-tz 'make-date tz)
; keep in sync with cmacros.ss declarations of dtvec-nsec, etc.
(let ([dtvec (vector nsec sec min hour day mon (- year 1900) 0 0 0 tz)])
(unless ($mktime dtvec) ; for effect on dtvec
($oops 'make-date "invalid combination of arguments"))
(unless (fx= (vector-ref dtvec (constant dtvec-mday)) day)
($oops 'make-date "invalid day ~s for month ~s and year ~s" day mon year))
(make-dt dtvec))))
(set! date? (lambda (x) (dt? x)))
(let ()
(define-syntax date-getter
(syntax-rules ()
[(_ name index)
(set! name
(lambda (dt)
(check-dt 'name dt)
(vector-ref (dt-vec dt) index)))]))
(date-getter date-nanosecond (constant dtvec-nsec))
(date-getter date-second (constant dtvec-sec))
(date-getter date-minute (constant dtvec-min))
(date-getter date-hour (constant dtvec-hour))
(date-getter date-day (constant dtvec-mday))
(date-getter date-month (constant dtvec-mon))
; date-year is below
(date-getter date-week-day (constant dtvec-wday))
(date-getter date-year-day (constant dtvec-yday))
(date-getter date-zone-offset (constant dtvec-tzoff)))
(set! date-year
(lambda (dt)
(check-dt 'date-year dt)
(+ (vector-ref (dt-vec dt) (constant dtvec-year)) 1900)))
#;(set! date-week-number
(lambda (dt dowsw)
(unless (or (eq? dossw 0) (eq? dossw 1))
($oops 'date-week-number "invalid week starting day" dossw))
???))
(set-who! time-utc->date
(case-lambda
[(t)
(unless (and (ts? t) (eq? (ts-typeno t) (constant time-utc)))
($oops who "~s is not a utc time record" t))
(let ([dtvec ($gmtime #f (ts-pair t))])
(unless dtvec ($oops who "failed"))
(make-dt dtvec))]
[(t tz)
(unless (and (ts? t) (eq? (ts-typeno t) (constant time-utc)))
($oops who "~s is not a utc time record" t))
(check-tz 'current-date tz)
(let ([dtvec ($gmtime tz (ts-pair t))])
(unless dtvec ($oops who "failed"))
(make-dt dtvec))]))
(set-who! date->time-utc
(lambda (dt)
(check-dt who dt)
(let ([p ($mktime (dt-vec dt))])
(unless p ($oops who "conversion failed for ~s" dt))
(make-ts (constant time-utc) p))))
)