srfi-19.test 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. ;;;; srfi-19.test --- test suite for SRFI-19 -*- scheme -*-
  2. ;;;; Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de> --- June 2001
  3. ;;;;
  4. ;;;; Copyright (C) 2001, 2003-2008, 2011, 2014, 2017, 2018
  5. ;;;; Free Software Foundation, Inc.
  6. ;;;;
  7. ;;;; This library is free software; you can redistribute it and/or
  8. ;;;; modify it under the terms of the GNU Lesser General Public
  9. ;;;; License as published by the Free Software Foundation; either
  10. ;;;; version 3 of the License, or (at your option) any later version.
  11. ;;;;
  12. ;;;; This library is distributed in the hope that it will be useful,
  13. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. ;;;; Lesser General Public License for more details.
  16. ;;;;
  17. ;;;; You should have received a copy of the GNU Lesser General Public
  18. ;;;; License along with this library; if not, write to the Free Software
  19. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. ;; SRFI-19 overrides current-date, so we have to do the test in a
  21. ;; separate module, or later tests will fail.
  22. (define-module (test-suite test-srfi-19)
  23. #:duplicates (last) ;; avoid warning about srfi-19 replacing `current-time'
  24. #:use-module (test-suite lib)
  25. #:use-module (srfi srfi-1)
  26. #:use-module (srfi srfi-19)
  27. #:use-module (ice-9 format))
  28. ;; Make sure we use the default locale.
  29. (when (defined? 'setlocale)
  30. (setlocale LC_ALL "C"))
  31. (define (with-tz* tz thunk)
  32. "Temporarily set the TZ environment variable to the passed string
  33. value and call THUNK."
  34. (let ((old-tz #f))
  35. (dynamic-wind
  36. (lambda ()
  37. (set! old-tz (getenv "TZ"))
  38. (putenv (format #f "TZ=~A" tz)))
  39. thunk
  40. (lambda ()
  41. (if old-tz
  42. (putenv (format #f "TZ=~A" old-tz))
  43. (putenv "TZ"))))))
  44. (defmacro with-tz (tz . body)
  45. `(with-tz* ,tz (lambda () ,@body)))
  46. (define (test-integral-time-structure date->time)
  47. "Test whether the given DATE->TIME procedure creates a time
  48. structure with integral seconds. (The seconds shall be maintained as
  49. integers, or precision may go away silently. The SRFI-19 reference
  50. implementation was not OK for Guile in this respect because of Guile's
  51. incomplete numerical tower implementation.)"
  52. (pass-if (format #f "~A makes integer seconds"
  53. date->time)
  54. (exact? (time-second
  55. (date->time (make-date 0 0 0 12 1 6 2001 0))))))
  56. (define (test-time->date time->date date->time)
  57. (pass-if (format #f "~A works"
  58. time->date)
  59. (begin
  60. (time->date (date->time (make-date 0 0 0 12 1 6 2001 0)))
  61. #t)))
  62. (define (test-dst time->date date->time)
  63. (pass-if (format #f "~A respects local DST if no TZ-OFFSET given"
  64. time->date)
  65. (let ((time (date->time (make-date 0 0 0 12 1 6 2001 0))))
  66. ;; on 2001-06-01, there should be 4 hours zone offset
  67. ;; between EST (EDT) and GMT
  68. (= (date-zone-offset
  69. (with-tz "EST5EDT"
  70. (time->date time)))
  71. -14400))))
  72. (define-macro (test-time-conversion a b)
  73. (let* ((a->b-sym (symbol-append a '-> b))
  74. (b->a-sym (symbol-append b '-> a)))
  75. `(pass-if (format #f "~A and ~A work and are inverses of each other"
  76. ',a->b-sym ',b->a-sym)
  77. (let ((time (make-time ,a 12345 67890123)))
  78. (time=? time (,b->a-sym (,a->b-sym time)))))))
  79. (define (test-time-comparison cmp a b)
  80. (pass-if (format #f "~A works" cmp)
  81. (cmp a b)))
  82. (define (test-time-arithmetic op a b res)
  83. (pass-if (format #f "~A works" op)
  84. (time=? (op a b) res)))
  85. ;; return true if time objects X and Y are equal
  86. (define (time-equal? x y)
  87. (and (eq? (time-type x) (time-type y))
  88. (eqv? (time-second x) (time-second y))
  89. (eqv? (time-nanosecond x) (time-nanosecond y))))
  90. (with-test-prefix "SRFI date/time library"
  91. ;; check for typos and silly errors
  92. (pass-if "date-zone-offset is defined"
  93. (and (defined? 'date-zone-offset)
  94. date-zone-offset
  95. #t))
  96. (pass-if "add-duration is defined"
  97. (and (defined? 'add-duration)
  98. add-duration
  99. #t))
  100. (pass-if "(current-time time-tai) works"
  101. (time? (current-time time-tai)))
  102. (pass-if "(current-time time-process) works"
  103. (time? (current-time time-process)))
  104. (test-time-conversion time-utc time-tai)
  105. (test-time-conversion time-utc time-monotonic)
  106. (test-time-conversion time-tai time-monotonic)
  107. (pass-if "string->date works"
  108. (begin (string->date "2001-06-01@14:00" "~Y-~m-~d@~H:~M")
  109. #t))
  110. ;; check for code paths where reals were passed to quotient, which
  111. ;; doesn't work in Guile (and is unspecified in R5RS)
  112. (test-time->date time-utc->date date->time-utc)
  113. (test-time->date time-tai->date date->time-tai)
  114. (test-time->date time-monotonic->date date->time-monotonic)
  115. (pass-if "Fractional nanoseconds are handled"
  116. (begin (make-time time-duration 1000000000.5 0) #t))
  117. ;; the seconds in a time shall be maintained as integers, or
  118. ;; precision may silently go away
  119. (test-integral-time-structure date->time-utc)
  120. (test-integral-time-structure date->time-tai)
  121. (test-integral-time-structure date->time-monotonic)
  122. ;; check for DST and zone related problems
  123. (pass-if "date->time-utc is the inverse of time-utc->date"
  124. (let ((time (date->time-utc
  125. (make-date 0 0 0 14 1 6 2001 7200))))
  126. (time=? time
  127. (date->time-utc (time-utc->date time 7200)))))
  128. (test-dst time-utc->date date->time-utc)
  129. (test-dst time-tai->date date->time-tai)
  130. (test-dst time-monotonic->date date->time-monotonic)
  131. (test-dst julian-day->date date->julian-day)
  132. (test-dst modified-julian-day->date date->modified-julian-day)
  133. (pass-if "`date->julian-day' honors timezone"
  134. (let ((now (current-date -14400)))
  135. (time=? (date->time-utc (julian-day->date (date->julian-day now)))
  136. (date->time-utc now))))
  137. (pass-if "string->date respects local DST if no time zone is read"
  138. (time=? (date->time-utc
  139. (with-tz "EST5EDT"
  140. (string->date "2001-06-01@08:00" "~Y-~m-~d@~H:~M")))
  141. (date->time-utc
  142. (make-date 0 0 0 12 1 6 2001 0))))
  143. (pass-if "string->date understands days and months"
  144. (time=? (let ((d (string->date "Saturday, December 9, 2006"
  145. "~A, ~B ~d, ~Y")))
  146. (date->time-utc (make-date (date-nanosecond d)
  147. (date-second d)
  148. (date-minute d)
  149. (date-hour d)
  150. (date-day d)
  151. (date-month d)
  152. (date-year d)
  153. 0)))
  154. (date->time-utc
  155. (make-date 0 0 0 0 9 12 2006 0))))
  156. (pass-if "string->date works on Sunday"
  157. ;; `string->date' never rests!
  158. (let* ((str "Sun, 05 Jun 2005 18:33:00 +0200")
  159. (date (string->date str "~a, ~d ~b ~Y ~H:~M:~S ~z")))
  160. (equal? "Sun Jun 05 18:33:00+0200 2005"
  161. (date->string date))))
  162. (pass-if "string->date understands nanoseconds (1)"
  163. (let ((date (string->date "2018-12-10 10:53:24.189"
  164. "~Y-~m-~d ~H:~M:~S.~N")))
  165. (time=? (date->time-utc date)
  166. (date->time-utc (make-date 189000000 24 53 10 10 12 2018
  167. (date-zone-offset date))))))
  168. (pass-if "string->date understands nanoseconds (2)"
  169. (let ((date (string->date "2018-12-10 10:53:24.189654321"
  170. "~Y-~m-~d ~H:~M:~S.~N")))
  171. (time=? (date->time-utc date)
  172. (date->time-utc (make-date 189654321 24 53 10 10 12 2018
  173. (date-zone-offset date))))))
  174. (pass-if "date->string pads small nanoseconds values correctly"
  175. (let* ((date (make-date 99999999 5 34 12 26 3 2017 0)))
  176. (equal? "099999999"
  177. (date->string date "~N"))))
  178. (pass-if "date->string ~f without leading zeroes"
  179. (let ((date (make-date 200000000 5 34 12 26 3 2017 0)))
  180. (equal? "5.2" (date->string date "~f"))))
  181. (pass-if "date->string ~f proper fractional part"
  182. (let ((date (make-date 550000 56 34 12 26 3 2017 0)))
  183. (equal? "56.00055" (date->string date "~f"))))
  184. ;; check time comparison procedures
  185. (let* ((time1 (make-time time-monotonic 0 0))
  186. (time2 (make-time time-monotonic 0 0))
  187. (time3 (make-time time-monotonic 385907 998360432))
  188. (time4 (make-time time-monotonic 385907 998360432)))
  189. (test-time-comparison time<=? time1 time3)
  190. (test-time-comparison time<? time1 time3)
  191. (test-time-comparison time=? time1 time2)
  192. (test-time-comparison time>=? time3 time3)
  193. (test-time-comparison time>? time3 time2))
  194. ;; check time arithmetic procedures
  195. (let* ((time1 (make-time time-monotonic 0 0))
  196. (time2 (make-time time-monotonic 385907 998360432))
  197. (diff (time-difference time2 time1)))
  198. (test-time-arithmetic add-duration time1 diff time2)
  199. (test-time-arithmetic subtract-duration time2 diff time1))
  200. (with-test-prefix "nanosecond normalization"
  201. (pass-if "small positive duration"
  202. (time-equal? (make-time time-duration 999999000 0)
  203. (time-difference (make-time time-tai 0 1) (make-time time-tai 1000 0))))
  204. (pass-if "small negative duration"
  205. (time-equal? (make-time time-duration -999999000 0)
  206. (time-difference (make-time time-tai 1000 0) (make-time time-tai 0 1)))))
  207. (with-test-prefix "date->time-tai"
  208. ;; leap second 1 Jan 1999, 1 second of UTC in make-date is out as 2
  209. ;; seconds of TAI in date->time-tai
  210. (pass-if "31dec98 23:59:59"
  211. (time-equal? (make-time time-tai 0 915148830)
  212. (date->time-tai (make-date 0 59 59 23 31 12 1998 0))))
  213. (pass-if "31dec98 23:59:60"
  214. (time-equal? (make-time time-tai 0 915148831)
  215. (date->time-tai (make-date 0 60 59 23 31 12 1998 0))))
  216. (pass-if "1jan99 0:00:00"
  217. (time-equal? (make-time time-tai 0 915148832)
  218. (date->time-tai (make-date 0 0 0 0 1 1 1999 0))))
  219. ;; leap second 1 Jan 2006, 1 second of UTC in make-date is out as 2
  220. ;; seconds of TAI in date->time-tai
  221. (pass-if "31dec05 23:59:59"
  222. (time-equal? (make-time time-tai 0 1136073631)
  223. (date->time-tai (make-date 0 59 59 23 31 12 2005 0))))
  224. (pass-if "31dec05 23:59:60"
  225. (time-equal? (make-time time-tai 0 1136073632)
  226. (date->time-tai (make-date 0 60 59 23 31 12 2005 0))))
  227. (pass-if "1jan06 0:00:00"
  228. (time-equal? (make-time time-tai 0 1136073633)
  229. (date->time-tai (make-date 0 0 0 0 1 1 2006 0)))))
  230. (with-test-prefix "date->time-monotonic"
  231. ;; leap second 1 Jan 1999, 1 second of UTC in make-date is out as 2
  232. ;; seconds of MONOTONIC in date->time-monotonic
  233. (pass-if "31dec98 23:59:59"
  234. (time-equal? (make-time time-monotonic 0 915148830)
  235. (date->time-monotonic (make-date 0 59 59 23 31 12 1998 0))))
  236. (pass-if "31dec98 23:59:60"
  237. (time-equal? (make-time time-monotonic 0 915148831)
  238. (date->time-monotonic (make-date 0 60 59 23 31 12 1998 0))))
  239. (pass-if "1jan99 0:00:00"
  240. (time-equal? (make-time time-monotonic 0 915148832)
  241. (date->time-monotonic (make-date 0 0 0 0 1 1 1999 0))))
  242. ;; leap second 1 Jan 2006, 1 second of UTC in make-date is out as 2
  243. ;; seconds of MONOTONIC in date->time-monotonic
  244. (pass-if "31dec05 23:59:59"
  245. (time-equal? (make-time time-monotonic 0 1136073631)
  246. (date->time-monotonic (make-date 0 59 59 23 31 12 2005 0))))
  247. (pass-if "31dec05 23:59:60"
  248. (time-equal? (make-time time-monotonic 0 1136073632)
  249. (date->time-monotonic (make-date 0 60 59 23 31 12 2005 0))))
  250. (pass-if "1jan06 0:00:00"
  251. (time-equal? (make-time time-monotonic 0 1136073633)
  252. (date->time-monotonic (make-date 0 0 0 0 1 1 2006 0)))))
  253. (with-test-prefix "julian-day->date"
  254. (pass-if-equal "0002-07-29T12:00:00Z" "0002-07-29T12:00:00Z"
  255. (date->string (julian-day->date 1722000 0) "~4"))
  256. (pass-if-equal "0024-06-23T12:00:00Z" "0024-06-23T12:00:00Z"
  257. (date->string (julian-day->date 1730000 0) "~4"))
  258. (pass-if-equal "2000-01-01T00:00:00Z" "2000-01-01T00:00:00Z"
  259. (date->string (julian-day->date 4903089/2 0) "~4"))
  260. (pass-if-equal "9999-12-31T12:00:00Z" "9999-12-31T12:00:00Z"
  261. (date->string (julian-day->date 5373484 0) "~4"))
  262. (pass-if-equal "+10000-01-01T12:00:00Z" "+10000-01-01T12:00:00Z"
  263. (date->string (julian-day->date 5373485 0) "~4"))
  264. (pass-if-equal "negative julian days"
  265. '((-2000000 . "-10188-02-01T14:24:00Z wk=04 dow=6 doy=032")
  266. (-20000 . "-4767-02-20T14:24:00Z wk=08 dow=0 doy=051")
  267. (-10 . "-4713-11-14T14:24:00Z wk=45 dow=5 doy=318")
  268. (-9 . "-4713-11-15T14:24:00Z wk=45 dow=6 doy=319")
  269. (-8 . "-4713-11-16T14:24:00Z wk=46 dow=0 doy=320")
  270. (-7 . "-4713-11-17T14:24:00Z wk=46 dow=1 doy=321")
  271. (-6 . "-4713-11-18T14:24:00Z wk=46 dow=2 doy=322")
  272. (-5 . "-4713-11-19T14:24:00Z wk=46 dow=3 doy=323")
  273. (-4 . "-4713-11-20T14:24:00Z wk=46 dow=4 doy=324")
  274. (-3 . "-4713-11-21T14:24:00Z wk=46 dow=5 doy=325")
  275. (-2 . "-4713-11-22T14:24:00Z wk=46 dow=6 doy=326")
  276. (-1 . "-4713-11-23T14:24:00Z wk=47 dow=0 doy=327")
  277. (0 . "-4713-11-24T14:24:00Z wk=47 dow=1 doy=328")
  278. (1 . "-4713-11-25T14:24:00Z wk=47 dow=2 doy=329")
  279. (2 . "-4713-11-26T14:24:00Z wk=47 dow=3 doy=330")
  280. (3 . "-4713-11-27T14:24:00Z wk=47 dow=4 doy=331")
  281. (4 . "-4713-11-28T14:24:00Z wk=47 dow=5 doy=332")
  282. (5 . "-4713-11-29T14:24:00Z wk=47 dow=6 doy=333")
  283. (6 . "-4713-11-30T14:24:00Z wk=48 dow=0 doy=334")
  284. (7 . "-4713-12-01T14:24:00Z wk=48 dow=1 doy=335")
  285. (8 . "-4713-12-02T14:24:00Z wk=48 dow=2 doy=336")
  286. (9 . "-4713-12-03T14:24:00Z wk=48 dow=3 doy=337"))
  287. (map (lambda (n)
  288. (cons n (date->string (julian-day->date (+ n 1/10) 0)
  289. "~4 wk=~U dow=~w doy=~j")))
  290. (cons* -2000000 -20000 (iota 20 -10))))
  291. (pass-if-equal "negative year numbers"
  292. '((1721055 . "-0001-12-27T14:24:00Z wk=52 dow=1 doy=361")
  293. (1721056 . "-0001-12-28T14:24:00Z wk=52 dow=2 doy=362")
  294. (1721057 . "-0001-12-29T14:24:00Z wk=52 dow=3 doy=363")
  295. (1721058 . "-0001-12-30T14:24:00Z wk=52 dow=4 doy=364")
  296. (1721059 . "-0001-12-31T14:24:00Z wk=52 dow=5 doy=365")
  297. (1721060 . "0000-01-01T14:24:00Z wk=00 dow=6 doy=001")
  298. (1721061 . "0000-01-02T14:24:00Z wk=01 dow=0 doy=002")
  299. (1721062 . "0000-01-03T14:24:00Z wk=01 dow=1 doy=003")
  300. (1721063 . "0000-01-04T14:24:00Z wk=01 dow=2 doy=004")
  301. (1721064 . "0000-01-05T14:24:00Z wk=01 dow=3 doy=005"))
  302. (map (lambda (n)
  303. (cons n (date->string (julian-day->date (+ n 1/10) 0)
  304. "~4 wk=~U dow=~w doy=~j")))
  305. (iota 10 1721055))))
  306. (with-test-prefix "time-utc->date"
  307. (pass-if-equal "2012-07-01T00:59:59+0100" "2012-07-01T00:59:59+0100"
  308. (date->string (time-utc->date (make-time time-utc 0 1341100799)
  309. 3600)
  310. "~4"))
  311. (pass-if-equal "2012-07-01T01:00:00+0100" "2012-07-01T01:00:00+0100"
  312. (date->string (time-utc->date (make-time time-utc 0 1341100800)
  313. 3600)
  314. "~4"))
  315. (pass-if-equal "2012-07-01T01:00:01+0100" "2012-07-01T01:00:01+0100"
  316. (date->string (time-utc->date (make-time time-utc 0 1341100801)
  317. 3600)
  318. "~4")))
  319. (with-test-prefix "time-tai->date"
  320. (pass-if-equal "2012-07-01T00:59:59+0100" "2012-07-01T00:59:59+0100"
  321. (date->string (time-tai->date (make-time time-tai 0 1341100833)
  322. 3600)
  323. "~4"))
  324. (pass-if-equal "2012-07-01T00:59:60+0100" "2012-07-01T00:59:60+0100"
  325. (date->string (time-tai->date (make-time time-tai 0 1341100834)
  326. 3600)
  327. "~4"))
  328. (pass-if-equal "2012-07-01T01:00:00+0100" "2012-07-01T01:00:00+0100"
  329. (date->string (time-tai->date (make-time time-tai 0 1341100835)
  330. 3600)
  331. "~4"))
  332. (pass-if-equal "2012-07-01T01:00:01+0100" "2012-07-01T01:00:01+0100"
  333. (date->string (time-tai->date (make-time time-tai 0 1341100836)
  334. 3600)
  335. "~4")))
  336. (with-test-prefix "time-monotonic->date"
  337. (pass-if-equal "2012-07-01T00:59:59+0100" "2012-07-01T00:59:59+0100"
  338. (date->string (time-monotonic->date (make-time time-monotonic
  339. 0 1341100833)
  340. 3600)
  341. "~4"))
  342. (pass-if-equal "2012-07-01T00:59:60+0100" "2012-07-01T00:59:60+0100"
  343. (date->string (time-monotonic->date (make-time time-monotonic
  344. 0 1341100834)
  345. 3600)
  346. "~4"))
  347. (pass-if-equal "2012-07-01T01:00:00+0100" "2012-07-01T01:00:00+0100"
  348. (date->string (time-monotonic->date (make-time time-monotonic
  349. 0 1341100835)
  350. 3600)
  351. "~4"))
  352. (pass-if-equal "2012-07-01T01:00:01+0100" "2012-07-01T01:00:01+0100"
  353. (date->string (time-monotonic->date (make-time time-monotonic
  354. 0 1341100836)
  355. 3600)
  356. "~4")))
  357. (with-test-prefix "time-tai->julian-day"
  358. (pass-if-equal "2012-07-01T00:59:59+0100" 212207860799/86400
  359. (time-tai->julian-day (make-time time-tai 0 1341100833)))
  360. (pass-if-equal "2012-07-01T00:59:60+0100" 4912219/2
  361. (time-tai->julian-day (make-time time-tai 0 1341100834)))
  362. (pass-if-equal "2012-07-01T01:00:00+0100" 4912219/2
  363. (time-tai->julian-day (make-time time-tai 0 1341100835)))
  364. (pass-if-equal "2012-07-01T01:00:01+0100" 212207860801/86400
  365. (time-tai->julian-day (make-time time-tai 0 1341100836))))
  366. (with-test-prefix "time-monotonic->julian-day"
  367. (pass-if-equal "2012-07-01T00:59:59+0100" 212207860799/86400
  368. (time-monotonic->julian-day (make-time time-monotonic 0 1341100833)))
  369. (pass-if-equal "2012-07-01T00:59:60+0100" 4912219/2
  370. (time-monotonic->julian-day (make-time time-monotonic 0 1341100834)))
  371. (pass-if-equal "2012-07-01T01:00:00+0100" 4912219/2
  372. (time-monotonic->julian-day (make-time time-monotonic 0 1341100835)))
  373. (pass-if-equal "2012-07-01T01:00:01+0100" 212207860801/86400
  374. (time-monotonic->julian-day (make-time time-monotonic 0 1341100836))))
  375. (with-test-prefix "date-week-number"
  376. (pass-if (= 0 (date-week-number (make-date 0 0 0 0 1 1 1984 0) 0)))
  377. (pass-if (= 0 (date-week-number (make-date 0 0 0 0 7 1 1984 0) 0)))
  378. (pass-if (= 1 (date-week-number (make-date 0 0 0 0 8 1 1984 0) 0)))))
  379. ;; Local Variables:
  380. ;; eval: (put 'with-tz 'scheme-indent-function 1)
  381. ;; End: