Skip to content

Commit e42b61f

Browse files
mroeschkeharisbal
authored and
harisbal
committed
BUG: Do not round DatetimeIndex nanosecond precision when iterating (pandas-dev#19628)
1 parent 16fc751 commit e42b61f

File tree

4 files changed

+27
-8
lines changed

4 files changed

+27
-8
lines changed

doc/source/whatsnew/v0.23.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ Timezones
729729
- Bug in :func:`DatetimeIndex.insert` where inserting ``NaT`` into a timezone-aware index incorrectly raised (:issue:`16357`)
730730
- Bug in the :class:`DataFrame` constructor, where tz-aware Datetimeindex and a given column name will result in an empty ``DataFrame`` (:issue:`19157`)
731731
- Bug in :func:`Timestamp.tz_localize` where localizing a timestamp near the minimum or maximum valid values could overflow and return a timestamp with an incorrect nanosecond value (:issue:`12677`)
732+
- Bug when iterating over :class:`DatetimeIndex` that was localized with fixed timezone offset that rounded nanosecond precision to microseconds (:issue:`19603`)
732733

733734
Offsets
734735
^^^^^^^

pandas/_libs/tslib.pyx

+8-7
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ from tslibs.timezones cimport (is_utc, is_tzlocal, is_fixed_offset,
4646
treat_tz_as_pytz, get_dst_info)
4747
from tslibs.conversion cimport (tz_convert_single, _TSObject,
4848
convert_datetime_to_tsobject,
49-
get_datetime64_nanos)
49+
get_datetime64_nanos,
50+
tz_convert_utc_to_tzlocal)
5051
from tslibs.conversion import tz_convert_single
5152

5253
from tslibs.nattype import NaT, nat_strings, iNaT
@@ -144,12 +145,12 @@ def ints_to_pydatetime(ndarray[int64_t] arr, tz=None, freq=None,
144145
if value == NPY_NAT:
145146
result[i] = NaT
146147
else:
147-
dt64_to_dtstruct(value, &dts)
148-
dt = create_datetime_from_ts(value, dts, tz, freq)
149-
dt = dt + tz.utcoffset(dt)
150-
if box:
151-
dt = Timestamp(dt)
152-
result[i] = dt
148+
# Python datetime objects do not support nanosecond
149+
# resolution (yet, PEP 564). Need to compute new value
150+
# using the i8 representation.
151+
local_value = tz_convert_utc_to_tzlocal(value, tz)
152+
dt64_to_dtstruct(local_value, &dts)
153+
result[i] = func_create(value, dts, tz, freq)
153154
else:
154155
trans, deltas, typ = get_dst_info(tz)
155156

pandas/conftest.py

+6
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,9 @@ def compression_no_zip(request):
9393
except zip
9494
"""
9595
return request.param
96+
97+
98+
@pytest.fixture(scope='module')
99+
def datetime_tz_utc():
100+
from datetime import timezone
101+
return timezone.utc

pandas/tests/indexes/datetimes/test_timezones.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import pandas as pd
1818
from pandas._libs import tslib
1919
from pandas._libs.tslibs import timezones
20-
from pandas.compat import lrange, zip
20+
from pandas.compat import lrange, zip, PY3
2121
from pandas import (DatetimeIndex, date_range, bdate_range,
2222
Timestamp, isna, to_datetime, Index)
2323

@@ -949,6 +949,17 @@ def test_dti_union_aware(self):
949949
result = rng.union(rng2)
950950
assert result.tz.zone == 'UTC'
951951

952+
@pytest.mark.parametrize('tz', [None, 'UTC', "US/Central",
953+
dateutil.tz.tzoffset(None, -28800)])
954+
@pytest.mark.usefixtures("datetime_tz_utc")
955+
@pytest.mark.skipif(not PY3, reason="datetime.timezone not in PY2")
956+
def test_iteration_preserves_nanoseconds(self, tz):
957+
# GH 19603
958+
index = DatetimeIndex(["2018-02-08 15:00:00.168456358",
959+
"2018-02-08 15:00:00.168456359"], tz=tz)
960+
for i, ts in enumerate(index):
961+
assert ts == index[i]
962+
952963

953964
class TestDateRange(object):
954965
"""Tests for date_range with timezones"""

0 commit comments

Comments
 (0)