Skip to content

Commit 98d4cb4

Browse files
author
Sylvain MARIE
committed
New Timestamp.fast_strftime, leveraged by format_array_from_datetime. Fixes issue related to timezone-aware timestamp formatting. Added corresponding tests.
1 parent 087425f commit 98d4cb4

File tree

3 files changed

+49
-7
lines changed

3 files changed

+49
-7
lines changed

pandas/_libs/tslib.pyx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,19 @@ def format_array_from_datetime(
199199

200200
elif fast_strftime:
201201

202-
dt64_to_dtstruct(val, &dts)
203-
204-
# Use string formatting for faster strftime
205-
result[i] = str_format % dict(
206-
year=dts.year, month=dts.month, day=dts.day, hour=dts.hour,
207-
min=dts.min, sec=dts.sec, us=dts.us
208-
)
202+
if tz is None:
203+
dt64_to_dtstruct(val, &dts)
204+
205+
# Use string formatting for faster strftime
206+
result[i] = str_format % dict(
207+
year=dts.year, month=dts.month, day=dts.day, hour=dts.hour,
208+
min=dts.min, sec=dts.sec, us=dts.us
209+
)
210+
else:
211+
ts = Timestamp(val, tz=tz)
209212

213+
# Use string formatting for faster strftime
214+
result[i] = ts.fast_strftime(str_format)
210215
else:
211216

212217
ts = Timestamp(val, tz=tz)

pandas/_libs/tslibs/timestamps.pyx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,25 @@ class Timestamp(_Timestamp):
11951195
tz = maybe_get_tz(tz)
11961196
return cls(datetime.fromtimestamp(ts, tz))
11971197

1198+
def fast_strftime(self, fmt_str: str) -> str:
1199+
"""A faster alternative to `strftime` using string formatting.
1200+
1201+
`fmt_str` should be created using `convert_strftime_format(fmt)`.
1202+
1203+
See also `self.strftime`, that relies on `datetime.strftime`.
1204+
1205+
Examples
1206+
--------
1207+
>>> ts = pd.Timestamp('2020-03-14T15:32:52.192548651')
1208+
>>> fmt = convert_strftime_format('%Y-%m-%d %X')
1209+
>>> ts.fast_strftime(fmt)
1210+
'2020-03-14 15:32:52'
1211+
"""
1212+
return fmt_str % dict(
1213+
year=self.year, month=self.month, day=self.day, hour=self.hour,
1214+
min=self.minute, sec=self.second, us=self.microsecond
1215+
)
1216+
11981217
def strftime(self, format):
11991218
"""
12001219
Timestamp.strftime(format)

pandas/tests/io/formats/test_format.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3175,6 +3175,24 @@ def test_datetime(self):
31753175
assert formatted[0] == "2003-01-01 12:00:00"
31763176
assert formatted[1] == "NaT"
31773177

3178+
def test_datetime_tz(self):
3179+
# This timestamp is in 2013 in Europe/Paris but is 2012 in UTC
3180+
dt = pd.to_datetime(["2013-01-01 00:00:00+01:00"], utc=True)
3181+
# If tz is currently set as utc, we'll see 2012
3182+
assert dt.format()[0] == "2012-12-31 23:00:00+00:00"
3183+
# If tz is currently set as paris, we'll see 2013
3184+
dt = dt.tz_convert("Europe/Paris")
3185+
assert dt.format()[0] == "2013-01-01 00:00:00+01:00"
3186+
3187+
def test_datetime_tz_custom(self):
3188+
# This timestamp is in 2013 in Europe/Paris but is 2012 in UTC
3189+
dt = pd.to_datetime(["2013-01-01 00:00:00+01:00"], utc=True)
3190+
# If tz is currently set as utc, we'll see 2012
3191+
assert dt.format(date_format="%Y-%m-%d__foo__%H:%M:%S")[0] == "2012-12-31__foo__23:00:00"
3192+
# If tz is currently set as paris, we'll see 2013
3193+
dt = dt.tz_convert("Europe/Paris")
3194+
assert dt.format(date_format="%Y-%m-%d__foo__%H:%M:%S")[0] == "2013-01-01__foo__00:00:00"
3195+
31783196
def test_date(self):
31793197
formatted = pd.to_datetime([datetime(2003, 1, 1), NaT]).format()
31803198
assert formatted[0] == "2003-01-01"

0 commit comments

Comments
 (0)