Skip to content

Commit f2b083b

Browse files
committed
Merge pull request #11997 from jreback/td
BUG: accept unicode in Timedelta constructor, #11995
2 parents e684388 + 3acd93d commit f2b083b

File tree

3 files changed

+83
-28
lines changed

3 files changed

+83
-28
lines changed

doc/source/whatsnew/v0.18.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ Bug Fixes
438438
- Regression in ``.clip`` with tz-aware datetimes (:issue:`11838`)
439439
- Bug in ``date_range`` when the boundaries fell on the frequency (:issue:`11804`)
440440
- Bug in consistency of passing nested dicts to ``.groupby(...).agg(...)`` (:issue:`9052`)
441-
441+
- Accept unicode in ``Timedelta`` constructor (:issue:`11995`)
442442

443443

444444

pandas/tseries/tests/test_timedeltas.py

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -161,25 +161,64 @@ def test_construction(self):
161161
self.assertTrue(isnull(Timedelta('nat')))
162162

163163
# offset
164-
self.assertEqual(to_timedelta(pd.offsets.Hour(2)),Timedelta('0 days, 02:00:00'))
165-
self.assertEqual(Timedelta(pd.offsets.Hour(2)),Timedelta('0 days, 02:00:00'))
166-
self.assertEqual(Timedelta(pd.offsets.Second(2)),Timedelta('0 days, 00:00:02'))
164+
self.assertEqual(to_timedelta(pd.offsets.Hour(2)),
165+
Timedelta('0 days, 02:00:00'))
166+
self.assertEqual(Timedelta(pd.offsets.Hour(2)),
167+
Timedelta('0 days, 02:00:00'))
168+
self.assertEqual(Timedelta(pd.offsets.Second(2)),
169+
Timedelta('0 days, 00:00:02'))
170+
171+
# unicode
172+
# GH 11995
173+
expected = Timedelta('1H')
174+
result = pd.Timedelta(u'1H')
175+
self.assertEqual(result, expected)
176+
self.assertEqual(to_timedelta(pd.offsets.Hour(2)),
177+
Timedelta(u'0 days, 02:00:00'))
178+
179+
self.assertRaises(ValueError, lambda: Timedelta(u'foo bar'))
167180

168181
def test_round(self):
169182

170183
t1 = Timedelta('1 days 02:34:56.789123456')
171184
t2 = Timedelta('-1 days 02:34:56.789123456')
172185

173186
for (freq, s1, s2) in [('N', t1, t2),
174-
('U', Timedelta('1 days 02:34:56.789123000'),Timedelta('-1 days 02:34:56.789123000')),
175-
('L', Timedelta('1 days 02:34:56.789000000'),Timedelta('-1 days 02:34:56.789000000')),
176-
('S', Timedelta('1 days 02:34:56'),Timedelta('-1 days 02:34:56')),
177-
('2S', Timedelta('1 days 02:34:56'),Timedelta('-1 days 02:34:56')),
178-
('5S', Timedelta('1 days 02:34:55'),Timedelta('-1 days 02:34:55')),
179-
('T', Timedelta('1 days 02:34:00'),Timedelta('-1 days 02:34:00')),
180-
('12T', Timedelta('1 days 02:24:00'),Timedelta('-1 days 02:24:00')),
181-
('H', Timedelta('1 days 02:00:00'),Timedelta('-1 days 02:00:00')),
182-
('d', Timedelta('1 days'),Timedelta('-1 days'))]:
187+
('U',
188+
Timedelta('1 days 02:34:56.789123000'),
189+
Timedelta('-1 days 02:34:56.789123000')
190+
),
191+
('L',
192+
Timedelta('1 days 02:34:56.789000000'),
193+
Timedelta('-1 days 02:34:56.789000000')
194+
),
195+
('S',
196+
Timedelta('1 days 02:34:56'),
197+
Timedelta('-1 days 02:34:56')
198+
),
199+
('2S',
200+
Timedelta('1 days 02:34:56'),
201+
Timedelta('-1 days 02:34:56')
202+
),
203+
('5S',
204+
Timedelta('1 days 02:34:55'),
205+
Timedelta('-1 days 02:34:55')
206+
),
207+
('T',
208+
Timedelta('1 days 02:34:00'),
209+
Timedelta('-1 days 02:34:00')
210+
),
211+
('12T',
212+
Timedelta('1 days 02:24:00'),
213+
Timedelta('-1 days 02:24:00')),
214+
('H',
215+
Timedelta('1 days 02:00:00'),
216+
Timedelta('-1 days 02:00:00')
217+
),
218+
('d',
219+
Timedelta('1 days'),
220+
Timedelta('-1 days')
221+
)]:
183222
r1 = t1.round(freq)
184223
self.assertEqual(r1, s1)
185224
r2 = t2.round(freq)
@@ -1104,20 +1143,32 @@ def test_components(self):
11041143
self.assertTrue(result.iloc[1].isnull().all())
11051144

11061145
def test_constructor(self):
1107-
expected = TimedeltaIndex(['1 days','1 days 00:00:05',
1108-
'2 days','2 days 00:00:02','0 days 00:00:03'])
1109-
result = TimedeltaIndex(['1 days','1 days, 00:00:05',
1110-
np.timedelta64(2,'D'),
1111-
timedelta(days=2,seconds=2),
1146+
expected = TimedeltaIndex(['1 days', '1 days 00:00:05',
1147+
'2 days', '2 days 00:00:02',
1148+
'0 days 00:00:03'])
1149+
result = TimedeltaIndex(['1 days', '1 days, 00:00:05',
1150+
np.timedelta64(2, 'D'),
1151+
timedelta(days=2, seconds=2),
1152+
pd.offsets.Second(3)])
1153+
tm.assert_index_equal(result, expected)
1154+
1155+
# unicode
1156+
result = TimedeltaIndex([u'1 days', '1 days, 00:00:05',
1157+
np.timedelta64(2, 'D'),
1158+
timedelta(days=2, seconds=2),
11121159
pd.offsets.Second(3)])
1113-
tm.assert_index_equal(result,expected)
11141160

1115-
expected = TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:01', '0 days 00:00:02'])
1161+
expected = TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:01',
1162+
'0 days 00:00:02'])
11161163
tm.assert_index_equal(TimedeltaIndex(range(3), unit='s'), expected)
1117-
expected = TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:05', '0 days 00:00:09'])
1164+
expected = TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:05',
1165+
'0 days 00:00:09'])
11181166
tm.assert_index_equal(TimedeltaIndex([0, 5, 9], unit='s'), expected)
1119-
expected = TimedeltaIndex(['0 days 00:00:00.400', '0 days 00:00:00.450', '0 days 00:00:01.200'])
1120-
tm.assert_index_equal(TimedeltaIndex([400, 450, 1200], unit='ms'), expected)
1167+
expected = TimedeltaIndex(['0 days 00:00:00.400',
1168+
'0 days 00:00:00.450',
1169+
'0 days 00:00:01.200'])
1170+
tm.assert_index_equal(TimedeltaIndex([400, 450, 1200], unit='ms'),
1171+
expected)
11211172

11221173
def test_constructor_coverage(self):
11231174
rng = timedelta_range('1 days', periods=10.5)

pandas/tslib.pyx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ from cpython cimport (
1717
PyLong_Check,
1818
PyObject_RichCompareBool,
1919
PyObject_RichCompare,
20-
PyString_Check,
21-
Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE
20+
Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE,
21+
PyUnicode_Check,
22+
PyUnicode_AsUTF8String,
2223
)
2324

2425
# Cython < 0.17 doesn't have this in cpython
@@ -31,10 +32,9 @@ cdef extern from "datetime_helper.h":
3132

3233
# this is our datetime.pxd
3334
from datetime cimport cmp_pandas_datetimestruct
34-
from util cimport is_integer_object, is_float_object, is_datetime64_object, is_timedelta64_object
35-
3635
from libc.stdlib cimport free
3736

37+
from util cimport is_integer_object, is_float_object, is_datetime64_object, is_timedelta64_object
3838
cimport util
3939

4040
from datetime cimport *
@@ -2769,7 +2769,7 @@ cdef inline parse_timedelta_string(object ts, coerce=False):
27692769
"""
27702770

27712771
cdef:
2772-
str c
2772+
unicode c
27732773
bint neg=0, have_dot=0, have_value=0, have_hhmmss=0
27742774
object current_unit=None
27752775
int64_t result=0, m=0, r
@@ -2783,6 +2783,10 @@ cdef inline parse_timedelta_string(object ts, coerce=False):
27832783
if ts in _nat_strings or not len(ts):
27842784
return NPY_NAT
27852785

2786+
# decode ts if necessary
2787+
if not PyUnicode_Check(ts) and not PY3:
2788+
ts = str(ts).decode('utf-8')
2789+
27862790
for c in ts:
27872791

27882792
# skip whitespace / commas

0 commit comments

Comments
 (0)