Skip to content

Commit c4ce14b

Browse files
committed
ENH: convert passed dates to UTC, add tz_convert method to Timestamp #1172
1 parent 99a7444 commit c4ce14b

File tree

3 files changed

+86
-8
lines changed

3 files changed

+86
-8
lines changed

pandas/src/datetime.pyx

+53-4
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ class Timestamp(_Timestamp):
125125
def freqstr(self):
126126
return getattr(self.offset, 'freqstr', self.offset)
127127

128+
def tz_convert(self, tz):
129+
if isinstance(tz, basestring):
130+
import pytz
131+
tz = pytz.timezone(tz)
132+
133+
conv = tz.normalize(self)
134+
return Timestamp(conv)
135+
128136
cdef inline bint is_timestamp(object o):
129137
return isinstance(o, Timestamp)
130138

@@ -232,6 +240,7 @@ cpdef convert_to_tsobject(object ts, object tz=None):
232240
"""
233241
cdef:
234242
_TSObject obj
243+
bint utc_convert = 1
235244

236245
obj = _TSObject()
237246

@@ -246,8 +255,8 @@ cpdef convert_to_tsobject(object ts, object tz=None):
246255
obj.value = PyArray_DatetimeStructToDatetime(NPY_FR_us, &obj.dts)
247256
elif PyDateTime_Check(ts):
248257
obj.value = _pydatetime_to_dts(ts, &obj.dts)
249-
if tz is None:
250-
tz = ts.tzinfo
258+
obj.tzinfo = ts.tzinfo
259+
utc_convert = 0
251260
elif PyDate_Check(ts):
252261
obj.value = _date_to_datetime64(ts, &obj.dts)
253262
else:
@@ -265,8 +274,11 @@ cpdef convert_to_tsobject(object ts, object tz=None):
265274
inf = tz._transition_info[pos]
266275

267276
obj.value = obj.value + deltas[pos]
268-
PyArray_DatetimeToDatetimeStruct(obj.value, NPY_FR_us, &obj.dts)
269-
obj.tzinfo = tz._tzinfos[inf]
277+
278+
if utc_convert:
279+
PyArray_DatetimeToDatetimeStruct(obj.value, NPY_FR_us,
280+
&obj.dts)
281+
obj.tzinfo = tz._tzinfos[inf]
270282

271283
return obj
272284

@@ -843,6 +855,43 @@ def tz_convert(ndarray[int64_t] vals, object tz1, object tz2):
843855

844856
return result
845857

858+
def tz_convert_single(int64_t val, object tz1, object tz2):
859+
cdef:
860+
ndarray[int64_t] trans, deltas
861+
Py_ssize_t pos
862+
int64_t v, offset, utc_date
863+
864+
865+
if not have_pytz:
866+
import pytz
867+
868+
# Convert to UTC
869+
870+
if tz1.zone != 'UTC':
871+
deltas = _get_deltas(tz1)
872+
trans = _get_transitions(tz1)
873+
pos = trans.searchsorted(val) - 1
874+
if pos < 0:
875+
raise ValueError('First time before start of DST info')
876+
offset = deltas[pos]
877+
utc_date = val - offset
878+
else:
879+
utc_date = val
880+
881+
if tz2.zone == 'UTC':
882+
return utc_date
883+
884+
# Convert UTC to other timezone
885+
trans = _get_transitions(tz2)
886+
deltas = _get_deltas(tz2)
887+
pos = trans.searchsorted(utc_date) - 1
888+
if pos < 0:
889+
raise ValueError('First time before start of DST info')
890+
891+
offset = deltas[pos]
892+
return utc_date + offset
893+
894+
846895
trans_cache = {}
847896
utc_offset_cache = {}
848897

pandas/tseries/index.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ def _as_i8(arg):
3030

3131
def _field_accessor(name, field):
3232
def f(self):
33-
return lib.fast_field_accessor(self.asi8, field)
33+
values = self.asi8
34+
if self.tz is not None:
35+
utc = _utc()
36+
if self.tz is not utc:
37+
values = lib.tz_convert(values, utc, self.tz)
38+
return lib.fast_field_accessor(values, field)
3439
f.__name__ = name
3540
return property(f)
3641

@@ -237,6 +242,14 @@ def __new__(cls, data=None,
237242
else:
238243
subarr = np.array(data, dtype='M8[us]', copy=copy)
239244

245+
if tz is not None:
246+
tz = tools._maybe_get_tz(tz)
247+
# Convert local to UTC
248+
ints = subarr.view('i8')
249+
lib.tz_localize_check(ints, tz)
250+
subarr = lib.tz_convert(ints, tz, _utc())
251+
subarr = subarr.view('M8[us]')
252+
240253
subarr = subarr.view(cls)
241254
subarr.name = name
242255
subarr.offset = offset

pandas/tseries/tests/test_timezones.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,29 @@ def test_utc_box_timestamp_and_localize(self):
119119
self.assert_('EDT' in repr(rng_eastern[0].tzinfo))
120120

121121
def test_timestamp_tz_convert(self):
122-
pass
122+
strdates = ['1/1/2012', '3/1/2012', '4/1/2012']
123+
idx = DatetimeIndex(strdates, tz='US/Eastern')
124+
125+
conv = idx[0].tz_convert('US/Pacific')
126+
expected = idx.tz_convert('US/Pacific')[0]
127+
128+
self.assertEquals(conv, expected)
123129

124130
def test_pass_dates_convert_to_utc(self):
125-
pass
131+
strdates = ['1/1/2012', '3/1/2012', '4/1/2012']
132+
133+
idx = DatetimeIndex(strdates)
134+
conv = idx.tz_convert('US/Eastern')
135+
136+
fromdates = DatetimeIndex(strdates, tz='US/Eastern')
137+
138+
self.assert_(conv.tz == fromdates.tz)
139+
self.assert_(np.array_equal(conv.values, fromdates.values))
126140

127141
def test_field_access_localize(self):
128-
pass
142+
strdates = ['1/1/2012', '3/1/2012', '4/1/2012']
143+
rng = DatetimeIndex(strdates, tz='US/Eastern')
144+
self.assert_((rng.hour == 0).all())
129145

130146
def test_with_tz(self):
131147
tz = pytz.timezone('US/Central')

0 commit comments

Comments
 (0)