Skip to content

Commit 97dddbc

Browse files
committed
BUG: don't segfault on Timestamp construction with pendulum objects
closes pandas-dev#15986
1 parent eccafcc commit 97dddbc

File tree

8 files changed

+59
-4
lines changed

8 files changed

+59
-4
lines changed

ci/requirements-2.7.pip

+1
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ py
88
PyCrypto
99
mock
1010
ipython
11+
pendulum

ci/requirements-3.6.pip

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
brotlipy
2+
pendulum

doc/source/whatsnew/v0.23.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ Conversion
282282
- Bug in :meth:`Index.astype` with a categorical dtype where the resultant index is not converted to a :class:`CategoricalIndex` for all types of index (:issue:`18630`)
283283
- Bug in :meth:`Series.astype` and ``Categorical.astype()`` where an existing categorical data does not get updated (:issue:`10696`, :issue:`18593`)
284284
- Bug in :class:`Series` constructor with an int or float list where specifying ``dtype=str``, ``dtype='str'`` or ``dtype='U'`` failed to convert the data elements to strings (:issue:`16605`)
285-
285+
- Bug ``Timestamp`` construction with ``pendulum`` objects causing a segfault; pandas does not support these. (:issue:`15986`)
286286

287287
Indexing
288288
^^^^^^^^

pandas/_libs/tslibs/conversion.pyx

+1-2
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,7 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
360360

361361
if not PyDateTime_CheckExact(ts):
362362
# datetime instance but not datetime type --> Timestamp
363-
obj.value += ts.nanosecond
364-
obj.dts.ps = ts.nanosecond * 1000
363+
nanos = getattr(ts, 'nanosecond', 0)
365364

366365
if nanos:
367366
obj.value += nanos

pandas/_libs/tslibs/timezones.pyx

+5-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ cpdef get_utcoffset(tzinfo, obj):
161161
try:
162162
return tzinfo._utcoffset
163163
except AttributeError:
164-
return tzinfo.utcoffset(obj)
164+
result = tzinfo.utcoffset(obj)
165+
if result is None:
166+
raise ValueError("tzinfo of type {} is not supported".format(
167+
type(tzinfo)))
168+
return result
165169

166170

167171
cdef inline bint is_fixed_offset(object tz):

pandas/tests/indexes/datetimes/test_tools.py

+9
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,15 @@ def test_datetime_invalid_datatype(self):
456456
with pytest.raises(TypeError):
457457
pd.to_datetime(pd.to_datetime)
458458

459+
@td.skip_if_no('pendulum')
460+
def test_constructor_invalid_tz(self):
461+
# construction is ok
462+
# gh-15986
463+
464+
import pendulum
465+
with pytest.raises(ValueError):
466+
date_range(start=pendulum.now(), end=pendulum.tomorrow())
467+
459468
@pytest.mark.parametrize("utc", [True, None])
460469
@pytest.mark.parametrize("format", ['%Y%m%d %H:%M:%S', None])
461470
@pytest.mark.parametrize("box", [True, False])

pandas/tests/scalar/test_timestamp.py

+18
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,24 @@ def test_constructor_offset_depr_fromordinal(self):
338338
Timestamp.fromordinal(base.toordinal(), offset='D', freq='D')
339339

340340

341+
@td.skip_if_no('pendulum')
342+
class TestTimestampPendulum(object):
343+
344+
def test_constructor_tz(self):
345+
# construction is ok
346+
# gh-15986
347+
348+
import pendulum
349+
now = pendulum.datetime(2017, 1, 2, 3, 4, 5, 6)
350+
351+
# works
352+
result = Timestamp(str(now), tz=now.tz)
353+
assert 'UTC' in str(result.tz)
354+
355+
result = Timestamp(now)
356+
assert 'UTC' in str(result.tz)
357+
358+
341359
class TestTimestamp(object):
342360

343361
def test_conversion(self):

pandas/tests/tseries/test_timezones.py

+23
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,29 @@ def test_tz_convert_tzlocal(self):
11671167
tm.assert_numpy_array_equal(dti2.asi8, dti.asi8)
11681168

11691169

1170+
@td.skip_if_no('pendulum')
1171+
class TestTimeZoneSupportPendulum(object):
1172+
1173+
def test_localize_tz(self):
1174+
# gh-15986
1175+
1176+
import pendulum
1177+
now = pendulum.datetime(2017, 1, 2, 3, 4, 5, 6)
1178+
result = Timestamp(now).tz_localize(None)
1179+
expected = Timestamp('2017-01-02 03:04:05.000006')
1180+
assert result == expected
1181+
1182+
def test_convert_tz(self):
1183+
# gh-15986
1184+
1185+
import pendulum
1186+
now = pendulum.datetime(2017, 1, 2, 3, 4, 5, 6)
1187+
result = Timestamp(now).tz_convert('US/Eastern')
1188+
expected = Timestamp('2017-01-02 03:04:05.000006',
1189+
tz='UTC').tz_convert('US/Eastern')
1190+
assert result == expected
1191+
1192+
11701193
class TestTimeZoneCacheKey(object):
11711194

11721195
def test_cache_keys_are_distinct_for_pytz_vs_dateutil(self):

0 commit comments

Comments
 (0)