test_strptime.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. """PyUnit testing against strptime"""
  2. import unittest
  3. import time
  4. import locale
  5. import re
  6. import os
  7. import sys
  8. from test import support
  9. from test.support import skip_if_buggy_ucrt_strfptime
  10. from datetime import date as datetime_date
  11. import _strptime
  12. class getlang_Tests(unittest.TestCase):
  13. """Test _getlang"""
  14. def test_basic(self):
  15. self.assertEqual(_strptime._getlang(), locale.getlocale(locale.LC_TIME))
  16. class LocaleTime_Tests(unittest.TestCase):
  17. """Tests for _strptime.LocaleTime.
  18. All values are lower-cased when stored in LocaleTime, so make sure to
  19. compare values after running ``lower`` on them.
  20. """
  21. def setUp(self):
  22. """Create time tuple based on current time."""
  23. self.time_tuple = time.localtime()
  24. self.LT_ins = _strptime.LocaleTime()
  25. def compare_against_time(self, testing, directive, tuple_position,
  26. error_msg):
  27. """Helper method that tests testing against directive based on the
  28. tuple_position of time_tuple. Uses error_msg as error message.
  29. """
  30. strftime_output = time.strftime(directive, self.time_tuple).lower()
  31. comparison = testing[self.time_tuple[tuple_position]]
  32. self.assertIn(strftime_output, testing,
  33. "%s: not found in tuple" % error_msg)
  34. self.assertEqual(comparison, strftime_output,
  35. "%s: position within tuple incorrect; %s != %s" %
  36. (error_msg, comparison, strftime_output))
  37. def test_weekday(self):
  38. # Make sure that full and abbreviated weekday names are correct in
  39. # both string and position with tuple
  40. self.compare_against_time(self.LT_ins.f_weekday, '%A', 6,
  41. "Testing of full weekday name failed")
  42. self.compare_against_time(self.LT_ins.a_weekday, '%a', 6,
  43. "Testing of abbreviated weekday name failed")
  44. def test_month(self):
  45. # Test full and abbreviated month names; both string and position
  46. # within the tuple
  47. self.compare_against_time(self.LT_ins.f_month, '%B', 1,
  48. "Testing against full month name failed")
  49. self.compare_against_time(self.LT_ins.a_month, '%b', 1,
  50. "Testing against abbreviated month name failed")
  51. def test_am_pm(self):
  52. # Make sure AM/PM representation done properly
  53. strftime_output = time.strftime("%p", self.time_tuple).lower()
  54. self.assertIn(strftime_output, self.LT_ins.am_pm,
  55. "AM/PM representation not in tuple")
  56. if self.time_tuple[3] < 12: position = 0
  57. else: position = 1
  58. self.assertEqual(self.LT_ins.am_pm[position], strftime_output,
  59. "AM/PM representation in the wrong position within the tuple")
  60. @unittest.skipIf(
  61. support.is_emscripten, "musl libc issue on Emscripten, bpo-46390"
  62. )
  63. def test_timezone(self):
  64. # Make sure timezone is correct
  65. timezone = time.strftime("%Z", self.time_tuple).lower()
  66. if timezone:
  67. self.assertTrue(timezone in self.LT_ins.timezone[0] or
  68. timezone in self.LT_ins.timezone[1],
  69. "timezone %s not found in %s" %
  70. (timezone, self.LT_ins.timezone))
  71. def test_date_time(self):
  72. # Check that LC_date_time, LC_date, and LC_time are correct
  73. # the magic date is used so as to not have issues with %c when day of
  74. # the month is a single digit and has a leading space. This is not an
  75. # issue since strptime still parses it correctly. The problem is
  76. # testing these directives for correctness by comparing strftime
  77. # output.
  78. magic_date = (1999, 3, 17, 22, 44, 55, 2, 76, 0)
  79. strftime_output = time.strftime("%c", magic_date)
  80. self.assertEqual(time.strftime(self.LT_ins.LC_date_time, magic_date),
  81. strftime_output, "LC_date_time incorrect")
  82. strftime_output = time.strftime("%x", magic_date)
  83. self.assertEqual(time.strftime(self.LT_ins.LC_date, magic_date),
  84. strftime_output, "LC_date incorrect")
  85. strftime_output = time.strftime("%X", magic_date)
  86. self.assertEqual(time.strftime(self.LT_ins.LC_time, magic_date),
  87. strftime_output, "LC_time incorrect")
  88. LT = _strptime.LocaleTime()
  89. LT.am_pm = ('', '')
  90. self.assertTrue(LT.LC_time, "LocaleTime's LC directives cannot handle "
  91. "empty strings")
  92. def test_lang(self):
  93. # Make sure lang is set to what _getlang() returns
  94. # Assuming locale has not changed between now and when self.LT_ins was created
  95. self.assertEqual(self.LT_ins.lang, _strptime._getlang())
  96. class TimeRETests(unittest.TestCase):
  97. """Tests for TimeRE."""
  98. def setUp(self):
  99. """Construct generic TimeRE object."""
  100. self.time_re = _strptime.TimeRE()
  101. self.locale_time = _strptime.LocaleTime()
  102. def test_pattern(self):
  103. # Test TimeRE.pattern
  104. pattern_string = self.time_re.pattern(r"%a %A %d")
  105. self.assertTrue(pattern_string.find(self.locale_time.a_weekday[2]) != -1,
  106. "did not find abbreviated weekday in pattern string '%s'" %
  107. pattern_string)
  108. self.assertTrue(pattern_string.find(self.locale_time.f_weekday[4]) != -1,
  109. "did not find full weekday in pattern string '%s'" %
  110. pattern_string)
  111. self.assertTrue(pattern_string.find(self.time_re['d']) != -1,
  112. "did not find 'd' directive pattern string '%s'" %
  113. pattern_string)
  114. def test_pattern_escaping(self):
  115. # Make sure any characters in the format string that might be taken as
  116. # regex syntax is escaped.
  117. pattern_string = self.time_re.pattern(r"\d+")
  118. self.assertIn(r"\\d\+", pattern_string,
  119. "%s does not have re characters escaped properly" %
  120. pattern_string)
  121. @skip_if_buggy_ucrt_strfptime
  122. def test_compile(self):
  123. # Check that compiled regex is correct
  124. found = self.time_re.compile(r"%A").match(self.locale_time.f_weekday[6])
  125. self.assertTrue(found and found.group('A') == self.locale_time.f_weekday[6],
  126. "re object for '%A' failed")
  127. compiled = self.time_re.compile(r"%a %b")
  128. found = compiled.match("%s %s" % (self.locale_time.a_weekday[4],
  129. self.locale_time.a_month[4]))
  130. self.assertTrue(found,
  131. "Match failed with '%s' regex and '%s' string" %
  132. (compiled.pattern, "%s %s" % (self.locale_time.a_weekday[4],
  133. self.locale_time.a_month[4])))
  134. self.assertTrue(found.group('a') == self.locale_time.a_weekday[4] and
  135. found.group('b') == self.locale_time.a_month[4],
  136. "re object couldn't find the abbreviated weekday month in "
  137. "'%s' using '%s'; group 'a' = '%s', group 'b' = %s'" %
  138. (found.string, found.re.pattern, found.group('a'),
  139. found.group('b')))
  140. for directive in ('a','A','b','B','c','d','G','H','I','j','m','M','p',
  141. 'S','u','U','V','w','W','x','X','y','Y','Z','%'):
  142. compiled = self.time_re.compile("%" + directive)
  143. found = compiled.match(time.strftime("%" + directive))
  144. self.assertTrue(found, "Matching failed on '%s' using '%s' regex" %
  145. (time.strftime("%" + directive),
  146. compiled.pattern))
  147. def test_blankpattern(self):
  148. # Make sure when tuple or something has no values no regex is generated.
  149. # Fixes bug #661354
  150. test_locale = _strptime.LocaleTime()
  151. test_locale.timezone = (frozenset(), frozenset())
  152. self.assertEqual(_strptime.TimeRE(test_locale).pattern("%Z"), '',
  153. "with timezone == ('',''), TimeRE().pattern('%Z') != ''")
  154. def test_matching_with_escapes(self):
  155. # Make sure a format that requires escaping of characters works
  156. compiled_re = self.time_re.compile(r"\w+ %m")
  157. found = compiled_re.match(r"\w+ 10")
  158. self.assertTrue(found, r"Escaping failed of format '\w+ 10'")
  159. def test_locale_data_w_regex_metacharacters(self):
  160. # Check that if locale data contains regex metacharacters they are
  161. # escaped properly.
  162. # Discovered by bug #1039270 .
  163. locale_time = _strptime.LocaleTime()
  164. locale_time.timezone = (frozenset(("utc", "gmt",
  165. "Tokyo (standard time)")),
  166. frozenset("Tokyo (daylight time)"))
  167. time_re = _strptime.TimeRE(locale_time)
  168. self.assertTrue(time_re.compile("%Z").match("Tokyo (standard time)"),
  169. "locale data that contains regex metacharacters is not"
  170. " properly escaped")
  171. def test_whitespace_substitution(self):
  172. # When pattern contains whitespace, make sure it is taken into account
  173. # so as to not allow subpatterns to end up next to each other and
  174. # "steal" characters from each other.
  175. pattern = self.time_re.pattern('%j %H')
  176. self.assertFalse(re.match(pattern, "180"))
  177. self.assertTrue(re.match(pattern, "18 0"))
  178. class StrptimeTests(unittest.TestCase):
  179. """Tests for _strptime.strptime."""
  180. def setUp(self):
  181. """Create testing time tuple."""
  182. self.time_tuple = time.gmtime()
  183. def test_ValueError(self):
  184. # Make sure ValueError is raised when match fails or format is bad
  185. self.assertRaises(ValueError, _strptime._strptime_time, data_string="%d",
  186. format="%A")
  187. for bad_format in ("%", "% ", "%e"):
  188. try:
  189. _strptime._strptime_time("2005", bad_format)
  190. except ValueError:
  191. continue
  192. except Exception as err:
  193. self.fail("'%s' raised %s, not ValueError" %
  194. (bad_format, err.__class__.__name__))
  195. else:
  196. self.fail("'%s' did not raise ValueError" % bad_format)
  197. # Ambiguous or incomplete cases using ISO year/week/weekday directives
  198. # 1. ISO week (%V) is specified, but the year is specified with %Y
  199. # instead of %G
  200. with self.assertRaises(ValueError):
  201. _strptime._strptime("1999 50", "%Y %V")
  202. # 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not
  203. with self.assertRaises(ValueError):
  204. _strptime._strptime("1999 51", "%G %V")
  205. # 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not
  206. for w in ('A', 'a', 'w', 'u'):
  207. with self.assertRaises(ValueError):
  208. _strptime._strptime("1999 51","%G %{}".format(w))
  209. # 4. ISO year is specified alone (e.g. time.strptime('2015', '%G'))
  210. with self.assertRaises(ValueError):
  211. _strptime._strptime("2015", "%G")
  212. # 5. Julian/ordinal day (%j) is specified with %G, but not %Y
  213. with self.assertRaises(ValueError):
  214. _strptime._strptime("1999 256", "%G %j")
  215. def test_strptime_exception_context(self):
  216. # check that this doesn't chain exceptions needlessly (see #17572)
  217. with self.assertRaises(ValueError) as e:
  218. _strptime._strptime_time('', '%D')
  219. self.assertIs(e.exception.__suppress_context__, True)
  220. # additional check for IndexError branch (issue #19545)
  221. with self.assertRaises(ValueError) as e:
  222. _strptime._strptime_time('19', '%Y %')
  223. self.assertIs(e.exception.__suppress_context__, True)
  224. def test_unconverteddata(self):
  225. # Check ValueError is raised when there is unconverted data
  226. self.assertRaises(ValueError, _strptime._strptime_time, "10 12", "%m")
  227. def helper(self, directive, position):
  228. """Helper fxn in testing."""
  229. strf_output = time.strftime("%" + directive, self.time_tuple)
  230. strp_output = _strptime._strptime_time(strf_output, "%" + directive)
  231. self.assertTrue(strp_output[position] == self.time_tuple[position],
  232. "testing of '%s' directive failed; '%s' -> %s != %s" %
  233. (directive, strf_output, strp_output[position],
  234. self.time_tuple[position]))
  235. def test_year(self):
  236. # Test that the year is handled properly
  237. for directive in ('y', 'Y'):
  238. self.helper(directive, 0)
  239. # Must also make sure %y values are correct for bounds set by Open Group
  240. for century, bounds in ((1900, ('69', '99')), (2000, ('00', '68'))):
  241. for bound in bounds:
  242. strp_output = _strptime._strptime_time(bound, '%y')
  243. expected_result = century + int(bound)
  244. self.assertTrue(strp_output[0] == expected_result,
  245. "'y' test failed; passed in '%s' "
  246. "and returned '%s'" % (bound, strp_output[0]))
  247. def test_month(self):
  248. # Test for month directives
  249. for directive in ('B', 'b', 'm'):
  250. self.helper(directive, 1)
  251. def test_day(self):
  252. # Test for day directives
  253. self.helper('d', 2)
  254. def test_hour(self):
  255. # Test hour directives
  256. self.helper('H', 3)
  257. strf_output = time.strftime("%I %p", self.time_tuple)
  258. strp_output = _strptime._strptime_time(strf_output, "%I %p")
  259. self.assertTrue(strp_output[3] == self.time_tuple[3],
  260. "testing of '%%I %%p' directive failed; '%s' -> %s != %s" %
  261. (strf_output, strp_output[3], self.time_tuple[3]))
  262. def test_minute(self):
  263. # Test minute directives
  264. self.helper('M', 4)
  265. def test_second(self):
  266. # Test second directives
  267. self.helper('S', 5)
  268. def test_fraction(self):
  269. # Test microseconds
  270. import datetime
  271. d = datetime.datetime(2012, 12, 20, 12, 34, 56, 78987)
  272. tup, frac, _ = _strptime._strptime(str(d), format="%Y-%m-%d %H:%M:%S.%f")
  273. self.assertEqual(frac, d.microsecond)
  274. def test_weekday(self):
  275. # Test weekday directives
  276. for directive in ('A', 'a', 'w', 'u'):
  277. self.helper(directive,6)
  278. def test_julian(self):
  279. # Test julian directives
  280. self.helper('j', 7)
  281. def test_offset(self):
  282. one_hour = 60 * 60
  283. half_hour = 30 * 60
  284. half_minute = 30
  285. (*_, offset), _, offset_fraction = _strptime._strptime("+0130", "%z")
  286. self.assertEqual(offset, one_hour + half_hour)
  287. self.assertEqual(offset_fraction, 0)
  288. (*_, offset), _, offset_fraction = _strptime._strptime("-0100", "%z")
  289. self.assertEqual(offset, -one_hour)
  290. self.assertEqual(offset_fraction, 0)
  291. (*_, offset), _, offset_fraction = _strptime._strptime("-013030", "%z")
  292. self.assertEqual(offset, -(one_hour + half_hour + half_minute))
  293. self.assertEqual(offset_fraction, 0)
  294. (*_, offset), _, offset_fraction = _strptime._strptime("-013030.000001", "%z")
  295. self.assertEqual(offset, -(one_hour + half_hour + half_minute))
  296. self.assertEqual(offset_fraction, -1)
  297. (*_, offset), _, offset_fraction = _strptime._strptime("+01:00", "%z")
  298. self.assertEqual(offset, one_hour)
  299. self.assertEqual(offset_fraction, 0)
  300. (*_, offset), _, offset_fraction = _strptime._strptime("-01:30", "%z")
  301. self.assertEqual(offset, -(one_hour + half_hour))
  302. self.assertEqual(offset_fraction, 0)
  303. (*_, offset), _, offset_fraction = _strptime._strptime("-01:30:30", "%z")
  304. self.assertEqual(offset, -(one_hour + half_hour + half_minute))
  305. self.assertEqual(offset_fraction, 0)
  306. (*_, offset), _, offset_fraction = _strptime._strptime("-01:30:30.000001", "%z")
  307. self.assertEqual(offset, -(one_hour + half_hour + half_minute))
  308. self.assertEqual(offset_fraction, -1)
  309. (*_, offset), _, offset_fraction = _strptime._strptime("+01:30:30.001", "%z")
  310. self.assertEqual(offset, one_hour + half_hour + half_minute)
  311. self.assertEqual(offset_fraction, 1000)
  312. (*_, offset), _, offset_fraction = _strptime._strptime("Z", "%z")
  313. self.assertEqual(offset, 0)
  314. self.assertEqual(offset_fraction, 0)
  315. def test_bad_offset(self):
  316. with self.assertRaises(ValueError):
  317. _strptime._strptime("-01:30:30.", "%z")
  318. with self.assertRaises(ValueError):
  319. _strptime._strptime("-0130:30", "%z")
  320. with self.assertRaises(ValueError):
  321. _strptime._strptime("-01:30:30.1234567", "%z")
  322. with self.assertRaises(ValueError):
  323. _strptime._strptime("-01:30:30:123456", "%z")
  324. with self.assertRaises(ValueError) as err:
  325. _strptime._strptime("-01:3030", "%z")
  326. self.assertEqual("Inconsistent use of : in -01:3030", str(err.exception))
  327. @skip_if_buggy_ucrt_strfptime
  328. @unittest.skipIf(
  329. support.is_emscripten, "musl libc issue on Emscripten, bpo-46390"
  330. )
  331. def test_timezone(self):
  332. # Test timezone directives.
  333. # When gmtime() is used with %Z, entire result of strftime() is empty.
  334. # Check for equal timezone names deals with bad locale info when this
  335. # occurs; first found in FreeBSD 4.4.
  336. strp_output = _strptime._strptime_time("UTC", "%Z")
  337. self.assertEqual(strp_output.tm_isdst, 0)
  338. strp_output = _strptime._strptime_time("GMT", "%Z")
  339. self.assertEqual(strp_output.tm_isdst, 0)
  340. time_tuple = time.localtime()
  341. strf_output = time.strftime("%Z") #UTC does not have a timezone
  342. strp_output = _strptime._strptime_time(strf_output, "%Z")
  343. locale_time = _strptime.LocaleTime()
  344. if time.tzname[0] != time.tzname[1] or not time.daylight:
  345. self.assertTrue(strp_output[8] == time_tuple[8],
  346. "timezone check failed; '%s' -> %s != %s" %
  347. (strf_output, strp_output[8], time_tuple[8]))
  348. else:
  349. self.assertTrue(strp_output[8] == -1,
  350. "LocaleTime().timezone has duplicate values and "
  351. "time.daylight but timezone value not set to -1")
  352. @unittest.skipUnless(
  353. hasattr(time, "tzset"), "time module has no attribute tzset"
  354. )
  355. def test_bad_timezone(self):
  356. # Explicitly test possibility of bad timezone;
  357. # when time.tzname[0] == time.tzname[1] and time.daylight
  358. tz_name = time.tzname[0]
  359. if tz_name.upper() in ("UTC", "GMT"):
  360. self.skipTest('need non-UTC/GMT timezone')
  361. with support.swap_attr(time, 'tzname', (tz_name, tz_name)), \
  362. support.swap_attr(time, 'daylight', 1), \
  363. support.swap_attr(time, 'tzset', lambda: None):
  364. time.tzname = (tz_name, tz_name)
  365. time.daylight = 1
  366. tz_value = _strptime._strptime_time(tz_name, "%Z")[8]
  367. self.assertEqual(tz_value, -1,
  368. "%s lead to a timezone value of %s instead of -1 when "
  369. "time.daylight set to %s and passing in %s" %
  370. (time.tzname, tz_value, time.daylight, tz_name))
  371. def test_date_time(self):
  372. # Test %c directive
  373. for position in range(6):
  374. self.helper('c', position)
  375. def test_date(self):
  376. # Test %x directive
  377. for position in range(0,3):
  378. self.helper('x', position)
  379. def test_time(self):
  380. # Test %X directive
  381. for position in range(3,6):
  382. self.helper('X', position)
  383. def test_percent(self):
  384. # Make sure % signs are handled properly
  385. strf_output = time.strftime("%m %% %Y", self.time_tuple)
  386. strp_output = _strptime._strptime_time(strf_output, "%m %% %Y")
  387. self.assertTrue(strp_output[0] == self.time_tuple[0] and
  388. strp_output[1] == self.time_tuple[1],
  389. "handling of percent sign failed")
  390. def test_caseinsensitive(self):
  391. # Should handle names case-insensitively.
  392. strf_output = time.strftime("%B", self.time_tuple)
  393. self.assertTrue(_strptime._strptime_time(strf_output.upper(), "%B"),
  394. "strptime does not handle ALL-CAPS names properly")
  395. self.assertTrue(_strptime._strptime_time(strf_output.lower(), "%B"),
  396. "strptime does not handle lowercase names properly")
  397. self.assertTrue(_strptime._strptime_time(strf_output.capitalize(), "%B"),
  398. "strptime does not handle capword names properly")
  399. def test_defaults(self):
  400. # Default return value should be (1900, 1, 1, 0, 0, 0, 0, 1, 0)
  401. defaults = (1900, 1, 1, 0, 0, 0, 0, 1, -1)
  402. strp_output = _strptime._strptime_time('1', '%m')
  403. self.assertTrue(strp_output == defaults,
  404. "Default values for strptime() are incorrect;"
  405. " %s != %s" % (strp_output, defaults))
  406. def test_escaping(self):
  407. # Make sure all characters that have regex significance are escaped.
  408. # Parentheses are in a purposeful order; will cause an error of
  409. # unbalanced parentheses when the regex is compiled if they are not
  410. # escaped.
  411. # Test instigated by bug #796149 .
  412. need_escaping = r".^$*+?{}\[]|)("
  413. self.assertTrue(_strptime._strptime_time(need_escaping, need_escaping))
  414. def test_feb29_on_leap_year_without_year(self):
  415. time.strptime("Feb 29", "%b %d")
  416. def test_mar1_comes_after_feb29_even_when_omitting_the_year(self):
  417. self.assertLess(
  418. time.strptime("Feb 29", "%b %d"),
  419. time.strptime("Mar 1", "%b %d"))
  420. class Strptime12AMPMTests(unittest.TestCase):
  421. """Test a _strptime regression in '%I %p' at 12 noon (12 PM)"""
  422. def test_twelve_noon_midnight(self):
  423. eq = self.assertEqual
  424. eq(time.strptime('12 PM', '%I %p')[3], 12)
  425. eq(time.strptime('12 AM', '%I %p')[3], 0)
  426. eq(_strptime._strptime_time('12 PM', '%I %p')[3], 12)
  427. eq(_strptime._strptime_time('12 AM', '%I %p')[3], 0)
  428. class JulianTests(unittest.TestCase):
  429. """Test a _strptime regression that all julian (1-366) are accepted"""
  430. def test_all_julian_days(self):
  431. eq = self.assertEqual
  432. for i in range(1, 367):
  433. # use 2004, since it is a leap year, we have 366 days
  434. eq(_strptime._strptime_time('%d 2004' % i, '%j %Y')[7], i)
  435. class CalculationTests(unittest.TestCase):
  436. """Test that strptime() fills in missing info correctly"""
  437. def setUp(self):
  438. self.time_tuple = time.gmtime()
  439. @skip_if_buggy_ucrt_strfptime
  440. def test_julian_calculation(self):
  441. # Make sure that when Julian is missing that it is calculated
  442. format_string = "%Y %m %d %H %M %S %w %Z"
  443. result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple),
  444. format_string)
  445. self.assertTrue(result.tm_yday == self.time_tuple.tm_yday,
  446. "Calculation of tm_yday failed; %s != %s" %
  447. (result.tm_yday, self.time_tuple.tm_yday))
  448. @skip_if_buggy_ucrt_strfptime
  449. def test_gregorian_calculation(self):
  450. # Test that Gregorian date can be calculated from Julian day
  451. format_string = "%Y %H %M %S %w %j %Z"
  452. result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple),
  453. format_string)
  454. self.assertTrue(result.tm_year == self.time_tuple.tm_year and
  455. result.tm_mon == self.time_tuple.tm_mon and
  456. result.tm_mday == self.time_tuple.tm_mday,
  457. "Calculation of Gregorian date failed; "
  458. "%s-%s-%s != %s-%s-%s" %
  459. (result.tm_year, result.tm_mon, result.tm_mday,
  460. self.time_tuple.tm_year, self.time_tuple.tm_mon,
  461. self.time_tuple.tm_mday))
  462. @skip_if_buggy_ucrt_strfptime
  463. def test_day_of_week_calculation(self):
  464. # Test that the day of the week is calculated as needed
  465. format_string = "%Y %m %d %H %S %j %Z"
  466. result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple),
  467. format_string)
  468. self.assertTrue(result.tm_wday == self.time_tuple.tm_wday,
  469. "Calculation of day of the week failed; "
  470. "%s != %s" % (result.tm_wday, self.time_tuple.tm_wday))
  471. if support.is_android:
  472. # Issue #26929: strftime() on Android incorrectly formats %V or %G for
  473. # the last or the first incomplete week in a year.
  474. _ymd_excluded = ((1905, 1, 1), (1906, 12, 31), (2008, 12, 29),
  475. (1917, 12, 31))
  476. _formats_excluded = ('%G %V',)
  477. else:
  478. _ymd_excluded = ()
  479. _formats_excluded = ()
  480. @unittest.skipIf(sys.platform.startswith('aix'),
  481. 'bpo-29972: broken test on AIX')
  482. def test_week_of_year_and_day_of_week_calculation(self):
  483. # Should be able to infer date if given year, week of year (%U or %W)
  484. # and day of the week
  485. def test_helper(ymd_tuple, test_reason):
  486. for year_week_format in ('%Y %W', '%Y %U', '%G %V'):
  487. if (year_week_format in self._formats_excluded and
  488. ymd_tuple in self._ymd_excluded):
  489. return
  490. for weekday_format in ('%w', '%u', '%a', '%A'):
  491. format_string = year_week_format + ' ' + weekday_format
  492. with self.subTest(test_reason,
  493. date=ymd_tuple,
  494. format=format_string):
  495. dt_date = datetime_date(*ymd_tuple)
  496. strp_input = dt_date.strftime(format_string)
  497. strp_output = _strptime._strptime_time(strp_input,
  498. format_string)
  499. msg = "%r: %s != %s" % (strp_input,
  500. strp_output[7],
  501. dt_date.timetuple()[7])
  502. self.assertEqual(strp_output[:3], ymd_tuple, msg)
  503. test_helper((1901, 1, 3), "week 0")
  504. test_helper((1901, 1, 8), "common case")
  505. test_helper((1901, 1, 13), "day on Sunday")
  506. test_helper((1901, 1, 14), "day on Monday")
  507. test_helper((1905, 1, 1), "Jan 1 on Sunday")
  508. test_helper((1906, 1, 1), "Jan 1 on Monday")
  509. test_helper((1906, 1, 7), "first Sunday in a year starting on Monday")
  510. test_helper((1905, 12, 31), "Dec 31 on Sunday")
  511. test_helper((1906, 12, 31), "Dec 31 on Monday")
  512. test_helper((2008, 12, 29), "Monday in the last week of the year")
  513. test_helper((2008, 12, 22), "Monday in the second-to-last week of the "
  514. "year")
  515. test_helper((1978, 10, 23), "randomly chosen date")
  516. test_helper((2004, 12, 18), "randomly chosen date")
  517. test_helper((1978, 10, 23), "year starting and ending on Monday while "
  518. "date not on Sunday or Monday")
  519. test_helper((1917, 12, 17), "year starting and ending on Monday with "
  520. "a Monday not at the beginning or end "
  521. "of the year")
  522. test_helper((1917, 12, 31), "Dec 31 on Monday with year starting and "
  523. "ending on Monday")
  524. test_helper((2007, 1, 7), "First Sunday of 2007")
  525. test_helper((2007, 1, 14), "Second Sunday of 2007")
  526. test_helper((2006, 12, 31), "Last Sunday of 2006")
  527. test_helper((2006, 12, 24), "Second to last Sunday of 2006")
  528. def test_week_0(self):
  529. def check(value, format, *expected):
  530. self.assertEqual(_strptime._strptime_time(value, format)[:-1], expected)
  531. check('2015 0 0', '%Y %U %w', 2014, 12, 28, 0, 0, 0, 6, 362)
  532. check('2015 0 0', '%Y %W %w', 2015, 1, 4, 0, 0, 0, 6, 4)
  533. check('2015 1 1', '%G %V %u', 2014, 12, 29, 0, 0, 0, 0, 363)
  534. check('2015 0 1', '%Y %U %w', 2014, 12, 29, 0, 0, 0, 0, 363)
  535. check('2015 0 1', '%Y %W %w', 2014, 12, 29, 0, 0, 0, 0, 363)
  536. check('2015 1 2', '%G %V %u', 2014, 12, 30, 0, 0, 0, 1, 364)
  537. check('2015 0 2', '%Y %U %w', 2014, 12, 30, 0, 0, 0, 1, 364)
  538. check('2015 0 2', '%Y %W %w', 2014, 12, 30, 0, 0, 0, 1, 364)
  539. check('2015 1 3', '%G %V %u', 2014, 12, 31, 0, 0, 0, 2, 365)
  540. check('2015 0 3', '%Y %U %w', 2014, 12, 31, 0, 0, 0, 2, 365)
  541. check('2015 0 3', '%Y %W %w', 2014, 12, 31, 0, 0, 0, 2, 365)
  542. check('2015 1 4', '%G %V %u', 2015, 1, 1, 0, 0, 0, 3, 1)
  543. check('2015 0 4', '%Y %U %w', 2015, 1, 1, 0, 0, 0, 3, 1)
  544. check('2015 0 4', '%Y %W %w', 2015, 1, 1, 0, 0, 0, 3, 1)
  545. check('2015 1 5', '%G %V %u', 2015, 1, 2, 0, 0, 0, 4, 2)
  546. check('2015 0 5', '%Y %U %w', 2015, 1, 2, 0, 0, 0, 4, 2)
  547. check('2015 0 5', '%Y %W %w', 2015, 1, 2, 0, 0, 0, 4, 2)
  548. check('2015 1 6', '%G %V %u', 2015, 1, 3, 0, 0, 0, 5, 3)
  549. check('2015 0 6', '%Y %U %w', 2015, 1, 3, 0, 0, 0, 5, 3)
  550. check('2015 0 6', '%Y %W %w', 2015, 1, 3, 0, 0, 0, 5, 3)
  551. check('2015 1 7', '%G %V %u', 2015, 1, 4, 0, 0, 0, 6, 4)
  552. check('2009 0 0', '%Y %U %w', 2008, 12, 28, 0, 0, 0, 6, 363)
  553. check('2009 0 0', '%Y %W %w', 2009, 1, 4, 0, 0, 0, 6, 4)
  554. check('2009 1 1', '%G %V %u', 2008, 12, 29, 0, 0, 0, 0, 364)
  555. check('2009 0 1', '%Y %U %w', 2008, 12, 29, 0, 0, 0, 0, 364)
  556. check('2009 0 1', '%Y %W %w', 2008, 12, 29, 0, 0, 0, 0, 364)
  557. check('2009 1 2', '%G %V %u', 2008, 12, 30, 0, 0, 0, 1, 365)
  558. check('2009 0 2', '%Y %U %w', 2008, 12, 30, 0, 0, 0, 1, 365)
  559. check('2009 0 2', '%Y %W %w', 2008, 12, 30, 0, 0, 0, 1, 365)
  560. check('2009 1 3', '%G %V %u', 2008, 12, 31, 0, 0, 0, 2, 366)
  561. check('2009 0 3', '%Y %U %w', 2008, 12, 31, 0, 0, 0, 2, 366)
  562. check('2009 0 3', '%Y %W %w', 2008, 12, 31, 0, 0, 0, 2, 366)
  563. check('2009 1 4', '%G %V %u', 2009, 1, 1, 0, 0, 0, 3, 1)
  564. check('2009 0 4', '%Y %U %w', 2009, 1, 1, 0, 0, 0, 3, 1)
  565. check('2009 0 4', '%Y %W %w', 2009, 1, 1, 0, 0, 0, 3, 1)
  566. check('2009 1 5', '%G %V %u', 2009, 1, 2, 0, 0, 0, 4, 2)
  567. check('2009 0 5', '%Y %U %w', 2009, 1, 2, 0, 0, 0, 4, 2)
  568. check('2009 0 5', '%Y %W %w', 2009, 1, 2, 0, 0, 0, 4, 2)
  569. check('2009 1 6', '%G %V %u', 2009, 1, 3, 0, 0, 0, 5, 3)
  570. check('2009 0 6', '%Y %U %w', 2009, 1, 3, 0, 0, 0, 5, 3)
  571. check('2009 0 6', '%Y %W %w', 2009, 1, 3, 0, 0, 0, 5, 3)
  572. check('2009 1 7', '%G %V %u', 2009, 1, 4, 0, 0, 0, 6, 4)
  573. class CacheTests(unittest.TestCase):
  574. """Test that caching works properly."""
  575. def test_time_re_recreation(self):
  576. # Make sure cache is recreated when current locale does not match what
  577. # cached object was created with.
  578. _strptime._strptime_time("10", "%d")
  579. _strptime._strptime_time("2005", "%Y")
  580. _strptime._TimeRE_cache.locale_time.lang = "Ni"
  581. original_time_re = _strptime._TimeRE_cache
  582. _strptime._strptime_time("10", "%d")
  583. self.assertIsNot(original_time_re, _strptime._TimeRE_cache)
  584. self.assertEqual(len(_strptime._regex_cache), 1)
  585. def test_regex_cleanup(self):
  586. # Make sure cached regexes are discarded when cache becomes "full".
  587. try:
  588. del _strptime._regex_cache['%d']
  589. except KeyError:
  590. pass
  591. bogus_key = 0
  592. while len(_strptime._regex_cache) <= _strptime._CACHE_MAX_SIZE:
  593. _strptime._regex_cache[bogus_key] = None
  594. bogus_key += 1
  595. _strptime._strptime_time("10", "%d")
  596. self.assertEqual(len(_strptime._regex_cache), 1)
  597. def test_new_localetime(self):
  598. # A new LocaleTime instance should be created when a new TimeRE object
  599. # is created.
  600. locale_time_id = _strptime._TimeRE_cache.locale_time
  601. _strptime._TimeRE_cache.locale_time.lang = "Ni"
  602. _strptime._strptime_time("10", "%d")
  603. self.assertIsNot(locale_time_id, _strptime._TimeRE_cache.locale_time)
  604. def test_TimeRE_recreation_locale(self):
  605. # The TimeRE instance should be recreated upon changing the locale.
  606. locale_info = locale.getlocale(locale.LC_TIME)
  607. try:
  608. locale.setlocale(locale.LC_TIME, ('en_US', 'UTF8'))
  609. except locale.Error:
  610. self.skipTest('test needs en_US.UTF8 locale')
  611. try:
  612. _strptime._strptime_time('10', '%d')
  613. # Get id of current cache object.
  614. first_time_re = _strptime._TimeRE_cache
  615. try:
  616. # Change the locale and force a recreation of the cache.
  617. locale.setlocale(locale.LC_TIME, ('de_DE', 'UTF8'))
  618. _strptime._strptime_time('10', '%d')
  619. # Get the new cache object's id.
  620. second_time_re = _strptime._TimeRE_cache
  621. # They should not be equal.
  622. self.assertIsNot(first_time_re, second_time_re)
  623. # Possible test locale is not supported while initial locale is.
  624. # If this is the case just suppress the exception and fall-through
  625. # to the resetting to the original locale.
  626. except locale.Error:
  627. self.skipTest('test needs de_DE.UTF8 locale')
  628. # Make sure we don't trample on the locale setting once we leave the
  629. # test.
  630. finally:
  631. locale.setlocale(locale.LC_TIME, locale_info)
  632. @support.run_with_tz('STD-1DST,M4.1.0,M10.1.0')
  633. def test_TimeRE_recreation_timezone(self):
  634. # The TimeRE instance should be recreated upon changing the timezone.
  635. oldtzname = time.tzname
  636. tm = _strptime._strptime_time(time.tzname[0], '%Z')
  637. self.assertEqual(tm.tm_isdst, 0)
  638. tm = _strptime._strptime_time(time.tzname[1], '%Z')
  639. self.assertEqual(tm.tm_isdst, 1)
  640. # Get id of current cache object.
  641. first_time_re = _strptime._TimeRE_cache
  642. # Change the timezone and force a recreation of the cache.
  643. os.environ['TZ'] = 'EST+05EDT,M3.2.0,M11.1.0'
  644. time.tzset()
  645. tm = _strptime._strptime_time(time.tzname[0], '%Z')
  646. self.assertEqual(tm.tm_isdst, 0)
  647. tm = _strptime._strptime_time(time.tzname[1], '%Z')
  648. self.assertEqual(tm.tm_isdst, 1)
  649. # Get the new cache object's id.
  650. second_time_re = _strptime._TimeRE_cache
  651. # They should not be equal.
  652. self.assertIsNot(first_time_re, second_time_re)
  653. # Make sure old names no longer accepted.
  654. with self.assertRaises(ValueError):
  655. _strptime._strptime_time(oldtzname[0], '%Z')
  656. with self.assertRaises(ValueError):
  657. _strptime._strptime_time(oldtzname[1], '%Z')
  658. if __name__ == '__main__':
  659. unittest.main()