summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorTW <tw@waldmann-edv.de>2022-08-28 21:27:42 (GMT)
committerGitHub <noreply@github.com>2022-08-28 21:27:42 (GMT)
commit023c51d9d8e2fd91069eea2bf12e373f1c71e9d2 (patch)
treecdd0d6e04e5788ee2fd54faa2dbb4fc6586d8337 /Lib
parente860e521ec0d84e175269aeb15cf24bd6053ad17 (diff)
downloadcpython-023c51d9d8e2fd91069eea2bf12e373f1c71e9d2.zip
cpython-023c51d9d8e2fd91069eea2bf12e373f1c71e9d2.tar.gz
cpython-023c51d9d8e2fd91069eea2bf12e373f1c71e9d2.tar.bz2
gh-69142: add %:z strftime format code (gh-95983)
datetime.isoformat generates the tzoffset with colons, but there was no format code to make strftime output the same format. for simplicity and consistency the %:z formatting behaves mostly as %z, with the exception of adding colons. this includes the dynamic behaviour of adding seconds and microseconds only when needed (when not 0). this fixes the still open "generate" part of this issue: https://github.com/python/cpython/issues/69142 Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Diffstat (limited to 'Lib')
-rw-r--r--Lib/datetime.py45
-rw-r--r--Lib/test/datetimetester.py21
2 files changed, 35 insertions, 31 deletions
diff --git a/Lib/datetime.py b/Lib/datetime.py
index 00ded32..007114a 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -179,7 +179,7 @@ def _format_time(hh, mm, ss, us, timespec='auto'):
else:
return fmt.format(hh, mm, ss, us)
-def _format_offset(off):
+def _format_offset(off, sep=':'):
s = ''
if off is not None:
if off.days < 0:
@@ -189,9 +189,9 @@ def _format_offset(off):
sign = "+"
hh, mm = divmod(off, timedelta(hours=1))
mm, ss = divmod(mm, timedelta(minutes=1))
- s += "%s%02d:%02d" % (sign, hh, mm)
+ s += "%s%02d%s%02d" % (sign, hh, sep, mm)
if ss or ss.microseconds:
- s += ":%02d" % ss.seconds
+ s += "%s%02d" % (sep, ss.seconds)
if ss.microseconds:
s += '.%06d' % ss.microseconds
@@ -202,9 +202,10 @@ def _wrap_strftime(object, format, timetuple):
# Don't call utcoffset() or tzname() unless actually needed.
freplace = None # the string to use for %f
zreplace = None # the string to use for %z
+ colonzreplace = None # the string to use for %:z
Zreplace = None # the string to use for %Z
- # Scan format for %z and %Z escapes, replacing as needed.
+ # Scan format for %z, %:z and %Z escapes, replacing as needed.
newformat = []
push = newformat.append
i, n = 0, len(format)
@@ -222,26 +223,28 @@ def _wrap_strftime(object, format, timetuple):
newformat.append(freplace)
elif ch == 'z':
if zreplace is None:
- zreplace = ""
if hasattr(object, "utcoffset"):
- offset = object.utcoffset()
- if offset is not None:
- sign = '+'
- if offset.days < 0:
- offset = -offset
- sign = '-'
- h, rest = divmod(offset, timedelta(hours=1))
- m, rest = divmod(rest, timedelta(minutes=1))
- s = rest.seconds
- u = offset.microseconds
- if u:
- zreplace = '%c%02d%02d%02d.%06d' % (sign, h, m, s, u)
- elif s:
- zreplace = '%c%02d%02d%02d' % (sign, h, m, s)
- else:
- zreplace = '%c%02d%02d' % (sign, h, m)
+ zreplace = _format_offset(object.utcoffset(), sep="")
+ else:
+ zreplace = ""
assert '%' not in zreplace
newformat.append(zreplace)
+ elif ch == ':':
+ if i < n:
+ ch2 = format[i]
+ i += 1
+ if ch2 == 'z':
+ if colonzreplace is None:
+ if hasattr(object, "utcoffset"):
+ colonzreplace = _format_offset(object.utcoffset(), sep=":")
+ else:
+ colonzreplace = ""
+ assert '%' not in colonzreplace
+ newformat.append(colonzreplace)
+ else:
+ push('%')
+ push(ch)
+ push(ch2)
elif ch == 'Z':
if Zreplace is None:
Zreplace = ""
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 7e7f4f3..bba9669 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -1463,8 +1463,8 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
# test that unicode input is allowed (issue 2782)
self.assertEqual(t.strftime("%m"), "03")
- # A naive object replaces %z and %Z w/ empty strings.
- self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
+ # A naive object replaces %z, %:z and %Z w/ empty strings.
+ self.assertEqual(t.strftime("'%z' '%:z' '%Z'"), "'' '' ''")
#make sure that invalid format specifiers are handled correctly
#self.assertRaises(ValueError, t.strftime, "%e")
@@ -1528,7 +1528,7 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
for fmt in ["m:%m d:%d y:%y",
"m:%m d:%d y:%y H:%H M:%M S:%S",
- "%z %Z",
+ "%z %:z %Z",
]:
self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
@@ -2134,7 +2134,7 @@ class TestDateTime(TestDate):
for fmt in ["m:%m d:%d y:%y",
"m:%m d:%d y:%y H:%H M:%M S:%S",
- "%z %Z",
+ "%z %:z %Z",
]:
self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
@@ -2777,6 +2777,7 @@ class TestDateTime(TestDate):
tz = timezone(-timedelta(hours=2, seconds=s, microseconds=us))
t = t.replace(tzinfo=tz)
self.assertEqual(t.strftime("%z"), "-0200" + z)
+ self.assertEqual(t.strftime("%:z"), "-02:00:" + z)
# bpo-34482: Check that surrogates don't cause a crash.
try:
@@ -3515,8 +3516,8 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase):
def test_strftime(self):
t = self.theclass(1, 2, 3, 4)
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'"), "'' ''")
+ # A naive object replaces %z, %:z and %Z with empty strings.
+ self.assertEqual(t.strftime("'%z' '%:z' '%Z'"), "'' '' ''")
# bpo-34482: Check that surrogates don't cause a crash.
try:
@@ -3934,10 +3935,10 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
- self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
- "07:47:00 %Z=EST %z=-0500")
- self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
- self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
+ self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z %%:z=%:z"),
+ "07:47:00 %Z=EST %z=-0500 %:z=-05:00")
+ self.assertEqual(t2.strftime("%H:%M:%S %Z %z %:z"), "12:47:00 UTC +0000 +00:00")
+ self.assertEqual(t3.strftime("%H:%M:%S %Z %z %:z"), "13:47:00 MET +0100 +01:00")
yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
t1 = time(23, 59, tzinfo=yuck)