import datetime as pdt import sys import pytest import rustapi_module.datetime as rdt from hypothesis import given from hypothesis import strategies as st from hypothesis.strategies import dates, datetimes # Constants def _get_utc(): timezone = getattr(pdt, "timezone", None) if timezone: return timezone.utc else: class UTC(pdt.tzinfo): def utcoffset(self, dt): return pdt.timedelta(0) def dst(self, dt): return pdt.timedelta(0) def tzname(self, dt): return "UTC" return UTC() UTC = _get_utc() MAX_SECONDS = int(pdt.timedelta.max.total_seconds()) MIN_SECONDS = int(pdt.timedelta.min.total_seconds()) try: MAX_DAYS = pdt.timedelta.max // pdt.timedelta(days=1) MIN_DAYS = pdt.timedelta.min // pdt.timedelta(days=1) except Exception: # Python 2 compatibility MAX_DAYS = MAX_SECONDS // pdt.timedelta(days=1).total_seconds() MIN_DAYS = MIN_SECONDS // pdt.timedelta(days=1).total_seconds() MAX_MICROSECONDS = int(pdt.timedelta.max.total_seconds() * 1e6) MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6) HAS_FOLD = getattr(pdt.datetime, "fold", False) # Helper functions get_timestamp = getattr(pdt.datetime, "timestamp", None) if get_timestamp is None: def get_timestamp(dt): # Python 2 compatibility return (dt - pdt.datetime(1970, 1, 1)).total_seconds() xfail_date_bounds = pytest.mark.xfail( sys.version_info < (3, 6), reason="Date bounds were not checked in the C constructor prior to version 3.6", ) # Tests def test_date(): assert rdt.make_date(2017, 9, 1) == pdt.date(2017, 9, 1) @given(d=st.dates()) def test_date_accessors(d): act = rdt.get_date_tuple(d) exp = (d.year, d.month, d.day) assert act == exp @xfail_date_bounds def test_invalid_date_fails(): with pytest.raises(ValueError): rdt.make_date(2017, 2, 30) @given(d=dates()) def test_date_from_timestamp(d): ts = get_timestamp(pdt.datetime.combine(d, pdt.time(0))) assert rdt.date_from_timestamp(int(ts)) == pdt.date.fromtimestamp(ts) @pytest.mark.parametrize( "args, kwargs", [ ((0, 0, 0, 0, None), {}), ((1, 12, 14, 124731), {}), ((1, 12, 14, 124731), {"tzinfo": UTC}), ], ) def test_time(args, kwargs): act = rdt.make_time(*args, **kwargs) exp = pdt.time(*args, **kwargs) assert act == exp assert act.tzinfo is exp.tzinfo @given(t=st.times()) def test_time(t): act = rdt.get_time_tuple(t) exp = (t.hour, t.minute, t.second, t.microsecond) assert act == exp @pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") @given(t=st.times()) def test_time_fold(t): t_nofold = t.replace(fold=0) t_fold = t.replace(fold=1) for t in (t_nofold, t_fold): act = rdt.get_time_tuple_fold(t) exp = (t.hour, t.minute, t.second, t.microsecond, t.fold) assert act == exp @pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") @pytest.mark.parametrize("fold", [False, True]) def test_time_fold(fold): t = rdt.time_with_fold(0, 0, 0, 0, None, fold) assert t.fold == fold @pytest.mark.xfail @pytest.mark.parametrize( "args", [(-1, 0, 0, 0), (0, -1, 0, 0), (0, 0, -1, 0), (0, 0, 0, -1)] ) def test_invalid_time_fails_xfail(args): with pytest.raises(ValueError): rdt.make_time(*args) @xfail_date_bounds @pytest.mark.parametrize( "args", [ (24, 0, 0, 0), (25, 0, 0, 0), (0, 60, 0, 0), (0, 61, 0, 0), (0, 0, 60, 0), (0, 0, 61, 0), (0, 0, 0, 1000000), ], ) def test_invalid_time_fails(args): with pytest.raises(ValueError): rdt.make_time(*args) @pytest.mark.parametrize( "args", [ ("0", 0, 0, 0), (0, "0", 0, 0), (0, 0, "0", 0), (0, 0, 0, "0"), (0, 0, 0, 0, "UTC"), ], ) def test_time_typeerror(args): with pytest.raises(TypeError): rdt.make_time(*args) @pytest.mark.parametrize( "args, kwargs", [((2017, 9, 1, 12, 45, 30, 0), {}), ((2017, 9, 1, 12, 45, 30, 0), {"tzinfo": UTC})], ) def test_datetime(args, kwargs): act = rdt.make_datetime(*args, **kwargs) exp = pdt.datetime(*args, **kwargs) assert act == exp assert act.tzinfo is exp.tzinfo @given(dt=st.datetimes()) def test_datetime_tuple(dt): act = rdt.get_datetime_tuple(dt) exp = dt.timetuple()[0:6] + (dt.microsecond,) assert act == exp @pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") @given(dt=st.datetimes()) def test_datetime_tuple_fold(dt): dt_fold = dt.replace(fold=1) dt_nofold = dt.replace(fold=0) for dt in (dt_fold, dt_nofold): act = rdt.get_datetime_tuple_fold(dt) exp = dt.timetuple()[0:6] + (dt.microsecond, dt.fold) assert act == exp @xfail_date_bounds def test_invalid_datetime_fails(): with pytest.raises(ValueError): rdt.make_datetime(2011, 1, 42, 0, 0, 0, 0) def test_datetime_typeerror(): with pytest.raises(TypeError): rdt.make_datetime("2011", 1, 1, 0, 0, 0, 0) @given(dt=datetimes()) def test_datetime_from_timestamp(dt): ts = get_timestamp(dt) assert rdt.datetime_from_timestamp(ts) == pdt.datetime.fromtimestamp(ts) def test_datetime_from_timestamp_tzinfo(): d1 = rdt.datetime_from_timestamp(0, tz=UTC) d2 = rdt.datetime_from_timestamp(0, tz=UTC) assert d1 == d2 assert d1.tzinfo is d2.tzinfo @pytest.mark.parametrize( "args", [ (0, 0, 0), (1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (1, -1, 0), (-1, 1, 0), (0, 0, 123456), (0, 0, -123456), ], ) def test_delta(args): act = pdt.timedelta(*args) exp = rdt.make_delta(*args) assert act == exp @given(td=st.timedeltas()) def test_delta_accessors(td): act = rdt.get_delta_tuple(td) exp = (td.days, td.seconds, td.microseconds) assert act == exp @pytest.mark.parametrize( "args,err_type", [ ((MAX_DAYS + 1, 0, 0), OverflowError), ((MIN_DAYS - 1, 0, 0), OverflowError), ((0, MAX_SECONDS + 1, 0), OverflowError), ((0, MIN_SECONDS - 1, 0), OverflowError), ((0, 0, MAX_MICROSECONDS + 1), OverflowError), ((0, 0, MIN_MICROSECONDS - 1), OverflowError), (("0", 0, 0), TypeError), ((0, "0", 0), TypeError), ((0, 0, "0"), TypeError), ], ) def test_delta_err(args, err_type): with pytest.raises(err_type): rdt.make_delta(*args) def test_issue_219(): rdt.issue_219() def test_tz_class(): tzi = rdt.TzClass() dt = pdt.datetime(2018, 1, 1, tzinfo=tzi) assert dt.tzname() == "+01:00" assert dt.utcoffset() == pdt.timedelta(hours=1) assert dt.dst() is None def test_tz_class_introspection(): tzi = rdt.TzClass() assert tzi.__class__ == rdt.TzClass assert repr(tzi).startswith("