FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-69142: add %:z strftime format code by ThomasWaldmann · Pull Request #95983 · python/cpython · GitHub

Repository navigation

Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension .c  (1) .py  (2) .rst  (2) All 3 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
6 changes: 6 additions & 0 deletions Doc/library/datetime.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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.
Resolved
+-----------+--------------------------------+------------------------+-------+
| ``%Z`` | Time zone name (empty string | (empty), UTC, GMT | \(6) |
| | if the object is naive). | | |
+-----------+--------------------------------+------------------------+-------+
Expand Down
45 changes: 24 additions & 21 deletions Lib/datetime.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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 = ""
Expand Down
21 changes: 11 additions & 10 deletions Lib/test/datetimetester.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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.
Resolved
69 changes: 47 additions & 22 deletions Modules/_datetimemodule.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1506,6 +1506,32 @@ format_utcoffset(char *buf, size_t buflen, const char *sep,
return 0;
}

static PyObject *
make_somezreplacement(PyObject *object, char *sep, PyObject *tzinfoarg)
{
char buf[100];
PyObject *tzinfo = get_tzinfo_member(object);
PyObject *replacement = PyBytes_FromStringAndSize(NULL, 0);

if (replacement == NULL)
return NULL;
if (tzinfo == Py_None || tzinfo == NULL)
return replacement;

Py_DECREF(replacement);

assert(tzinfoarg != NULL);
if (format_utcoffset(buf,
sizeof(buf),
sep,
tzinfo,
tzinfoarg) < 0)
return NULL;

replacement = PyBytes_FromStringAndSize(buf, strlen(buf));
return replacement;
Comment thread
ThomasWaldmann marked this conversation as resolved.
Resolved
}

static PyObject *
make_Zreplacement(PyObject *object, PyObject *tzinfoarg)
{
Expand Down Expand Up @@ -1566,7 +1592,7 @@ make_freplacement(PyObject *object)

/* I sure don't want to reproduce the strftime code from the time module,
* so this imports the module and calls it. All the hair is due to
* giving special meanings to the %z, %Z and %f format codes via a
* giving special meanings to the %z, %:z, %Z and %f format codes via a
* preprocessing step on the format string.
* tzinfoarg is the argument to pass to the object's tzinfo method, if
* needed.
Expand All @@ -1578,6 +1604,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.
Resolved
PyObject *Zreplacement = NULL; /* py string, replacement for %Z */
PyObject *freplacement = NULL; /* py string, replacement for %f */

Expand Down Expand Up @@ -1632,32 +1659,29 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
}
/* A % has been seen and ch is the character after it. */
else if (ch == 'z') {
/* %z -> +HHMM */
if (zreplacement == NULL) {
/* format utcoffset */
char buf[100];
PyObject *tzinfo = get_tzinfo_member(object);
zreplacement = PyBytes_FromStringAndSize("", 0);
if (zreplacement == NULL) goto Done;
if (tzinfo != Py_None && tzinfo != NULL) {
assert(tzinfoarg != NULL);
if (format_utcoffset(buf,
sizeof(buf),
"",
tzinfo,
tzinfoarg) < 0)
goto Done;
Py_DECREF(zreplacement);
zreplacement =
PyBytes_FromStringAndSize(buf,
strlen(buf));
if (zreplacement == NULL)
goto Done;
}
zreplacement = make_somezreplacement(object, "", tzinfoarg);
if (zreplacement == NULL)
goto Done;
}
assert(zreplacement != NULL);
assert(PyBytes_Check(zreplacement));
Comment thread
ThomasWaldmann marked this conversation as resolved.
Resolved
ptoappend = PyBytes_AS_STRING(zreplacement);
ntoappend = PyBytes_GET_SIZE(zreplacement);
}
else if (ch == ':' && *pin == 'z' && pin++) {
/* %:z -> +HH:MM */
if (colonzreplacement == NULL) {
colonzreplacement = make_somezreplacement(object, ":", tzinfoarg);
if (colonzreplacement == NULL)
goto Done;
}
assert(colonzreplacement != NULL);
assert(PyBytes_Check(colonzreplacement));
Comment thread
ThomasWaldmann marked this conversation as resolved.
Resolved
ptoappend = PyBytes_AS_STRING(colonzreplacement);
ntoappend = PyBytes_GET_SIZE(colonzreplacement);
}
else if (ch == 'Z') {
/* format tzname */
if (Zreplacement == NULL) {
Expand Down Expand Up @@ -1686,7 +1710,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 Expand Up @@ -1733,6 +1757,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
Done:
Py_XDECREF(freplacement);
Py_XDECREF(zreplacement);
Py_XDECREF(colonzreplacement);
Py_XDECREF(Zreplacement);
Py_XDECREF(newfmt);
return result;
Expand Down

Back | FazBrowse Home | New Git URL