Skip to content

Commit f744931

Browse files
authored
ENH: Support timespec argument in Timestamp.isoformat() (#44397)
* ENH: Support timespec argument in Timestamp.isoformat() * Get rid of tabs * Copy isoformat docstring to NaTType * Remove NaT docstring changes & update NaT tests * Fix another black issue
1 parent 4d69450 commit f744931

File tree

5 files changed

+119
-8
lines changed

5 files changed

+119
-8
lines changed

doc/source/whatsnew/v1.4.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ Other enhancements
209209
- :meth:`DataFrame.dropna` now accepts a single label as ``subset`` along with array-like (:issue:`41021`)
210210
- :meth:`read_excel` now accepts a ``decimal`` argument that allow the user to specify the decimal point when parsing string columns to numeric (:issue:`14403`)
211211
- :meth:`.GroupBy.mean` now supports `Numba <http://numba.pydata.org/>`_ execution with the ``engine`` keyword (:issue:`43731`)
212+
- :meth:`Timestamp.isoformat`, now handles the ``timespec`` argument from the base :class:``datetime`` class (:issue:`26131`)
212213

213214
.. ---------------------------------------------------------------------------
214215

pandas/_libs/tslibs/nattype.pyx

+1-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ cdef class _NaT(datetime):
295295
def __str__(self) -> str:
296296
return "NaT"
297297

298-
def isoformat(self, sep="T") -> str:
298+
def isoformat(self, sep: str = "T", timespec: str = "auto") -> str:
299299
# This allows Timestamp(ts.isoformat()) to always correctly roundtrip.
300300
return "NaT"
301301

pandas/_libs/tslibs/timestamps.pyx

+41-7
Original file line numberDiff line numberDiff line change
@@ -737,20 +737,54 @@ cdef class _Timestamp(ABCTimestamp):
737737
# -----------------------------------------------------------------
738738
# Rendering Methods
739739

740-
def isoformat(self, sep: str = "T") -> str:
741-
base = super(_Timestamp, self).isoformat(sep=sep)
742-
if self.nanosecond == 0:
740+
def isoformat(self, sep: str = "T", timespec: str = "auto") -> str:
741+
"""
742+
Return the time formatted according to ISO.
743+
744+
The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmmnnn'.
745+
By default, the fractional part is omitted if self.microsecond == 0
746+
and self.nanosecond == 0.
747+
748+
If self.tzinfo is not None, the UTC offset is also attached, giving
749+
giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmmnnn+HH:MM'.
750+
751+
Parameters
752+
----------
753+
sep : str, default 'T'
754+
String used as the separator between the date and time.
755+
756+
timespec : str, default 'auto'
757+
Specifies the number of additional terms of the time to include.
758+
The valid values are 'auto', 'hours', 'minutes', 'seconds',
759+
'milliseconds', 'microseconds', and 'nanoseconds'.
760+
761+
Returns
762+
-------
763+
str
764+
765+
Examples
766+
--------
767+
>>> ts = pd.Timestamp('2020-03-14T15:32:52.192548651')
768+
>>> ts.isoformat()
769+
'2020-03-14T15:32:52.192548651'
770+
>>> ts.isoformat(timespec='microseconds')
771+
'2020-03-14T15:32:52.192548'
772+
"""
773+
base_ts = "microseconds" if timespec == "nanoseconds" else timespec
774+
base = super(_Timestamp, self).isoformat(sep=sep, timespec=base_ts)
775+
if self.nanosecond == 0 and timespec != "nanoseconds":
743776
return base
744777

745778
if self.tzinfo is not None:
746779
base1, base2 = base[:-6], base[-6:]
747780
else:
748781
base1, base2 = base, ""
749782

750-
if self.microsecond != 0:
751-
base1 += f"{self.nanosecond:03d}"
752-
else:
753-
base1 += f".{self.nanosecond:09d}"
783+
if timespec == "nanoseconds" or (timespec == "auto" and self.nanosecond):
784+
if self.microsecond:
785+
base1 += f"{self.nanosecond:03d}"
786+
else:
787+
base1 += f".{self.nanosecond:09d}"
754788

755789
return base1 + base2
756790

pandas/tests/scalar/test_nat.py

+5
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def test_nat_methods_nat(method):
182182
def test_nat_iso_format(get_nat):
183183
# see gh-12300
184184
assert get_nat("NaT").isoformat() == "NaT"
185+
assert get_nat("NaT").isoformat(timespec="nanoseconds") == "NaT"
185186

186187

187188
@pytest.mark.parametrize(
@@ -325,6 +326,10 @@ def test_nat_doc_strings(compare):
325326
klass, method = compare
326327
klass_doc = getattr(klass, method).__doc__
327328

329+
# Ignore differences with Timestamp.isoformat() as they're intentional
330+
if klass == Timestamp and method == "isoformat":
331+
return
332+
328333
nat_doc = getattr(NaT, method).__doc__
329334
assert klass_doc == nat_doc
330335

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import pytest
2+
3+
from pandas import Timestamp
4+
5+
ts_no_ns = Timestamp(
6+
year=2019,
7+
month=5,
8+
day=18,
9+
hour=15,
10+
minute=17,
11+
second=8,
12+
microsecond=132263,
13+
)
14+
ts_ns = Timestamp(
15+
year=2019,
16+
month=5,
17+
day=18,
18+
hour=15,
19+
minute=17,
20+
second=8,
21+
microsecond=132263,
22+
nanosecond=123,
23+
)
24+
ts_ns_tz = Timestamp(
25+
year=2019,
26+
month=5,
27+
day=18,
28+
hour=15,
29+
minute=17,
30+
second=8,
31+
microsecond=132263,
32+
nanosecond=123,
33+
tz="UTC",
34+
)
35+
ts_no_us = Timestamp(
36+
year=2019,
37+
month=5,
38+
day=18,
39+
hour=15,
40+
minute=17,
41+
second=8,
42+
microsecond=0,
43+
nanosecond=123,
44+
)
45+
46+
47+
@pytest.mark.parametrize(
48+
"ts, timespec, expected_iso",
49+
[
50+
(ts_no_ns, "auto", "2019-05-18T15:17:08.132263"),
51+
(ts_no_ns, "seconds", "2019-05-18T15:17:08"),
52+
(ts_no_ns, "nanoseconds", "2019-05-18T15:17:08.132263000"),
53+
(ts_ns, "auto", "2019-05-18T15:17:08.132263123"),
54+
(ts_ns, "hours", "2019-05-18T15"),
55+
(ts_ns, "minutes", "2019-05-18T15:17"),
56+
(ts_ns, "seconds", "2019-05-18T15:17:08"),
57+
(ts_ns, "milliseconds", "2019-05-18T15:17:08.132"),
58+
(ts_ns, "microseconds", "2019-05-18T15:17:08.132263"),
59+
(ts_ns, "nanoseconds", "2019-05-18T15:17:08.132263123"),
60+
(ts_ns_tz, "auto", "2019-05-18T15:17:08.132263123+00:00"),
61+
(ts_ns_tz, "hours", "2019-05-18T15+00:00"),
62+
(ts_ns_tz, "minutes", "2019-05-18T15:17+00:00"),
63+
(ts_ns_tz, "seconds", "2019-05-18T15:17:08+00:00"),
64+
(ts_ns_tz, "milliseconds", "2019-05-18T15:17:08.132+00:00"),
65+
(ts_ns_tz, "microseconds", "2019-05-18T15:17:08.132263+00:00"),
66+
(ts_ns_tz, "nanoseconds", "2019-05-18T15:17:08.132263123+00:00"),
67+
(ts_no_us, "auto", "2019-05-18T15:17:08.000000123"),
68+
],
69+
)
70+
def test_isoformat(ts, timespec, expected_iso):
71+
assert ts.isoformat(timespec=timespec) == expected_iso

0 commit comments

Comments
 (0)