Skip to content

Commit d60c46a

Browse files
committed
Merge pull request #7534 from sinhrks/nanotz
BUG: Timestamp.tz_convert resets nanosecond
2 parents 1aefd4a + 812133b commit d60c46a

File tree

4 files changed

+48
-29
lines changed

4 files changed

+48
-29
lines changed

doc/source/v0.14.1.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ Bug Fixes
248248

249249
- BUG in ``resample`` raises ``ValueError`` when target contains ``NaT`` (:issue:`7227`)
250250

251-
251+
- Bug in ``Timestamp.tz_convert`` resets ``nanosecond`` info (:issue:`7534`)
252252

253253
- Bug in ``Index.astype(float)`` where it would return an ``object`` dtype
254254
``Index`` (:issue:`7464`).

pandas/tseries/tests/test_timezones.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pylint: disable-msg=E1101,W0612
2-
from datetime import datetime, time, timedelta, tzinfo, date
2+
from datetime import datetime, timedelta, tzinfo, date
33
import sys
44
import os
55
import unittest
@@ -8,10 +8,9 @@
88
import numpy as np
99
import pytz
1010

11-
from pandas import (Index, Series, TimeSeries, DataFrame, isnull,
12-
date_range, Timestamp)
11+
from pandas import (Index, Series, DataFrame, isnull, Timestamp)
1312

14-
from pandas import DatetimeIndex, Int64Index, to_datetime, NaT
13+
from pandas import DatetimeIndex, to_datetime, NaT
1514
from pandas import tslib
1615

1716
import pandas.core.datetools as datetools
@@ -20,17 +19,10 @@
2019
import pandas.tseries.tools as tools
2120
from pytz import NonExistentTimeError
2221

23-
from pandas.util.testing import assert_series_equal, assert_almost_equal, assertRaisesRegexp
2422
import pandas.util.testing as tm
2523

26-
import pandas.lib as lib
27-
import pandas.core.datetools as dt
28-
from numpy.random import rand
2924
from pandas.util.testing import assert_frame_equal
30-
import pandas.compat as compat
31-
from pandas.compat import range, lrange, zip, cPickle as pickle
32-
from pandas.core.datetools import BDay
33-
import pandas.core.common as com
25+
from pandas.compat import lrange, zip
3426

3527
from pandas import _np_version_under1p7
3628

@@ -544,13 +536,13 @@ def test_localized_at_time_between_time(self):
544536

545537
result = ts_local.at_time(time(10, 0))
546538
expected = ts.at_time(time(10, 0)).tz_localize(self.tzstr('US/Eastern'))
547-
assert_series_equal(result, expected)
539+
tm.assert_series_equal(result, expected)
548540
self.assertTrue(self.cmptz(result.index.tz, self.tz('US/Eastern')))
549541

550542
t1, t2 = time(10, 0), time(11, 0)
551543
result = ts_local.between_time(t1, t2)
552544
expected = ts.between_time(t1, t2).tz_localize(self.tzstr('US/Eastern'))
553-
assert_series_equal(result, expected)
545+
tm.assert_series_equal(result, expected)
554546
self.assertTrue(self.cmptz(result.index.tz, self.tz('US/Eastern')))
555547

556548
def test_string_index_alias_tz_aware(self):
@@ -631,7 +623,7 @@ def test_frame_no_datetime64_dtype(self):
631623
'datetimes_with_tz' : datetimes_with_tz })
632624
result = df.get_dtype_counts()
633625
expected = Series({ 'datetime64[ns]' : 3, 'object' : 1 })
634-
assert_series_equal(result, expected)
626+
tm.assert_series_equal(result, expected)
635627

636628
def test_hongkong_tz_convert(self):
637629
# #1673
@@ -863,7 +855,7 @@ def test_series_frame_tz_localize(self):
863855
# Can't localize if already tz-aware
864856
rng = date_range('1/1/2011', periods=100, freq='H', tz='utc')
865857
ts = Series(1, index=rng)
866-
assertRaisesRegexp(TypeError, 'Already tz-aware', ts.tz_localize, 'US/Eastern')
858+
tm.assertRaisesRegexp(TypeError, 'Already tz-aware', ts.tz_localize, 'US/Eastern')
867859

868860
def test_series_frame_tz_convert(self):
869861
rng = date_range('1/1/2011', periods=200, freq='D',
@@ -887,7 +879,7 @@ def test_series_frame_tz_convert(self):
887879
# can't convert tz-naive
888880
rng = date_range('1/1/2011', periods=200, freq='D')
889881
ts = Series(1, index=rng)
890-
assertRaisesRegexp(TypeError, "Cannot convert tz-naive", ts.tz_convert, 'US/Eastern')
882+
tm.assertRaisesRegexp(TypeError, "Cannot convert tz-naive", ts.tz_convert, 'US/Eastern')
891883

892884
def test_join_utc_convert(self):
893885
rng = date_range('1/1/2011', periods=100, freq='H', tz='utc')
@@ -1033,7 +1025,7 @@ def test_arith_utc_convert(self):
10331025
expected = uts1 + uts2
10341026

10351027
self.assertEqual(result.index.tz, pytz.UTC)
1036-
assert_series_equal(result, expected)
1028+
tm.assert_series_equal(result, expected)
10371029

10381030
def test_intersection(self):
10391031
rng = date_range('1/1/2011', periods=100, freq='H', tz='utc')

pandas/tseries/tests/test_tslib.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pandas.tslib import period_asfreq, period_ordinal
1010
from pandas.tseries.index import date_range
1111
from pandas.tseries.frequencies import get_freq
12+
import pandas.tseries.offsets as offsets
1213
from pandas import _np_version_under1p7
1314
import pandas.util.testing as tm
1415
from pandas.util.testing import assert_series_equal
@@ -61,7 +62,7 @@ def test_bounds_with_different_units(self):
6162
for unit in time_units:
6263
self.assertRaises(
6364
ValueError,
64-
tslib.Timestamp,
65+
Timestamp,
6566
np.datetime64(date_string, dtype='M8[%s]' % unit)
6667
)
6768

@@ -72,27 +73,48 @@ def test_bounds_with_different_units(self):
7273

7374
for date_string in in_bounds_dates:
7475
for unit in time_units:
75-
tslib.Timestamp(
76+
Timestamp(
7677
np.datetime64(date_string, dtype='M8[%s]' % unit)
7778
)
7879

80+
def test_tz(self):
81+
t = '2014-02-01 09:00'
82+
ts = Timestamp(t)
83+
local = ts.tz_localize('Asia/Tokyo')
84+
self.assertEqual(local.hour, 9)
85+
self.assertEqual(local, Timestamp(t, tz='Asia/Tokyo'))
86+
conv = local.tz_convert('US/Eastern')
87+
self.assertEqual(conv,
88+
Timestamp('2014-01-31 19:00', tz='US/Eastern'))
89+
self.assertEqual(conv.hour, 19)
90+
91+
# preserves nanosecond
92+
ts = Timestamp(t) + offsets.Nano(5)
93+
local = ts.tz_localize('Asia/Tokyo')
94+
self.assertEqual(local.hour, 9)
95+
self.assertEqual(local.nanosecond, 5)
96+
conv = local.tz_convert('US/Eastern')
97+
self.assertEqual(conv.nanosecond, 5)
98+
self.assertEqual(conv.hour, 19)
99+
79100
def test_barely_oob_dts(self):
80101
one_us = np.timedelta64(1)
81102

82103
# By definition we can't go out of bounds in [ns], so we
83104
# convert the datetime64s to [us] so we can go out of bounds
84-
min_ts_us = np.datetime64(tslib.Timestamp.min).astype('M8[us]')
85-
max_ts_us = np.datetime64(tslib.Timestamp.max).astype('M8[us]')
105+
min_ts_us = np.datetime64(Timestamp.min).astype('M8[us]')
106+
max_ts_us = np.datetime64(Timestamp.max).astype('M8[us]')
86107

87108
# No error for the min/max datetimes
88-
tslib.Timestamp(min_ts_us)
89-
tslib.Timestamp(max_ts_us)
109+
Timestamp(min_ts_us)
110+
Timestamp(max_ts_us)
90111

91112
# One us less than the minimum is an error
92-
self.assertRaises(ValueError, tslib.Timestamp, min_ts_us - one_us)
113+
self.assertRaises(ValueError, Timestamp, min_ts_us - one_us)
93114

94115
# One us more than the maximum is an error
95-
self.assertRaises(ValueError, tslib.Timestamp, max_ts_us + one_us)
116+
self.assertRaises(ValueError, Timestamp, max_ts_us + one_us)
117+
96118

97119
class TestDatetimeParsingWrappers(tm.TestCase):
98120
def test_does_not_convert_mixed_integer(self):

pandas/tslib.pyx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,21 +341,26 @@ class Timestamp(_Timestamp):
341341
def is_year_end(self):
342342
return self._get_start_end_field('is_year_end')
343343

344-
def tz_localize(self, tz):
344+
def tz_localize(self, tz, infer_dst=False):
345345
"""
346346
Convert naive Timestamp to local time zone
347347
348348
Parameters
349349
----------
350350
tz : pytz.timezone or dateutil.tz.tzfile
351+
infer_dst : boolean, default False
352+
Attempt to infer fall dst-transition hours based on order
351353
352354
Returns
353355
-------
354356
localized : Timestamp
355357
"""
356358
if self.tzinfo is None:
357359
# tz naive, localize
358-
return Timestamp(self.to_pydatetime(), tz=tz)
360+
tz = maybe_get_tz(tz)
361+
value = tz_localize_to_utc(np.array([self.value]), tz,
362+
infer_dst=infer_dst)[0]
363+
return Timestamp(value, tz=tz)
359364
else:
360365
raise Exception('Cannot localize tz-aware Timestamp, use '
361366
'tz_convert for conversions')

0 commit comments

Comments
 (0)