Skip to content

Commit fc2d3cb

Browse files
committed
Merge pull request #2935 from stephenwlin/daterange_tz
BUG: timezone offset double counted using date_range and DateTimeIndex.append (fixes #2906, #2938)
2 parents 8313870 + b0cc8b0 commit fc2d3cb

File tree

3 files changed

+78
-15
lines changed

3 files changed

+78
-15
lines changed

pandas/tseries/index.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,11 @@ def _generate(cls, start, end, periods, name, offset,
298298
if end is not None:
299299
end = Timestamp(end)
300300

301-
inferred_tz = tools._infer_tzinfo(start, end)
301+
try:
302+
inferred_tz = tools._infer_tzinfo(start, end)
303+
except:
304+
raise ValueError('Start and end cannot both be tz-aware with '
305+
'different timezones')
302306

303307
if tz is not None and inferred_tz is not None:
304308
assert(inferred_tz == tz)
@@ -1538,17 +1542,21 @@ def _generate_regular_range(start, end, periods, offset):
15381542
b = Timestamp(start).value
15391543
e = Timestamp(end).value
15401544
e += stride - e % stride
1545+
# end.tz == start.tz by this point due to _generate implementation
1546+
tz = start.tz
15411547
elif start is not None:
15421548
b = Timestamp(start).value
15431549
e = b + periods * stride
1550+
tz = start.tz
15441551
elif end is not None:
15451552
e = Timestamp(end).value + stride
15461553
b = e - periods * stride
1554+
tz = end.tz
15471555
else:
15481556
raise NotImplementedError
15491557

15501558
data = np.arange(b, e, stride, dtype=np.int64)
1551-
data = data.view(_NS_DTYPE)
1559+
data = DatetimeIndex._simple_new(data, None, tz=tz)
15521560
else:
15531561
if isinstance(start, Timestamp):
15541562
start = start.to_pydatetime()
@@ -1723,7 +1731,8 @@ def _process_concat_data(to_concat, name):
17231731
else:
17241732
to_concat = [x.values for x in to_concat]
17251733

1726-
klass = DatetimeIndex
1734+
# well, technically not a "class" anymore...oh well
1735+
klass = DatetimeIndex._simple_new
17271736
kwargs = {'tz': tz}
17281737
concat = com._concat_compat
17291738
else:

pandas/tseries/tests/test_daterange.py

+36-5
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,18 @@
1515
import pandas.core.datetools as datetools
1616

1717

18+
def _skip_if_no_pytz():
19+
try:
20+
import pytz
21+
except ImportError:
22+
raise nose.SkipTest
23+
24+
1825
def eq_gen_range(kwargs, expected):
1926
rng = generate_range(**kwargs)
2027
assert(np.array_equal(list(rng), expected))
2128

29+
2230
START, END = datetime(2009, 1, 1), datetime(2010, 1, 1)
2331

2432

@@ -246,11 +254,11 @@ def test_intersection_bug(self):
246254
def test_summary(self):
247255
self.rng.summary()
248256
self.rng[2:2].summary()
249-
try:
250-
import pytz
251-
bdate_range('1/1/2005', '1/1/2009', tz=pytz.utc).summary()
252-
except Exception:
253-
pass
257+
258+
def test_summary_pytz(self):
259+
_skip_if_no_pytz()
260+
import pytz
261+
bdate_range('1/1/2005', '1/1/2009', tz=pytz.utc).summary()
254262

255263
def test_misc(self):
256264
end = datetime(2009, 5, 13)
@@ -298,6 +306,29 @@ def test_range_bug(self):
298306
exp_values = [start + i * offset for i in range(5)]
299307
self.assert_(np.array_equal(result, DatetimeIndex(exp_values)))
300308

309+
def test_range_tz(self):
310+
# GH 2906
311+
_skip_if_no_pytz()
312+
from pytz import timezone as tz
313+
314+
start = datetime(2011, 1, 1, tzinfo=tz('US/Eastern'))
315+
end = datetime(2011, 1, 3, tzinfo=tz('US/Eastern'))
316+
317+
dr = date_range(start=start, periods=3)
318+
self.assert_(dr.tz == tz('US/Eastern'))
319+
self.assert_(dr[0] == start)
320+
self.assert_(dr[2] == end)
321+
322+
dr = date_range(end=end, periods=3)
323+
self.assert_(dr.tz == tz('US/Eastern'))
324+
self.assert_(dr[0] == start)
325+
self.assert_(dr[2] == end)
326+
327+
dr = date_range(start=start, end=end)
328+
self.assert_(dr.tz == tz('US/Eastern'))
329+
self.assert_(dr[0] == start)
330+
self.assert_(dr[2] == end)
331+
301332

302333
if __name__ == '__main__':
303334
import nose

pandas/tseries/tests/test_timeseries.py

+30-7
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@
4242
from numpy.testing.decorators import slow
4343

4444

45+
def _skip_if_no_pytz():
46+
try:
47+
import pytz
48+
except ImportError:
49+
raise nose.SkipTest
50+
51+
4552
class TestTimeSeriesDuplicates(unittest.TestCase):
4653
_multiprocess_can_split_ = True
4754

@@ -168,13 +175,6 @@ def assert_range_equal(left, right):
168175
assert(left.tz == right.tz)
169176

170177

171-
def _skip_if_no_pytz():
172-
try:
173-
import pytz
174-
except ImportError:
175-
raise nose.SkipTest
176-
177-
178178
class TestTimeSeries(unittest.TestCase):
179179
_multiprocess_can_split_ = True
180180

@@ -1265,6 +1265,29 @@ def test_append_concat(self):
12651265
self.assert_(rng1.append(rng1).name == 'foo')
12661266
self.assert_(rng1.append(rng2).name is None)
12671267

1268+
def test_append_concat_tz(self):
1269+
#GH 2938
1270+
_skip_if_no_pytz()
1271+
1272+
rng = date_range('5/8/2012 1:45', periods=10, freq='5T',
1273+
tz='US/Eastern')
1274+
rng2 = date_range('5/8/2012 2:35', periods=10, freq='5T',
1275+
tz='US/Eastern')
1276+
rng3 = date_range('5/8/2012 1:45', periods=20, freq='5T',
1277+
tz='US/Eastern')
1278+
ts = Series(np.random.randn(len(rng)), rng)
1279+
df = DataFrame(np.random.randn(len(rng), 4), index=rng)
1280+
ts2 = Series(np.random.randn(len(rng2)), rng2)
1281+
df2 = DataFrame(np.random.randn(len(rng2), 4), index=rng2)
1282+
1283+
result = ts.append(ts2)
1284+
result_df = df.append(df2)
1285+
self.assert_(result.index.equals(rng3))
1286+
self.assert_(result_df.index.equals(rng3))
1287+
1288+
appended = rng.append(rng2)
1289+
self.assert_(appended.equals(rng3))
1290+
12681291
def test_set_dataframe_column_ns_dtype(self):
12691292
x = DataFrame([datetime.now(), datetime.now()])
12701293
self.assert_(x[0].dtype == np.dtype('M8[ns]'))

0 commit comments

Comments
 (0)