Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2386,6 +2386,12 @@ requires, and these work on all platforms with a standard C implementation.
| | string if the object is | +063415, | |
| | naive). | -030712.345216 | |
+-----------+--------------------------------+------------------------+-------+
| ``%:z`` | UTC offset in the form | (empty), +00:00, | |
| | ``±HH:MM[:SS[.ffffff]]`` | -04:00, +10:30, | |
| | (empty string if the object is | +06:34:15, | |
| | naive). | -03:07:12.345216 | |
| | .. versionadded:: 3.12 | | |
Comment thread
ThomasWaldmann marked this conversation as resolved.
Outdated
+-----------+--------------------------------+------------------------+-------+
| ``%Z`` | Time zone name (empty string | (empty), UTC, GMT | \(6) |
| | if the object is naive). | | |
+-----------+--------------------------------+------------------------+-------+
Expand Down
21 changes: 11 additions & 10 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -1463,8 +1463,8 @@ def test_strftime(self):
# 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")
Expand Down Expand Up @@ -1528,7 +1528,7 @@ def strftime(self, format_spec):

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))
Expand Down Expand Up @@ -2134,7 +2134,7 @@ def strftime(self, format_spec):

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))
Expand Down Expand Up @@ -2777,6 +2777,7 @@ def test_more_strftime(self):
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:
Expand Down Expand Up @@ -3515,8 +3516,8 @@ def test_1653736(self):
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:
Expand Down Expand Up @@ -3934,10 +3935,10 @@ def test_zones(self):
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add %:z strftime format code (generates tzoffset with colons as separator)
Comment thread
merwok marked this conversation as resolved.
Outdated
35 changes: 23 additions & 12 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
PyObject *result = NULL; /* guilty until proved innocent */

PyObject *zreplacement = NULL; /* py string, replacement for %z */
PyObject *colonzreplacement = NULL; /* py string, replacement for %:z */
Comment thread
gvanrossum marked this conversation as resolved.
PyObject *Zreplacement = NULL; /* py string, replacement for %Z */
PyObject *freplacement = NULL; /* py string, replacement for %f */

Expand Down Expand Up @@ -1631,32 +1632,42 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
ntoappend = 1;
}
/* A % has been seen and ch is the character after it. */
else if (ch == 'z') {
if (zreplacement == NULL) {
else if (ch == 'z' || (ch == ':' && *pin == 'z' && pin++)) {
Comment thread
ThomasWaldmann marked this conversation as resolved.
Outdated
/* %z -> +HHMM, %:z -> +HH:MM */
PyObject **replacement_p;
char *sep;
if (ch == ':') {
sep = ":";
replacement_p = &colonzreplacement;
} else {
sep = "";
replacement_p = &zreplacement;
}
if (*replacement_p == NULL) {
/* format utcoffset */
char buf[100];
PyObject *tzinfo = get_tzinfo_member(object);
zreplacement = PyBytes_FromStringAndSize("", 0);
if (zreplacement == NULL) goto Done;
*replacement_p = PyBytes_FromStringAndSize("", 0);
if (*replacement_p == NULL) goto Done;
if (tzinfo != Py_None && tzinfo != NULL) {
assert(tzinfoarg != NULL);
if (format_utcoffset(buf,
sizeof(buf),
"",
sep,
tzinfo,
tzinfoarg) < 0)
goto Done;
Py_DECREF(zreplacement);
zreplacement =
Py_DECREF(*replacement_p);
*replacement_p =
PyBytes_FromStringAndSize(buf,
strlen(buf));
if (zreplacement == NULL)
if (*replacement_p == NULL)
goto Done;
}
}
assert(zreplacement != NULL);
ptoappend = PyBytes_AS_STRING(zreplacement);
ntoappend = PyBytes_GET_SIZE(zreplacement);
assert(*replacement_p != NULL);
ptoappend = PyBytes_AS_STRING(*replacement_p);
ntoappend = PyBytes_GET_SIZE(*replacement_p);
}
else if (ch == 'Z') {
/* format tzname */
Expand Down Expand Up @@ -1686,7 +1697,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
ntoappend = PyBytes_GET_SIZE(freplacement);
}
else {
/* percent followed by neither z nor Z */
/* percent followed by something else */
ptoappend = pin - 2;
ntoappend = 2;
}
Expand Down