Skip to content

Commit befedb5

Browse files
committed
BUG: conversion of array of tz-aware datetime.datetime to correct DatetimeIndex close #1777
1 parent 3bd9d15 commit befedb5

File tree

6 files changed

+75
-14
lines changed

6 files changed

+75
-14
lines changed

RELEASE.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ pandas 0.8.2
9494
from earlier version of pandas (#1757)
9595
- Handle non-float64 dtypes in fast DataFrame.corr/cov code paths (#1761)
9696
- Fix DatetimeIndex.isin to function properly (#1763)
97+
- Fix conversion of array of tz-aware datetime.datetime to DatetimeIndex with
98+
right time zone (#1777)
9799

98100
pandas 0.8.1
99101
============

pandas/core/index.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@
33
from datetime import time
44

55
from itertools import izip
6-
import weakref
76

87
import numpy as np
98

109
from pandas.core.common import ndtake
1110
from pandas.util.decorators import cache_readonly
12-
from pandas.util import py3compat
1311
import pandas.core.common as com
1412
import pandas.lib as lib
1513
import pandas._algos as _algos

pandas/src/datetime.pyx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,12 @@ cpdef convert_to_tsobject(object ts, object tz=None):
567567
cdef inline bint _is_utc(object tz):
568568
return tz is UTC or isinstance(tz, _du_utc)
569569

570+
cdef inline object _get_zone(object tz):
571+
if _is_utc(tz):
572+
return 'UTC'
573+
else:
574+
return tz.zone
575+
570576
cdef int64_t _NS_LOWER_BOUND = -9223285636854775809LL
571577
cdef int64_t _NS_UPPER_BOUND = -9223372036854775807LL
572578

@@ -648,6 +654,42 @@ cdef inline _string_to_dts(object val, pandas_datetimestruct* dts):
648654
if result == -1:
649655
raise ValueError('Unable to parse %s' % str(val))
650656

657+
def datetime_to_datetime64(ndarray[object] values):
658+
cdef:
659+
Py_ssize_t i, n = len(values)
660+
object val, inferred_tz = None
661+
ndarray[int64_t] iresult
662+
pandas_datetimestruct dts
663+
_TSObject _ts
664+
665+
result = np.empty(n, dtype='M8[ns]')
666+
iresult = result.view('i8')
667+
for i in range(n):
668+
val = values[i]
669+
if util._checknull(val):
670+
iresult[i] = iNaT
671+
elif PyDateTime_Check(val):
672+
if val.tzinfo is not None:
673+
if inferred_tz is not None:
674+
if _get_zone(val.tzinfo) != inferred_tz:
675+
raise ValueError('Array must be all same time zone')
676+
else:
677+
inferred_tz = _get_zone(val.tzinfo)
678+
679+
_ts = convert_to_tsobject(val)
680+
iresult[i] = _ts.value
681+
_check_dts_bounds(iresult[i], &_ts.dts)
682+
else:
683+
if inferred_tz is not None:
684+
raise ValueError('Cannot mix tz-aware with tz-naive values')
685+
iresult[i] = _pydatetime_to_dts(val, &dts)
686+
_check_dts_bounds(iresult[i], &dts)
687+
else:
688+
raise TypeError('Unrecognized value type: %s' % type(val))
689+
690+
return result, inferred_tz
691+
692+
651693
def array_to_datetime(ndarray[object] values, raise_=False, dayfirst=False,
652694
format=None, utc=None):
653695
cdef:

pandas/tseries/index.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ def __new__(cls, data=None,
197197
subarr = _str_to_dt_array(data, offset)
198198
elif issubclass(data.dtype.type, np.datetime64):
199199
if isinstance(data, DatetimeIndex):
200+
if tz is None:
201+
tz = data.tz
202+
200203
subarr = data.values
204+
201205
if offset is None:
202206
offset = data.offset
203207
verify_integrity = False

pandas/tseries/tests/test_timezones.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,8 @@ def test_convert_tz_aware_datetime_datetime(self):
361361
datetime(2000, 1, 3)]
362362

363363
dates_aware = [tz.localize(x) for x in dates]
364-
365-
self.assertRaises(Exception, to_datetime, dates_aware)
364+
result = to_datetime(dates_aware)
365+
self.assert_(result.tz.zone == 'US/Eastern')
366366

367367
converted = to_datetime(dates_aware, utc=True)
368368
ex_vals = [Timestamp(x).value for x in dates_aware]
@@ -418,6 +418,12 @@ def test_static_tzinfo(self):
418418
index.hour
419419
index[0]
420420

421+
def test_tzaware_datetime_to_index(self):
422+
d = [datetime(2012, 8, 19, tzinfo=pytz.timezone('US/Eastern'))]
423+
424+
index = DatetimeIndex(d)
425+
self.assert_(index.tz.zone == 'US/Eastern')
426+
421427
class TestTimeZones(unittest.TestCase):
422428

423429
def setUp(self):

pandas/tseries/tools.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,34 @@ def to_datetime(arg, errors='ignore', dayfirst=False, utc=None, box=True):
6262
"""
6363
from pandas.core.series import Series
6464
from pandas.tseries.index import DatetimeIndex
65+
66+
def _convert_f(arg):
67+
arg = com._ensure_object(arg)
68+
69+
try:
70+
result = lib.array_to_datetime(arg, raise_=errors == 'raise',
71+
utc=utc, dayfirst=dayfirst)
72+
if com.is_datetime64_dtype(result) and box:
73+
result = DatetimeIndex(result, tz='utc' if utc else None)
74+
return result
75+
except ValueError, e:
76+
try:
77+
values, tz = lib.datetime_to_datetime64(arg)
78+
return DatetimeIndex(values, tz=tz)
79+
except (ValueError, TypeError):
80+
raise e
81+
6582
if arg is None:
6683
return arg
6784
elif isinstance(arg, datetime):
6885
return arg
6986
elif isinstance(arg, Series):
70-
values = lib.array_to_datetime(com._ensure_object(arg.values),
71-
raise_=errors == 'raise',
72-
utc=utc,
73-
dayfirst=dayfirst)
87+
values = _convert_f(arg.values)
7488
return Series(values, index=arg.index, name=arg.name)
7589
elif isinstance(arg, (np.ndarray, list)):
7690
if isinstance(arg, list):
7791
arg = np.array(arg, dtype='O')
78-
result = lib.array_to_datetime(com._ensure_object(arg),
79-
raise_=errors == 'raise',
80-
utc=utc,
81-
dayfirst=dayfirst)
82-
if com.is_datetime64_dtype(result) and box:
83-
result = DatetimeIndex(result, tz='utc' if utc else None)
92+
result = _convert_f(arg)
8493
return result
8594
try:
8695
if not arg:

0 commit comments

Comments
 (0)