Skip to content

Commit 72164a8

Browse files
thejohnfreemanjreback
authored andcommitted
API/COMPAT: add pydatetime-style positional args to Timestamp constructor
closes #10758 closes #11630 Author: John Freeman <[email protected]> Closes #12482 from thejohnfreeman/GH-10758 and squashes the following commits: 79d63d4 [John Freeman] Format comments, test for NaT 0ac786f [John Freeman] Special object for missing first argument a334eab [John Freeman] PEP8 9c1e2dc [John Freeman] review fixes: versionadded, issue links, repr tests, legacy 6fad30b [John Freeman] Fix docstring; add tests for kwargs 5c34c04 [John Freeman] Support positional and keyword arguments for Timestamp 0d6884b [John Freeman] API/COMPAT: add pydatetime-style positional args to Timestamp constructor
1 parent cc25040 commit 72164a8

File tree

4 files changed

+114
-2
lines changed

4 files changed

+114
-2
lines changed

doc/source/timeseries.rst

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ time.
9898
9999
pd.Timestamp(datetime(2012, 5, 1))
100100
pd.Timestamp('2012-05-01')
101+
pd.Timestamp(2012, 5, 1)
101102
102103
However, in many cases it is more natural to associate things like change
103104
variables with a time span instead. The span represented by ``Period`` can be

doc/source/whatsnew/v0.18.2.txt

+8
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ Other enhancements
3939
idx = pd.Index(["a1a2", "b1", "c1"])
4040
idx.str.extractall("[ab](?P<digit>\d)")
4141

42+
- ``Timestamp``s can now accept positional and keyword parameters like :func:`datetime.datetime` (:issue:`10758`, :issue:`11630`)
43+
44+
.. ipython:: python
45+
46+
pd.Timestamp(2012, 1, 1)
47+
48+
pd.Timestamp(year=2012, month=1, day=1, hour=8, minute=30)
49+
4250
.. _whatsnew_0182.api:
4351

4452
API changes

pandas/tseries/tests/test_tslib.py

+46
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,52 @@ def test_constructor_invalid(self):
180180
with tm.assertRaisesRegexp(ValueError, 'Cannot convert Period'):
181181
Timestamp(Period('1000-01-01'))
182182

183+
def test_constructor_positional(self):
184+
# GH 10758
185+
with tm.assertRaises(TypeError):
186+
Timestamp(2000, 1)
187+
with tm.assertRaises(ValueError):
188+
Timestamp(2000, 0, 1)
189+
with tm.assertRaises(ValueError):
190+
Timestamp(2000, 13, 1)
191+
with tm.assertRaises(ValueError):
192+
Timestamp(2000, 1, 0)
193+
with tm.assertRaises(ValueError):
194+
Timestamp(2000, 1, 32)
195+
196+
# GH 11630
197+
self.assertEqual(
198+
repr(Timestamp(2015, 11, 12)),
199+
repr(Timestamp('20151112')))
200+
201+
self.assertEqual(
202+
repr(Timestamp(2015, 11, 12, 1, 2, 3, 999999)),
203+
repr(Timestamp('2015-11-12 01:02:03.999999')))
204+
205+
self.assertIs(Timestamp(None), pd.NaT)
206+
207+
def test_constructor_keyword(self):
208+
# GH 10758
209+
with tm.assertRaises(TypeError):
210+
Timestamp(year=2000, month=1)
211+
with tm.assertRaises(ValueError):
212+
Timestamp(year=2000, month=0, day=1)
213+
with tm.assertRaises(ValueError):
214+
Timestamp(year=2000, month=13, day=1)
215+
with tm.assertRaises(ValueError):
216+
Timestamp(year=2000, month=1, day=0)
217+
with tm.assertRaises(ValueError):
218+
Timestamp(year=2000, month=1, day=32)
219+
220+
self.assertEqual(
221+
repr(Timestamp(year=2015, month=11, day=12)),
222+
repr(Timestamp('20151112')))
223+
224+
self.assertEqual(
225+
repr(Timestamp(year=2015, month=11, day=12,
226+
hour=1, minute=2, second=3, microsecond=999999)),
227+
repr(Timestamp('2015-11-12 01:02:03.999999')))
228+
183229
def test_conversion(self):
184230
# GH 9255
185231
ts = Timestamp('2000-01-01')

pandas/tslib.pyx

+59-2
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ cdef inline bint _is_fixed_offset(object tz):
214214
return 0
215215
return 1
216216

217-
218217
_zero_time = datetime_time(0, 0)
218+
_no_input = object()
219219

220220
# Python front end to C extension type _Timestamp
221221
# This serves as the box for datetime64
@@ -225,6 +225,10 @@ class Timestamp(_Timestamp):
225225
for the entries that make up a DatetimeIndex, and other timeseries
226226
oriented data structures in pandas.
227227
228+
There are essentially three calling conventions for the constructor. The
229+
primary form accepts four parameters. They can be passed by position or
230+
keyword.
231+
228232
Parameters
229233
----------
230234
ts_input : datetime-like, str, int, float
@@ -235,6 +239,23 @@ class Timestamp(_Timestamp):
235239
Time zone for time which Timestamp will have.
236240
unit : string
237241
numpy unit used for conversion, if ts_input is int or float
242+
243+
The other two forms mimic the parameters from ``datetime.datetime``. They
244+
can be passed by either position or keyword, but not both mixed together.
245+
246+
:func:`datetime.datetime` Parameters
247+
------------------------------------
248+
249+
.. versionadded:: 0.18.2
250+
251+
year : int
252+
month : int
253+
day : int
254+
hour : int, optional, default is 0
255+
minute : int, optional, default is 0
256+
second : int, optional, default is 0
257+
microsecond : int, optional, default is 0
258+
tzinfo : datetime.tzinfo, optional, default is None
238259
"""
239260

240261
@classmethod
@@ -288,10 +309,46 @@ class Timestamp(_Timestamp):
288309
def combine(cls, date, time):
289310
return cls(datetime.combine(date, time))
290311

291-
def __new__(cls, object ts_input, object offset=None, tz=None, unit=None):
312+
def __new__(cls,
313+
object ts_input=_no_input, object offset=None, tz=None, unit=None,
314+
year=None, month=None, day=None,
315+
hour=None, minute=None, second=None, microsecond=None,
316+
tzinfo=None):
317+
# The parameter list folds together legacy parameter names (the first
318+
# four) and positional and keyword parameter names from pydatetime.
319+
#
320+
# There are three calling forms:
321+
#
322+
# - In the legacy form, the first parameter, ts_input, is required
323+
# and may be datetime-like, str, int, or float. The second
324+
# parameter, offset, is optional and may be str or DateOffset.
325+
#
326+
# - ints in the first, second, and third arguments indicate
327+
# pydatetime positional arguments. Only the first 8 arguments
328+
# (standing in for year, month, day, hour, minute, second,
329+
# microsecond, tzinfo) may be non-None. As a shortcut, we just
330+
# check that the second argument is an int.
331+
#
332+
# - Nones for the first four (legacy) arguments indicate pydatetime
333+
# keyword arguments. year, month, and day are required. As a
334+
# shortcut, we just check that the first argument was not passed.
335+
#
336+
# Mixing pydatetime positional and keyword arguments is forbidden!
337+
292338
cdef _TSObject ts
293339
cdef _Timestamp ts_base
294340

341+
if ts_input is _no_input:
342+
# User passed keyword arguments.
343+
return Timestamp(datetime(year, month, day, hour or 0,
344+
minute or 0, second or 0, microsecond or 0, tzinfo),
345+
tz=tzinfo)
346+
elif is_integer_object(offset):
347+
# User passed positional arguments:
348+
# Timestamp(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]])
349+
return Timestamp(datetime(ts_input, offset, tz, unit or 0,
350+
year or 0, month or 0, day or 0, hour), tz=hour)
351+
295352
ts = convert_to_tsobject(ts_input, tz, unit, 0, 0)
296353

297354
if ts.value == NPY_NAT:

0 commit comments

Comments
 (0)