From 0d6884bac947efb522269537e7a5c9c276fbcb34 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Fri, 26 Feb 2016 16:05:59 -0500 Subject: [PATCH 1/7] API/COMPAT: add pydatetime-style positional args to Timestamp constructor --- doc/source/timeseries.rst | 1 + doc/source/whatsnew/v0.18.2.txt | 8 ++++++++ pandas/tseries/tests/test_tslib.py | 30 ++++++++++++++++++++++++++++++ pandas/tslib.pyx | 29 ++++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/doc/source/timeseries.rst b/doc/source/timeseries.rst index 114607f117756..62601821488d3 100644 --- a/doc/source/timeseries.rst +++ b/doc/source/timeseries.rst @@ -98,6 +98,7 @@ time. pd.Timestamp(datetime(2012, 5, 1)) pd.Timestamp('2012-05-01') + pd.Timestamp(2012, 5, 1) However, in many cases it is more natural to associate things like change variables with a time span instead. The span represented by ``Period`` can be diff --git a/doc/source/whatsnew/v0.18.2.txt b/doc/source/whatsnew/v0.18.2.txt index b86a7a81625e2..acd3a99848b10 100644 --- a/doc/source/whatsnew/v0.18.2.txt +++ b/doc/source/whatsnew/v0.18.2.txt @@ -38,6 +38,14 @@ Other enhancements idx = pd.Index(["a1a2", "b1", "c1"]) idx.str.extractall("[ab](?P\d)") +- ``Timestamp``s can now accept positional parameters like :func:`datetime.datetime`: + + .. ipython:: python + + Timestamp(2012, 1, 1) + + Timestamp(2012, 1, 1, 8, 30) + .. _whatsnew_0182.api: API changes diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py index 4543047a8a72a..5b7b10db2dd34 100644 --- a/pandas/tseries/tests/test_tslib.py +++ b/pandas/tseries/tests/test_tslib.py @@ -180,6 +180,36 @@ def test_constructor_invalid(self): with tm.assertRaisesRegexp(ValueError, 'Cannot convert Period'): Timestamp(Period('1000-01-01')) + def test_constructor_positional(self): + # GH 10758 + with tm.assertRaises(TypeError): + Timestamp(2000, 1) + with tm.assertRaises(ValueError): + Timestamp(2000, 0, 1) + with tm.assertRaises(ValueError): + Timestamp(2000, 13, 1) + with tm.assertRaises(ValueError): + Timestamp(2000, 1, 0) + with tm.assertRaises(ValueError): + Timestamp(2000, 1, 32) + with tm.assertRaises(TypeError): + Timestamp(2000, 1, 1, None) + with tm.assertRaises(TypeError): + Timestamp(2000, 1, 1, None, None) + with tm.assertRaises(TypeError): + Timestamp(2000, 1, 1, None, None, None) + + ts = Timestamp(2000, 1, 2) + + actual = ts.to_pydatetime() + expected = datetime.datetime(2000, 1, 2) + self.assertEqual(actual, expected) + self.assertEqual(type(actual), type(expected)) + + pos_args = [2000, 1, 2, 3, 4, 5, 999999] + self.assertEqual(Timestamp(*pos_args).to_pydatetime(), + datetime.datetime(*pos_args)) + def test_conversion(self): # GH 9255 ts = Timestamp('2000-01-01') diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index a240558025090..ec2b7ca5df263 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -216,6 +216,7 @@ cdef inline bint _is_fixed_offset(object tz): _zero_time = datetime_time(0, 0) +_no_unit = object() # Python front end to C extension type _Timestamp # This serves as the box for datetime64 @@ -288,10 +289,36 @@ class Timestamp(_Timestamp): def combine(cls, date, time): return cls(datetime.combine(date, time)) - def __new__(cls, object ts_input, object offset=None, tz=None, unit=None): + def __new__(cls, + object ts_input, object offset=None, tz=None, unit=_no_unit, + minute=0, second=0, microsecond=0, tzinfo=None): + # The parameter list folds together legacy parameter names (the first + # four) and positional parameter names from pydatetime (starting with + # `minute`). + # + # When using the pydatetime form, the first three parameters are + # required, but the fourth (called `unit` but standing in for `hour`) + # is optional. However, when using the legacy form, only the first + # parameter is required and the fourth parameter defaults to `None`. + # We use a special non-`None` constant to distinguish when callers + # pass `None` as the fourth pydatetime parameter. + cdef _TSObject ts cdef _Timestamp ts_base + if offset is not None and is_integer_object(offset): + # User passed positional arguments: + # Timestamp(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]]) + # When using the positional form, the first three parameters are + # required. Assign defaults to the rest. + if unit is _no_unit: + unit = 0 + # Forward positional arguments to datetime constructor. + return Timestamp(datetime(ts_input, offset, tz, unit, minute, second, microsecond, tzinfo), + tz=tzinfo) + elif unit is _no_unit: + unit = None + ts = convert_to_tsobject(ts_input, tz, unit, 0, 0) if ts.value == NPY_NAT: From 5c34c043768fb23957c8246c5b8d8d5e8863118d Mon Sep 17 00:00:00 2001 From: John Freeman Date: Tue, 10 May 2016 22:34:30 -0500 Subject: [PATCH 2/7] Support positional and keyword arguments for Timestamp --- pandas/tseries/tests/test_tslib.py | 6 ---- pandas/tslib.pyx | 48 +++++++++++++++++------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py index 5b7b10db2dd34..756648bc79260 100644 --- a/pandas/tseries/tests/test_tslib.py +++ b/pandas/tseries/tests/test_tslib.py @@ -192,12 +192,6 @@ def test_constructor_positional(self): Timestamp(2000, 1, 0) with tm.assertRaises(ValueError): Timestamp(2000, 1, 32) - with tm.assertRaises(TypeError): - Timestamp(2000, 1, 1, None) - with tm.assertRaises(TypeError): - Timestamp(2000, 1, 1, None, None) - with tm.assertRaises(TypeError): - Timestamp(2000, 1, 1, None, None, None) ts = Timestamp(2000, 1, 2) diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index ec2b7ca5df263..65bc34a0bd8ce 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -216,7 +216,6 @@ cdef inline bint _is_fixed_offset(object tz): _zero_time = datetime_time(0, 0) -_no_unit = object() # Python front end to C extension type _Timestamp # This serves as the box for datetime64 @@ -290,34 +289,41 @@ class Timestamp(_Timestamp): return cls(datetime.combine(date, time)) def __new__(cls, - object ts_input, object offset=None, tz=None, unit=_no_unit, - minute=0, second=0, microsecond=0, tzinfo=None): + object ts_input=None, object offset=None, tz=None, unit=None, + year=None, month=None, day=None, + hour=None, minute=None, second=None, microsecond=None, + tzinfo=None): # The parameter list folds together legacy parameter names (the first - # four) and positional parameter names from pydatetime (starting with - # `minute`). + # four) and positional and keyword parameter names from pydatetime. # - # When using the pydatetime form, the first three parameters are - # required, but the fourth (called `unit` but standing in for `hour`) - # is optional. However, when using the legacy form, only the first - # parameter is required and the fourth parameter defaults to `None`. - # We use a special non-`None` constant to distinguish when callers - # pass `None` as the fourth pydatetime parameter. + # There are three calling forms: + # - In the legacy form, the first parameter, ts_input, is required + # and may be datetime-like, str, int, or float. The second parameter, + # offset, is optional and may be str or DateOffset. + # - ints in the first, second, and third arguments indicate + # pydatetime positional arguments. Only the first 8 arguments + # (standing in for year, month, day, hour, minute, second, + # microsecond, tzinfo) may be non-None. As a shortcut, we just check + # that the second argument is an int. + # - Nones for the first four (legacy) arguments indicate pydatetime + # keyword arguments. year, month, and day are required. As a + # shortcut, we just check that the first argument is None. + # + # Mixing pydatetime positional and keyword arguments is forbidden! cdef _TSObject ts cdef _Timestamp ts_base - if offset is not None and is_integer_object(offset): + if ts_input is None: + # User passed keyword arguments. + return Timestamp(datetime(year, month, day, hour or 0, + minute or 0, second or 0, microsecond or 0, tzinfo), + tz=tzinfo) + if is_integer_object(offset): # User passed positional arguments: # Timestamp(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]]) - # When using the positional form, the first three parameters are - # required. Assign defaults to the rest. - if unit is _no_unit: - unit = 0 - # Forward positional arguments to datetime constructor. - return Timestamp(datetime(ts_input, offset, tz, unit, minute, second, microsecond, tzinfo), - tz=tzinfo) - elif unit is _no_unit: - unit = None + return Timestamp(datetime(ts_input, offset, tz, unit or 0, + year or 0, month or 0, day or 0, hour), tz=hour) ts = convert_to_tsobject(ts_input, tz, unit, 0, 0) From 6fad30bc6150f00f69fbb49ebb913b02d7397d42 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Wed, 11 May 2016 18:54:54 -0500 Subject: [PATCH 3/7] Fix docstring; add tests for kwargs --- pandas/tseries/tests/test_tslib.py | 33 ++++++++++++++++++++++++++++++ pandas/tslib.pyx | 24 +++++++++++++++++++--- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py index 756648bc79260..87194d575df93 100644 --- a/pandas/tseries/tests/test_tslib.py +++ b/pandas/tseries/tests/test_tslib.py @@ -204,6 +204,39 @@ def test_constructor_positional(self): self.assertEqual(Timestamp(*pos_args).to_pydatetime(), datetime.datetime(*pos_args)) + def test_constructor_keyword(self): + # GH 10758 + with tm.assertRaises(TypeError): + Timestamp(year=2000, month=1) + with tm.assertRaises(ValueError): + Timestamp(year=2000, month=0, day=1) + with tm.assertRaises(ValueError): + Timestamp(year=2000, month=13, day=1) + with tm.assertRaises(ValueError): + Timestamp(year=2000, month=1, day=0) + with tm.assertRaises(ValueError): + Timestamp(year=2000, month=1, day=32) + + ts = Timestamp(year=2000, month=1, day=2) + + actual = ts.to_pydatetime() + expected = datetime.datetime(year=2000, month=1, day=2) + self.assertEqual(actual, expected) + self.assertEqual(type(actual), type(expected)) + + pos_args = [2000, 1, 2, 3, 4, 5, 999999] + kw_args = { + 'year': 2000, + 'month': 1, + 'day': 2, + 'hour': 3, + 'minute': 4, + 'second': 5, + 'microsecond': 999999, + } + self.assertEqual(Timestamp(**kw_args).to_pydatetime(), + datetime.datetime(**kw_args)) + def test_conversion(self): # GH 9255 ts = Timestamp('2000-01-01') diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 65bc34a0bd8ce..fc74662c1f498 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -225,8 +225,12 @@ class Timestamp(_Timestamp): for the entries that make up a DatetimeIndex, and other timeseries oriented data structures in pandas. - Parameters - ---------- + There are essentially three calling conventions for the constructor. The + first, legacy form accepts four parameters. They can be passed by + position or keyword. + + Legacy Parameters + ----------------- ts_input : datetime-like, str, int, float Value to be converted to Timestamp offset : str, DateOffset @@ -235,6 +239,20 @@ class Timestamp(_Timestamp): Time zone for time which Timestamp will have. unit : string numpy unit used for conversion, if ts_input is int or float + + The other two forms copy the parameters from datetime.datetime. They can + be passed by either position or keyword, but not both mixed together. + + datetime.datetime Parameters + ------------------------------ + year : int + month : int + day : int + hour : int [optional] + minute : int [optional] + second : int [optional] + microsecond : int [optional] + tzinfo : datetime.tzinfo [optional] """ @classmethod @@ -319,7 +337,7 @@ class Timestamp(_Timestamp): return Timestamp(datetime(year, month, day, hour or 0, minute or 0, second or 0, microsecond or 0, tzinfo), tz=tzinfo) - if is_integer_object(offset): + elif is_integer_object(offset): # User passed positional arguments: # Timestamp(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]]) return Timestamp(datetime(ts_input, offset, tz, unit or 0, From 9c1e2dc56c597f8fd18a89b4b074d95fac257087 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Thu, 12 May 2016 11:14:02 -0500 Subject: [PATCH 4/7] review fixes: versionadded, issue links, repr tests, legacy --- doc/source/whatsnew/v0.18.2.txt | 2 +- pandas/tseries/tests/test_tslib.py | 43 +++++++++++------------------- pandas/tslib.pyx | 11 +++++--- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/doc/source/whatsnew/v0.18.2.txt b/doc/source/whatsnew/v0.18.2.txt index acd3a99848b10..29a177f2bcceb 100644 --- a/doc/source/whatsnew/v0.18.2.txt +++ b/doc/source/whatsnew/v0.18.2.txt @@ -38,7 +38,7 @@ Other enhancements idx = pd.Index(["a1a2", "b1", "c1"]) idx.str.extractall("[ab](?P\d)") -- ``Timestamp``s can now accept positional parameters like :func:`datetime.datetime`: +- ``Timestamp``s can now accept positional and keyword parameters like :func:`datetime.datetime` (:issue:`10758`, :issue:`11630`) .. ipython:: python diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py index 87194d575df93..f5228228aaaab 100644 --- a/pandas/tseries/tests/test_tslib.py +++ b/pandas/tseries/tests/test_tslib.py @@ -193,16 +193,14 @@ def test_constructor_positional(self): with tm.assertRaises(ValueError): Timestamp(2000, 1, 32) - ts = Timestamp(2000, 1, 2) - - actual = ts.to_pydatetime() - expected = datetime.datetime(2000, 1, 2) - self.assertEqual(actual, expected) - self.assertEqual(type(actual), type(expected)) + # GH 11630 + self.assertEqual( + repr(Timestamp(2015, 11, 12)), + repr(Timestamp('20151112'))) - pos_args = [2000, 1, 2, 3, 4, 5, 999999] - self.assertEqual(Timestamp(*pos_args).to_pydatetime(), - datetime.datetime(*pos_args)) + self.assertEqual( + repr(Timestamp(2015, 11, 12, 1, 2, 3, 999999)), + repr(Timestamp('2015-11-12 01:02:03.999999'))) def test_constructor_keyword(self): # GH 10758 @@ -217,25 +215,14 @@ def test_constructor_keyword(self): with tm.assertRaises(ValueError): Timestamp(year=2000, month=1, day=32) - ts = Timestamp(year=2000, month=1, day=2) - - actual = ts.to_pydatetime() - expected = datetime.datetime(year=2000, month=1, day=2) - self.assertEqual(actual, expected) - self.assertEqual(type(actual), type(expected)) - - pos_args = [2000, 1, 2, 3, 4, 5, 999999] - kw_args = { - 'year': 2000, - 'month': 1, - 'day': 2, - 'hour': 3, - 'minute': 4, - 'second': 5, - 'microsecond': 999999, - } - self.assertEqual(Timestamp(**kw_args).to_pydatetime(), - datetime.datetime(**kw_args)) + self.assertEqual( + repr(Timestamp(year=2015, month=11, day=12)), + repr(Timestamp('20151112'))) + + self.assertEqual( + repr(Timestamp(year=2015, month=11, day=12, + hour=1, minute=2, second=3, microsecond=999999)), + repr(Timestamp('2015-11-12 01:02:03.999999'))) def test_conversion(self): # GH 9255 diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index fc74662c1f498..8efab8138db03 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -226,10 +226,10 @@ class Timestamp(_Timestamp): oriented data structures in pandas. There are essentially three calling conventions for the constructor. The - first, legacy form accepts four parameters. They can be passed by - position or keyword. + primary form accepts four parameters. They can be passed by position or + keyword. - Legacy Parameters + Parameters ----------------- ts_input : datetime-like, str, int, float Value to be converted to Timestamp @@ -243,8 +243,11 @@ class Timestamp(_Timestamp): The other two forms copy the parameters from datetime.datetime. They can be passed by either position or keyword, but not both mixed together. - datetime.datetime Parameters + :func:`datetime.datetime` Parameters ------------------------------ + + .. versionadded:: 0.18.2 + year : int month : int day : int From a334eabd32a4af92dc635a26007bf0eb5f7f2746 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Thu, 12 May 2016 11:22:20 -0500 Subject: [PATCH 5/7] PEP8 --- pandas/tseries/tests/test_tslib.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py index f5228228aaaab..9bfcabe8c13de 100644 --- a/pandas/tseries/tests/test_tslib.py +++ b/pandas/tseries/tests/test_tslib.py @@ -195,12 +195,12 @@ def test_constructor_positional(self): # GH 11630 self.assertEqual( - repr(Timestamp(2015, 11, 12)), - repr(Timestamp('20151112'))) + repr(Timestamp(2015, 11, 12)), + repr(Timestamp('20151112'))) self.assertEqual( - repr(Timestamp(2015, 11, 12, 1, 2, 3, 999999)), - repr(Timestamp('2015-11-12 01:02:03.999999'))) + repr(Timestamp(2015, 11, 12, 1, 2, 3, 999999)), + repr(Timestamp('2015-11-12 01:02:03.999999'))) def test_constructor_keyword(self): # GH 10758 @@ -216,13 +216,13 @@ def test_constructor_keyword(self): Timestamp(year=2000, month=1, day=32) self.assertEqual( - repr(Timestamp(year=2015, month=11, day=12)), - repr(Timestamp('20151112'))) + repr(Timestamp(year=2015, month=11, day=12)), + repr(Timestamp('20151112'))) self.assertEqual( - repr(Timestamp(year=2015, month=11, day=12, - hour=1, minute=2, second=3, microsecond=999999)), - repr(Timestamp('2015-11-12 01:02:03.999999'))) + repr(Timestamp(year=2015, month=11, day=12, + hour=1, minute=2, second=3, microsecond=999999)), + repr(Timestamp('2015-11-12 01:02:03.999999'))) def test_conversion(self): # GH 9255 From 0ac786f36e12c542669220b424602ca06faeb5e5 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Thu, 12 May 2016 20:27:04 -0500 Subject: [PATCH 6/7] Special object for missing first argument --- pandas/tslib.pyx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 8efab8138db03..f2dff6b5c9ebf 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -216,6 +216,7 @@ cdef inline bint _is_fixed_offset(object tz): _zero_time = datetime_time(0, 0) +_no_input = object() # Python front end to C extension type _Timestamp # This serves as the box for datetime64 @@ -230,7 +231,7 @@ class Timestamp(_Timestamp): keyword. Parameters - ----------------- + ---------- ts_input : datetime-like, str, int, float Value to be converted to Timestamp offset : str, DateOffset @@ -240,7 +241,7 @@ class Timestamp(_Timestamp): unit : string numpy unit used for conversion, if ts_input is int or float - The other two forms copy the parameters from datetime.datetime. They can + The other two forms mimic the parameters from datetime.datetime. They can be passed by either position or keyword, but not both mixed together. :func:`datetime.datetime` Parameters @@ -310,7 +311,7 @@ class Timestamp(_Timestamp): return cls(datetime.combine(date, time)) def __new__(cls, - object ts_input=None, object offset=None, tz=None, unit=None, + object ts_input=_no_input, object offset=None, tz=None, unit=None, year=None, month=None, day=None, hour=None, minute=None, second=None, microsecond=None, tzinfo=None): @@ -328,14 +329,14 @@ class Timestamp(_Timestamp): # that the second argument is an int. # - Nones for the first four (legacy) arguments indicate pydatetime # keyword arguments. year, month, and day are required. As a - # shortcut, we just check that the first argument is None. + # shortcut, we just check that the first argument was not passed. # # Mixing pydatetime positional and keyword arguments is forbidden! cdef _TSObject ts cdef _Timestamp ts_base - if ts_input is None: + if ts_input is _no_input: # User passed keyword arguments. return Timestamp(datetime(year, month, day, hour or 0, minute or 0, second or 0, microsecond or 0, tzinfo), From 79d63d4226359303751112ec26281c316596bfc7 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Fri, 13 May 2016 09:39:36 -0500 Subject: [PATCH 7/7] Format comments, test for NaT --- pandas/tseries/tests/test_tslib.py | 2 ++ pandas/tslib.pyx | 36 ++++++++++++++++-------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py index 9bfcabe8c13de..6ae1354730987 100644 --- a/pandas/tseries/tests/test_tslib.py +++ b/pandas/tseries/tests/test_tslib.py @@ -202,6 +202,8 @@ def test_constructor_positional(self): repr(Timestamp(2015, 11, 12, 1, 2, 3, 999999)), repr(Timestamp('2015-11-12 01:02:03.999999'))) + self.assertIs(Timestamp(None), pd.NaT) + def test_constructor_keyword(self): # GH 10758 with tm.assertRaises(TypeError): diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index f2dff6b5c9ebf..9588a6c420422 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -214,7 +214,6 @@ cdef inline bint _is_fixed_offset(object tz): return 0 return 1 - _zero_time = datetime_time(0, 0) _no_input = object() @@ -241,22 +240,22 @@ class Timestamp(_Timestamp): unit : string numpy unit used for conversion, if ts_input is int or float - The other two forms mimic the parameters from datetime.datetime. They can - be passed by either position or keyword, but not both mixed together. + The other two forms mimic the parameters from ``datetime.datetime``. They + can be passed by either position or keyword, but not both mixed together. :func:`datetime.datetime` Parameters - ------------------------------ + ------------------------------------ .. versionadded:: 0.18.2 year : int month : int day : int - hour : int [optional] - minute : int [optional] - second : int [optional] - microsecond : int [optional] - tzinfo : datetime.tzinfo [optional] + hour : int, optional, default is 0 + minute : int, optional, default is 0 + second : int, optional, default is 0 + microsecond : int, optional, default is 0 + tzinfo : datetime.tzinfo, optional, default is None """ @classmethod @@ -319,17 +318,20 @@ class Timestamp(_Timestamp): # four) and positional and keyword parameter names from pydatetime. # # There are three calling forms: + # # - In the legacy form, the first parameter, ts_input, is required - # and may be datetime-like, str, int, or float. The second parameter, - # offset, is optional and may be str or DateOffset. + # and may be datetime-like, str, int, or float. The second + # parameter, offset, is optional and may be str or DateOffset. + # # - ints in the first, second, and third arguments indicate - # pydatetime positional arguments. Only the first 8 arguments - # (standing in for year, month, day, hour, minute, second, - # microsecond, tzinfo) may be non-None. As a shortcut, we just check - # that the second argument is an int. + # pydatetime positional arguments. Only the first 8 arguments + # (standing in for year, month, day, hour, minute, second, + # microsecond, tzinfo) may be non-None. As a shortcut, we just + # check that the second argument is an int. + # # - Nones for the first four (legacy) arguments indicate pydatetime - # keyword arguments. year, month, and day are required. As a - # shortcut, we just check that the first argument was not passed. + # keyword arguments. year, month, and day are required. As a + # shortcut, we just check that the first argument was not passed. # # Mixing pydatetime positional and keyword arguments is forbidden!