summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorSkip Montanaro <skip@pobox.com>2008-03-15 16:04:45 (GMT)
committerSkip Montanaro <skip@pobox.com>2008-03-15 16:04:45 (GMT)
commitfc070d27316ebb0ed877fade811130ac566bbc14 (patch)
tree67a02d38e9c09a5fd96cd383b7259d9f91a074b9 /Lib
parent75e51680f1902d714a4f33efa3d857df9e7e59eb (diff)
downloadcpython-fc070d27316ebb0ed877fade811130ac566bbc14.zip
cpython-fc070d27316ebb0ed877fade811130ac566bbc14.tar.gz
cpython-fc070d27316ebb0ed877fade811130ac566bbc14.tar.bz2
add %f format to datetime - issue 1158
Diffstat (limited to 'Lib')
-rw-r--r--Lib/_strptime.py21
-rw-r--r--Lib/test/test_datetime.py17
-rw-r--r--Lib/test/test_strptime.py66
3 files changed, 60 insertions, 44 deletions
diff --git a/Lib/_strptime.py b/Lib/_strptime.py
index 166cf82..d9563b9 100644
--- a/Lib/_strptime.py
+++ b/Lib/_strptime.py
@@ -22,7 +22,7 @@ try:
except:
from dummy_thread import allocate_lock as _thread_allocate_lock
-__all__ = ['strptime']
+__all__ = []
def _getlang():
# Figure out what the current language is set to.
@@ -190,6 +190,7 @@ class TimeRE(dict):
base.__init__({
# The " \d" part of the regex is to make %c from ANSI C work
'd': r"(?P<d>3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])",
+ 'f': r"(?P<f>[0-9]{1,6})",
'H': r"(?P<H>2[0-3]|[0-1]\d|\d)",
'I': r"(?P<I>1[0-2]|0[1-9]|[1-9])",
'j': r"(?P<j>36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|[1-9]\d|0[1-9]|[1-9])",
@@ -291,7 +292,7 @@ def _calc_julian_from_U_or_W(year, week_of_year, day_of_week, week_starts_Mon):
return 1 + days_to_week + day_of_week
-def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
+def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a time struct based on the input string and the format string."""
global _TimeRE_cache, _regex_cache
with _cache_lock:
@@ -327,7 +328,7 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
data_string[found.end():])
year = 1900
month = day = 1
- hour = minute = second = 0
+ hour = minute = second = fraction = 0
tz = -1
# Default to -1 to signify that values not known; not critical to have,
# though
@@ -384,6 +385,11 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
minute = int(found_dict['M'])
elif group_key == 'S':
second = int(found_dict['S'])
+ elif group_key == 'f':
+ s = found_dict['f']
+ # Pad to always return microseconds.
+ s += "0" * (6 - len(s))
+ fraction = int(s)
elif group_key == 'A':
weekday = locale_time.f_weekday.index(found_dict['A'].lower())
elif group_key == 'a':
@@ -440,6 +446,9 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
day = datetime_result.day
if weekday == -1:
weekday = datetime_date(year, month, day).weekday()
- return time.struct_time((year, month, day,
- hour, minute, second,
- weekday, julian, tz))
+ return (time.struct_time((year, month, day,
+ hour, minute, second,
+ weekday, julian, tz)), fraction)
+
+def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"):
+ return _strptime(data_string, format)[0]
diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py
index ee104e4..84f45b4 100644
--- a/Lib/test/test_datetime.py
+++ b/Lib/test/test_datetime.py
@@ -1507,11 +1507,12 @@ class TestDateTime(TestDate):
self.failUnless(abs(from_timestamp - from_now) <= tolerance)
def test_strptime(self):
- import time
+ import _strptime
- string = '2004-12-01 13:02:47'
- format = '%Y-%m-%d %H:%M:%S'
- expected = self.theclass(*(time.strptime(string, format)[0:6]))
+ string = '2004-12-01 13:02:47.197'
+ format = '%Y-%m-%d %H:%M:%S.%f'
+ result, frac = _strptime._strptime(string, format)
+ expected = self.theclass(*(result[0:6]+(frac,)))
got = self.theclass.strptime(string, format)
self.assertEqual(expected, got)
@@ -1539,9 +1540,9 @@ class TestDateTime(TestDate):
def test_more_strftime(self):
# This tests fields beyond those tested by the TestDate.test_strftime.
- t = self.theclass(2004, 12, 31, 6, 22, 33)
- self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
- "12 31 04 33 22 06 366")
+ t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
+ self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
+ "12 31 04 000047 33 22 06 366")
def test_extract(self):
dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
@@ -1814,7 +1815,7 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase):
def test_strftime(self):
t = self.theclass(1, 2, 3, 4)
- self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
+ self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
# A naive object replaces %z and %Z with empty strings.
self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py
index 92c722a..2aa4bc9 100644
--- a/Lib/test/test_strptime.py
+++ b/Lib/test/test_strptime.py
@@ -208,11 +208,11 @@ class StrptimeTests(unittest.TestCase):
def test_ValueError(self):
# Make sure ValueError is raised when match fails or format is bad
- self.assertRaises(ValueError, _strptime.strptime, data_string="%d",
+ self.assertRaises(ValueError, _strptime._strptime_time, data_string="%d",
format="%A")
for bad_format in ("%", "% ", "%e"):
try:
- _strptime.strptime("2005", bad_format)
+ _strptime._strptime_time("2005", bad_format)
except ValueError:
continue
except Exception, err:
@@ -223,12 +223,12 @@ class StrptimeTests(unittest.TestCase):
def test_unconverteddata(self):
# Check ValueError is raised when there is unconverted data
- self.assertRaises(ValueError, _strptime.strptime, "10 12", "%m")
+ self.assertRaises(ValueError, _strptime._strptime_time, "10 12", "%m")
def helper(self, directive, position):
"""Helper fxn in testing."""
strf_output = time.strftime("%" + directive, self.time_tuple)
- strp_output = _strptime.strptime(strf_output, "%" + directive)
+ strp_output = _strptime._strptime_time(strf_output, "%" + directive)
self.failUnless(strp_output[position] == self.time_tuple[position],
"testing of '%s' directive failed; '%s' -> %s != %s" %
(directive, strf_output, strp_output[position],
@@ -241,7 +241,7 @@ class StrptimeTests(unittest.TestCase):
# Must also make sure %y values are correct for bounds set by Open Group
for century, bounds in ((1900, ('69', '99')), (2000, ('00', '68'))):
for bound in bounds:
- strp_output = _strptime.strptime(bound, '%y')
+ strp_output = _strptime._strptime_time(bound, '%y')
expected_result = century + int(bound)
self.failUnless(strp_output[0] == expected_result,
"'y' test failed; passed in '%s' "
@@ -260,7 +260,7 @@ class StrptimeTests(unittest.TestCase):
# Test hour directives
self.helper('H', 3)
strf_output = time.strftime("%I %p", self.time_tuple)
- strp_output = _strptime.strptime(strf_output, "%I %p")
+ strp_output = _strptime._strptime_time(strf_output, "%I %p")
self.failUnless(strp_output[3] == self.time_tuple[3],
"testing of '%%I %%p' directive failed; '%s' -> %s != %s" %
(strf_output, strp_output[3], self.time_tuple[3]))
@@ -273,6 +273,12 @@ class StrptimeTests(unittest.TestCase):
# Test second directives
self.helper('S', 5)
+ def test_fraction(self):
+ import datetime
+ now = datetime.datetime.now()
+ tup, frac = _strptime._strptime(str(now), format="%Y-%m-%d %H:%M:%S.%f")
+ self.assertEqual(frac, now.microsecond)
+
def test_weekday(self):
# Test weekday directives
for directive in ('A', 'a', 'w'):
@@ -287,16 +293,16 @@ class StrptimeTests(unittest.TestCase):
# When gmtime() is used with %Z, entire result of strftime() is empty.
# Check for equal timezone names deals with bad locale info when this
# occurs; first found in FreeBSD 4.4.
- strp_output = _strptime.strptime("UTC", "%Z")
+ strp_output = _strptime._strptime_time("UTC", "%Z")
self.failUnlessEqual(strp_output.tm_isdst, 0)
- strp_output = _strptime.strptime("GMT", "%Z")
+ strp_output = _strptime._strptime_time("GMT", "%Z")
self.failUnlessEqual(strp_output.tm_isdst, 0)
if sys.platform == "mac":
# Timezones don't really work on MacOS9
return
time_tuple = time.localtime()
strf_output = time.strftime("%Z") #UTC does not have a timezone
- strp_output = _strptime.strptime(strf_output, "%Z")
+ strp_output = _strptime._strptime_time(strf_output, "%Z")
locale_time = _strptime.LocaleTime()
if time.tzname[0] != time.tzname[1] or not time.daylight:
self.failUnless(strp_output[8] == time_tuple[8],
@@ -320,7 +326,7 @@ class StrptimeTests(unittest.TestCase):
original_daylight = time.daylight
time.tzname = (tz_name, tz_name)
time.daylight = 1
- tz_value = _strptime.strptime(tz_name, "%Z")[8]
+ tz_value = _strptime._strptime_time(tz_name, "%Z")[8]
self.failUnlessEqual(tz_value, -1,
"%s lead to a timezone value of %s instead of -1 when "
"time.daylight set to %s and passing in %s" %
@@ -347,7 +353,7 @@ class StrptimeTests(unittest.TestCase):
def test_percent(self):
# Make sure % signs are handled properly
strf_output = time.strftime("%m %% %Y", self.time_tuple)
- strp_output = _strptime.strptime(strf_output, "%m %% %Y")
+ strp_output = _strptime._strptime_time(strf_output, "%m %% %Y")
self.failUnless(strp_output[0] == self.time_tuple[0] and
strp_output[1] == self.time_tuple[1],
"handling of percent sign failed")
@@ -355,17 +361,17 @@ class StrptimeTests(unittest.TestCase):
def test_caseinsensitive(self):
# Should handle names case-insensitively.
strf_output = time.strftime("%B", self.time_tuple)
- self.failUnless(_strptime.strptime(strf_output.upper(), "%B"),
+ self.failUnless(_strptime._strptime_time(strf_output.upper(), "%B"),
"strptime does not handle ALL-CAPS names properly")
- self.failUnless(_strptime.strptime(strf_output.lower(), "%B"),
+ self.failUnless(_strptime._strptime_time(strf_output.lower(), "%B"),
"strptime does not handle lowercase names properly")
- self.failUnless(_strptime.strptime(strf_output.capitalize(), "%B"),
+ self.failUnless(_strptime._strptime_time(strf_output.capitalize(), "%B"),
"strptime does not handle capword names properly")
def test_defaults(self):
# Default return value should be (1900, 1, 1, 0, 0, 0, 0, 1, 0)
defaults = (1900, 1, 1, 0, 0, 0, 0, 1, -1)
- strp_output = _strptime.strptime('1', '%m')
+ strp_output = _strptime._strptime_time('1', '%m')
self.failUnless(strp_output == defaults,
"Default values for strptime() are incorrect;"
" %s != %s" % (strp_output, defaults))
@@ -377,7 +383,7 @@ class StrptimeTests(unittest.TestCase):
# escaped.
# Test instigated by bug #796149 .
need_escaping = ".^$*+?{}\[]|)("
- self.failUnless(_strptime.strptime(need_escaping, need_escaping))
+ self.failUnless(_strptime._strptime_time(need_escaping, need_escaping))
class Strptime12AMPMTests(unittest.TestCase):
"""Test a _strptime regression in '%I %p' at 12 noon (12 PM)"""
@@ -386,8 +392,8 @@ class Strptime12AMPMTests(unittest.TestCase):
eq = self.assertEqual
eq(time.strptime('12 PM', '%I %p')[3], 12)
eq(time.strptime('12 AM', '%I %p')[3], 0)
- eq(_strptime.strptime('12 PM', '%I %p')[3], 12)
- eq(_strptime.strptime('12 AM', '%I %p')[3], 0)
+ eq(_strptime._strptime_time('12 PM', '%I %p')[3], 12)
+ eq(_strptime._strptime_time('12 AM', '%I %p')[3], 0)
class JulianTests(unittest.TestCase):
@@ -397,7 +403,7 @@ class JulianTests(unittest.TestCase):
eq = self.assertEqual
for i in range(1, 367):
# use 2004, since it is a leap year, we have 366 days
- eq(_strptime.strptime('%d 2004' % i, '%j %Y')[7], i)
+ eq(_strptime._strptime_time('%d 2004' % i, '%j %Y')[7], i)
class CalculationTests(unittest.TestCase):
"""Test that strptime() fills in missing info correctly"""
@@ -408,7 +414,7 @@ class CalculationTests(unittest.TestCase):
def test_julian_calculation(self):
# Make sure that when Julian is missing that it is calculated
format_string = "%Y %m %d %H %M %S %w %Z"
- result = _strptime.strptime(time.strftime(format_string, self.time_tuple),
+ result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple),
format_string)
self.failUnless(result.tm_yday == self.time_tuple.tm_yday,
"Calculation of tm_yday failed; %s != %s" %
@@ -417,7 +423,7 @@ class CalculationTests(unittest.TestCase):
def test_gregorian_calculation(self):
# Test that Gregorian date can be calculated from Julian day
format_string = "%Y %H %M %S %w %j %Z"
- result = _strptime.strptime(time.strftime(format_string, self.time_tuple),
+ result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple),
format_string)
self.failUnless(result.tm_year == self.time_tuple.tm_year and
result.tm_mon == self.time_tuple.tm_mon and
@@ -431,7 +437,7 @@ class CalculationTests(unittest.TestCase):
def test_day_of_week_calculation(self):
# Test that the day of the week is calculated as needed
format_string = "%Y %m %d %H %S %j %Z"
- result = _strptime.strptime(time.strftime(format_string, self.time_tuple),
+ result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple),
format_string)
self.failUnless(result.tm_wday == self.time_tuple.tm_wday,
"Calculation of day of the week failed;"
@@ -445,7 +451,7 @@ class CalculationTests(unittest.TestCase):
format_string = "%%Y %%%s %%w" % directive
dt_date = datetime_date(*ymd_tuple)
strp_input = dt_date.strftime(format_string)
- strp_output = _strptime.strptime(strp_input, format_string)
+ strp_output = _strptime._strptime_time(strp_input, format_string)
self.failUnless(strp_output[:3] == ymd_tuple,
"%s(%s) test failed w/ '%s': %s != %s (%s != %s)" %
(test_reason, directive, strp_input,
@@ -484,11 +490,11 @@ class CacheTests(unittest.TestCase):
def test_time_re_recreation(self):
# Make sure cache is recreated when current locale does not match what
# cached object was created with.
- _strptime.strptime("10", "%d")
- _strptime.strptime("2005", "%Y")
+ _strptime._strptime_time("10", "%d")
+ _strptime._strptime_time("2005", "%Y")
_strptime._TimeRE_cache.locale_time.lang = "Ni"
original_time_re = id(_strptime._TimeRE_cache)
- _strptime.strptime("10", "%d")
+ _strptime._strptime_time("10", "%d")
self.failIfEqual(original_time_re, id(_strptime._TimeRE_cache))
self.failUnlessEqual(len(_strptime._regex_cache), 1)
@@ -502,7 +508,7 @@ class CacheTests(unittest.TestCase):
while len(_strptime._regex_cache) <= _strptime._CACHE_MAX_SIZE:
_strptime._regex_cache[bogus_key] = None
bogus_key += 1
- _strptime.strptime("10", "%d")
+ _strptime._strptime_time("10", "%d")
self.failUnlessEqual(len(_strptime._regex_cache), 1)
def test_new_localetime(self):
@@ -510,7 +516,7 @@ class CacheTests(unittest.TestCase):
# is created.
locale_time_id = id(_strptime._TimeRE_cache.locale_time)
_strptime._TimeRE_cache.locale_time.lang = "Ni"
- _strptime.strptime("10", "%d")
+ _strptime._strptime_time("10", "%d")
self.failIfEqual(locale_time_id,
id(_strptime._TimeRE_cache.locale_time))
@@ -522,13 +528,13 @@ class CacheTests(unittest.TestCase):
except locale.Error:
return
try:
- _strptime.strptime('10', '%d')
+ _strptime._strptime_time('10', '%d')
# Get id of current cache object.
first_time_re_id = id(_strptime._TimeRE_cache)
try:
# Change the locale and force a recreation of the cache.
locale.setlocale(locale.LC_TIME, ('de_DE', 'UTF8'))
- _strptime.strptime('10', '%d')
+ _strptime._strptime_time('10', '%d')
# Get the new cache object's id.
second_time_re_id = id(_strptime._TimeRE_cache)
# They should not be equal.