图片解析应用
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.

1710 lines
60 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2021, Brandon Nielsen
  3. # All rights reserved.
  4. #
  5. # This software may be modified and distributed under the terms
  6. # of the BSD license. See the LICENSE file for details.
  7. import datetime
  8. import unittest
  9. from aniso8601 import compat
  10. from aniso8601.builders import (
  11. DatetimeTuple,
  12. DateTuple,
  13. DurationTuple,
  14. IntervalTuple,
  15. Limit,
  16. TimeTuple,
  17. TimezoneTuple,
  18. )
  19. from aniso8601.builders.python import (
  20. FractionalComponent,
  21. PythonTimeBuilder,
  22. _cast_to_fractional_component,
  23. fractional_range_check,
  24. year_range_check,
  25. )
  26. from aniso8601.exceptions import (
  27. DayOutOfBoundsError,
  28. HoursOutOfBoundsError,
  29. ISOFormatError,
  30. LeapSecondError,
  31. MidnightBoundsError,
  32. MinutesOutOfBoundsError,
  33. MonthOutOfBoundsError,
  34. SecondsOutOfBoundsError,
  35. WeekOutOfBoundsError,
  36. YearOutOfBoundsError,
  37. )
  38. from aniso8601.utcoffset import UTCOffset
  39. class TestPythonTimeBuilder_UtiltyFunctions(unittest.TestCase):
  40. def test_year_range_check(self):
  41. yearlimit = Limit(
  42. "Invalid year string.",
  43. 0000,
  44. 9999,
  45. YearOutOfBoundsError,
  46. "Year must be between 1..9999.",
  47. None,
  48. )
  49. self.assertEqual(year_range_check("1", yearlimit), 1000)
  50. def test_fractional_range_check(self):
  51. limit = Limit(
  52. "Invalid string.", -1, 1, ValueError, "Value must be between -1..1.", None
  53. )
  54. self.assertEqual(fractional_range_check(10, "1", limit), 1)
  55. self.assertEqual(fractional_range_check(10, "-1", limit), -1)
  56. self.assertEqual(
  57. fractional_range_check(10, "0.1", limit), FractionalComponent(0, 1)
  58. )
  59. self.assertEqual(
  60. fractional_range_check(10, "-0.1", limit), FractionalComponent(-0, 1)
  61. )
  62. with self.assertRaises(ValueError):
  63. fractional_range_check(10, "1.1", limit)
  64. with self.assertRaises(ValueError):
  65. fractional_range_check(10, "-1.1", limit)
  66. def test_cast_to_fractional_component(self):
  67. self.assertEqual(
  68. _cast_to_fractional_component(10, "1.1"), FractionalComponent(1, 1)
  69. )
  70. self.assertEqual(
  71. _cast_to_fractional_component(10, "-1.1"), FractionalComponent(-1, 1)
  72. )
  73. self.assertEqual(
  74. _cast_to_fractional_component(100, "1.1"), FractionalComponent(1, 10)
  75. )
  76. self.assertEqual(
  77. _cast_to_fractional_component(100, "-1.1"), FractionalComponent(-1, 10)
  78. )
  79. class TestPythonTimeBuilder(unittest.TestCase):
  80. def test_build_date(self):
  81. testtuples = (
  82. (
  83. {
  84. "YYYY": "2013",
  85. "MM": None,
  86. "DD": None,
  87. "Www": None,
  88. "D": None,
  89. "DDD": None,
  90. },
  91. datetime.date(2013, 1, 1),
  92. ),
  93. (
  94. {
  95. "YYYY": "0001",
  96. "MM": None,
  97. "DD": None,
  98. "Www": None,
  99. "D": None,
  100. "DDD": None,
  101. },
  102. datetime.date(1, 1, 1),
  103. ),
  104. (
  105. {
  106. "YYYY": "1900",
  107. "MM": None,
  108. "DD": None,
  109. "Www": None,
  110. "D": None,
  111. "DDD": None,
  112. },
  113. datetime.date(1900, 1, 1),
  114. ),
  115. (
  116. {
  117. "YYYY": "1981",
  118. "MM": "04",
  119. "DD": "05",
  120. "Www": None,
  121. "D": None,
  122. "DDD": None,
  123. },
  124. datetime.date(1981, 4, 5),
  125. ),
  126. (
  127. {
  128. "YYYY": "1981",
  129. "MM": "04",
  130. "DD": None,
  131. "Www": None,
  132. "D": None,
  133. "DDD": None,
  134. },
  135. datetime.date(1981, 4, 1),
  136. ),
  137. (
  138. {
  139. "YYYY": "1981",
  140. "MM": None,
  141. "DD": None,
  142. "Www": None,
  143. "D": None,
  144. "DDD": "095",
  145. },
  146. datetime.date(1981, 4, 5),
  147. ),
  148. (
  149. {
  150. "YYYY": "1981",
  151. "MM": None,
  152. "DD": None,
  153. "Www": None,
  154. "D": None,
  155. "DDD": "365",
  156. },
  157. datetime.date(1981, 12, 31),
  158. ),
  159. (
  160. {
  161. "YYYY": "1980",
  162. "MM": None,
  163. "DD": None,
  164. "Www": None,
  165. "D": None,
  166. "DDD": "366",
  167. },
  168. datetime.date(1980, 12, 31),
  169. ),
  170. # Make sure we shift in zeros
  171. (
  172. {
  173. "YYYY": "1",
  174. "MM": None,
  175. "DD": None,
  176. "Www": None,
  177. "D": None,
  178. "DDD": None,
  179. },
  180. datetime.date(1000, 1, 1),
  181. ),
  182. (
  183. {
  184. "YYYY": "12",
  185. "MM": None,
  186. "DD": None,
  187. "Www": None,
  188. "D": None,
  189. "DDD": None,
  190. },
  191. datetime.date(1200, 1, 1),
  192. ),
  193. (
  194. {
  195. "YYYY": "123",
  196. "MM": None,
  197. "DD": None,
  198. "Www": None,
  199. "D": None,
  200. "DDD": None,
  201. },
  202. datetime.date(1230, 1, 1),
  203. ),
  204. )
  205. for testtuple in testtuples:
  206. result = PythonTimeBuilder.build_date(**testtuple[0])
  207. self.assertEqual(result, testtuple[1])
  208. # Test weekday
  209. testtuples = (
  210. (
  211. {
  212. "YYYY": "2004",
  213. "MM": None,
  214. "DD": None,
  215. "Www": "53",
  216. "D": None,
  217. "DDD": None,
  218. },
  219. datetime.date(2004, 12, 27),
  220. 0,
  221. ),
  222. (
  223. {
  224. "YYYY": "2009",
  225. "MM": None,
  226. "DD": None,
  227. "Www": "01",
  228. "D": None,
  229. "DDD": None,
  230. },
  231. datetime.date(2008, 12, 29),
  232. 0,
  233. ),
  234. (
  235. {
  236. "YYYY": "2010",
  237. "MM": None,
  238. "DD": None,
  239. "Www": "01",
  240. "D": None,
  241. "DDD": None,
  242. },
  243. datetime.date(2010, 1, 4),
  244. 0,
  245. ),
  246. (
  247. {
  248. "YYYY": "2009",
  249. "MM": None,
  250. "DD": None,
  251. "Www": "53",
  252. "D": None,
  253. "DDD": None,
  254. },
  255. datetime.date(2009, 12, 28),
  256. 0,
  257. ),
  258. (
  259. {
  260. "YYYY": "2009",
  261. "MM": None,
  262. "DD": None,
  263. "Www": "01",
  264. "D": "1",
  265. "DDD": None,
  266. },
  267. datetime.date(2008, 12, 29),
  268. 0,
  269. ),
  270. (
  271. {
  272. "YYYY": "2009",
  273. "MM": None,
  274. "DD": None,
  275. "Www": "53",
  276. "D": "7",
  277. "DDD": None,
  278. },
  279. datetime.date(2010, 1, 3),
  280. 6,
  281. ),
  282. (
  283. {
  284. "YYYY": "2010",
  285. "MM": None,
  286. "DD": None,
  287. "Www": "01",
  288. "D": "1",
  289. "DDD": None,
  290. },
  291. datetime.date(2010, 1, 4),
  292. 0,
  293. ),
  294. (
  295. {
  296. "YYYY": "2004",
  297. "MM": None,
  298. "DD": None,
  299. "Www": "53",
  300. "D": "6",
  301. "DDD": None,
  302. },
  303. datetime.date(2005, 1, 1),
  304. 5,
  305. ),
  306. )
  307. for testtuple in testtuples:
  308. result = PythonTimeBuilder.build_date(**testtuple[0])
  309. self.assertEqual(result, testtuple[1])
  310. self.assertEqual(result.weekday(), testtuple[2])
  311. def test_build_time(self):
  312. testtuples = (
  313. ({}, datetime.time()),
  314. ({"hh": "12.5"}, datetime.time(hour=12, minute=30)),
  315. (
  316. {"hh": "23.99999999997"},
  317. datetime.time(hour=23, minute=59, second=59, microsecond=999999),
  318. ),
  319. ({"hh": "1", "mm": "23"}, datetime.time(hour=1, minute=23)),
  320. (
  321. {"hh": "1", "mm": "23.4567"},
  322. datetime.time(hour=1, minute=23, second=27, microsecond=402000),
  323. ),
  324. (
  325. {"hh": "14", "mm": "43.999999997"},
  326. datetime.time(hour=14, minute=43, second=59, microsecond=999999),
  327. ),
  328. (
  329. {"hh": "1", "mm": "23", "ss": "45"},
  330. datetime.time(hour=1, minute=23, second=45),
  331. ),
  332. (
  333. {"hh": "23", "mm": "21", "ss": "28.512400"},
  334. datetime.time(hour=23, minute=21, second=28, microsecond=512400),
  335. ),
  336. (
  337. {"hh": "01", "mm": "03", "ss": "11.858714"},
  338. datetime.time(hour=1, minute=3, second=11, microsecond=858714),
  339. ),
  340. (
  341. {"hh": "14", "mm": "43", "ss": "59.9999997"},
  342. datetime.time(hour=14, minute=43, second=59, microsecond=999999),
  343. ),
  344. ({"hh": "24"}, datetime.time(hour=0)),
  345. ({"hh": "24", "mm": "00"}, datetime.time(hour=0)),
  346. ({"hh": "24", "mm": "00", "ss": "00"}, datetime.time(hour=0)),
  347. (
  348. {"tz": TimezoneTuple(False, None, "00", "00", "UTC")},
  349. datetime.time(tzinfo=UTCOffset(name="UTC", minutes=0)),
  350. ),
  351. (
  352. {
  353. "hh": "23",
  354. "mm": "21",
  355. "ss": "28.512400",
  356. "tz": TimezoneTuple(False, None, "00", "00", "+00:00"),
  357. },
  358. datetime.time(
  359. hour=23,
  360. minute=21,
  361. second=28,
  362. microsecond=512400,
  363. tzinfo=UTCOffset(name="+00:00", minutes=0),
  364. ),
  365. ),
  366. (
  367. {
  368. "hh": "1",
  369. "mm": "23",
  370. "tz": TimezoneTuple(False, None, "01", "00", "+1"),
  371. },
  372. datetime.time(
  373. hour=1, minute=23, tzinfo=UTCOffset(name="+1", minutes=60)
  374. ),
  375. ),
  376. (
  377. {
  378. "hh": "1",
  379. "mm": "23.4567",
  380. "tz": TimezoneTuple(True, None, "01", "00", "-1"),
  381. },
  382. datetime.time(
  383. hour=1,
  384. minute=23,
  385. second=27,
  386. microsecond=402000,
  387. tzinfo=UTCOffset(name="-1", minutes=-60),
  388. ),
  389. ),
  390. (
  391. {
  392. "hh": "23",
  393. "mm": "21",
  394. "ss": "28.512400",
  395. "tz": TimezoneTuple(False, None, "01", "30", "+1:30"),
  396. },
  397. datetime.time(
  398. hour=23,
  399. minute=21,
  400. second=28,
  401. microsecond=512400,
  402. tzinfo=UTCOffset(name="+1:30", minutes=90),
  403. ),
  404. ),
  405. (
  406. {
  407. "hh": "23",
  408. "mm": "21",
  409. "ss": "28.512400",
  410. "tz": TimezoneTuple(False, None, "11", "15", "+11:15"),
  411. },
  412. datetime.time(
  413. hour=23,
  414. minute=21,
  415. second=28,
  416. microsecond=512400,
  417. tzinfo=UTCOffset(name="+11:15", minutes=675),
  418. ),
  419. ),
  420. (
  421. {
  422. "hh": "23",
  423. "mm": "21",
  424. "ss": "28.512400",
  425. "tz": TimezoneTuple(False, None, "12", "34", "+12:34"),
  426. },
  427. datetime.time(
  428. hour=23,
  429. minute=21,
  430. second=28,
  431. microsecond=512400,
  432. tzinfo=UTCOffset(name="+12:34", minutes=754),
  433. ),
  434. ),
  435. (
  436. {
  437. "hh": "23",
  438. "mm": "21",
  439. "ss": "28.512400",
  440. "tz": TimezoneTuple(False, None, "00", "00", "UTC"),
  441. },
  442. datetime.time(
  443. hour=23,
  444. minute=21,
  445. second=28,
  446. microsecond=512400,
  447. tzinfo=UTCOffset(name="UTC", minutes=0),
  448. ),
  449. ),
  450. # Make sure we truncate, not round
  451. # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
  452. # https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when
  453. (
  454. {"hh": "14.9999999999999999"},
  455. datetime.time(hour=14, minute=59, second=59, microsecond=999999),
  456. ),
  457. ({"mm": "0.00000000999"}, datetime.time()),
  458. ({"mm": "0.0000000999"}, datetime.time(microsecond=5)),
  459. ({"ss": "0.0000001"}, datetime.time()),
  460. ({"ss": "2.0000048"}, datetime.time(second=2, microsecond=4)),
  461. )
  462. for testtuple in testtuples:
  463. result = PythonTimeBuilder.build_time(**testtuple[0])
  464. self.assertEqual(result, testtuple[1])
  465. def test_build_datetime(self):
  466. testtuples = (
  467. (
  468. (
  469. DateTuple("2019", "06", "05", None, None, None),
  470. TimeTuple("01", "03", "11.858714", None),
  471. ),
  472. datetime.datetime(
  473. 2019, 6, 5, hour=1, minute=3, second=11, microsecond=858714
  474. ),
  475. ),
  476. (
  477. (
  478. DateTuple("1234", "02", "03", None, None, None),
  479. TimeTuple("23", "21", "28.512400", None),
  480. ),
  481. datetime.datetime(
  482. 1234, 2, 3, hour=23, minute=21, second=28, microsecond=512400
  483. ),
  484. ),
  485. (
  486. (
  487. DateTuple("1981", "04", "05", None, None, None),
  488. TimeTuple(
  489. "23",
  490. "21",
  491. "28.512400",
  492. TimezoneTuple(False, None, "11", "15", "+11:15"),
  493. ),
  494. ),
  495. datetime.datetime(
  496. 1981,
  497. 4,
  498. 5,
  499. hour=23,
  500. minute=21,
  501. second=28,
  502. microsecond=512400,
  503. tzinfo=UTCOffset(name="+11:15", minutes=675),
  504. ),
  505. ),
  506. )
  507. for testtuple in testtuples:
  508. result = PythonTimeBuilder.build_datetime(*testtuple[0])
  509. self.assertEqual(result, testtuple[1])
  510. def test_build_duration(self):
  511. testtuples = (
  512. (
  513. {
  514. "PnY": "1",
  515. "PnM": "2",
  516. "PnD": "3",
  517. "TnH": "4",
  518. "TnM": "54",
  519. "TnS": "6",
  520. },
  521. datetime.timedelta(days=428, hours=4, minutes=54, seconds=6),
  522. ),
  523. (
  524. {
  525. "PnY": "1",
  526. "PnM": "2",
  527. "PnD": "3",
  528. "TnH": "4",
  529. "TnM": "54",
  530. "TnS": "6.5",
  531. },
  532. datetime.timedelta(days=428, hours=4, minutes=54, seconds=6.5),
  533. ),
  534. ({"PnY": "1", "PnM": "2", "PnD": "3"}, datetime.timedelta(days=428)),
  535. ({"PnY": "1", "PnM": "2", "PnD": "3.5"}, datetime.timedelta(days=428.5)),
  536. (
  537. {"TnH": "4", "TnM": "54", "TnS": "6.5"},
  538. datetime.timedelta(hours=4, minutes=54, seconds=6.5),
  539. ),
  540. (
  541. {"TnH": "1", "TnM": "3", "TnS": "11.858714"},
  542. datetime.timedelta(hours=1, minutes=3, seconds=11, microseconds=858714),
  543. ),
  544. (
  545. {"TnH": "4", "TnM": "54", "TnS": "28.512400"},
  546. datetime.timedelta(
  547. hours=4, minutes=54, seconds=28, microseconds=512400
  548. ),
  549. ),
  550. # Make sure we truncate, not round
  551. # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
  552. # https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when
  553. (
  554. {"PnY": "1999.9999999999999999"},
  555. datetime.timedelta(days=729999, seconds=86399, microseconds=999999),
  556. ),
  557. (
  558. {"PnM": "1.9999999999999999"},
  559. datetime.timedelta(
  560. days=59, hours=23, minutes=59, seconds=59, microseconds=999999
  561. ),
  562. ),
  563. (
  564. {"PnW": "1.9999999999999999"},
  565. datetime.timedelta(
  566. days=13, hours=23, minutes=59, seconds=59, microseconds=999999
  567. ),
  568. ),
  569. (
  570. {"PnD": "1.9999999999999999"},
  571. datetime.timedelta(
  572. days=1, hours=23, minutes=59, seconds=59, microseconds=999999
  573. ),
  574. ),
  575. (
  576. {"TnH": "14.9999999999999999"},
  577. datetime.timedelta(
  578. hours=14, minutes=59, seconds=59, microseconds=999999
  579. ),
  580. ),
  581. ({"TnM": "0.00000000999"}, datetime.timedelta(0)),
  582. ({"TnM": "0.0000000999"}, datetime.timedelta(microseconds=5)),
  583. ({"TnS": "0.0000001"}, datetime.timedelta(0)),
  584. ({"TnS": "2.0000048"}, datetime.timedelta(seconds=2, microseconds=4)),
  585. ({"PnY": "1"}, datetime.timedelta(days=365)),
  586. ({"PnY": "1.5"}, datetime.timedelta(days=547.5)),
  587. ({"PnM": "1"}, datetime.timedelta(days=30)),
  588. ({"PnM": "1.5"}, datetime.timedelta(days=45)),
  589. ({"PnW": "1"}, datetime.timedelta(days=7)),
  590. ({"PnW": "1.5"}, datetime.timedelta(days=10.5)),
  591. ({"PnD": "1"}, datetime.timedelta(days=1)),
  592. ({"PnD": "1.5"}, datetime.timedelta(days=1.5)),
  593. (
  594. {
  595. "PnY": "0003",
  596. "PnM": "06",
  597. "PnD": "04",
  598. "TnH": "12",
  599. "TnM": "30",
  600. "TnS": "05",
  601. },
  602. datetime.timedelta(days=1279, hours=12, minutes=30, seconds=5),
  603. ),
  604. (
  605. {
  606. "PnY": "0003",
  607. "PnM": "06",
  608. "PnD": "04",
  609. "TnH": "12",
  610. "TnM": "30",
  611. "TnS": "05.5",
  612. },
  613. datetime.timedelta(days=1279, hours=12, minutes=30, seconds=5.5),
  614. ),
  615. # Test timedelta limit
  616. (
  617. {"PnD": "999999999", "TnH": "23", "TnM": "59", "TnS": "59.999999"},
  618. datetime.timedelta.max,
  619. ),
  620. # Make sure we truncate, not round
  621. # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
  622. (
  623. {
  624. "PnY": "0001",
  625. "PnM": "02",
  626. "PnD": "03",
  627. "TnH": "14",
  628. "TnM": "43",
  629. "TnS": "59.9999997",
  630. },
  631. datetime.timedelta(
  632. days=428, hours=14, minutes=43, seconds=59, microseconds=999999
  633. ),
  634. ),
  635. # Verify overflows
  636. ({"TnH": "36"}, datetime.timedelta(days=1, hours=12)),
  637. )
  638. for testtuple in testtuples:
  639. result = PythonTimeBuilder.build_duration(**testtuple[0])
  640. self.assertEqual(result, testtuple[1])
  641. def test_build_interval(self):
  642. testtuples = (
  643. (
  644. {
  645. "end": DatetimeTuple(
  646. DateTuple("1981", "04", "05", None, None, None),
  647. TimeTuple("01", "01", "00", None),
  648. ),
  649. "duration": DurationTuple(None, "1", None, None, None, None, None),
  650. },
  651. datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1),
  652. datetime.datetime(year=1981, month=3, day=6, hour=1, minute=1),
  653. ),
  654. (
  655. {
  656. "end": DateTuple("1981", "04", "05", None, None, None),
  657. "duration": DurationTuple(None, "1", None, None, None, None, None),
  658. },
  659. datetime.date(year=1981, month=4, day=5),
  660. datetime.date(year=1981, month=3, day=6),
  661. ),
  662. (
  663. {
  664. "end": DateTuple("2018", "03", "06", None, None, None),
  665. "duration": DurationTuple(
  666. "1.5", None, None, None, None, None, None
  667. ),
  668. },
  669. datetime.date(year=2018, month=3, day=6),
  670. datetime.datetime(year=2016, month=9, day=4, hour=12),
  671. ),
  672. (
  673. {
  674. "end": DateTuple("2014", "11", "12", None, None, None),
  675. "duration": DurationTuple(None, None, None, None, "1", None, None),
  676. },
  677. datetime.date(year=2014, month=11, day=12),
  678. datetime.datetime(year=2014, month=11, day=11, hour=23),
  679. ),
  680. (
  681. {
  682. "end": DateTuple("2014", "11", "12", None, None, None),
  683. "duration": DurationTuple(None, None, None, None, "4", "54", "6.5"),
  684. },
  685. datetime.date(year=2014, month=11, day=12),
  686. datetime.datetime(
  687. year=2014,
  688. month=11,
  689. day=11,
  690. hour=19,
  691. minute=5,
  692. second=53,
  693. microsecond=500000,
  694. ),
  695. ),
  696. (
  697. {
  698. "end": DatetimeTuple(
  699. DateTuple("2050", "03", "01", None, None, None),
  700. TimeTuple(
  701. "13",
  702. "00",
  703. "00",
  704. TimezoneTuple(False, True, None, None, "Z"),
  705. ),
  706. ),
  707. "duration": DurationTuple(None, None, None, None, "10", None, None),
  708. },
  709. datetime.datetime(
  710. year=2050,
  711. month=3,
  712. day=1,
  713. hour=13,
  714. tzinfo=UTCOffset(name="UTC", minutes=0),
  715. ),
  716. datetime.datetime(
  717. year=2050,
  718. month=3,
  719. day=1,
  720. hour=3,
  721. tzinfo=UTCOffset(name="UTC", minutes=0),
  722. ),
  723. ),
  724. # Make sure we truncate, not round
  725. # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
  726. # https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when
  727. (
  728. {
  729. "end": DateTuple("2000", "01", "01", None, None, None),
  730. "duration": DurationTuple(
  731. "1999.9999999999999999", None, None, None, None, None, None
  732. ),
  733. },
  734. datetime.date(year=2000, month=1, day=1),
  735. datetime.datetime(
  736. year=1, month=4, day=30, hour=0, minute=0, second=0, microsecond=1
  737. ),
  738. ),
  739. (
  740. {
  741. "end": DateTuple("1989", "03", "01", None, None, None),
  742. "duration": DurationTuple(
  743. None, "1.9999999999999999", None, None, None, None, None
  744. ),
  745. },
  746. datetime.date(year=1989, month=3, day=1),
  747. datetime.datetime(
  748. year=1988,
  749. month=12,
  750. day=31,
  751. hour=0,
  752. minute=0,
  753. second=0,
  754. microsecond=1,
  755. ),
  756. ),
  757. (
  758. {
  759. "end": DateTuple("1989", "03", "01", None, None, None),
  760. "duration": DurationTuple(
  761. None, None, "1.9999999999999999", None, None, None, None
  762. ),
  763. },
  764. datetime.date(year=1989, month=3, day=1),
  765. datetime.datetime(
  766. year=1989,
  767. month=2,
  768. day=15,
  769. hour=0,
  770. minute=0,
  771. second=0,
  772. microsecond=1,
  773. ),
  774. ),
  775. (
  776. {
  777. "end": DateTuple("1989", "03", "01", None, None, None),
  778. "duration": DurationTuple(
  779. None, None, None, "1.9999999999999999", None, None, None
  780. ),
  781. },
  782. datetime.date(year=1989, month=3, day=1),
  783. datetime.datetime(
  784. year=1989,
  785. month=2,
  786. day=27,
  787. hour=0,
  788. minute=0,
  789. second=0,
  790. microsecond=1,
  791. ),
  792. ),
  793. (
  794. {
  795. "end": DateTuple("2001", "01", "01", None, None, None),
  796. "duration": DurationTuple(
  797. None, None, None, None, "14.9999999999999999", None, None
  798. ),
  799. },
  800. datetime.date(year=2001, month=1, day=1),
  801. datetime.datetime(
  802. year=2000,
  803. month=12,
  804. day=31,
  805. hour=9,
  806. minute=0,
  807. second=0,
  808. microsecond=1,
  809. ),
  810. ),
  811. (
  812. {
  813. "end": DateTuple("2001", "01", "01", None, None, None),
  814. "duration": DurationTuple(
  815. None, None, None, None, None, "0.00000000999", None
  816. ),
  817. },
  818. datetime.date(year=2001, month=1, day=1),
  819. datetime.datetime(year=2001, month=1, day=1),
  820. ),
  821. (
  822. {
  823. "end": DateTuple("2001", "01", "01", None, None, None),
  824. "duration": DurationTuple(
  825. None, None, None, None, None, "0.0000000999", None
  826. ),
  827. },
  828. datetime.date(year=2001, month=1, day=1),
  829. datetime.datetime(
  830. year=2000,
  831. month=12,
  832. day=31,
  833. hour=23,
  834. minute=59,
  835. second=59,
  836. microsecond=999995,
  837. ),
  838. ),
  839. (
  840. {
  841. "end": DateTuple("2018", "03", "06", None, None, None),
  842. "duration": DurationTuple(
  843. None, None, None, None, None, None, "0.0000001"
  844. ),
  845. },
  846. datetime.date(year=2018, month=3, day=6),
  847. datetime.datetime(year=2018, month=3, day=6),
  848. ),
  849. (
  850. {
  851. "end": DateTuple("2018", "03", "06", None, None, None),
  852. "duration": DurationTuple(
  853. None, None, None, None, None, None, "2.0000048"
  854. ),
  855. },
  856. datetime.date(year=2018, month=3, day=6),
  857. datetime.datetime(
  858. year=2018,
  859. month=3,
  860. day=5,
  861. hour=23,
  862. minute=59,
  863. second=57,
  864. microsecond=999996,
  865. ),
  866. ),
  867. (
  868. {
  869. "start": DatetimeTuple(
  870. DateTuple("1981", "04", "05", None, None, None),
  871. TimeTuple("01", "01", "00", None),
  872. ),
  873. "duration": DurationTuple(None, "1", None, "1", None, "1", None),
  874. },
  875. datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1),
  876. datetime.datetime(year=1981, month=5, day=6, hour=1, minute=2),
  877. ),
  878. (
  879. {
  880. "start": DateTuple("1981", "04", "05", None, None, None),
  881. "duration": DurationTuple(None, "1", None, "1", None, None, None),
  882. },
  883. datetime.date(year=1981, month=4, day=5),
  884. datetime.date(year=1981, month=5, day=6),
  885. ),
  886. (
  887. {
  888. "start": DateTuple("2018", "03", "06", None, None, None),
  889. "duration": DurationTuple(
  890. None, "2.5", None, None, None, None, None
  891. ),
  892. },
  893. datetime.date(year=2018, month=3, day=6),
  894. datetime.date(year=2018, month=5, day=20),
  895. ),
  896. (
  897. {
  898. "start": DateTuple("2014", "11", "12", None, None, None),
  899. "duration": DurationTuple(None, None, None, None, "1", None, None),
  900. },
  901. datetime.date(year=2014, month=11, day=12),
  902. datetime.datetime(year=2014, month=11, day=12, hour=1, minute=0),
  903. ),
  904. (
  905. {
  906. "start": DateTuple("2014", "11", "12", None, None, None),
  907. "duration": DurationTuple(None, None, None, None, "4", "54", "6.5"),
  908. },
  909. datetime.date(year=2014, month=11, day=12),
  910. datetime.datetime(
  911. year=2014,
  912. month=11,
  913. day=12,
  914. hour=4,
  915. minute=54,
  916. second=6,
  917. microsecond=500000,
  918. ),
  919. ),
  920. (
  921. {
  922. "start": DatetimeTuple(
  923. DateTuple("2050", "03", "01", None, None, None),
  924. TimeTuple(
  925. "13",
  926. "00",
  927. "00",
  928. TimezoneTuple(False, True, None, None, "Z"),
  929. ),
  930. ),
  931. "duration": DurationTuple(None, None, None, None, "10", None, None),
  932. },
  933. datetime.datetime(
  934. year=2050,
  935. month=3,
  936. day=1,
  937. hour=13,
  938. tzinfo=UTCOffset(name="UTC", minutes=0),
  939. ),
  940. datetime.datetime(
  941. year=2050,
  942. month=3,
  943. day=1,
  944. hour=23,
  945. tzinfo=UTCOffset(name="UTC", minutes=0),
  946. ),
  947. ),
  948. # Make sure we truncate, not round
  949. # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
  950. (
  951. {
  952. "start": DateTuple("0001", "01", "01", None, None, None),
  953. "duration": DurationTuple(
  954. "1999.9999999999999999", None, None, None, None, None, None
  955. ),
  956. },
  957. datetime.date(year=1, month=1, day=1),
  958. datetime.datetime(
  959. year=1999,
  960. month=9,
  961. day=3,
  962. hour=23,
  963. minute=59,
  964. second=59,
  965. microsecond=999999,
  966. ),
  967. ),
  968. (
  969. {
  970. "start": DateTuple("1989", "03", "01", None, None, None),
  971. "duration": DurationTuple(
  972. None, "1.9999999999999999", None, None, None, None, None
  973. ),
  974. },
  975. datetime.date(year=1989, month=3, day=1),
  976. datetime.datetime(
  977. year=1989,
  978. month=4,
  979. day=29,
  980. hour=23,
  981. minute=59,
  982. second=59,
  983. microsecond=999999,
  984. ),
  985. ),
  986. (
  987. {
  988. "start": DateTuple("1989", "03", "01", None, None, None),
  989. "duration": DurationTuple(
  990. None, None, "1.9999999999999999", None, None, None, None
  991. ),
  992. },
  993. datetime.date(year=1989, month=3, day=1),
  994. datetime.datetime(
  995. year=1989,
  996. month=3,
  997. day=14,
  998. hour=23,
  999. minute=59,
  1000. second=59,
  1001. microsecond=999999,
  1002. ),
  1003. ),
  1004. (
  1005. {
  1006. "start": DateTuple("1989", "03", "01", None, None, None),
  1007. "duration": DurationTuple(
  1008. None, None, None, "1.9999999999999999", None, None, None
  1009. ),
  1010. },
  1011. datetime.date(year=1989, month=3, day=1),
  1012. datetime.datetime(
  1013. year=1989,
  1014. month=3,
  1015. day=2,
  1016. hour=23,
  1017. minute=59,
  1018. second=59,
  1019. microsecond=999999,
  1020. ),
  1021. ),
  1022. (
  1023. {
  1024. "start": DateTuple("2001", "01", "01", None, None, None),
  1025. "duration": DurationTuple(
  1026. None, None, None, None, "14.9999999999999999", None, None
  1027. ),
  1028. },
  1029. datetime.date(year=2001, month=1, day=1),
  1030. datetime.datetime(
  1031. year=2001,
  1032. month=1,
  1033. day=1,
  1034. hour=14,
  1035. minute=59,
  1036. second=59,
  1037. microsecond=999999,
  1038. ),
  1039. ),
  1040. (
  1041. {
  1042. "start": DateTuple("2001", "01", "01", None, None, None),
  1043. "duration": DurationTuple(
  1044. None, None, None, None, None, "0.00000000999", None
  1045. ),
  1046. },
  1047. datetime.date(year=2001, month=1, day=1),
  1048. datetime.datetime(year=2001, month=1, day=1),
  1049. ),
  1050. (
  1051. {
  1052. "start": DateTuple("2001", "01", "01", None, None, None),
  1053. "duration": DurationTuple(
  1054. None, None, None, None, None, "0.0000000999", None
  1055. ),
  1056. },
  1057. datetime.date(year=2001, month=1, day=1),
  1058. datetime.datetime(
  1059. year=2001, month=1, day=1, hour=0, minute=0, second=0, microsecond=5
  1060. ),
  1061. ),
  1062. (
  1063. {
  1064. "start": DateTuple("2018", "03", "06", None, None, None),
  1065. "duration": DurationTuple(
  1066. None, None, None, None, None, None, "0.0000001"
  1067. ),
  1068. },
  1069. datetime.date(year=2018, month=3, day=6),
  1070. datetime.datetime(year=2018, month=3, day=6),
  1071. ),
  1072. (
  1073. {
  1074. "start": DateTuple("2018", "03", "06", None, None, None),
  1075. "duration": DurationTuple(
  1076. None, None, None, None, None, None, "2.0000048"
  1077. ),
  1078. },
  1079. datetime.date(year=2018, month=3, day=6),
  1080. datetime.datetime(
  1081. year=2018, month=3, day=6, hour=0, minute=0, second=2, microsecond=4
  1082. ),
  1083. ),
  1084. (
  1085. {
  1086. "start": DatetimeTuple(
  1087. DateTuple("1980", "03", "05", None, None, None),
  1088. TimeTuple("01", "01", "00", None),
  1089. ),
  1090. "end": DatetimeTuple(
  1091. DateTuple("1981", "04", "05", None, None, None),
  1092. TimeTuple("01", "01", "00", None),
  1093. ),
  1094. },
  1095. datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1),
  1096. datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1),
  1097. ),
  1098. (
  1099. {
  1100. "start": DatetimeTuple(
  1101. DateTuple("1980", "03", "05", None, None, None),
  1102. TimeTuple("01", "01", "00", None),
  1103. ),
  1104. "end": DateTuple("1981", "04", "05", None, None, None),
  1105. },
  1106. datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1),
  1107. datetime.date(year=1981, month=4, day=5),
  1108. ),
  1109. (
  1110. {
  1111. "start": DateTuple("1980", "03", "05", None, None, None),
  1112. "end": DatetimeTuple(
  1113. DateTuple("1981", "04", "05", None, None, None),
  1114. TimeTuple("01", "01", "00", None),
  1115. ),
  1116. },
  1117. datetime.date(year=1980, month=3, day=5),
  1118. datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1),
  1119. ),
  1120. (
  1121. {
  1122. "start": DateTuple("1980", "03", "05", None, None, None),
  1123. "end": DateTuple("1981", "04", "05", None, None, None),
  1124. },
  1125. datetime.date(year=1980, month=3, day=5),
  1126. datetime.date(year=1981, month=4, day=5),
  1127. ),
  1128. (
  1129. {
  1130. "start": DateTuple("1981", "04", "05", None, None, None),
  1131. "end": DateTuple("1980", "03", "05", None, None, None),
  1132. },
  1133. datetime.date(year=1981, month=4, day=5),
  1134. datetime.date(year=1980, month=3, day=5),
  1135. ),
  1136. (
  1137. {
  1138. "start": DatetimeTuple(
  1139. DateTuple("2050", "03", "01", None, None, None),
  1140. TimeTuple(
  1141. "13",
  1142. "00",
  1143. "00",
  1144. TimezoneTuple(False, True, None, None, "Z"),
  1145. ),
  1146. ),
  1147. "end": DatetimeTuple(
  1148. DateTuple("2050", "05", "11", None, None, None),
  1149. TimeTuple(
  1150. "15",
  1151. "30",
  1152. "00",
  1153. TimezoneTuple(False, True, None, None, "Z"),
  1154. ),
  1155. ),
  1156. },
  1157. datetime.datetime(
  1158. year=2050,
  1159. month=3,
  1160. day=1,
  1161. hour=13,
  1162. tzinfo=UTCOffset(name="UTC", minutes=0),
  1163. ),
  1164. datetime.datetime(
  1165. year=2050,
  1166. month=5,
  1167. day=11,
  1168. hour=15,
  1169. minute=30,
  1170. tzinfo=UTCOffset(name="UTC", minutes=0),
  1171. ),
  1172. ),
  1173. # Test concise representation
  1174. (
  1175. {
  1176. "start": DateTuple("2020", "01", "01", None, None, None),
  1177. "end": DateTuple(None, None, "02", None, None, None),
  1178. },
  1179. datetime.date(year=2020, month=1, day=1),
  1180. datetime.date(year=2020, month=1, day=2),
  1181. ),
  1182. (
  1183. {
  1184. "start": DateTuple("2008", "02", "15", None, None, None),
  1185. "end": DateTuple(None, "03", "14", None, None, None),
  1186. },
  1187. datetime.date(year=2008, month=2, day=15),
  1188. datetime.date(year=2008, month=3, day=14),
  1189. ),
  1190. (
  1191. {
  1192. "start": DatetimeTuple(
  1193. DateTuple("2007", "12", "14", None, None, None),
  1194. TimeTuple("13", "30", None, None),
  1195. ),
  1196. "end": TimeTuple("15", "30", None, None),
  1197. },
  1198. datetime.datetime(year=2007, month=12, day=14, hour=13, minute=30),
  1199. datetime.datetime(year=2007, month=12, day=14, hour=15, minute=30),
  1200. ),
  1201. (
  1202. {
  1203. "start": DatetimeTuple(
  1204. DateTuple("2007", "11", "13", None, None, None),
  1205. TimeTuple("09", "00", None, None),
  1206. ),
  1207. "end": DatetimeTuple(
  1208. DateTuple(None, None, "15", None, None, None),
  1209. TimeTuple("17", "00", None, None),
  1210. ),
  1211. },
  1212. datetime.datetime(year=2007, month=11, day=13, hour=9),
  1213. datetime.datetime(year=2007, month=11, day=15, hour=17),
  1214. ),
  1215. (
  1216. {
  1217. "start": DatetimeTuple(
  1218. DateTuple("2007", "11", "13", None, None, None),
  1219. TimeTuple("00", "00", None, None),
  1220. ),
  1221. "end": DatetimeTuple(
  1222. DateTuple(None, None, "16", None, None, None),
  1223. TimeTuple("00", "00", None, None),
  1224. ),
  1225. },
  1226. datetime.datetime(year=2007, month=11, day=13),
  1227. datetime.datetime(year=2007, month=11, day=16),
  1228. ),
  1229. (
  1230. {
  1231. "start": DatetimeTuple(
  1232. DateTuple("2007", "11", "13", None, None, None),
  1233. TimeTuple(
  1234. "09",
  1235. "00",
  1236. None,
  1237. TimezoneTuple(False, True, None, None, "Z"),
  1238. ),
  1239. ),
  1240. "end": DatetimeTuple(
  1241. DateTuple(None, None, "15", None, None, None),
  1242. TimeTuple("17", "00", None, None),
  1243. ),
  1244. },
  1245. datetime.datetime(
  1246. year=2007,
  1247. month=11,
  1248. day=13,
  1249. hour=9,
  1250. tzinfo=UTCOffset(name="UTC", minutes=0),
  1251. ),
  1252. datetime.datetime(
  1253. year=2007,
  1254. month=11,
  1255. day=15,
  1256. hour=17,
  1257. tzinfo=UTCOffset(name="UTC", minutes=0),
  1258. ),
  1259. ),
  1260. (
  1261. {
  1262. "start": DatetimeTuple(
  1263. DateTuple("2007", "11", "13", None, None, None),
  1264. TimeTuple("09", "00", None, None),
  1265. ),
  1266. "end": TimeTuple("12", "34.567", None, None),
  1267. },
  1268. datetime.datetime(year=2007, month=11, day=13, hour=9),
  1269. datetime.datetime(
  1270. year=2007,
  1271. month=11,
  1272. day=13,
  1273. hour=12,
  1274. minute=34,
  1275. second=34,
  1276. microsecond=20000,
  1277. ),
  1278. ),
  1279. (
  1280. {
  1281. "start": DateTuple("2007", "11", "13", None, None, None),
  1282. "end": TimeTuple("12", "34", None, None),
  1283. },
  1284. datetime.date(year=2007, month=11, day=13),
  1285. datetime.datetime(year=2007, month=11, day=13, hour=12, minute=34),
  1286. ),
  1287. # Make sure we truncate, not round
  1288. # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
  1289. (
  1290. {
  1291. "start": DatetimeTuple(
  1292. DateTuple("1980", "03", "05", None, None, None),
  1293. TimeTuple("01", "01", "00.0000001", None),
  1294. ),
  1295. "end": DatetimeTuple(
  1296. DateTuple("1981", "04", "05", None, None, None),
  1297. TimeTuple("14", "43", "59.9999997", None),
  1298. ),
  1299. },
  1300. datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1),
  1301. datetime.datetime(
  1302. year=1981,
  1303. month=4,
  1304. day=5,
  1305. hour=14,
  1306. minute=43,
  1307. second=59,
  1308. microsecond=999999,
  1309. ),
  1310. ),
  1311. )
  1312. for testtuple in testtuples:
  1313. result = PythonTimeBuilder.build_interval(**testtuple[0])
  1314. self.assertEqual(result[0], testtuple[1])
  1315. self.assertEqual(result[1], testtuple[2])
  1316. def test_build_repeating_interval(self):
  1317. args = {
  1318. "Rnn": "3",
  1319. "interval": IntervalTuple(
  1320. DateTuple("1981", "04", "05", None, None, None),
  1321. None,
  1322. DurationTuple(None, None, None, "1", None, None, None),
  1323. ),
  1324. }
  1325. results = list(PythonTimeBuilder.build_repeating_interval(**args))
  1326. self.assertEqual(results[0], datetime.date(year=1981, month=4, day=5))
  1327. self.assertEqual(results[1], datetime.date(year=1981, month=4, day=6))
  1328. self.assertEqual(results[2], datetime.date(year=1981, month=4, day=7))
  1329. args = {
  1330. "Rnn": "11",
  1331. "interval": IntervalTuple(
  1332. None,
  1333. DatetimeTuple(
  1334. DateTuple("1980", "03", "05", None, None, None),
  1335. TimeTuple("01", "01", "00", None),
  1336. ),
  1337. DurationTuple(None, None, None, None, "1", "2", None),
  1338. ),
  1339. }
  1340. results = list(PythonTimeBuilder.build_repeating_interval(**args))
  1341. for dateindex in compat.range(0, 11):
  1342. self.assertEqual(
  1343. results[dateindex],
  1344. datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1)
  1345. - dateindex * datetime.timedelta(hours=1, minutes=2),
  1346. )
  1347. args = {
  1348. "Rnn": "2",
  1349. "interval": IntervalTuple(
  1350. DatetimeTuple(
  1351. DateTuple("1980", "03", "05", None, None, None),
  1352. TimeTuple("01", "01", "00", None),
  1353. ),
  1354. DatetimeTuple(
  1355. DateTuple("1981", "04", "05", None, None, None),
  1356. TimeTuple("01", "01", "00", None),
  1357. ),
  1358. None,
  1359. ),
  1360. }
  1361. results = list(PythonTimeBuilder.build_repeating_interval(**args))
  1362. self.assertEqual(
  1363. results[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1)
  1364. )
  1365. self.assertEqual(
  1366. results[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1)
  1367. )
  1368. args = {
  1369. "Rnn": "2",
  1370. "interval": IntervalTuple(
  1371. DatetimeTuple(
  1372. DateTuple("1980", "03", "05", None, None, None),
  1373. TimeTuple("01", "01", "00", None),
  1374. ),
  1375. DatetimeTuple(
  1376. DateTuple("1981", "04", "05", None, None, None),
  1377. TimeTuple("01", "01", "00", None),
  1378. ),
  1379. None,
  1380. ),
  1381. }
  1382. results = list(PythonTimeBuilder.build_repeating_interval(**args))
  1383. self.assertEqual(
  1384. results[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1)
  1385. )
  1386. self.assertEqual(
  1387. results[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1)
  1388. )
  1389. args = {
  1390. "R": True,
  1391. "interval": IntervalTuple(
  1392. None,
  1393. DatetimeTuple(
  1394. DateTuple("1980", "03", "05", None, None, None),
  1395. TimeTuple("01", "01", "00", None),
  1396. ),
  1397. DurationTuple(None, None, None, None, "1", "2", None),
  1398. ),
  1399. }
  1400. resultgenerator = PythonTimeBuilder.build_repeating_interval(**args)
  1401. # Test the first 11 generated
  1402. for dateindex in compat.range(0, 11):
  1403. self.assertEqual(
  1404. next(resultgenerator),
  1405. datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1)
  1406. - dateindex * datetime.timedelta(hours=1, minutes=2),
  1407. )
  1408. args = {
  1409. "R": True,
  1410. "interval": IntervalTuple(
  1411. DateTuple("1981", "04", "05", None, None, None),
  1412. None,
  1413. DurationTuple(None, None, None, "1", None, None, None),
  1414. ),
  1415. }
  1416. resultgenerator = PythonTimeBuilder.build_repeating_interval(**args)
  1417. # Test the first 11 generated
  1418. for dateindex in compat.range(0, 11):
  1419. self.assertEqual(
  1420. next(resultgenerator),
  1421. (
  1422. datetime.datetime(year=1981, month=4, day=5, hour=0, minute=0)
  1423. + dateindex * datetime.timedelta(days=1)
  1424. ).date(),
  1425. )
  1426. def test_build_timezone(self):
  1427. testtuples = (
  1428. ({"Z": True, "name": "Z"}, datetime.timedelta(hours=0), "UTC"),
  1429. (
  1430. {"negative": False, "hh": "00", "mm": "00", "name": "+00:00"},
  1431. datetime.timedelta(hours=0),
  1432. "+00:00",
  1433. ),
  1434. (
  1435. {"negative": False, "hh": "01", "mm": "00", "name": "+01:00"},
  1436. datetime.timedelta(hours=1),
  1437. "+01:00",
  1438. ),
  1439. (
  1440. {"negative": True, "hh": "01", "mm": "00", "name": "-01:00"},
  1441. -datetime.timedelta(hours=1),
  1442. "-01:00",
  1443. ),
  1444. (
  1445. {"negative": False, "hh": "00", "mm": "12", "name": "+00:12"},
  1446. datetime.timedelta(minutes=12),
  1447. "+00:12",
  1448. ),
  1449. (
  1450. {"negative": False, "hh": "01", "mm": "23", "name": "+01:23"},
  1451. datetime.timedelta(hours=1, minutes=23),
  1452. "+01:23",
  1453. ),
  1454. (
  1455. {"negative": True, "hh": "01", "mm": "23", "name": "-01:23"},
  1456. -datetime.timedelta(hours=1, minutes=23),
  1457. "-01:23",
  1458. ),
  1459. (
  1460. {"negative": False, "hh": "00", "name": "+00"},
  1461. datetime.timedelta(hours=0),
  1462. "+00",
  1463. ),
  1464. (
  1465. {"negative": False, "hh": "01", "name": "+01"},
  1466. datetime.timedelta(hours=1),
  1467. "+01",
  1468. ),
  1469. (
  1470. {"negative": True, "hh": "01", "name": "-01"},
  1471. -datetime.timedelta(hours=1),
  1472. "-01",
  1473. ),
  1474. (
  1475. {"negative": False, "hh": "12", "name": "+12"},
  1476. datetime.timedelta(hours=12),
  1477. "+12",
  1478. ),
  1479. (
  1480. {"negative": True, "hh": "12", "name": "-12"},
  1481. -datetime.timedelta(hours=12),
  1482. "-12",
  1483. ),
  1484. )
  1485. for testtuple in testtuples:
  1486. result = PythonTimeBuilder.build_timezone(**testtuple[0])
  1487. self.assertEqual(result.utcoffset(None), testtuple[1])
  1488. self.assertEqual(result.tzname(None), testtuple[2])
  1489. def test_range_check_date(self):
  1490. # 0 isn't a valid year for a Python builder
  1491. with self.assertRaises(YearOutOfBoundsError):
  1492. PythonTimeBuilder.build_date(YYYY="0000")
  1493. # Leap year
  1494. # https://bitbucket.org/nielsenb/aniso8601/issues/14/parsing-ordinal-dates-should-only-allow
  1495. with self.assertRaises(DayOutOfBoundsError):
  1496. PythonTimeBuilder.build_date(YYYY="1981", DDD="366")
  1497. def test_range_check_time(self):
  1498. # Hour 24 can only represent midnight
  1499. with self.assertRaises(MidnightBoundsError):
  1500. PythonTimeBuilder.build_time(hh="24", mm="00", ss="01")
  1501. with self.assertRaises(MidnightBoundsError):
  1502. PythonTimeBuilder.build_time(hh="24", mm="00.1")
  1503. with self.assertRaises(MidnightBoundsError):
  1504. PythonTimeBuilder.build_time(hh="24", mm="01")
  1505. with self.assertRaises(MidnightBoundsError):
  1506. PythonTimeBuilder.build_time(hh="24.1")
  1507. def test_range_check_duration(self):
  1508. with self.assertRaises(YearOutOfBoundsError):
  1509. PythonTimeBuilder.build_duration(
  1510. PnY=str((datetime.timedelta.max.days // 365) + 1)
  1511. )
  1512. with self.assertRaises(MonthOutOfBoundsError):
  1513. PythonTimeBuilder.build_duration(
  1514. PnM=str((datetime.timedelta.max.days // 30) + 1)
  1515. )
  1516. with self.assertRaises(DayOutOfBoundsError):
  1517. PythonTimeBuilder.build_duration(PnD=str(datetime.timedelta.max.days + 1))
  1518. with self.assertRaises(WeekOutOfBoundsError):
  1519. PythonTimeBuilder.build_duration(
  1520. PnW=str((datetime.timedelta.max.days // 7) + 1)
  1521. )
  1522. with self.assertRaises(HoursOutOfBoundsError):
  1523. PythonTimeBuilder.build_duration(
  1524. TnH=str((datetime.timedelta.max.days * 24) + 24)
  1525. )
  1526. with self.assertRaises(MinutesOutOfBoundsError):
  1527. PythonTimeBuilder.build_duration(
  1528. TnM=str((datetime.timedelta.max.days * 24 * 60) + 24 * 60)
  1529. )
  1530. with self.assertRaises(SecondsOutOfBoundsError):
  1531. PythonTimeBuilder.build_duration(
  1532. TnS=str((datetime.timedelta.max.days * 24 * 60 * 60) + 24 * 60 * 60)
  1533. )
  1534. # Split max range across all parts
  1535. maxpart = datetime.timedelta.max.days // 7
  1536. with self.assertRaises(DayOutOfBoundsError):
  1537. PythonTimeBuilder.build_duration(
  1538. PnY=str((maxpart // 365) + 1),
  1539. PnM=str((maxpart // 30) + 1),
  1540. PnD=str((maxpart + 1)),
  1541. PnW=str((maxpart // 7) + 1),
  1542. TnH=str((maxpart * 24) + 1),
  1543. TnM=str((maxpart * 24 * 60) + 1),
  1544. TnS=str((maxpart * 24 * 60 * 60) + 1),
  1545. )
  1546. def test_range_check_interval(self):
  1547. with self.assertRaises(YearOutOfBoundsError):
  1548. PythonTimeBuilder.build_interval(
  1549. start=DateTuple("0007", None, None, None, None, None),
  1550. duration=DurationTuple(
  1551. None, None, None, str(datetime.timedelta.max.days), None, None, None
  1552. ),
  1553. )
  1554. with self.assertRaises(YearOutOfBoundsError):
  1555. PythonTimeBuilder.build_interval(
  1556. start=DatetimeTuple(
  1557. DateTuple("0007", None, None, None, None, None),
  1558. TimeTuple("1", None, None, None),
  1559. ),
  1560. duration=DurationTuple(
  1561. str(datetime.timedelta.max.days // 365),
  1562. None,
  1563. None,
  1564. None,
  1565. None,
  1566. None,
  1567. None,
  1568. ),
  1569. )
  1570. with self.assertRaises(YearOutOfBoundsError):
  1571. PythonTimeBuilder.build_interval(
  1572. end=DateTuple("0001", None, None, None, None, None),
  1573. duration=DurationTuple("3", None, None, None, None, None, None),
  1574. )
  1575. with self.assertRaises(YearOutOfBoundsError):
  1576. PythonTimeBuilder.build_interval(
  1577. end=DatetimeTuple(
  1578. DateTuple("0001", None, None, None, None, None),
  1579. TimeTuple("1", None, None, None),
  1580. ),
  1581. duration=DurationTuple("2", None, None, None, None, None, None),
  1582. )
  1583. def test_build_week_date(self):
  1584. weekdate = PythonTimeBuilder._build_week_date(2009, 1)
  1585. self.assertEqual(weekdate, datetime.date(year=2008, month=12, day=29))
  1586. weekdate = PythonTimeBuilder._build_week_date(2009, 53, isoday=7)
  1587. self.assertEqual(weekdate, datetime.date(year=2010, month=1, day=3))
  1588. def test_build_ordinal_date(self):
  1589. ordinaldate = PythonTimeBuilder._build_ordinal_date(1981, 95)
  1590. self.assertEqual(ordinaldate, datetime.date(year=1981, month=4, day=5))
  1591. def test_iso_year_start(self):
  1592. yearstart = PythonTimeBuilder._iso_year_start(2004)
  1593. self.assertEqual(yearstart, datetime.date(year=2003, month=12, day=29))
  1594. yearstart = PythonTimeBuilder._iso_year_start(2010)
  1595. self.assertEqual(yearstart, datetime.date(year=2010, month=1, day=4))
  1596. yearstart = PythonTimeBuilder._iso_year_start(2009)
  1597. self.assertEqual(yearstart, datetime.date(year=2008, month=12, day=29))
  1598. def test_date_generator(self):
  1599. startdate = datetime.date(year=2018, month=8, day=29)
  1600. timedelta = datetime.timedelta(days=1)
  1601. iterations = 10
  1602. generator = PythonTimeBuilder._date_generator(startdate, timedelta, iterations)
  1603. results = list(generator)
  1604. for dateindex in compat.range(0, 10):
  1605. self.assertEqual(
  1606. results[dateindex],
  1607. datetime.date(year=2018, month=8, day=29)
  1608. + dateindex * datetime.timedelta(days=1),
  1609. )
  1610. def test_date_generator_unbounded(self):
  1611. startdate = datetime.date(year=2018, month=8, day=29)
  1612. timedelta = datetime.timedelta(days=5)
  1613. generator = PythonTimeBuilder._date_generator_unbounded(startdate, timedelta)
  1614. # Check the first 10 results
  1615. for dateindex in compat.range(0, 10):
  1616. self.assertEqual(
  1617. next(generator),
  1618. datetime.date(year=2018, month=8, day=29)
  1619. + dateindex * datetime.timedelta(days=5),
  1620. )
  1621. def test_distribute_microseconds(self):
  1622. self.assertEqual(PythonTimeBuilder._distribute_microseconds(1, (), ()), (1,))
  1623. self.assertEqual(
  1624. PythonTimeBuilder._distribute_microseconds(11, (0,), (10,)), (1, 1)
  1625. )
  1626. self.assertEqual(
  1627. PythonTimeBuilder._distribute_microseconds(211, (0, 0), (100, 10)),
  1628. (2, 1, 1),
  1629. )
  1630. self.assertEqual(PythonTimeBuilder._distribute_microseconds(1, (), ()), (1,))
  1631. self.assertEqual(
  1632. PythonTimeBuilder._distribute_microseconds(11, (5,), (10,)), (6, 1)
  1633. )
  1634. self.assertEqual(
  1635. PythonTimeBuilder._distribute_microseconds(211, (10, 5), (100, 10)),
  1636. (12, 6, 1),
  1637. )