------------------------------------------------------------------------ r11771 | jomae | 2013-04-10 13:27:58 +0200 (Wed, 10 Apr 2013) | 2 lines 1.0.2dev: improved formatting iso8601 using `datetime.isoformat()` even if before 1900 Index: trac/util/datefmt.py =================================================================== --- trac/util/datefmt.py (revision 11770) +++ trac/util/datefmt.py (revision 11771) @@ -158,51 +158,45 @@ 'date': {'short': '%x', 'medium': '%x', 'long': '%x', 'full': '%x'}, 'time': {'short': '%H:%M', 'medium': '%X', 'long': '%X', 'full': '%X'}, } -_ISO8601_FORMATS = { - 'datetime': { - '%x %X': 'iso8601', '%x': 'iso8601date', '%X': 'iso8601time', - 'short': '%Y-%m-%dT%H:%M', 'medium': '%Y-%m-%dT%H:%M:%S', - 'long': 'iso8601', 'full': 'iso8601', - 'iso8601': 'iso8601', None: 'iso8601'}, - 'date': { - '%x %X': 'iso8601', '%x': 'iso8601date', '%X': 'iso8601time', - 'short': 'iso8601date', 'medium': 'iso8601date', - 'long': 'iso8601date', 'full': 'iso8601date', - 'iso8601': 'iso8601date', None: 'iso8601date'}, - 'time': { - '%x %X': 'iso8601', '%x': 'iso8601date', '%X': 'iso8601time', - 'short': '%H:%M', 'medium': '%H:%M:%S', - 'long': 'iso8601time', 'full': 'iso8601time', - 'iso8601': 'iso8601time', None: 'iso8601time'}, -} _STRFTIME_HINTS = {'%x %X': 'datetime', '%x': 'date', '%X': 'time'} def _format_datetime_without_babel(t, format): - normalize_Z = False - if format.lower().startswith('iso8601'): - if 'date' in format: - format = '%Y-%m-%d' - elif 'time' in format: - format = '%H:%M:%S%z' - normalize_Z = True - else: - format = '%Y-%m-%dT%H:%M:%S%z' - normalize_Z = True text = t.strftime(str(format)) - if normalize_Z: - text = text.replace('+0000', 'Z') - if not text.endswith('Z'): - text = text[:-2] + ":" + text[-2:] encoding = getlocale(LC_TIME)[1] or getpreferredencoding() \ or sys.getdefaultencoding() return unicode(text, encoding, 'replace') +def _format_datetime_iso8601(t, format, hint): + if format != 'full': + t = t.replace(microsecond=0) + text = t.isoformat() # YYYY-MM-DDThh:mm:ss.SSSSSSĀ±hh:mm + if format == 'short': + text = text[:16] # YYYY-MM-DDThh:mm + elif format == 'medium': + text = text[:19] # YYYY-MM-DDThh:mm:ss + elif text.endswith('+00:00'): + text = text[:-6] + 'Z' + if hint == 'date': + text = text.split('T', 1)[0] + elif hint == 'time': + text = text.split('T', 1)[1] + return unicode(text, 'ascii') + def _format_datetime(t, format, tzinfo, locale, hint): t = to_datetime(t, tzinfo or localtz) - if (format in ('iso8601', 'iso8601date', 'iso8601time') or - locale == 'iso8601'): - format = _ISO8601_FORMATS[hint].get(format, format) + if format == 'iso8601': + return _format_datetime_iso8601(t, 'long', hint) + if format in ('iso8601date', 'iso8601time'): + return _format_datetime_iso8601(t, 'long', format[7:]) + if locale == 'iso8601': + if format is None: + format = 'long' + elif format in _STRFTIME_HINTS: + hint = _STRFTIME_HINTS[format] + format = 'long' + if format in ('short', 'medium', 'long', 'full'): + return _format_datetime_iso8601(t, format, hint) return _format_datetime_without_babel(t, format) if babel and locale: Index: trac/util/tests/datefmt.py =================================================================== --- trac/util/tests/datefmt.py (revision 11770) +++ trac/util/tests/datefmt.py (revision 11771) @@ -559,6 +559,21 @@ self.assertEqual(datefmt.format_time(t, 'iso8601', gmt01), expected.split('T')[1]) + def test_format_iso8601_before_1900(self): + t = datetime.datetime(1899, 12, 30, 23, 58, 59, 123456, datefmt.utc) + self.assertEqual('1899-12-30T23:58:59Z', + datefmt.format_datetime(t, 'iso8601', datefmt.utc)) + self.assertEqual('1899-12-30', + datefmt.format_datetime(t, 'iso8601date', + datefmt.utc)) + self.assertEqual('1899-12-30', + datefmt.format_date(t, 'iso8601', datefmt.utc)) + self.assertEqual('23:58:59Z', + datefmt.format_datetime(t, 'iso8601time', + datefmt.utc)) + self.assertEqual('23:58:59Z', + datefmt.format_time(t, 'iso8601', datefmt.utc)) + def test_format_date_accepts_date_instances(self): a_date = datetime.date(2009, 8, 20) self.assertEqual('2009-08-20', @@ -660,12 +675,38 @@ datefmt.format_time(t, 'medium', tz, 'iso8601')) self.assertEqual('2010-08-28T11:45:56', datefmt.format_datetime(t, 'medium', tz, 'iso8601')) - for f in ('long', 'full'): - self.assertEqual('11:45:56+02:00', - datefmt.format_time(t, f, tz, 'iso8601')) - self.assertEqual('2010-08-28T11:45:56+02:00', - datefmt.format_datetime(t, f, tz, 'iso8601')) + self.assertEqual('11:45:56+02:00', + datefmt.format_time(t, 'long', tz, 'iso8601')) + self.assertEqual('2010-08-28T11:45:56+02:00', + datefmt.format_datetime(t, 'long', tz, 'iso8601')) + self.assertEqual('11:45:56.123456+02:00', + datefmt.format_time(t, 'full', tz, 'iso8601')) + self.assertEqual('2010-08-28T11:45:56.123456+02:00', + datefmt.format_datetime(t, 'full', tz, 'iso8601')) + def test_with_babel_format_before_1900(self): + tz = datefmt.timezone('GMT +2:00') + t = datetime.datetime(1899, 8, 28, 11, 45, 56, 123456, tz) + for f in ('short', 'medium', 'long', 'full'): + self.assertEqual('1899-08-28', + datefmt.format_date(t, f, tz, 'iso8601')) + self.assertEqual('11:45', + datefmt.format_time(t, 'short', tz, 'iso8601')) + self.assertEqual('1899-08-28T11:45', + datefmt.format_datetime(t, 'short', tz, 'iso8601')) + self.assertEqual('11:45:56', + datefmt.format_time(t, 'medium', tz, 'iso8601')) + self.assertEqual('1899-08-28T11:45:56', + datefmt.format_datetime(t, 'medium', tz, 'iso8601')) + self.assertEqual('11:45:56+02:00', + datefmt.format_time(t, 'long', tz, 'iso8601')) + self.assertEqual('1899-08-28T11:45:56+02:00', + datefmt.format_datetime(t, 'long', tz, 'iso8601')) + self.assertEqual('11:45:56.123456+02:00', + datefmt.format_time(t, 'full', tz, 'iso8601')) + self.assertEqual('1899-08-28T11:45:56.123456+02:00', + datefmt.format_datetime(t, 'full', tz, 'iso8601')) + def test_hint(self): try: datefmt.parse_date('***', locale='iso8601', hint='date') ------------------------------------------------------------------------