You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

443 lines
12 KiB

  1. // Copyright 2016 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package civil
  15. import (
  16. "encoding/json"
  17. "testing"
  18. "time"
  19. "github.com/google/go-cmp/cmp"
  20. )
  21. func TestDates(t *testing.T) {
  22. for _, test := range []struct {
  23. date Date
  24. loc *time.Location
  25. wantStr string
  26. wantTime time.Time
  27. }{
  28. {
  29. date: Date{2014, 7, 29},
  30. loc: time.Local,
  31. wantStr: "2014-07-29",
  32. wantTime: time.Date(2014, time.July, 29, 0, 0, 0, 0, time.Local),
  33. },
  34. {
  35. date: DateOf(time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local)),
  36. loc: time.UTC,
  37. wantStr: "2014-08-20",
  38. wantTime: time.Date(2014, 8, 20, 0, 0, 0, 0, time.UTC),
  39. },
  40. {
  41. date: DateOf(time.Date(999, time.January, 26, 0, 0, 0, 0, time.Local)),
  42. loc: time.UTC,
  43. wantStr: "0999-01-26",
  44. wantTime: time.Date(999, 1, 26, 0, 0, 0, 0, time.UTC),
  45. },
  46. } {
  47. if got := test.date.String(); got != test.wantStr {
  48. t.Errorf("%#v.String() = %q, want %q", test.date, got, test.wantStr)
  49. }
  50. if got := test.date.In(test.loc); !got.Equal(test.wantTime) {
  51. t.Errorf("%#v.In(%v) = %v, want %v", test.date, test.loc, got, test.wantTime)
  52. }
  53. }
  54. }
  55. func TestDateIsValid(t *testing.T) {
  56. for _, test := range []struct {
  57. date Date
  58. want bool
  59. }{
  60. {Date{2014, 7, 29}, true},
  61. {Date{2000, 2, 29}, true},
  62. {Date{10000, 12, 31}, true},
  63. {Date{1, 1, 1}, true},
  64. {Date{0, 1, 1}, true}, // year zero is OK
  65. {Date{-1, 1, 1}, true}, // negative year is OK
  66. {Date{1, 0, 1}, false},
  67. {Date{1, 1, 0}, false},
  68. {Date{2016, 1, 32}, false},
  69. {Date{2016, 13, 1}, false},
  70. {Date{1, -1, 1}, false},
  71. {Date{1, 1, -1}, false},
  72. } {
  73. got := test.date.IsValid()
  74. if got != test.want {
  75. t.Errorf("%#v: got %t, want %t", test.date, got, test.want)
  76. }
  77. }
  78. }
  79. func TestParseDate(t *testing.T) {
  80. for _, test := range []struct {
  81. str string
  82. want Date // if empty, expect an error
  83. }{
  84. {"2016-01-02", Date{2016, 1, 2}},
  85. {"2016-12-31", Date{2016, 12, 31}},
  86. {"0003-02-04", Date{3, 2, 4}},
  87. {"999-01-26", Date{}},
  88. {"", Date{}},
  89. {"2016-01-02x", Date{}},
  90. } {
  91. got, err := ParseDate(test.str)
  92. if got != test.want {
  93. t.Errorf("ParseDate(%q) = %+v, want %+v", test.str, got, test.want)
  94. }
  95. if err != nil && test.want != (Date{}) {
  96. t.Errorf("Unexpected error %v from ParseDate(%q)", err, test.str)
  97. }
  98. }
  99. }
  100. func TestDateArithmetic(t *testing.T) {
  101. for _, test := range []struct {
  102. desc string
  103. start Date
  104. end Date
  105. days int
  106. }{
  107. {
  108. desc: "zero days noop",
  109. start: Date{2014, 5, 9},
  110. end: Date{2014, 5, 9},
  111. days: 0,
  112. },
  113. {
  114. desc: "crossing a year boundary",
  115. start: Date{2014, 12, 31},
  116. end: Date{2015, 1, 1},
  117. days: 1,
  118. },
  119. {
  120. desc: "negative number of days",
  121. start: Date{2015, 1, 1},
  122. end: Date{2014, 12, 31},
  123. days: -1,
  124. },
  125. {
  126. desc: "full leap year",
  127. start: Date{2004, 1, 1},
  128. end: Date{2005, 1, 1},
  129. days: 366,
  130. },
  131. {
  132. desc: "full non-leap year",
  133. start: Date{2001, 1, 1},
  134. end: Date{2002, 1, 1},
  135. days: 365,
  136. },
  137. {
  138. desc: "crossing a leap second",
  139. start: Date{1972, 6, 30},
  140. end: Date{1972, 7, 1},
  141. days: 1,
  142. },
  143. {
  144. desc: "dates before the unix epoch",
  145. start: Date{101, 1, 1},
  146. end: Date{102, 1, 1},
  147. days: 365,
  148. },
  149. } {
  150. if got := test.start.AddDays(test.days); got != test.end {
  151. t.Errorf("[%s] %#v.AddDays(%v) = %#v, want %#v", test.desc, test.start, test.days, got, test.end)
  152. }
  153. if got := test.end.DaysSince(test.start); got != test.days {
  154. t.Errorf("[%s] %#v.Sub(%#v) = %v, want %v", test.desc, test.end, test.start, got, test.days)
  155. }
  156. }
  157. }
  158. func TestDateBefore(t *testing.T) {
  159. for _, test := range []struct {
  160. d1, d2 Date
  161. want bool
  162. }{
  163. {Date{2016, 12, 31}, Date{2017, 1, 1}, true},
  164. {Date{2016, 1, 1}, Date{2016, 1, 1}, false},
  165. {Date{2016, 12, 30}, Date{2016, 12, 31}, true},
  166. } {
  167. if got := test.d1.Before(test.d2); got != test.want {
  168. t.Errorf("%v.Before(%v): got %t, want %t", test.d1, test.d2, got, test.want)
  169. }
  170. }
  171. }
  172. func TestDateAfter(t *testing.T) {
  173. for _, test := range []struct {
  174. d1, d2 Date
  175. want bool
  176. }{
  177. {Date{2016, 12, 31}, Date{2017, 1, 1}, false},
  178. {Date{2016, 1, 1}, Date{2016, 1, 1}, false},
  179. {Date{2016, 12, 30}, Date{2016, 12, 31}, false},
  180. } {
  181. if got := test.d1.After(test.d2); got != test.want {
  182. t.Errorf("%v.After(%v): got %t, want %t", test.d1, test.d2, got, test.want)
  183. }
  184. }
  185. }
  186. func TestTimeToString(t *testing.T) {
  187. for _, test := range []struct {
  188. str string
  189. time Time
  190. roundTrip bool // ParseTime(str).String() == str?
  191. }{
  192. {"13:26:33", Time{13, 26, 33, 0}, true},
  193. {"01:02:03.000023456", Time{1, 2, 3, 23456}, true},
  194. {"00:00:00.000000001", Time{0, 0, 0, 1}, true},
  195. {"13:26:03.1", Time{13, 26, 3, 100000000}, false},
  196. {"13:26:33.0000003", Time{13, 26, 33, 300}, false},
  197. } {
  198. gotTime, err := ParseTime(test.str)
  199. if err != nil {
  200. t.Errorf("ParseTime(%q): got error: %v", test.str, err)
  201. continue
  202. }
  203. if gotTime != test.time {
  204. t.Errorf("ParseTime(%q) = %+v, want %+v", test.str, gotTime, test.time)
  205. }
  206. if test.roundTrip {
  207. gotStr := test.time.String()
  208. if gotStr != test.str {
  209. t.Errorf("%#v.String() = %q, want %q", test.time, gotStr, test.str)
  210. }
  211. }
  212. }
  213. }
  214. func TestTimeOf(t *testing.T) {
  215. for _, test := range []struct {
  216. time time.Time
  217. want Time
  218. }{
  219. {time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local), Time{15, 8, 43, 1}},
  220. {time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), Time{0, 0, 0, 0}},
  221. } {
  222. if got := TimeOf(test.time); got != test.want {
  223. t.Errorf("TimeOf(%v) = %+v, want %+v", test.time, got, test.want)
  224. }
  225. }
  226. }
  227. func TestTimeIsValid(t *testing.T) {
  228. for _, test := range []struct {
  229. time Time
  230. want bool
  231. }{
  232. {Time{0, 0, 0, 0}, true},
  233. {Time{23, 0, 0, 0}, true},
  234. {Time{23, 59, 59, 999999999}, true},
  235. {Time{24, 59, 59, 999999999}, false},
  236. {Time{23, 60, 59, 999999999}, false},
  237. {Time{23, 59, 60, 999999999}, false},
  238. {Time{23, 59, 59, 1000000000}, false},
  239. {Time{-1, 0, 0, 0}, false},
  240. {Time{0, -1, 0, 0}, false},
  241. {Time{0, 0, -1, 0}, false},
  242. {Time{0, 0, 0, -1}, false},
  243. } {
  244. got := test.time.IsValid()
  245. if got != test.want {
  246. t.Errorf("%#v: got %t, want %t", test.time, got, test.want)
  247. }
  248. }
  249. }
  250. func TestDateTimeToString(t *testing.T) {
  251. for _, test := range []struct {
  252. str string
  253. dateTime DateTime
  254. roundTrip bool // ParseDateTime(str).String() == str?
  255. }{
  256. {"2016-03-22T13:26:33", DateTime{Date{2016, 03, 22}, Time{13, 26, 33, 0}}, true},
  257. {"2016-03-22T13:26:33.000000600", DateTime{Date{2016, 03, 22}, Time{13, 26, 33, 600}}, true},
  258. {"2016-03-22t13:26:33", DateTime{Date{2016, 03, 22}, Time{13, 26, 33, 0}}, false},
  259. } {
  260. gotDateTime, err := ParseDateTime(test.str)
  261. if err != nil {
  262. t.Errorf("ParseDateTime(%q): got error: %v", test.str, err)
  263. continue
  264. }
  265. if gotDateTime != test.dateTime {
  266. t.Errorf("ParseDateTime(%q) = %+v, want %+v", test.str, gotDateTime, test.dateTime)
  267. }
  268. if test.roundTrip {
  269. gotStr := test.dateTime.String()
  270. if gotStr != test.str {
  271. t.Errorf("%#v.String() = %q, want %q", test.dateTime, gotStr, test.str)
  272. }
  273. }
  274. }
  275. }
  276. func TestParseDateTimeErrors(t *testing.T) {
  277. for _, str := range []string{
  278. "",
  279. "2016-03-22", // just a date
  280. "13:26:33", // just a time
  281. "2016-03-22 13:26:33", // wrong separating character
  282. "2016-03-22T13:26:33x", // extra at end
  283. } {
  284. if _, err := ParseDateTime(str); err == nil {
  285. t.Errorf("ParseDateTime(%q) succeeded, want error", str)
  286. }
  287. }
  288. }
  289. func TestDateTimeOf(t *testing.T) {
  290. for _, test := range []struct {
  291. time time.Time
  292. want DateTime
  293. }{
  294. {time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local),
  295. DateTime{Date{2014, 8, 20}, Time{15, 8, 43, 1}}},
  296. {time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC),
  297. DateTime{Date{1, 1, 1}, Time{0, 0, 0, 0}}},
  298. } {
  299. if got := DateTimeOf(test.time); got != test.want {
  300. t.Errorf("DateTimeOf(%v) = %+v, want %+v", test.time, got, test.want)
  301. }
  302. }
  303. }
  304. func TestDateTimeIsValid(t *testing.T) {
  305. // No need to be exhaustive here; it's just Date.IsValid && Time.IsValid.
  306. for _, test := range []struct {
  307. dt DateTime
  308. want bool
  309. }{
  310. {DateTime{Date{2016, 3, 20}, Time{0, 0, 0, 0}}, true},
  311. {DateTime{Date{2016, -3, 20}, Time{0, 0, 0, 0}}, false},
  312. {DateTime{Date{2016, 3, 20}, Time{24, 0, 0, 0}}, false},
  313. } {
  314. got := test.dt.IsValid()
  315. if got != test.want {
  316. t.Errorf("%#v: got %t, want %t", test.dt, got, test.want)
  317. }
  318. }
  319. }
  320. func TestDateTimeIn(t *testing.T) {
  321. dt := DateTime{Date{2016, 1, 2}, Time{3, 4, 5, 6}}
  322. got := dt.In(time.UTC)
  323. want := time.Date(2016, 1, 2, 3, 4, 5, 6, time.UTC)
  324. if !got.Equal(want) {
  325. t.Errorf("got %v, want %v", got, want)
  326. }
  327. }
  328. func TestDateTimeBefore(t *testing.T) {
  329. d1 := Date{2016, 12, 31}
  330. d2 := Date{2017, 1, 1}
  331. t1 := Time{5, 6, 7, 8}
  332. t2 := Time{5, 6, 7, 9}
  333. for _, test := range []struct {
  334. dt1, dt2 DateTime
  335. want bool
  336. }{
  337. {DateTime{d1, t1}, DateTime{d2, t1}, true},
  338. {DateTime{d1, t1}, DateTime{d1, t2}, true},
  339. {DateTime{d2, t1}, DateTime{d1, t1}, false},
  340. {DateTime{d2, t1}, DateTime{d2, t1}, false},
  341. } {
  342. if got := test.dt1.Before(test.dt2); got != test.want {
  343. t.Errorf("%v.Before(%v): got %t, want %t", test.dt1, test.dt2, got, test.want)
  344. }
  345. }
  346. }
  347. func TestDateTimeAfter(t *testing.T) {
  348. d1 := Date{2016, 12, 31}
  349. d2 := Date{2017, 1, 1}
  350. t1 := Time{5, 6, 7, 8}
  351. t2 := Time{5, 6, 7, 9}
  352. for _, test := range []struct {
  353. dt1, dt2 DateTime
  354. want bool
  355. }{
  356. {DateTime{d1, t1}, DateTime{d2, t1}, false},
  357. {DateTime{d1, t1}, DateTime{d1, t2}, false},
  358. {DateTime{d2, t1}, DateTime{d1, t1}, true},
  359. {DateTime{d2, t1}, DateTime{d2, t1}, false},
  360. } {
  361. if got := test.dt1.After(test.dt2); got != test.want {
  362. t.Errorf("%v.After(%v): got %t, want %t", test.dt1, test.dt2, got, test.want)
  363. }
  364. }
  365. }
  366. func TestMarshalJSON(t *testing.T) {
  367. for _, test := range []struct {
  368. value interface{}
  369. want string
  370. }{
  371. {Date{1987, 4, 15}, `"1987-04-15"`},
  372. {Time{18, 54, 2, 0}, `"18:54:02"`},
  373. {DateTime{Date{1987, 4, 15}, Time{18, 54, 2, 0}}, `"1987-04-15T18:54:02"`},
  374. } {
  375. bgot, err := json.Marshal(test.value)
  376. if err != nil {
  377. t.Fatal(err)
  378. }
  379. if got := string(bgot); got != test.want {
  380. t.Errorf("%#v: got %s, want %s", test.value, got, test.want)
  381. }
  382. }
  383. }
  384. func TestUnmarshalJSON(t *testing.T) {
  385. var d Date
  386. var tm Time
  387. var dt DateTime
  388. for _, test := range []struct {
  389. data string
  390. ptr interface{}
  391. want interface{}
  392. }{
  393. {`"1987-04-15"`, &d, &Date{1987, 4, 15}},
  394. {`"1987-04-\u0031\u0035"`, &d, &Date{1987, 4, 15}},
  395. {`"18:54:02"`, &tm, &Time{18, 54, 2, 0}},
  396. {`"1987-04-15T18:54:02"`, &dt, &DateTime{Date{1987, 4, 15}, Time{18, 54, 2, 0}}},
  397. } {
  398. if err := json.Unmarshal([]byte(test.data), test.ptr); err != nil {
  399. t.Fatalf("%s: %v", test.data, err)
  400. }
  401. if !cmp.Equal(test.ptr, test.want) {
  402. t.Errorf("%s: got %#v, want %#v", test.data, test.ptr, test.want)
  403. }
  404. }
  405. for _, bad := range []string{"", `""`, `"bad"`, `"1987-04-15x"`,
  406. `19870415`, // a JSON number
  407. `11987-04-15x`, // not a JSON string
  408. } {
  409. if json.Unmarshal([]byte(bad), &d) == nil {
  410. t.Errorf("%q, Date: got nil, want error", bad)
  411. }
  412. if json.Unmarshal([]byte(bad), &tm) == nil {
  413. t.Errorf("%q, Time: got nil, want error", bad)
  414. }
  415. if json.Unmarshal([]byte(bad), &dt) == nil {
  416. t.Errorf("%q, DateTime: got nil, want error", bad)
  417. }
  418. }
  419. }