summaryrefslogtreecommitdiffstats
path: root/tests/datetime
diff options
context:
space:
mode:
Diffstat (limited to 'tests/datetime')
-rw-r--r--tests/datetime/__init__.py0
-rw-r--r--tests/datetime/test_add.py268
-rw-r--r--tests/datetime/test_behavior.py172
-rw-r--r--tests/datetime/test_comparison.py394
-rw-r--r--tests/datetime/test_construct.py182
-rw-r--r--tests/datetime/test_create_from_timestamp.py24
-rw-r--r--tests/datetime/test_day_of_week_modifiers.py314
-rw-r--r--tests/datetime/test_diff.py880
-rw-r--r--tests/datetime/test_fluent_setters.py181
-rw-r--r--tests/datetime/test_from_format.py203
-rw-r--r--tests/datetime/test_getters.py248
-rw-r--r--tests/datetime/test_naive.py78
-rw-r--r--tests/datetime/test_replace.py61
-rw-r--r--tests/datetime/test_start_end_of.py285
-rw-r--r--tests/datetime/test_strings.py141
-rw-r--r--tests/datetime/test_sub.py251
-rw-r--r--tests/datetime/test_timezone.py38
17 files changed, 3720 insertions, 0 deletions
diff --git a/tests/datetime/__init__.py b/tests/datetime/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/datetime/__init__.py
diff --git a/tests/datetime/test_add.py b/tests/datetime/test_add.py
new file mode 100644
index 0000000..87ea39f
--- /dev/null
+++ b/tests/datetime/test_add.py
@@ -0,0 +1,268 @@
+from __future__ import annotations
+
+from datetime import timedelta
+
+import pytest
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_add_years_positive():
+ assert pendulum.datetime(1975, 1, 1).add(years=1).year == 1976
+
+
+def test_add_years_zero():
+ assert pendulum.datetime(1975, 1, 1).add(years=0).year == 1975
+
+
+def test_add_years_negative():
+ assert pendulum.datetime(1975, 1, 1).add(years=-1).year == 1974
+
+
+def test_add_months_positive():
+ assert pendulum.datetime(1975, 12, 1).add(months=1).month == 1
+
+
+def test_add_months_zero():
+ assert pendulum.datetime(1975, 12, 1).add(months=0).month == 12
+
+
+def test_add_months_negative():
+ assert pendulum.datetime(1975, 12, 1).add(months=-1).month == 11
+
+
+def test_add_month_with_overflow():
+ assert pendulum.datetime(2012, 1, 31).add(months=1).month == 2
+
+
+def test_add_days_positive():
+ assert pendulum.datetime(1975, 5, 31).add(days=1).day == 1
+
+
+def test_add_days_zero():
+ assert pendulum.datetime(1975, 5, 31).add(days=0).day == 31
+
+
+def test_add_days_negative():
+ assert pendulum.datetime(1975, 5, 31).add(days=-1).day == 30
+
+
+def test_add_weeks_positive():
+ assert pendulum.datetime(1975, 5, 21).add(weeks=1).day == 28
+
+
+def test_add_weeks_zero():
+ assert pendulum.datetime(1975, 5, 21).add(weeks=0).day == 21
+
+
+def test_add_weeks_negative():
+ assert pendulum.datetime(1975, 5, 21).add(weeks=-1).day == 14
+
+
+def test_add_hours_positive():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(hours=1).hour == 1
+
+
+def test_add_hours_zero():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(hours=0).hour == 0
+
+
+def test_add_hours_negative():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(hours=-1).hour == 23
+
+
+def test_add_minutes_positive():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(minutes=1).minute == 1
+
+
+def test_add_minutes_zero():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(minutes=0).minute == 0
+
+
+def test_add_minutes_negative():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(minutes=-1).minute == 59
+
+
+def test_add_seconds_positive():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(seconds=1).second == 1
+
+
+def test_add_seconds_zero():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(seconds=0).second == 0
+
+
+def test_add_seconds_negative():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).add(seconds=-1).second == 59
+
+
+def test_add_timedelta():
+ delta = timedelta(days=6, seconds=45, microseconds=123456)
+ d = pendulum.datetime(2015, 3, 14, 3, 12, 15, 654321)
+
+ d = d + delta
+ assert d.day == 20
+ assert d.minute == 13
+ assert d.second == 0
+ assert d.microsecond == 777777
+
+
+def test_add_duration():
+ duration = pendulum.duration(
+ years=2, months=3, days=6, seconds=45, microseconds=123456
+ )
+ d = pendulum.datetime(2015, 3, 14, 3, 12, 15, 654321)
+
+ d = d + duration
+ assert d.year == 2017
+ assert d.month == 6
+ assert d.day == 20
+ assert d.hour == 3
+ assert d.minute == 13
+ assert d.second == 0
+ assert d.microsecond == 777777
+
+
+def test_addition_invalid_type():
+ d = pendulum.datetime(2015, 3, 14, 3, 12, 15, 654321)
+
+ with pytest.raises(TypeError):
+ d + 3
+
+ with pytest.raises(TypeError):
+ 3 + d
+
+
+def test_add_to_fixed_timezones():
+ dt = pendulum.parse("2015-03-08T01:00:00-06:00")
+ dt = dt.add(weeks=1)
+ dt = dt.add(hours=1)
+
+ assert_datetime(dt, 2015, 3, 15, 2, 0, 0)
+ assert dt.timezone_name == "-06:00"
+ assert dt.offset == -6 * 3600
+
+
+def test_add_time_to_new_transition_skipped():
+ dt = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz="Europe/Paris")
+
+ assert_datetime(dt, 2013, 3, 31, 1, 59, 59, 999999)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 3600
+ assert not dt.is_dst()
+
+ dt = dt.add(microseconds=1)
+
+ assert_datetime(dt, 2013, 3, 31, 3, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+ dt = pendulum.datetime(2013, 3, 10, 1, 59, 59, 999999, tz="America/New_York")
+
+ assert_datetime(dt, 2013, 3, 10, 1, 59, 59, 999999)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -5 * 3600
+ assert not dt.is_dst()
+
+ dt = dt.add(microseconds=1)
+
+ assert_datetime(dt, 2013, 3, 10, 3, 0, 0, 0)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -4 * 3600
+ assert dt.is_dst()
+
+ dt = pendulum.datetime(1957, 4, 28, 1, 59, 59, 999999, tz="America/New_York")
+
+ assert_datetime(dt, 1957, 4, 28, 1, 59, 59, 999999)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -5 * 3600
+ assert not dt.is_dst()
+
+ dt = dt.add(microseconds=1)
+
+ assert_datetime(dt, 1957, 4, 28, 3, 0, 0, 0)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -4 * 3600
+ assert dt.is_dst()
+
+
+def test_add_time_to_new_transition_skipped_big():
+ dt = pendulum.datetime(2013, 3, 31, 1, tz="Europe/Paris")
+
+ assert_datetime(dt, 2013, 3, 31, 1, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 3600
+ assert not dt.is_dst()
+
+ dt = dt.add(weeks=1)
+
+ assert_datetime(dt, 2013, 4, 7, 1, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+
+def test_add_time_to_new_transition_repeated():
+ dt = pendulum.datetime(2013, 10, 27, 1, 59, 59, 999999, tz="Europe/Paris")
+ dt = dt.add(hours=1)
+
+ assert_datetime(dt, 2013, 10, 27, 2, 59, 59, 999999)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+ dt = dt.add(microseconds=1)
+
+ assert_datetime(dt, 2013, 10, 27, 2, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 3600
+ assert not dt.is_dst()
+
+ dt = pendulum.datetime(2013, 11, 3, 0, 59, 59, 999999, tz="America/New_York")
+ dt = dt.add(hours=1)
+
+ assert_datetime(dt, 2013, 11, 3, 1, 59, 59, 999999)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -4 * 3600
+ assert dt.is_dst()
+
+ dt = dt.add(microseconds=1)
+
+ assert_datetime(dt, 2013, 11, 3, 1, 0, 0, 0)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -5 * 3600
+ assert not dt.is_dst()
+
+
+def test_add_time_to_new_transition_repeated_big():
+ dt = pendulum.datetime(2013, 10, 27, 1, tz="Europe/Paris")
+
+ assert_datetime(dt, 2013, 10, 27, 1, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+ dt = dt.add(weeks=1)
+
+ assert_datetime(dt, 2013, 11, 3, 1, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 3600
+ assert not dt.is_dst()
+
+
+def test_add_interval():
+ dt = pendulum.datetime(2017, 3, 11, 10, 45, tz="America/Los_Angeles")
+ new = dt + pendulum.duration(hours=24)
+
+ assert_datetime(new, 2017, 3, 12, 11, 45)
+
+
+def test_period_over_midnight_tz():
+ start = pendulum.datetime(2018, 2, 25, tz="Europe/Paris")
+ end = start.add(hours=1)
+ period = end - start
+ new_end = start + period
+
+ assert new_end == end
diff --git a/tests/datetime/test_behavior.py b/tests/datetime/test_behavior.py
new file mode 100644
index 0000000..e02323a
--- /dev/null
+++ b/tests/datetime/test_behavior.py
@@ -0,0 +1,172 @@
+from __future__ import annotations
+
+import pickle
+
+from copy import deepcopy
+from datetime import date
+from datetime import datetime
+from datetime import time
+
+import pytest
+
+import pendulum
+
+from pendulum import timezone
+from pendulum.tz.timezone import Timezone
+from pendulum.utils._compat import zoneinfo
+
+
+@pytest.fixture
+def p():
+ return pendulum.datetime(2016, 8, 27, 12, 34, 56, 123456, tz="Europe/Paris")
+
+
+@pytest.fixture
+def p1(p):
+ return p.in_tz("America/New_York")
+
+
+@pytest.fixture
+def dt():
+ tz = timezone("Europe/Paris")
+
+ return tz.convert(datetime(2016, 8, 27, 12, 34, 56, 123456))
+
+
+def test_timetuple(p, dt):
+ assert dt.timetuple() == p.timetuple()
+
+
+def test_utctimetuple(p, dt):
+ assert dt.utctimetuple() == p.utctimetuple()
+
+
+def test_date(p, dt):
+ assert p.date() == dt.date()
+
+
+def test_time(p, dt):
+ assert p.time() == dt.time()
+
+
+def test_timetz(p, dt):
+ assert p.timetz() == dt.timetz()
+
+
+def test_astimezone(p, dt, p1):
+ assert p.astimezone(p1.tzinfo) == dt.astimezone(p1.tzinfo)
+
+
+def test_ctime(p, dt):
+ assert p.ctime() == dt.ctime()
+
+
+def test_isoformat(p, dt):
+ assert p.isoformat() == dt.isoformat()
+
+
+def test_utcoffset(p, dt):
+ assert p.utcoffset() == dt.utcoffset()
+
+
+def test_tzname(p, dt):
+ assert p.tzname() == dt.tzname()
+
+
+def test_dst(p, dt):
+ assert p.dst() == dt.dst()
+
+
+def test_toordinal(p, dt):
+ assert p.toordinal() == dt.toordinal()
+
+
+def test_weekday(p, dt):
+ assert p.weekday() == dt.weekday()
+
+
+def test_isoweekday(p, dt):
+ assert p.isoweekday() == dt.isoweekday()
+
+
+def test_isocalendar(p, dt):
+ assert p.isocalendar() == dt.isocalendar()
+
+
+def test_fromtimestamp():
+ p = pendulum.DateTime.fromtimestamp(0, pendulum.UTC)
+ dt = datetime.fromtimestamp(0, pendulum.UTC)
+
+ assert p == dt
+
+
+def test_utcfromtimestamp():
+ p = pendulum.DateTime.utcfromtimestamp(0)
+ dt = datetime.utcfromtimestamp(0)
+
+ assert p == dt
+
+
+def test_fromordinal():
+ assert datetime.fromordinal(730120) == pendulum.DateTime.fromordinal(730120)
+
+
+def test_combine():
+ p = pendulum.DateTime.combine(date(2016, 1, 1), time(1, 2, 3, 123456))
+ dt = datetime.combine(date(2016, 1, 1), time(1, 2, 3, 123456))
+
+ assert p == dt
+
+
+def test_hash(p, dt):
+ assert hash(p) == hash(dt)
+
+ dt1 = pendulum.datetime(2016, 8, 27, 12, 34, 56, 123456, tz="Europe/Paris")
+ dt2 = pendulum.datetime(2016, 8, 27, 12, 34, 56, 123456, tz="Europe/Paris")
+ dt3 = pendulum.datetime(2016, 8, 27, 12, 34, 56, 123456, tz="America/Toronto")
+
+ assert hash(dt1) == hash(dt2)
+ assert hash(dt1) != hash(dt3)
+
+
+def test_pickle():
+ dt1 = pendulum.datetime(2016, 8, 27, 12, 34, 56, 123456, tz="Europe/Paris")
+ s = pickle.dumps(dt1)
+ dt2 = pickle.loads(s)
+
+ assert dt1 == dt2
+
+
+def test_pickle_with_integer_tzinfo():
+ dt1 = pendulum.datetime(2016, 8, 27, 12, 34, 56, 123456, tz=0)
+ s = pickle.dumps(dt1)
+ dt2 = pickle.loads(s)
+
+ assert dt1 == dt2
+
+
+def test_proper_dst():
+ dt = pendulum.datetime(1941, 7, 1, tz="Europe/Amsterdam")
+ native_dt = datetime(1941, 7, 1, tzinfo=zoneinfo.ZoneInfo("Europe/Amsterdam"))
+
+ assert dt.dst() == native_dt.dst()
+
+
+def test_deepcopy():
+ dt = pendulum.datetime(1941, 7, 1, tz="Europe/Amsterdam")
+
+ assert dt == deepcopy(dt)
+
+
+def test_pickle_timezone():
+ dt1 = pendulum.timezone("Europe/Amsterdam")
+ s = pickle.dumps(dt1)
+ dt2 = pickle.loads(s)
+
+ assert isinstance(dt2, Timezone)
+
+ dt1 = pendulum.timezone("UTC")
+ s = pickle.dumps(dt1)
+ dt2 = pickle.loads(s)
+
+ assert isinstance(dt2, Timezone)
diff --git a/tests/datetime/test_comparison.py b/tests/datetime/test_comparison.py
new file mode 100644
index 0000000..ad81e73
--- /dev/null
+++ b/tests/datetime/test_comparison.py
@@ -0,0 +1,394 @@
+from __future__ import annotations
+
+from datetime import datetime
+
+import pytz
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_equal_to_true():
+ d1 = pendulum.datetime(2000, 1, 1, 1, 2, 3)
+ d2 = pendulum.datetime(2000, 1, 1, 1, 2, 3)
+ d3 = datetime(2000, 1, 1, 1, 2, 3, tzinfo=pendulum.UTC)
+
+ assert d2 == d1
+ assert d3 == d1
+
+
+def test_equal_to_false():
+ d1 = pendulum.datetime(2000, 1, 1, 1, 2, 3)
+ d2 = pendulum.datetime(2000, 1, 2, 1, 2, 3)
+ d3 = datetime(2000, 1, 2, 1, 2, 3, tzinfo=pendulum.UTC)
+
+ assert d2 != d1
+ assert d3 != d1
+
+
+def test_equal_with_timezone_true():
+ d1 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d2 = pendulum.datetime(2000, 1, 1, 9, 0, 0, tz="America/Vancouver")
+ d3 = datetime(2000, 1, 1, 12, 0, 0, tzinfo=pendulum.timezone("America/Toronto"))
+
+ assert d2 == d1
+ assert d3 == d1
+
+
+def test_equal_with_timezone_false():
+ d1 = pendulum.datetime(2000, 1, 1, tz="America/Toronto")
+ d2 = pendulum.datetime(2000, 1, 1, tz="America/Vancouver")
+ d3 = datetime(2000, 1, 1, tzinfo=pendulum.timezone("America/Toronto"))
+
+ assert d2 != d1
+ assert d3 == d1
+
+
+def test_not_equal_to_true():
+ d1 = pendulum.datetime(2000, 1, 1, 1, 2, 3)
+ d2 = pendulum.datetime(2000, 1, 2, 1, 2, 3)
+ d3 = datetime(2000, 1, 2, 1, 2, 3, tzinfo=pendulum.UTC)
+
+ assert d2 != d1
+ assert d3 != d1
+
+
+def test_not_equal_to_false():
+ d1 = pendulum.datetime(2000, 1, 1, 1, 2, 3)
+ d2 = pendulum.datetime(2000, 1, 1, 1, 2, 3)
+ d3 = datetime(2000, 1, 1, 1, 2, 3, tzinfo=pendulum.UTC)
+
+ assert d2 == d1
+ assert d3 == d1
+
+
+def test_not_equal_with_timezone_true():
+ d1 = pendulum.datetime(2000, 1, 1, tz="America/Toronto")
+ d2 = pendulum.datetime(2000, 1, 1, tz="America/Vancouver")
+ d3 = datetime(2000, 1, 1, tzinfo=pendulum.timezone("America/Toronto"))
+
+ assert d2 != d1
+ assert d3 == d1
+
+
+def test_not_equal_to_none():
+ d1 = pendulum.datetime(2000, 1, 1, 1, 2, 3)
+
+ assert d1 != None # noqa
+
+
+def test_greater_than_true():
+ d1 = pendulum.datetime(2000, 1, 1)
+ d2 = pendulum.datetime(1999, 12, 31)
+ d3 = datetime(1999, 12, 31, tzinfo=pendulum.UTC)
+
+ assert d1 > d2
+ assert d1 > d3
+
+
+def test_greater_than_false():
+ d1 = pendulum.datetime(2000, 1, 1)
+ d2 = pendulum.datetime(2000, 1, 2)
+ d3 = datetime(2000, 1, 2, tzinfo=pendulum.UTC)
+
+ assert not d1 > d2
+ assert not d1 > d3
+
+
+def test_greater_than_with_timezone_true():
+ d1 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d2 = pendulum.datetime(2000, 1, 1, 8, 59, 59, tz="America/Vancouver")
+ d3 = pytz.timezone("America/Vancouver").localize(datetime(2000, 1, 1, 8, 59, 59))
+
+ assert d1 > d2
+ assert d1 > d3
+
+
+def test_greater_than_with_timezone_false():
+ d1 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d2 = pendulum.datetime(2000, 1, 1, 9, 0, 1, tz="America/Vancouver")
+ d3 = pytz.timezone("America/Vancouver").localize(datetime(2000, 1, 1, 9, 0, 1))
+
+ assert not d1 > d2
+ assert not d1 > d3
+
+
+def test_greater_than_or_equal_true():
+ d1 = pendulum.datetime(2000, 1, 1)
+ d2 = pendulum.datetime(1999, 12, 31)
+ d3 = datetime(1999, 12, 31, tzinfo=pendulum.UTC)
+
+ assert d1 >= d2
+ assert d1 >= d3
+
+
+def test_greater_than_or_equal_true_equal():
+ d1 = pendulum.datetime(2000, 1, 1)
+ d2 = pendulum.datetime(2000, 1, 1)
+ d3 = datetime(2000, 1, 1, tzinfo=pendulum.UTC)
+
+ assert d1 >= d2
+ assert d1 >= d3
+
+
+def test_greater_than_or_equal_false():
+ d1 = pendulum.datetime(2000, 1, 1)
+ d2 = pendulum.datetime(2000, 1, 2)
+ d3 = datetime(2000, 1, 2, tzinfo=pendulum.UTC)
+
+ assert not d1 >= d2
+ assert not d1 >= d3
+
+
+def test_greater_than_or_equal_with_timezone_true():
+ d1 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d2 = pendulum.datetime(2000, 1, 1, 8, 59, 59, tz="America/Vancouver")
+ d3 = pytz.timezone("America/Vancouver").localize(datetime(2000, 1, 1, 8, 59, 59))
+
+ assert d1 >= d2
+ assert d1 >= d3
+
+
+def test_greater_than_or_equal_with_timezone_false():
+ d1 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d2 = pendulum.datetime(2000, 1, 1, 9, 0, 1, tz="America/Vancouver")
+ d3 = pytz.timezone("America/Vancouver").localize(datetime(2000, 1, 1, 9, 0, 1))
+
+ assert not d1 >= d2
+ assert not d1 >= d3
+
+
+def test_less_than_true():
+ d1 = pendulum.datetime(2000, 1, 1)
+ d2 = pendulum.datetime(2000, 1, 2)
+ d3 = datetime(2000, 1, 2, tzinfo=pendulum.UTC)
+
+ assert d1 < d2
+ assert d1 < d3
+
+
+def test_less_than_false():
+ d1 = pendulum.datetime(2000, 1, 2)
+ d2 = pendulum.datetime(2000, 1, 1)
+ d3 = datetime(2000, 1, 1, tzinfo=pendulum.UTC)
+
+ assert not d1 < d2
+ assert not d1 < d3
+
+
+def test_less_than_with_timezone_true():
+ d1 = pendulum.datetime(2000, 1, 1, 8, 59, 59, tz="America/Vancouver")
+ d2 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d3 = pytz.timezone("America/Toronto").localize(datetime(2000, 1, 1, 12, 0, 0))
+
+ assert d1 < d2
+ assert d1 < d3
+
+
+def test_less_than_with_timezone_false():
+ d1 = pendulum.datetime(2000, 1, 1, 9, 0, 1, tz="America/Vancouver")
+ d2 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d3 = pytz.timezone("America/Toronto").localize(datetime(2000, 1, 1, 12, 0, 0))
+
+ assert not d1 < d2
+ assert not d1 < d3
+
+
+def test_less_than_or_equal_true():
+ d1 = pendulum.datetime(2000, 1, 1)
+ d2 = pendulum.datetime(2000, 1, 2)
+ d3 = datetime(2000, 1, 2, tzinfo=pendulum.UTC)
+
+ assert d1 <= d2
+ assert d1 <= d3
+
+
+def test_less_than_or_equal_true_equal():
+ d1 = pendulum.datetime(2000, 1, 1)
+ d2 = pendulum.datetime(2000, 1, 1)
+ d3 = datetime(2000, 1, 1, tzinfo=pendulum.UTC)
+
+ assert d1 <= d2
+ assert d1 <= d3
+
+
+def test_less_than_or_equal_false():
+ d1 = pendulum.datetime(2000, 1, 2)
+ d2 = pendulum.datetime(2000, 1, 1)
+ d3 = datetime(2000, 1, 1, tzinfo=pendulum.UTC)
+
+ assert not d1 <= d2
+ assert not d1 <= d3
+
+
+def test_less_than_or_equal_with_timezone_true():
+ d1 = pendulum.datetime(2000, 1, 1, 8, 59, 59, tz="America/Vancouver")
+ d2 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d3 = pytz.timezone("America/Toronto").localize(datetime(2000, 1, 1, 12, 0, 0))
+
+ assert d1 <= d2
+ assert d1 <= d3
+
+
+def test_less_than_or_equal_with_timezone_false():
+ d1 = pendulum.datetime(2000, 1, 1, 9, 0, 1, tz="America/Vancouver")
+ d2 = pendulum.datetime(2000, 1, 1, 12, 0, 0, tz="America/Toronto")
+ d3 = pytz.timezone("America/Toronto").localize(datetime(2000, 1, 1, 12, 0, 0))
+
+ assert not d1 <= d2
+ assert not d1 <= d3
+
+
+def test_is_anniversary():
+ with pendulum.travel_to(pendulum.now()):
+ d = pendulum.now()
+ an_anniversary = d.subtract(years=1)
+ assert an_anniversary.is_anniversary()
+ not_an_anniversary = d.subtract(days=1)
+ assert not not_an_anniversary.is_anniversary()
+ also_not_an_anniversary = d.add(days=2)
+ assert not also_not_an_anniversary.is_anniversary()
+
+ d1 = pendulum.datetime(1987, 4, 23)
+ d2 = pendulum.datetime(2014, 9, 26)
+ d3 = pendulum.datetime(2014, 4, 23)
+ assert not d2.is_anniversary(d1)
+ assert d3.is_anniversary(d1)
+
+
+def test_is_birthday(): # backward compatibility
+ with pendulum.travel_to(pendulum.now()):
+ d = pendulum.now()
+ an_anniversary = d.subtract(years=1)
+ assert an_anniversary.is_birthday()
+ not_an_anniversary = d.subtract(days=1)
+ assert not not_an_anniversary.is_birthday()
+ also_not_an_anniversary = d.add(days=2)
+ assert not also_not_an_anniversary.is_birthday()
+
+ d1 = pendulum.datetime(1987, 4, 23)
+ d2 = pendulum.datetime(2014, 9, 26)
+ d3 = pendulum.datetime(2014, 4, 23)
+ assert not d2.is_birthday(d1)
+ assert d3.is_birthday(d1)
+
+
+def test_closest():
+ instance = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt1 = pendulum.datetime(2015, 5, 28, 11, 0, 0)
+ dt2 = pendulum.datetime(2015, 5, 28, 14, 0, 0)
+ closest = instance.closest(dt1, dt2)
+ assert closest == dt1
+
+ closest = instance.closest(dt2, dt1)
+ assert closest == dt1
+
+ dts = [
+ pendulum.datetime(2015, 5, 28, 16, 0, 0) + pendulum.duration(hours=x)
+ for x in range(4)
+ ]
+ closest = instance.closest(*dts)
+ assert closest == dts[0]
+
+ closest = instance.closest(*(dts[::-1]))
+ assert closest == dts[0]
+
+
+def test_closest_with_datetime():
+ instance = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt1 = datetime(2015, 5, 28, 11, 0, 0)
+ dt2 = datetime(2015, 5, 28, 14, 0, 0)
+ closest = instance.closest(dt1, dt2)
+ assert_datetime(closest, 2015, 5, 28, 11, 0, 0)
+
+ dts = [
+ pendulum.datetime(2015, 5, 28, 16, 0, 0) + pendulum.duration(hours=x)
+ for x in range(4)
+ ]
+ closest = instance.closest(dt1, dt2, *dts)
+
+ assert_datetime(closest, 2015, 5, 28, 11, 0, 0)
+
+
+def test_closest_with_equals():
+ instance = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt1 = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt2 = pendulum.datetime(2015, 5, 28, 14, 0, 0)
+ closest = instance.closest(dt1, dt2)
+ assert closest == dt1
+
+
+def test_farthest():
+ instance = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt1 = pendulum.datetime(2015, 5, 28, 11, 0, 0)
+ dt2 = pendulum.datetime(2015, 5, 28, 14, 0, 0)
+ farthest = instance.farthest(dt1, dt2)
+ assert farthest == dt2
+
+ farthest = instance.farthest(dt2, dt1)
+ assert farthest == dt2
+
+ dts = [
+ pendulum.datetime(2015, 5, 28, 16, 0, 0) + pendulum.duration(hours=x)
+ for x in range(4)
+ ]
+ farthest = instance.farthest(*dts)
+ assert farthest == dts[-1]
+
+ farthest = instance.farthest(*(dts[::-1]))
+ assert farthest == dts[-1]
+
+ f = pendulum.datetime(2010, 1, 1, 0, 0, 0)
+ assert f == instance.farthest(f, *(dts))
+
+
+def test_farthest_with_datetime():
+ instance = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt1 = datetime(2015, 5, 28, 11, 0, 0, tzinfo=pendulum.UTC)
+ dt2 = datetime(2015, 5, 28, 14, 0, 0, tzinfo=pendulum.UTC)
+ farthest = instance.farthest(dt1, dt2)
+ assert_datetime(farthest, 2015, 5, 28, 14, 0, 0)
+
+ dts = [
+ pendulum.datetime(2015, 5, 28, 16, 0, 0) + pendulum.duration(hours=x)
+ for x in range(4)
+ ]
+ farthest = instance.farthest(dt1, dt2, *dts)
+
+ assert_datetime(farthest, 2015, 5, 28, 19, 0, 0)
+
+
+def test_farthest_with_equals():
+ instance = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt1 = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt2 = pendulum.datetime(2015, 5, 28, 14, 0, 0)
+ farthest = instance.farthest(dt1, dt2)
+ assert farthest == dt2
+
+ dts = [
+ pendulum.datetime(2015, 5, 28, 16, 0, 0) + pendulum.duration(hours=x)
+ for x in range(4)
+ ]
+ farthest = instance.farthest(dt1, dt2, *dts)
+ assert farthest == dts[-1]
+
+
+def test_is_same_day():
+ dt1 = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt2 = pendulum.datetime(2015, 5, 29, 12, 0, 0)
+ dt3 = pendulum.datetime(2015, 5, 28, 12, 0, 0)
+ dt4 = datetime(2015, 5, 28, 12, 0, 0, tzinfo=pendulum.UTC)
+ dt5 = datetime(2015, 5, 29, 12, 0, 0, tzinfo=pendulum.UTC)
+
+ assert not dt1.is_same_day(dt2)
+ assert dt1.is_same_day(dt3)
+ assert dt1.is_same_day(dt4)
+ assert not dt1.is_same_day(dt5)
+
+
+def test_comparison_to_unsupported():
+ dt1 = pendulum.now()
+
+ assert dt1 != "test"
+ assert dt1 not in ["test"]
diff --git a/tests/datetime/test_construct.py b/tests/datetime/test_construct.py
new file mode 100644
index 0000000..9488c08
--- /dev/null
+++ b/tests/datetime/test_construct.py
@@ -0,0 +1,182 @@
+from __future__ import annotations
+
+import os
+
+from datetime import datetime
+
+import pytest
+import pytz
+
+from dateutil import tz
+
+import pendulum
+
+from pendulum import DateTime
+from pendulum.tz import timezone
+from pendulum.utils._compat import PYPY
+from tests.conftest import assert_datetime
+
+if not PYPY:
+ import time_machine
+else:
+ time_machine = None
+
+
+@pytest.fixture(autouse=True)
+def _setup():
+ yield
+
+ if os.getenv("TZ"):
+ del os.environ["TZ"]
+
+
+def test_creates_an_instance_default_to_utcnow():
+ now = pendulum.now("UTC")
+ p = pendulum.datetime(
+ now.year, now.month, now.day, now.hour, now.minute, now.second
+ )
+ assert now.timezone_name == p.timezone_name
+
+ assert_datetime(p, now.year, now.month, now.day, now.hour, now.minute, now.second)
+
+
+def test_setting_timezone():
+ tz = "Europe/London"
+ dtz = timezone(tz)
+ dt = datetime.utcnow()
+ offset = dtz.convert(dt).utcoffset().total_seconds() / 3600
+
+ p = pendulum.datetime(dt.year, dt.month, dt.day, tz=dtz)
+ assert p.timezone_name == tz
+ assert p.offset_hours == int(offset)
+
+
+def test_setting_timezone_with_string():
+ tz = "Europe/London"
+ dtz = timezone(tz)
+ dt = datetime.utcnow()
+ offset = dtz.convert(dt).utcoffset().total_seconds() / 3600
+
+ p = pendulum.datetime(dt.year, dt.month, dt.day, tz=tz)
+ assert p.timezone_name == tz
+ assert p.offset_hours == int(offset)
+
+
+def test_today():
+ today = pendulum.today()
+ assert isinstance(today, DateTime)
+
+
+def test_tomorrow():
+ now = pendulum.now().start_of("day")
+ tomorrow = pendulum.tomorrow()
+ assert isinstance(tomorrow, DateTime)
+ assert now.diff(tomorrow).in_days() == 1
+
+
+def test_yesterday():
+ now = pendulum.now().start_of("day")
+ yesterday = pendulum.yesterday()
+
+ assert isinstance(yesterday, DateTime)
+ assert now.diff(yesterday, False).in_days() == -1
+
+
+def test_instance_naive_datetime_defaults_to_utc():
+ now = pendulum.instance(datetime.now())
+ assert now.timezone_name == "UTC"
+
+
+def test_instance_timezone_aware_datetime():
+ now = pendulum.instance(datetime.now(timezone("Europe/Paris")))
+ assert now.timezone_name == "Europe/Paris"
+
+
+def test_instance_timezone_aware_datetime_pytz():
+ now = pendulum.instance(datetime.now(pytz.timezone("Europe/Paris")))
+ assert now.timezone_name == "Europe/Paris"
+
+
+def test_instance_timezone_aware_datetime_any_tzinfo():
+ dt = datetime(2016, 8, 7, 12, 34, 56, tzinfo=tz.gettz("Europe/Paris"))
+ now = pendulum.instance(dt)
+ assert now.timezone_name == "+02:00"
+
+
+def test_now():
+ now = pendulum.now("America/Toronto")
+ in_paris = pendulum.now("Europe/Paris")
+
+ assert now.hour != in_paris.hour
+
+
+if time_machine:
+
+ @time_machine.travel("2016-03-27 00:30:00Z", tick=False)
+ def test_now_dst_off():
+ utc = pendulum.now("UTC")
+ in_paris = pendulum.now("Europe/Paris")
+ in_paris_from_utc = utc.in_tz("Europe/Paris")
+ assert in_paris.hour == 1
+ assert not in_paris.is_dst()
+ assert in_paris.isoformat() == in_paris_from_utc.isoformat()
+
+ @time_machine.travel("2016-03-27 01:30:00Z", tick=False)
+ def test_now_dst_transitioning_on():
+ utc = pendulum.now("UTC")
+ in_paris = pendulum.now("Europe/Paris")
+ in_paris_from_utc = utc.in_tz("Europe/Paris")
+ assert in_paris.hour == 3
+ assert in_paris.is_dst()
+ assert in_paris.isoformat() == in_paris_from_utc.isoformat()
+
+ @time_machine.travel("2016-10-30 00:30:00Z", tick=False)
+ def test_now_dst_on():
+ utc = pendulum.now("UTC")
+ in_paris = pendulum.now("Europe/Paris")
+ in_paris_from_utc = utc.in_tz("Europe/Paris")
+ assert in_paris.hour == 2
+ assert in_paris.is_dst()
+ assert in_paris.isoformat() == in_paris_from_utc.isoformat()
+
+ @time_machine.travel("2016-10-30 01:30:00Z", tick=False)
+ def test_now_dst_transitioning_off():
+ utc = pendulum.now("UTC")
+ in_paris = pendulum.now("Europe/Paris")
+ in_paris_from_utc = utc.in_tz("Europe/Paris")
+ assert in_paris.hour == 2
+ assert not in_paris.is_dst()
+ assert in_paris.isoformat() == in_paris_from_utc.isoformat()
+
+
+def test_now_with_fixed_offset():
+ now = pendulum.now(6)
+
+ assert now.timezone_name == "+06:00"
+
+
+def test_create_with_no_transition_timezone():
+ dt = pendulum.now("Etc/UTC")
+
+ assert dt.timezone_name == "Etc/UTC"
+
+
+def test_create_maintains_microseconds():
+ d = pendulum.datetime(2016, 11, 12, 2, 9, 39, 594000, tz="America/Panama")
+ assert_datetime(d, 2016, 11, 12, 2, 9, 39, 594000)
+
+ d = pendulum.datetime(2316, 11, 12, 2, 9, 39, 857, tz="America/Panama")
+ assert_datetime(d, 2316, 11, 12, 2, 9, 39, 857)
+
+
+def test_second_inaccuracy_on_past_datetimes():
+ dt = pendulum.datetime(1901, 12, 13, 0, 0, 0, 555555, tz="US/Central")
+
+ assert_datetime(dt, 1901, 12, 13, 0, 0, 0, 555555)
+
+
+def test_local():
+ local = pendulum.local(2018, 2, 2, 12, 34, 56, 123456)
+
+ assert_datetime(local, 2018, 2, 2, 12, 34, 56, 123456)
+ assert local.timezone_name == "America/Toronto"
diff --git a/tests/datetime/test_create_from_timestamp.py b/tests/datetime/test_create_from_timestamp.py
new file mode 100644
index 0000000..121a7c2
--- /dev/null
+++ b/tests/datetime/test_create_from_timestamp.py
@@ -0,0 +1,24 @@
+from __future__ import annotations
+
+import pendulum
+
+from pendulum import timezone
+from tests.conftest import assert_datetime
+
+
+def test_create_from_timestamp_returns_pendulum():
+ d = pendulum.from_timestamp(pendulum.datetime(1975, 5, 21, 22, 32, 5).timestamp())
+ assert_datetime(d, 1975, 5, 21, 22, 32, 5)
+ assert d.timezone_name == "UTC"
+
+
+def test_create_from_timestamp_with_timezone_string():
+ d = pendulum.from_timestamp(0, "America/Toronto")
+ assert d.timezone_name == "America/Toronto"
+ assert_datetime(d, 1969, 12, 31, 19, 0, 0)
+
+
+def test_create_from_timestamp_with_timezone():
+ d = pendulum.from_timestamp(0, timezone("America/Toronto"))
+ assert d.timezone_name == "America/Toronto"
+ assert_datetime(d, 1969, 12, 31, 19, 0, 0)
diff --git a/tests/datetime/test_day_of_week_modifiers.py b/tests/datetime/test_day_of_week_modifiers.py
new file mode 100644
index 0000000..46de84e
--- /dev/null
+++ b/tests/datetime/test_day_of_week_modifiers.py
@@ -0,0 +1,314 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+from pendulum.exceptions import PendulumException
+from tests.conftest import assert_datetime
+
+
+def test_start_of_week():
+ d = pendulum.datetime(1980, 8, 7, 12, 11, 9).start_of("week")
+ assert_datetime(d, 1980, 8, 4, 0, 0, 0)
+
+
+def test_start_of_week_from_week_start():
+ d = pendulum.datetime(1980, 8, 4).start_of("week")
+ assert_datetime(d, 1980, 8, 4, 0, 0, 0)
+
+
+def test_start_of_week_crossing_year_boundary():
+ d = pendulum.datetime(2014, 1, 1).start_of("week")
+ assert_datetime(d, 2013, 12, 30, 0, 0, 0)
+
+
+def test_end_of_week():
+ d = pendulum.datetime(1980, 8, 7, 12, 11, 9).end_of("week")
+ assert_datetime(d, 1980, 8, 10, 23, 59, 59)
+
+
+def test_end_of_week_from_week_end():
+ d = pendulum.datetime(1980, 8, 10).end_of("week")
+ assert_datetime(d, 1980, 8, 10, 23, 59, 59)
+
+
+def test_end_of_week_crossing_year_boundary():
+ d = pendulum.datetime(2013, 12, 31).end_of("week")
+ assert_datetime(d, 2014, 1, 5, 23, 59, 59)
+
+
+def test_next():
+ d = pendulum.datetime(1975, 5, 21).next()
+ assert_datetime(d, 1975, 5, 28, 0, 0, 0)
+
+
+def test_next_monday():
+ d = pendulum.datetime(1975, 5, 21).next(pendulum.MONDAY)
+ assert_datetime(d, 1975, 5, 26, 0, 0, 0)
+
+
+def test_next_saturday():
+ d = pendulum.datetime(1975, 5, 21).next(6)
+ assert_datetime(d, 1975, 5, 24, 0, 0, 0)
+
+
+def test_next_keep_time():
+ d = pendulum.datetime(1975, 5, 21, 12).next()
+ assert_datetime(d, 1975, 5, 28, 0, 0, 0)
+
+ d = pendulum.datetime(1975, 5, 21, 12).next(keep_time=True)
+ assert_datetime(d, 1975, 5, 28, 12, 0, 0)
+
+
+def test_next_invalid():
+ dt = pendulum.datetime(1975, 5, 21, 12)
+
+ with pytest.raises(ValueError):
+ dt.next(7)
+
+
+def test_previous():
+ d = pendulum.datetime(1975, 5, 21).previous()
+ assert_datetime(d, 1975, 5, 14, 0, 0, 0)
+
+
+def test_previous_monday():
+ d = pendulum.datetime(1975, 5, 21).previous(pendulum.MONDAY)
+ assert_datetime(d, 1975, 5, 19, 0, 0, 0)
+
+
+def test_previous_saturday():
+ d = pendulum.datetime(1975, 5, 21).previous(6)
+ assert_datetime(d, 1975, 5, 17, 0, 0, 0)
+
+
+def test_previous_keep_time():
+ d = pendulum.datetime(1975, 5, 21, 12).previous()
+ assert_datetime(d, 1975, 5, 14, 0, 0, 0)
+
+ d = pendulum.datetime(1975, 5, 21, 12).previous(keep_time=True)
+ assert_datetime(d, 1975, 5, 14, 12, 0, 0)
+
+
+def test_previous_invalid():
+ dt = pendulum.datetime(1975, 5, 21, 12)
+
+ with pytest.raises(ValueError):
+ dt.previous(7)
+
+
+def test_first_day_of_month():
+ d = pendulum.datetime(1975, 11, 21).first_of("month")
+ assert_datetime(d, 1975, 11, 1, 0, 0, 0)
+
+
+def test_first_wednesday_of_month():
+ d = pendulum.datetime(1975, 11, 21).first_of("month", pendulum.WEDNESDAY)
+ assert_datetime(d, 1975, 11, 5, 0, 0, 0)
+
+
+def test_first_friday_of_month():
+ d = pendulum.datetime(1975, 11, 21).first_of("month", 5)
+ assert_datetime(d, 1975, 11, 7, 0, 0, 0)
+
+
+def test_last_day_of_month():
+ d = pendulum.datetime(1975, 12, 5).last_of("month")
+ assert_datetime(d, 1975, 12, 31, 0, 0, 0)
+
+
+def test_last_tuesday_of_month():
+ d = pendulum.datetime(1975, 12, 1).last_of("month", pendulum.TUESDAY)
+ assert_datetime(d, 1975, 12, 30, 0, 0, 0)
+
+
+def test_last_friday_of_month():
+ d = pendulum.datetime(1975, 12, 5).last_of("month", 5)
+ assert_datetime(d, 1975, 12, 26, 0, 0, 0)
+
+
+def test_nth_of_month_outside_scope():
+ d = pendulum.datetime(1975, 6, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("month", 6, pendulum.MONDAY)
+
+
+def test_nth_of_month_outside_year():
+ d = pendulum.datetime(1975, 12, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("month", 55, pendulum.MONDAY)
+
+
+def test_nth_of_month_first():
+ d = pendulum.datetime(1975, 12, 5).nth_of("month", 1, pendulum.MONDAY)
+
+ assert_datetime(d, 1975, 12, 1, 0, 0, 0)
+
+
+def test_2nd_monday_of_month():
+ d = pendulum.datetime(1975, 12, 5).nth_of("month", 2, pendulum.MONDAY)
+
+ assert_datetime(d, 1975, 12, 8, 0, 0, 0)
+
+
+def test_3rd_wednesday_of_month():
+ d = pendulum.datetime(1975, 12, 5).nth_of("month", 3, 3)
+
+ assert_datetime(d, 1975, 12, 17, 0, 0, 0)
+
+
+def test_first_day_of_quarter():
+ d = pendulum.datetime(1975, 11, 21).first_of("quarter")
+ assert_datetime(d, 1975, 10, 1, 0, 0, 0)
+
+
+def test_first_wednesday_of_quarter():
+ d = pendulum.datetime(1975, 11, 21).first_of("quarter", pendulum.WEDNESDAY)
+ assert_datetime(d, 1975, 10, 1, 0, 0, 0)
+
+
+def test_first_friday_of_quarter():
+ d = pendulum.datetime(1975, 11, 21).first_of("quarter", 5)
+ assert_datetime(d, 1975, 10, 3, 0, 0, 0)
+
+
+def test_first_of_quarter_from_a_day_that_will_not_exist_in_the_first_month():
+ d = pendulum.datetime(2014, 5, 31).first_of("quarter")
+ assert_datetime(d, 2014, 4, 1, 0, 0, 0)
+
+
+def test_last_day_of_quarter():
+ d = pendulum.datetime(1975, 8, 5).last_of("quarter")
+ assert_datetime(d, 1975, 9, 30, 0, 0, 0)
+
+
+def test_last_tuesday_of_quarter():
+ d = pendulum.datetime(1975, 8, 5).last_of("quarter", pendulum.TUESDAY)
+ assert_datetime(d, 1975, 9, 30, 0, 0, 0)
+
+
+def test_last_friday_of_quarter():
+ d = pendulum.datetime(1975, 8, 5).last_of("quarter", pendulum.FRIDAY)
+ assert_datetime(d, 1975, 9, 26, 0, 0, 0)
+
+
+def test_last_day_of_quarter_that_will_not_exist_in_the_last_month():
+ d = pendulum.datetime(2014, 5, 31).last_of("quarter")
+ assert_datetime(d, 2014, 6, 30, 0, 0, 0)
+
+
+def test_nth_of_quarter_outside_scope():
+ d = pendulum.datetime(1975, 1, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("quarter", 20, pendulum.MONDAY)
+
+
+def test_nth_of_quarter_outside_year():
+ d = pendulum.datetime(1975, 1, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("quarter", 55, pendulum.MONDAY)
+
+
+def test_nth_of_quarter_first():
+ d = pendulum.datetime(1975, 12, 5).nth_of("quarter", 1, pendulum.MONDAY)
+
+ assert_datetime(d, 1975, 10, 6, 0, 0, 0)
+
+
+def test_nth_of_quarter_from_a_day_that_will_not_exist_in_the_first_month():
+ d = pendulum.datetime(2014, 5, 31).nth_of("quarter", 2, pendulum.MONDAY)
+ assert_datetime(d, 2014, 4, 14, 0, 0, 0)
+
+
+def test_2nd_monday_of_quarter():
+ d = pendulum.datetime(1975, 8, 5).nth_of("quarter", 2, pendulum.MONDAY)
+ assert_datetime(d, 1975, 7, 14, 0, 0, 0)
+
+
+def test_3rd_wednesday_of_quarter():
+ d = pendulum.datetime(1975, 8, 5).nth_of("quarter", 3, 3)
+ assert_datetime(d, 1975, 7, 16, 0, 0, 0)
+
+
+def test_first_day_of_year():
+ d = pendulum.datetime(1975, 11, 21).first_of("year")
+ assert_datetime(d, 1975, 1, 1, 0, 0, 0)
+
+
+def test_first_wednesday_of_year():
+ d = pendulum.datetime(1975, 11, 21).first_of("year", pendulum.WEDNESDAY)
+ assert_datetime(d, 1975, 1, 1, 0, 0, 0)
+
+
+def test_first_friday_of_year():
+ d = pendulum.datetime(1975, 11, 21).first_of("year", 5)
+ assert_datetime(d, 1975, 1, 3, 0, 0, 0)
+
+
+def test_last_day_of_year():
+ d = pendulum.datetime(1975, 8, 5).last_of("year")
+ assert_datetime(d, 1975, 12, 31, 0, 0, 0)
+
+
+def test_last_tuesday_of_year():
+ d = pendulum.datetime(1975, 8, 5).last_of("year", pendulum.TUESDAY)
+ assert_datetime(d, 1975, 12, 30, 0, 0, 0)
+
+
+def test_last_friday_of_year():
+ d = pendulum.datetime(1975, 8, 5).last_of("year", 5)
+ assert_datetime(d, 1975, 12, 26, 0, 0, 0)
+
+
+def test_nth_of_year_outside_scope():
+ d = pendulum.datetime(1975, 1, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("year", 55, pendulum.MONDAY)
+
+
+def test_nth_of_year_first():
+ d = pendulum.datetime(1975, 12, 5).nth_of("year", 1, pendulum.MONDAY)
+
+ assert_datetime(d, 1975, 1, 6, 0, 0, 0)
+
+
+def test_2nd_monday_of_year():
+ d = pendulum.datetime(1975, 8, 5).nth_of("year", 2, pendulum.MONDAY)
+ assert_datetime(d, 1975, 1, 13, 0, 0, 0)
+
+
+def test_2rd_wednesday_of_year():
+ d = pendulum.datetime(1975, 8, 5).nth_of("year", 3, pendulum.WEDNESDAY)
+ assert_datetime(d, 1975, 1, 15, 0, 0, 0)
+
+
+def test_7th_thursday_of_year():
+ d = pendulum.datetime(1975, 8, 31).nth_of("year", 7, pendulum.THURSDAY)
+ assert_datetime(d, 1975, 2, 13, 0, 0, 0)
+
+
+def test_first_of_invalid_unit():
+ d = pendulum.datetime(1975, 8, 5)
+
+ with pytest.raises(ValueError):
+ d.first_of("invalid")
+
+
+def test_last_of_invalid_unit():
+ d = pendulum.datetime(1975, 8, 5)
+
+ with pytest.raises(ValueError):
+ d.last_of("invalid")
+
+
+def test_nth_of_invalid_unit():
+ d = pendulum.datetime(1975, 8, 5)
+
+ with pytest.raises(ValueError):
+ d.nth_of("invalid", 3, pendulum.MONDAY)
diff --git a/tests/datetime/test_diff.py b/tests/datetime/test_diff.py
new file mode 100644
index 0000000..7a31507
--- /dev/null
+++ b/tests/datetime/test_diff.py
@@ -0,0 +1,880 @@
+from __future__ import annotations
+
+from datetime import datetime
+
+import pytest
+
+import pendulum
+
+
+def test_diff_in_years_positive():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(years=1)).in_years() == 1
+
+
+def test_diff_in_years_negative_with_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1), False).in_years() == -1
+
+
+def test_diff_in_years_negative_no_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1)).in_years() == 1
+
+
+def test_diff_in_years_vs_default_now():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(years=1).diff().in_years() == 1
+
+
+def test_diff_in_years_ensure_is_truncated():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(years=1).add(months=7)).in_years() == 1
+
+
+def test_diff_in_months_positive():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(years=1).add(months=1)).in_months() == 13
+
+
+def test_diff_in_months_negative_with_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1).add(months=1), False).in_months() == -11
+
+
+def test_diff_in_months_negative_no_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1).add(months=1)).in_months() == 11
+
+
+def test_diff_in_months_vs_default_now():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(years=1).diff().in_months() == 12
+
+
+def test_diff_in_months_ensure_is_truncated():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(months=1).add(days=16)).in_months() == 1
+
+
+def test_diff_in_days_positive():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(years=1)).in_days() == 366
+
+
+def test_diff_in_days_negative_with_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1), False).in_days() == -365
+
+
+def test_diff_in_days_negative_no_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1)).in_days() == 365
+
+
+def test_diff_in_days_vs_default_now():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(weeks=1).diff().in_days() == 7
+
+
+def test_diff_in_days_ensure_is_truncated():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(days=1).add(hours=13)).in_days() == 1
+
+
+def test_diff_in_weeks_positive():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(years=1)).in_weeks() == 52
+
+
+def test_diff_in_weeks_negative_with_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1), False).in_weeks() == -52
+
+
+def test_diff_in_weeks_negative_no_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1)).in_weeks() == 52
+
+
+def test_diff_in_weeks_vs_default_now():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(weeks=1).diff().in_weeks() == 1
+
+
+def test_diff_in_weeks_ensure_is_truncated():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(weeks=1).subtract(days=1)).in_weeks() == 0
+
+
+def test_diff_in_hours_positive():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(days=1).add(hours=2)).in_hours() == 26
+
+
+def test_diff_in_hours_negative_with_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(days=1).add(hours=2), False).in_hours() == -22
+
+
+def test_diff_in_hours_negative_no_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(days=1).add(hours=2)).in_hours() == 22
+
+
+def test_diff_in_hours_vs_default_now():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 15), freeze=True):
+ assert pendulum.now().subtract(days=2).diff().in_hours() == 48
+
+
+def test_diff_in_hours_ensure_is_truncated():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(hours=1).add(minutes=31)).in_hours() == 1
+
+
+def test_diff_in_minutes_positive():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(hours=1).add(minutes=2)).in_minutes() == 62
+
+
+def test_diff_in_minutes_positive_big():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(hours=25).add(minutes=2)).in_minutes() == 1502
+
+
+def test_diff_in_minutes_negative_with_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(hours=1).add(minutes=2), False).in_minutes() == -58
+
+
+def test_diff_in_minutes_negative_no_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(hours=1).add(minutes=2)).in_minutes() == 58
+
+
+def test_diff_in_minutes_vs_default_now():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(hours=1).diff().in_minutes() == 60
+
+
+def test_diff_in_minutes_ensure_is_truncated():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(minutes=1).add(seconds=59)).in_minutes() == 1
+
+
+def test_diff_in_seconds_positive():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(minutes=1).add(seconds=2)).in_seconds() == 62
+
+
+def test_diff_in_seconds_positive_big():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(hours=2).add(seconds=2)).in_seconds() == 7202
+
+
+def test_diff_in_seconds_negative_with_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(minutes=1).add(seconds=2), False).in_seconds() == -58
+
+
+def test_diff_in_seconds_negative_no_sign():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.subtract(minutes=1).add(seconds=2)).in_seconds() == 58
+
+
+def test_diff_in_seconds_vs_default_now():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(hours=1).diff().in_seconds() == 3600
+
+
+def test_diff_in_seconds_ensure_is_truncated():
+ dt = pendulum.datetime(2000, 1, 1)
+ assert dt.diff(dt.add(seconds=1.9)).in_seconds() == 1
+
+
+def test_diff_in_seconds_with_timezones():
+ dt_ottawa = pendulum.datetime(2000, 1, 1, 13, tz="America/Toronto")
+ dt_vancouver = pendulum.datetime(2000, 1, 1, 13, tz="America/Vancouver")
+ assert dt_ottawa.diff(dt_vancouver).in_seconds() == 3 * 60 * 60
+
+
+def test_diff_for_humans_now_and_second():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().diff_for_humans() == "a few seconds ago"
+
+
+def test_diff_for_humans_now_and_second_with_timezone():
+ van_now = pendulum.now("America/Vancouver")
+ here_now = van_now.in_timezone(pendulum.now().timezone)
+
+ with pendulum.travel_to(here_now, freeze=True):
+ assert here_now.diff_for_humans() == "a few seconds ago"
+
+
+def test_diff_for_humans_now_and_seconds():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().subtract(seconds=2).diff_for_humans() == "a few seconds ago"
+ )
+
+
+def test_diff_for_humans_now_and_nearly_minute():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(seconds=59).diff_for_humans() == "59 seconds ago"
+
+
+def test_diff_for_humans_now_and_minute():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(minutes=1).diff_for_humans() == "1 minute ago"
+
+
+def test_diff_for_humans_now_and_minutes():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(minutes=2).diff_for_humans() == "2 minutes ago"
+
+
+def test_diff_for_humans_now_and_nearly_hour():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(minutes=59).diff_for_humans() == "59 minutes ago"
+
+
+def test_diff_for_humans_now_and_hour():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(hours=1).diff_for_humans() == "1 hour ago"
+
+
+def test_diff_for_humans_now_and_hours():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(hours=2).diff_for_humans() == "2 hours ago"
+
+
+def test_diff_for_humans_now_and_nearly_day():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(hours=23).diff_for_humans() == "23 hours ago"
+
+
+def test_diff_for_humans_now_and_day():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(days=1).diff_for_humans() == "1 day ago"
+
+
+def test_diff_for_humans_now_and_days():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(days=2).diff_for_humans() == "2 days ago"
+
+
+def test_diff_for_humans_now_and_nearly_week():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(days=6).diff_for_humans() == "6 days ago"
+
+
+def test_diff_for_humans_now_and_week():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(weeks=1).diff_for_humans() == "1 week ago"
+
+
+def test_diff_for_humans_now_and_weeks():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(weeks=2).diff_for_humans() == "2 weeks ago"
+
+
+def test_diff_for_humans_now_and_nearly_month():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(weeks=3).diff_for_humans() == "3 weeks ago"
+
+
+def test_diff_for_humans_now_and_month():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(weeks=4).diff_for_humans() == "4 weeks ago"
+ assert pendulum.now().subtract(months=1).diff_for_humans() == "1 month ago"
+
+
+def test_diff_for_humans_now_and_months():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(months=2).diff_for_humans() == "2 months ago"
+
+
+def test_diff_for_humans_now_and_nearly_year():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(months=11).diff_for_humans() == "11 months ago"
+
+
+def test_diff_for_humans_now_and_year():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(years=1).diff_for_humans() == "1 year ago"
+
+
+def test_diff_for_humans_now_and_years():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().subtract(years=2).diff_for_humans() == "2 years ago"
+
+
+def test_diff_for_humans_now_and_future_second():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(seconds=1).diff_for_humans() == "in a few seconds"
+
+
+def test_diff_for_humans_now_and_future_seconds():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(seconds=2).diff_for_humans() == "in a few seconds"
+
+
+def test_diff_for_humans_now_and_nearly_future_minute():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(seconds=59).diff_for_humans() == "in 59 seconds"
+
+
+def test_diff_for_humans_now_and_future_minute():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(minutes=1).diff_for_humans() == "in 1 minute"
+
+
+def test_diff_for_humans_now_and_future_minutes():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(minutes=2).diff_for_humans() == "in 2 minutes"
+
+
+def test_diff_for_humans_now_and_nearly_future_hour():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(minutes=59).diff_for_humans() == "in 59 minutes"
+
+
+def test_diff_for_humans_now_and_future_hour():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(hours=1).diff_for_humans() == "in 1 hour"
+
+
+def test_diff_for_humans_now_and_future_hours():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(hours=2).diff_for_humans() == "in 2 hours"
+
+
+def test_diff_for_humans_now_and_nearly_future_day():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(hours=23).diff_for_humans() == "in 23 hours"
+
+
+def test_diff_for_humans_now_and_future_day():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(days=1).diff_for_humans() == "in 1 day"
+
+
+def test_diff_for_humans_now_and_future_days():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(days=2).diff_for_humans() == "in 2 days"
+
+
+def test_diff_for_humans_now_and_nearly_future_week():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(days=6).diff_for_humans() == "in 6 days"
+
+
+def test_diff_for_humans_now_and_future_week():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(weeks=1).diff_for_humans() == "in 1 week"
+
+
+def test_diff_for_humans_now_and_future_weeks():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(weeks=2).diff_for_humans() == "in 2 weeks"
+
+
+def test_diff_for_humans_now_and_nearly_future_month():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(weeks=3).diff_for_humans() == "in 3 weeks"
+
+
+def test_diff_for_humans_now_and_future_month():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(weeks=4).diff_for_humans() == "in 4 weeks"
+ assert pendulum.now().add(months=1).diff_for_humans() == "in 1 month"
+
+
+def test_diff_for_humans_now_and_future_months():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(months=2).diff_for_humans() == "in 2 months"
+
+
+def test_diff_for_humans_now_and_nearly_future_year():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(months=11).diff_for_humans() == "in 11 months"
+
+
+def test_diff_for_humans_now_and_future_year():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(years=1).diff_for_humans() == "in 1 year"
+
+
+def test_diff_for_humans_now_and_future_years():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert pendulum.now().add(years=2).diff_for_humans() == "in 2 years"
+
+
+def test_diff_for_humans_other_and_second():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(seconds=1))
+ == "a few seconds before"
+ )
+
+
+def test_diff_for_humans_other_and_seconds():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(seconds=2))
+ == "a few seconds before"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_minute():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(seconds=59))
+ == "59 seconds before"
+ )
+
+
+def test_diff_for_humans_other_and_minute():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(minutes=1))
+ == "1 minute before"
+ )
+
+
+def test_diff_for_humans_other_and_minutes():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(minutes=2))
+ == "2 minutes before"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_hour():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(minutes=59))
+ == "59 minutes before"
+ )
+
+
+def test_diff_for_humans_other_and_hour():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(hours=1))
+ == "1 hour before"
+ )
+
+
+def test_diff_for_humans_other_and_hours():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(hours=2))
+ == "2 hours before"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_day():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(hours=23))
+ == "23 hours before"
+ )
+
+
+def test_diff_for_humans_other_and_day():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(days=1)) == "1 day before"
+ )
+
+
+def test_diff_for_humans_other_and_days():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(days=2))
+ == "2 days before"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_week():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(days=6))
+ == "6 days before"
+ )
+
+
+def test_diff_for_humans_other_and_week():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(weeks=1))
+ == "1 week before"
+ )
+
+
+def test_diff_for_humans_other_and_weeks():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(weeks=2))
+ == "2 weeks before"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_month():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(weeks=3))
+ == "3 weeks before"
+ )
+
+
+def test_diff_for_humans_other_and_month():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(weeks=4))
+ == "4 weeks before"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(months=1))
+ == "1 month before"
+ )
+
+
+def test_diff_for_humans_other_and_months():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(months=2))
+ == "2 months before"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_year():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(months=11))
+ == "11 months before"
+ )
+
+
+def test_diff_for_humans_other_and_year():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(years=1))
+ == "1 year before"
+ )
+
+
+def test_diff_for_humans_other_and_years():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(years=2))
+ == "2 years before"
+ )
+
+
+def test_diff_for_humans_other_and_future_second():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(seconds=1))
+ == "a few seconds after"
+ )
+
+
+def test_diff_for_humans_other_and_future_seconds():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(seconds=2))
+ == "a few seconds after"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_future_minute():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(seconds=59))
+ == "59 seconds after"
+ )
+
+
+def test_diff_for_humans_other_and_future_minute():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(minutes=1))
+ == "1 minute after"
+ )
+
+
+def test_diff_for_humans_other_and_future_minutes():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(minutes=2))
+ == "2 minutes after"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_future_hour():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(minutes=59))
+ == "59 minutes after"
+ )
+
+
+def test_diff_for_humans_other_and_future_hour():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(hours=1))
+ == "1 hour after"
+ )
+
+
+def test_diff_for_humans_other_and_future_hours():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(hours=2))
+ == "2 hours after"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_future_day():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(hours=23))
+ == "23 hours after"
+ )
+
+
+def test_diff_for_humans_other_and_future_day():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(days=1))
+ == "1 day after"
+ )
+
+
+def test_diff_for_humans_other_and_future_days():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(days=2))
+ == "2 days after"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_future_week():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(days=6))
+ == "6 days after"
+ )
+
+
+def test_diff_for_humans_other_and_future_week():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(weeks=1))
+ == "1 week after"
+ )
+
+
+def test_diff_for_humans_other_and_future_weeks():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(weeks=2))
+ == "2 weeks after"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_future_month():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(weeks=3))
+ == "3 weeks after"
+ )
+
+
+def test_diff_for_humans_other_and_future_month():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(weeks=4))
+ == "4 weeks after"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(months=1))
+ == "1 month after"
+ )
+
+
+def test_diff_for_humans_other_and_future_months():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(months=2))
+ == "2 months after"
+ )
+
+
+def test_diff_for_humans_other_and_nearly_future_year():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(months=11))
+ == "11 months after"
+ )
+
+
+def test_diff_for_humans_other_and_future_year():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(years=1))
+ == "1 year after"
+ )
+
+
+def test_diff_for_humans_other_and_future_years():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(years=2))
+ == "2 years after"
+ )
+
+
+def test_diff_for_humans_absolute_seconds():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(seconds=59), True)
+ == "59 seconds"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(seconds=59), True)
+ == "59 seconds"
+ )
+
+
+def test_diff_for_humans_absolute_minutes():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(minutes=30), True)
+ == "30 minutes"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(minutes=30), True)
+ == "30 minutes"
+ )
+
+
+def test_diff_for_humans_absolute_hours():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(hours=3), True)
+ == "3 hours"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(hours=3), True)
+ == "3 hours"
+ )
+
+
+def test_diff_for_humans_absolute_days():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(days=2), True)
+ == "2 days"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(days=2), True) == "2 days"
+ )
+
+
+def test_diff_for_humans_absolute_weeks():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(weeks=2), True)
+ == "2 weeks"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(weeks=2), True)
+ == "2 weeks"
+ )
+
+
+def test_diff_for_humans_absolute_months():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(months=2), True)
+ == "2 months"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(months=2), True)
+ == "2 months"
+ )
+
+
+def test_diff_for_humans_absolute_years():
+ with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().subtract(years=1), True)
+ == "1 year"
+ )
+ assert (
+ pendulum.now().diff_for_humans(pendulum.now().add(years=1), True)
+ == "1 year"
+ )
+
+
+def test_diff_for_humans_accuracy():
+ now = pendulum.now("utc")
+
+ with pendulum.travel_to(now.add(microseconds=200), freeze=True):
+ assert now.add(years=1).diff_for_humans(absolute=True) == "1 year"
+ assert now.add(months=11).diff_for_humans(absolute=True) == "11 months"
+ assert now.add(days=27).diff_for_humans(absolute=True) == "4 weeks"
+ assert now.add(years=1, months=3).diff_for_humans(absolute=True) == "1 year"
+ assert now.add(years=1, months=8).diff_for_humans(absolute=True) == "2 years"
+
+ # DST
+ now = pendulum.datetime(2017, 3, 7, tz="America/Toronto")
+ with pendulum.travel_to(now, freeze=True):
+ assert now.add(days=6).diff_for_humans(absolute=True) == "6 days"
+
+
+def test_subtraction():
+ d = pendulum.naive(2016, 7, 5, 12, 32, 25, 0)
+ future_dt = datetime(2016, 7, 5, 13, 32, 25, 0)
+ future = d.add(hours=1)
+
+ assert (future - d).total_seconds() == 3600
+ assert (future_dt - d).total_seconds() == 3600
+
+
+def test_subtraction_aware_naive():
+ dt = pendulum.datetime(2016, 7, 5, 12, 32, 25, 0)
+ future_dt = datetime(2016, 7, 5, 13, 32, 25, 0)
+
+ with pytest.raises(TypeError):
+ future_dt - dt
+
+ future_dt = pendulum.naive(2016, 7, 5, 13, 32, 25, 0)
+
+ with pytest.raises(TypeError):
+ future_dt - dt
+
+
+def test_subtraction_with_timezone():
+ dt = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz="Europe/Paris")
+ post = dt.add(microseconds=1)
+
+ assert (post - dt).total_seconds() == 1e-06
+
+ dt = pendulum.datetime(
+ 2013,
+ 10,
+ 27,
+ 2,
+ 59,
+ 59,
+ 999999,
+ tz="Europe/Paris",
+ fold=0,
+ )
+ post = dt.add(microseconds=1)
+
+ assert (post - dt).total_seconds() == 1e-06
diff --git a/tests/datetime/test_fluent_setters.py b/tests/datetime/test_fluent_setters.py
new file mode 100644
index 0000000..cedbd26
--- /dev/null
+++ b/tests/datetime/test_fluent_setters.py
@@ -0,0 +1,181 @@
+from __future__ import annotations
+
+from datetime import datetime
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_fluid_year_setter():
+ d = pendulum.now()
+ new = d.set(year=1995)
+ assert isinstance(new, datetime)
+ assert new.year == 1995
+ assert d.year != new.year
+
+
+def test_fluid_month_setter():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.set(month=11)
+ assert isinstance(new, datetime)
+ assert new.month == 11
+ assert d.month == 7
+
+
+def test_fluid_day_setter():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.set(day=9)
+ assert isinstance(new, datetime)
+ assert new.day == 9
+ assert d.day == 2
+
+
+def test_fluid_hour_setter():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.set(hour=5)
+ assert isinstance(new, datetime)
+ assert new.hour == 5
+ assert d.hour == 0
+
+
+def test_fluid_minute_setter():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.set(minute=32)
+ assert isinstance(new, datetime)
+ assert new.minute == 32
+ assert d.minute == 41
+
+
+def test_fluid_second_setter():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.set(second=49)
+ assert isinstance(new, datetime)
+ assert new.second == 49
+ assert d.second == 20
+
+
+def test_fluid_microsecond_setter():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20, 123456)
+ new = d.set(microsecond=987654)
+ assert isinstance(new, datetime)
+ assert new.microsecond == 987654
+ assert d.microsecond == 123456
+
+
+def test_fluid_setter_keeps_timezone():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20, 123456, tz="Europe/Paris")
+ new = d.set(microsecond=987654)
+ assert_datetime(new, 2016, 7, 2, 0, 41, 20, 987654)
+
+
+def test_fluid_timezone_setter():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.set(tz="Europe/Paris")
+ assert isinstance(new, datetime)
+ assert new.timezone_name == "Europe/Paris"
+ assert new.tzinfo.name == "Europe/Paris"
+
+
+def test_fluid_on():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.on(1995, 11, 9)
+ assert isinstance(new, datetime)
+ assert new.year == 1995
+ assert new.month == 11
+ assert new.day == 9
+ assert d.year == 2016
+ assert d.month == 7
+ assert d.day == 2
+
+
+def test_fluid_on_with_transition():
+ d = pendulum.datetime(2013, 3, 31, 0, 0, 0, 0, tz="Europe/Paris")
+ new = d.on(2013, 4, 1)
+ assert isinstance(new, datetime)
+ assert new.year == 2013
+ assert new.month == 4
+ assert new.day == 1
+ assert new.offset == 7200
+ assert d.year == 2013
+ assert d.month == 3
+ assert d.day == 31
+ assert d.offset == 3600
+
+
+def test_fluid_at():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.at(5, 32, 49, 123456)
+ assert isinstance(new, datetime)
+ assert new.hour == 5
+ assert new.minute == 32
+ assert new.second == 49
+ assert new.microsecond == 123456
+ assert d.hour == 0
+ assert d.minute == 41
+ assert d.second == 20
+ assert d.microsecond == 0
+
+
+def test_fluid_at_partial():
+ d = pendulum.datetime(2016, 7, 2, 0, 41, 20)
+ new = d.at(10)
+
+ assert_datetime(new, 2016, 7, 2, 10, 0, 0, 0)
+
+ new = d.at(10, 30)
+
+ assert_datetime(new, 2016, 7, 2, 10, 30, 0, 0)
+
+ new = d.at(10, 30, 45)
+
+ assert_datetime(new, 2016, 7, 2, 10, 30, 45, 0)
+
+
+def test_fluid_at_with_transition():
+ d = pendulum.datetime(2013, 3, 31, 0, 0, 0, 0, tz="Europe/Paris")
+ new = d.at(2, 30, 0)
+ assert isinstance(new, datetime)
+ assert new.hour == 3
+ assert new.minute == 30
+ assert new.second == 0
+
+
+def test_replace_tzinfo_dst_off():
+ d = pendulum.datetime(2016, 3, 27, 0, 30) # 30 min before DST turning on
+ new = d.replace(tzinfo=pendulum.timezone("Europe/Paris"))
+
+ assert_datetime(new, 2016, 3, 27, 0, 30)
+ assert not new.is_dst()
+ assert new.offset == 3600
+ assert new.timezone_name == "Europe/Paris"
+
+
+def test_replace_tzinfo_dst_transitioning_on():
+ d = pendulum.datetime(2016, 3, 27, 1, 30) # In middle of turning on
+ new = d.replace(tzinfo=pendulum.timezone("Europe/Paris"))
+
+ assert_datetime(new, 2016, 3, 27, 1, 30)
+ assert not new.is_dst()
+ assert new.offset == 3600
+ assert new.timezone_name == "Europe/Paris"
+
+
+def test_replace_tzinfo_dst_on():
+ d = pendulum.datetime(2016, 10, 30, 0, 30) # 30 min before DST turning off
+ new = d.replace(tzinfo=pendulum.timezone("Europe/Paris"))
+
+ assert_datetime(new, 2016, 10, 30, 0, 30)
+ assert new.is_dst()
+ assert new.offset == 7200
+ assert new.timezone_name == "Europe/Paris"
+
+
+def test_replace_tzinfo_dst_transitioning_off():
+ d = pendulum.datetime(2016, 10, 30, 1, 30) # In the middle of turning off
+ new = d.replace(tzinfo=pendulum.timezone("Europe/Paris"))
+
+ assert_datetime(new, 2016, 10, 30, 1, 30)
+ assert new.is_dst()
+ assert new.offset == 7200
+ assert new.timezone_name == "Europe/Paris"
diff --git a/tests/datetime/test_from_format.py b/tests/datetime/test_from_format.py
new file mode 100644
index 0000000..10c4a23
--- /dev/null
+++ b/tests/datetime/test_from_format.py
@@ -0,0 +1,203 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_from_format_returns_datetime():
+ d = pendulum.from_format("1975-05-21 22:32:11", "YYYY-MM-DD HH:mm:ss")
+ assert_datetime(d, 1975, 5, 21, 22, 32, 11)
+ assert isinstance(d, pendulum.DateTime)
+ assert d.timezone_name == "UTC"
+
+
+def test_from_format_rejects_extra_text():
+ with pytest.raises(ValueError):
+ pendulum.from_format("1975-05-21 22:32:11 extra text", "YYYY-MM-DD HH:mm:ss")
+
+
+def test_from_format_with_timezone_string():
+ d = pendulum.from_format(
+ "1975-05-21 22:32:11", "YYYY-MM-DD HH:mm:ss", tz="Europe/London"
+ )
+ assert_datetime(d, 1975, 5, 21, 22, 32, 11)
+ assert d.timezone_name == "Europe/London"
+
+
+def test_from_format_with_timezone():
+ d = pendulum.from_format(
+ "1975-05-21 22:32:11",
+ "YYYY-MM-DD HH:mm:ss",
+ tz=pendulum.timezone("Europe/London"),
+ )
+ assert_datetime(d, 1975, 5, 21, 22, 32, 11)
+ assert d.timezone_name == "Europe/London"
+
+
+def test_from_format_with_square_bracket_in_timezone():
+ with pytest.raises(ValueError, match="^String does not match format"):
+ pendulum.from_format(
+ "1975-05-21 22:32:11 Eu[rope/London",
+ "YYYY-MM-DD HH:mm:ss z",
+ )
+
+
+def test_from_format_with_escaped_elements():
+ d = pendulum.from_format("1975-05-21T22:32:11+00:00", "YYYY-MM-DD[T]HH:mm:ssZ")
+ assert_datetime(d, 1975, 5, 21, 22, 32, 11)
+ assert d.timezone_name == "+00:00"
+
+
+def test_from_format_with_escaped_elements_valid_tokens():
+ d = pendulum.from_format("1975-05-21T22:32:11.123Z", "YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")
+ assert_datetime(d, 1975, 5, 21, 22, 32, 11)
+ assert d.timezone_name == "UTC"
+
+
+def test_from_format_with_millis():
+ d = pendulum.from_format("1975-05-21 22:32:11.123456", "YYYY-MM-DD HH:mm:ss.SSSSSS")
+ assert_datetime(d, 1975, 5, 21, 22, 32, 11, 123456)
+
+
+def test_from_format_with_padded_day():
+ d = pendulum.from_format("Apr 2 12:00:00 2020 GMT", "MMM DD HH:mm:ss YYYY z")
+ assert_datetime(d, 2020, 4, 2, 12)
+
+
+def test_from_format_with_invalid_padded_day():
+ with pytest.raises(ValueError):
+ pendulum.from_format("Apr 2 12:00:00 2020 GMT", "MMM DD HH:mm:ss YYYY z")
+
+
+@pytest.mark.parametrize(
+ "text,fmt,expected,now",
+ [
+ ("2014-4", "YYYY-Q", "2014-10-01T00:00:00+00:00", None),
+ ("12-02-1999", "MM-DD-YYYY", "1999-12-02T00:00:00+00:00", None),
+ ("12-02-1999", "DD-MM-YYYY", "1999-02-12T00:00:00+00:00", None),
+ ("12/02/1999", "DD/MM/YYYY", "1999-02-12T00:00:00+00:00", None),
+ ("12_02_1999", "DD_MM_YYYY", "1999-02-12T00:00:00+00:00", None),
+ ("12:02:1999", "DD:MM:YYYY", "1999-02-12T00:00:00+00:00", None),
+ ("2-2-99", "D-M-YY", "2099-02-02T00:00:00+00:00", None),
+ ("2-2-99", "D-M-YY", "1999-02-02T00:00:00+00:00", "1990-01-01"),
+ ("99", "YY", "2099-01-01T00:00:00+00:00", None),
+ ("300-1999", "DDD-YYYY", "1999-10-27T00:00:00+00:00", None),
+ ("12-02-1999 2:45:10", "DD-MM-YYYY h:m:s", "1999-02-12T02:45:10+00:00", None),
+ ("12-02-1999 12:45:10", "DD-MM-YYYY h:m:s", "1999-02-12T12:45:10+00:00", None),
+ ("12:00:00", "HH:mm:ss", "2015-11-12T12:00:00+00:00", None),
+ ("12:30:00", "HH:mm:ss", "2015-11-12T12:30:00+00:00", None),
+ ("00:00:00", "HH:mm:ss", "2015-11-12T00:00:00+00:00", None),
+ ("00:30:00 1", "HH:mm:ss S", "2015-11-12T00:30:00.100000+00:00", None),
+ ("00:30:00 12", "HH:mm:ss SS", "2015-11-12T00:30:00.120000+00:00", None),
+ ("00:30:00 123", "HH:mm:ss SSS", "2015-11-12T00:30:00.123000+00:00", None),
+ ("1234567890", "X", "2009-02-13T23:31:30+00:00", None),
+ ("1234567890123", "x", "2009-02-13T23:31:30.123000+00:00", None),
+ ("2016-10-06", "YYYY-MM-DD", "2016-10-06T00:00:00+00:00", None),
+ ("Tuesday", "dddd", "2015-11-10T00:00:00+00:00", None),
+ ("Monday", "dddd", "2018-01-29T00:00:00+00:00", "2018-02-02"),
+ ("Mon", "ddd", "2018-01-29T00:00:00+00:00", "2018-02-02"),
+ ("Mo", "dd", "2018-01-29T00:00:00+00:00", "2018-02-02"),
+ ("0", "d", "2018-02-04T00:00:00+00:00", "2018-02-02"),
+ ("1", "E", "2018-01-29T00:00:00+00:00", "2018-02-02"),
+ ("March", "MMMM", "2018-03-01T00:00:00+00:00", "2018-02-02"),
+ ("Mar", "MMM", "2018-03-01T00:00:00+00:00", "2018-02-02"),
+ (
+ "Thursday 25th December 1975 02:15:16 PM",
+ "dddd Do MMMM YYYY hh:mm:ss A",
+ "1975-12-25T14:15:16+00:00",
+ None,
+ ),
+ (
+ "Thursday 25th December 1975 02:15:16 PM -05:00",
+ "dddd Do MMMM YYYY hh:mm:ss A Z",
+ "1975-12-25T14:15:16-05:00",
+ None,
+ ),
+ (
+ "1975-12-25T14:15:16 America/Guayaquil",
+ "YYYY-MM-DDTHH:mm:ss z",
+ "1975-12-25T14:15:16-05:00",
+ None,
+ ),
+ (
+ "1975-12-25T14:15:16 America/New_York",
+ "YYYY-MM-DDTHH:mm:ss z",
+ "1975-12-25T14:15:16-05:00",
+ None,
+ ),
+ (
+ "1975-12-25T14:15:16 Africa/Porto-Novo",
+ "YYYY-MM-DDTHH:mm:ss z",
+ "1975-12-25T14:15:16+01:00",
+ None,
+ ),
+ (
+ "1975-12-25T14:15:16 Etc/GMT+0",
+ "YYYY-MM-DDTHH:mm:ss z",
+ "1975-12-25T14:15:16+00:00",
+ None,
+ ),
+ (
+ "1975-12-25T14:15:16 W-SU",
+ "YYYY-MM-DDTHH:mm:ss z",
+ "1975-12-25T14:15:16+03:00",
+ None,
+ ),
+ ("190022215", "YYDDDDHHmm", "2019-01-02T22:15:00+00:00", None),
+ ],
+)
+def test_from_format(text, fmt, expected, now):
+ if now is None:
+ now = pendulum.datetime(2015, 11, 12)
+ else:
+ now = pendulum.parse(now)
+
+ with pendulum.travel_to(now, freeze=True):
+ assert pendulum.from_format(text, fmt).isoformat() == expected
+
+
+@pytest.mark.parametrize(
+ "text,fmt,expected",
+ [
+ ("lundi", "dddd", "2018-01-29T00:00:00+00:00"),
+ ("lun.", "ddd", "2018-01-29T00:00:00+00:00"),
+ ("lu", "dd", "2018-01-29T00:00:00+00:00"),
+ ("mars", "MMMM", "2018-03-01T00:00:00+00:00"),
+ ("mars", "MMM", "2018-03-01T00:00:00+00:00"),
+ ],
+)
+def test_from_format_with_locale(text, fmt, expected):
+ now = pendulum.datetime(2018, 2, 2)
+
+ with pendulum.travel_to(now, freeze=True):
+ formatted = pendulum.from_format(text, fmt, locale="fr").isoformat()
+ assert formatted == expected
+
+
+@pytest.mark.parametrize(
+ "text,fmt,locale",
+ [
+ ("23:00", "hh:mm", "en"),
+ ("23:00 am", "HH:mm a", "en"),
+ ("invalid", "dddd", "en"),
+ ("invalid", "ddd", "en"),
+ ("invalid", "dd", "en"),
+ ("invalid", "MMMM", "en"),
+ ("invalid", "MMM", "en"),
+ ],
+)
+def test_from_format_error(text, fmt, locale):
+ now = pendulum.datetime(2018, 2, 2)
+
+ with pendulum.travel_to(now, freeze=True), pytest.raises(ValueError):
+ pendulum.from_format(text, fmt, locale=locale)
+
+
+def test_strptime():
+ d = pendulum.DateTime.strptime("1975-05-21 22:32:11", "%Y-%m-%d %H:%M:%S")
+ assert_datetime(d, 1975, 5, 21, 22, 32, 11)
+ assert isinstance(d, pendulum.DateTime)
+ assert d.timezone_name == "UTC"
diff --git a/tests/datetime/test_getters.py b/tests/datetime/test_getters.py
new file mode 100644
index 0000000..5074623
--- /dev/null
+++ b/tests/datetime/test_getters.py
@@ -0,0 +1,248 @@
+from __future__ import annotations
+
+import struct
+
+import pytest
+
+import pendulum
+
+from pendulum import DateTime
+from pendulum.tz import timezone
+from tests.conftest import assert_date
+from tests.conftest import assert_time
+
+
+def test_year():
+ d = pendulum.datetime(1234, 5, 6, 7, 8, 9)
+ assert d.year == 1234
+
+
+def test_month():
+ d = pendulum.datetime(1234, 5, 6, 7, 8, 9)
+ assert d.month == 5
+
+
+def test_day():
+ d = pendulum.datetime(1234, 5, 6, 7, 8, 9)
+ assert d.day == 6
+
+
+def test_hour():
+ d = pendulum.datetime(1234, 5, 6, 7, 8, 9)
+ assert d.hour == 7
+
+
+def test_minute():
+ d = pendulum.datetime(1234, 5, 6, 7, 8, 9)
+ assert d.minute == 8
+
+
+def test_second():
+ d = pendulum.datetime(1234, 5, 6, 7, 8, 9)
+ assert d.second == 9
+
+
+def test_microsecond():
+ d = pendulum.datetime(1234, 5, 6, 7, 8, 9)
+ assert d.microsecond == 0
+
+ d = pendulum.datetime(1234, 5, 6, 7, 8, 9, 101112)
+ assert d.microsecond == 101112
+
+
+def test_tzinfo():
+ d = pendulum.now()
+ assert d.tzinfo.name == timezone("America/Toronto").name
+
+
+def test_day_of_week():
+ d = pendulum.datetime(2012, 5, 7, 7, 8, 9)
+ assert d.day_of_week == pendulum.MONDAY
+
+
+def test_day_of_year():
+ d = pendulum.datetime(2012, 5, 7)
+ assert d.day_of_year == 128
+
+
+def test_days_in_month():
+ d = pendulum.datetime(2012, 5, 7)
+ assert d.days_in_month == 31
+
+
+def test_timestamp():
+ d = pendulum.datetime(1970, 1, 1, 0, 0, 0)
+ assert d.timestamp() == 0
+ assert d.add(minutes=1, microseconds=123456).timestamp() == 60.123456
+
+
+def test_float_timestamp():
+ d = pendulum.datetime(1970, 1, 1, 0, 0, 0, 123456)
+ assert d.float_timestamp == 0.123456
+
+
+def test_int_timestamp():
+ d = pendulum.datetime(1970, 1, 1, 0, 0, 0)
+ assert d.int_timestamp == 0
+ assert d.add(minutes=1, microseconds=123456).int_timestamp == 60
+
+
+@pytest.mark.skipif(
+ struct.calcsize("P") * 8 == 32, reason="Test only available for 64bit systems"
+)
+def test_int_timestamp_accuracy():
+ d = pendulum.datetime(3000, 10, 1, 12, 23, 10, 999999)
+
+ assert d.int_timestamp == 32527311790
+
+
+def test_timestamp_with_transition():
+ d_pre = pendulum.datetime(2012, 10, 28, 2, 0, tz="Europe/Warsaw", fold=0)
+ d_post = pendulum.datetime(2012, 10, 28, 2, 0, tz="Europe/Warsaw", fold=1)
+
+ # the difference between the timestamps before and after is equal to one hour
+ assert d_post.timestamp() - d_pre.timestamp() == pendulum.SECONDS_PER_HOUR
+ assert d_post.float_timestamp - d_pre.float_timestamp == (pendulum.SECONDS_PER_HOUR)
+ assert d_post.int_timestamp - d_pre.int_timestamp == pendulum.SECONDS_PER_HOUR
+
+
+def test_age():
+ d = pendulum.now()
+ assert d.age == 0
+ assert d.add(years=1).age == -1
+ assert d.subtract(years=1).age == 1
+
+
+def test_local():
+ assert pendulum.datetime(2012, 1, 1, tz="America/Toronto").is_local()
+ assert pendulum.datetime(2012, 1, 1, tz="America/New_York").is_local()
+ assert not pendulum.datetime(2012, 1, 1, tz="UTC").is_local()
+ assert not pendulum.datetime(2012, 1, 1, tz="Europe/London").is_local()
+
+
+def test_utc():
+ assert not pendulum.datetime(2012, 1, 1, tz="America/Toronto").is_utc()
+ assert not pendulum.datetime(2012, 1, 1, tz="Europe/Paris").is_utc()
+ assert pendulum.datetime(2012, 1, 1, tz="UTC").is_utc()
+ assert pendulum.datetime(2012, 1, 1, tz=0).is_utc()
+ assert not pendulum.datetime(2012, 1, 1, tz=5).is_utc()
+ # There is no time difference between Greenwich Mean Time and Coordinated Universal Time
+ assert pendulum.datetime(2012, 1, 1, tz="GMT").is_utc()
+
+
+def test_is_dst():
+ assert not pendulum.datetime(2012, 1, 1, tz="America/Toronto").is_dst()
+ assert pendulum.datetime(2012, 7, 1, tz="America/Toronto").is_dst()
+
+
+def test_offset_with_dst():
+ assert pendulum.datetime(2012, 1, 1, tz="America/Toronto").offset == -18000
+
+
+def test_offset_no_dst():
+ assert pendulum.datetime(2012, 6, 1, tz="America/Toronto").offset == -14400
+
+
+def test_offset_for_gmt():
+ assert pendulum.datetime(2012, 6, 1, tz="GMT").offset == 0
+
+
+def test_offset_hours_with_dst():
+ assert pendulum.datetime(2012, 1, 1, tz="America/Toronto").offset_hours == -5
+
+
+def test_offset_hours_no_dst():
+ assert pendulum.datetime(2012, 6, 1, tz="America/Toronto").offset_hours == -4
+
+
+def test_offset_hours_for_gmt():
+ assert pendulum.datetime(2012, 6, 1, tz="GMT").offset_hours == 0
+
+
+def test_offset_hours_float():
+ assert pendulum.datetime(2012, 6, 1, tz=9.5).offset_hours == 9.5
+
+
+def test_is_leap_year():
+ assert pendulum.datetime(2012, 1, 1).is_leap_year()
+ assert not pendulum.datetime(2011, 1, 1).is_leap_year()
+
+
+def test_is_long_year():
+ assert pendulum.datetime(2015, 1, 1).is_long_year()
+ assert not pendulum.datetime(2016, 1, 1).is_long_year()
+
+
+def test_week_of_month():
+ assert pendulum.datetime(2012, 9, 30).week_of_month == 5
+ assert pendulum.datetime(2012, 9, 28).week_of_month == 5
+ assert pendulum.datetime(2012, 9, 20).week_of_month == 4
+ assert pendulum.datetime(2012, 9, 8).week_of_month == 2
+ assert pendulum.datetime(2012, 9, 1).week_of_month == 1
+ assert pendulum.datetime(2020, 1, 1).week_of_month == 1
+ assert pendulum.datetime(2020, 1, 7).week_of_month == 2
+ assert pendulum.datetime(2020, 1, 14).week_of_month == 3
+
+
+def test_week_of_year_first_week():
+ assert pendulum.datetime(2012, 1, 1).week_of_year == 52
+ assert pendulum.datetime(2012, 1, 2).week_of_year == 1
+
+
+def test_week_of_year_last_week():
+ assert pendulum.datetime(2012, 12, 30).week_of_year == 52
+ assert pendulum.datetime(2012, 12, 31).week_of_year == 1
+
+
+def test_timezone():
+ d = pendulum.datetime(2000, 1, 1, tz="America/Toronto")
+ assert d.timezone.name == "America/Toronto"
+
+ d = pendulum.datetime(2000, 1, 1, tz=-5)
+ assert d.timezone.name == "-05:00"
+
+
+def test_tz():
+ d = pendulum.datetime(2000, 1, 1, tz="America/Toronto")
+ assert d.tz.name == "America/Toronto"
+
+ d = pendulum.datetime(2000, 1, 1, tz=-5)
+ assert d.tz.name == "-05:00"
+
+
+def test_timezone_name():
+ d = pendulum.datetime(2000, 1, 1, tz="America/Toronto")
+ assert d.timezone_name == "America/Toronto"
+
+ d = pendulum.datetime(2000, 1, 1, tz=-5)
+ assert d.timezone_name == "-05:00"
+
+
+def test_is_future():
+ with pendulum.travel_to(DateTime(2000, 1, 1)):
+ d = pendulum.now()
+ assert not d.is_future()
+ d = d.add(days=1)
+ assert d.is_future()
+
+
+def test_is_past():
+ with pendulum.travel_to(DateTime(2000, 1, 1), freeze=True):
+ d = pendulum.now()
+ assert not d.is_past()
+ d = d.subtract(days=1)
+ assert d.is_past()
+
+
+def test_date():
+ dt = pendulum.datetime(2016, 10, 20, 10, 40, 34, 123456)
+ d = dt.date()
+ assert isinstance(d, pendulum.Date)
+ assert_date(d, 2016, 10, 20)
+
+
+def test_time():
+ dt = pendulum.datetime(2016, 10, 20, 10, 40, 34, 123456)
+ t = dt.time()
+ assert isinstance(t, pendulum.Time)
+ assert_time(t, 10, 40, 34, 123456)
diff --git a/tests/datetime/test_naive.py b/tests/datetime/test_naive.py
new file mode 100644
index 0000000..95bd2d5
--- /dev/null
+++ b/tests/datetime/test_naive.py
@@ -0,0 +1,78 @@
+from __future__ import annotations
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_naive():
+ dt = pendulum.naive(2018, 2, 2, 12, 34, 56, 123456)
+
+ assert_datetime(dt, 2018, 2, 2, 12, 34, 56, 123456)
+ assert dt.tzinfo is None
+ assert dt.timezone is None
+ assert dt.timezone_name is None
+
+
+def test_naive_add():
+ dt = pendulum.naive(2013, 3, 31, 1, 30)
+ new = dt.add(hours=1)
+
+ assert_datetime(new, 2013, 3, 31, 2, 30)
+
+
+def test_naive_subtract():
+ dt = pendulum.naive(2013, 3, 31, 1, 30)
+ new = dt.subtract(hours=1)
+
+ assert_datetime(new, 2013, 3, 31, 0, 30)
+
+
+def test_naive_in_timezone():
+ dt = pendulum.naive(2013, 3, 31, 1, 30)
+ new = dt.in_timezone("Europe/Paris")
+
+ assert_datetime(new, 2013, 3, 31, 1, 30)
+ assert new.timezone_name == "Europe/Paris"
+
+
+def test_naive_in_timezone_dst():
+ dt = pendulum.naive(2013, 3, 31, 2, 30)
+ new = dt.in_timezone("Europe/Paris")
+
+ assert_datetime(new, 2013, 3, 31, 3, 30)
+ assert new.timezone_name == "Europe/Paris"
+
+
+def test_add():
+ dt = pendulum.naive(2013, 3, 31, 2, 30)
+ new = dt.add(days=3)
+
+ assert_datetime(new, 2013, 4, 3, 2, 30)
+
+
+def test_subtract():
+ dt = pendulum.naive(2013, 3, 31, 2, 30)
+ new = dt.subtract(days=3)
+
+ assert_datetime(new, 2013, 3, 28, 2, 30)
+
+
+def test_to_strings():
+ dt = pendulum.naive(2013, 3, 31, 2, 30)
+
+ assert dt.isoformat() == "2013-03-31T02:30:00"
+ assert dt.to_iso8601_string() == "2013-03-31T02:30:00"
+ assert dt.to_rfc3339_string() == "2013-03-31T02:30:00"
+ assert dt.to_atom_string() == "2013-03-31T02:30:00"
+ assert dt.to_cookie_string() == "Sunday, 31-Mar-2013 02:30:00 "
+
+
+def test_naive_method():
+ dt = pendulum.datetime(2018, 2, 2, 12, 34, 56, 123456)
+ dt = dt.naive()
+
+ assert_datetime(dt, 2018, 2, 2, 12, 34, 56, 123456)
+ assert dt.tzinfo is None
+ assert dt.timezone is None
+ assert dt.timezone_name is None
diff --git a/tests/datetime/test_replace.py b/tests/datetime/test_replace.py
new file mode 100644
index 0000000..694ef7b
--- /dev/null
+++ b/tests/datetime/test_replace.py
@@ -0,0 +1,61 @@
+from __future__ import annotations
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_replace_tzinfo_dst_off():
+ utc = pendulum.datetime(2016, 3, 27, 0, 30) # 30 min before DST turning on
+ in_paris = utc.in_tz("Europe/Paris")
+
+ assert_datetime(in_paris, 2016, 3, 27, 1, 30, 0)
+
+ in_paris = in_paris.replace(second=1)
+
+ assert_datetime(in_paris, 2016, 3, 27, 1, 30, 1)
+ assert not in_paris.is_dst()
+ assert in_paris.offset == 3600
+ assert in_paris.timezone_name == "Europe/Paris"
+
+
+def test_replace_tzinfo_dst_transitioning_on():
+ utc = pendulum.datetime(2016, 3, 27, 1, 30) # In middle of turning on
+ in_paris = utc.in_tz("Europe/Paris")
+
+ assert_datetime(in_paris, 2016, 3, 27, 3, 30, 0)
+
+ in_paris = in_paris.replace(second=1)
+
+ assert_datetime(in_paris, 2016, 3, 27, 3, 30, 1)
+ assert in_paris.is_dst()
+ assert in_paris.offset == 7200
+ assert in_paris.timezone_name == "Europe/Paris"
+
+
+def test_replace_tzinfo_dst_on():
+ utc = pendulum.datetime(2016, 10, 30, 0, 30) # 30 min before DST turning off
+ in_paris = utc.in_tz("Europe/Paris")
+
+ assert_datetime(in_paris, 2016, 10, 30, 2, 30, 0)
+
+ in_paris = in_paris.replace(second=1)
+
+ assert_datetime(in_paris, 2016, 10, 30, 2, 30, 1)
+ assert in_paris.is_dst()
+ assert in_paris.offset == 7200
+ assert in_paris.timezone_name == "Europe/Paris"
+
+
+def test_replace_tzinfo_dst_transitioning_off():
+ utc = pendulum.datetime(2016, 10, 30, 1, 30) # In the middle of turning off
+ in_paris = utc.in_tz("Europe/Paris")
+
+ assert_datetime(in_paris, 2016, 10, 30, 2, 30, 0)
+
+ in_paris = in_paris.replace(second=1)
+
+ assert_datetime(in_paris, 2016, 10, 30, 2, 30, 1)
+ assert not in_paris.is_dst()
+ assert in_paris.offset == 3600
+ assert in_paris.timezone_name == "Europe/Paris"
diff --git a/tests/datetime/test_start_end_of.py b/tests/datetime/test_start_end_of.py
new file mode 100644
index 0000000..597dbeb
--- /dev/null
+++ b/tests/datetime/test_start_end_of.py
@@ -0,0 +1,285 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_start_of_second():
+ d = pendulum.now()
+ new = d.start_of("second")
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, d.year, d.month, d.day, d.hour, d.minute, d.second, 0)
+
+
+def test_end_of_second():
+ d = pendulum.now()
+ new = d.end_of("second")
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, d.year, d.month, d.day, d.hour, d.minute, d.second, 999999)
+
+
+def test_start_of_minute():
+ d = pendulum.now()
+ new = d.start_of("minute")
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, d.year, d.month, d.day, d.hour, d.minute, 0, 0)
+
+
+def test_end_of_minute():
+ d = pendulum.now()
+ new = d.end_of("minute")
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, d.year, d.month, d.day, d.hour, d.minute, 59, 999999)
+
+
+def test_start_of_hour():
+ d = pendulum.now()
+ new = d.start_of("hour")
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, d.year, d.month, d.day, d.hour, 0, 0, 0)
+
+
+def test_end_of_hour():
+ d = pendulum.now()
+ new = d.end_of("hour")
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, d.year, d.month, d.day, d.hour, 59, 59, 999999)
+
+
+def test_start_of_day():
+ d = pendulum.now()
+ new = d.start_of("day")
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, d.year, d.month, d.day, 0, 0, 0, 0)
+
+
+def test_end_of_day():
+ d = pendulum.now()
+ new = d.end_of("day")
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, d.year, d.month, d.day, 23, 59, 59, 999999)
+
+
+def test_start_of_month_is_fluid():
+ d = pendulum.now()
+ assert isinstance(d.start_of("month"), pendulum.DateTime)
+
+
+def test_start_of_month_from_now():
+ d = pendulum.now()
+ new = d.start_of("month")
+ assert_datetime(new, d.year, d.month, 1, 0, 0, 0, 0)
+
+
+def test_start_of_month_from_last_day():
+ d = pendulum.datetime(2000, 1, 31, 2, 3, 4)
+ new = d.start_of("month")
+ assert_datetime(new, 2000, 1, 1, 0, 0, 0, 0)
+
+
+def test_start_of_year_is_fluid():
+ d = pendulum.now()
+ new = d.start_of("year")
+ assert isinstance(new, pendulum.DateTime)
+
+
+def test_start_of_year_from_now():
+ d = pendulum.now()
+ new = d.start_of("year")
+ assert_datetime(new, d.year, 1, 1, 0, 0, 0, 0)
+
+
+def test_start_of_year_from_first_day():
+ d = pendulum.datetime(2000, 1, 1, 1, 1, 1)
+ new = d.start_of("year")
+ assert_datetime(new, 2000, 1, 1, 0, 0, 0, 0)
+
+
+def test_start_of_year_from_last_day():
+ d = pendulum.datetime(2000, 12, 31, 23, 59, 59)
+ new = d.start_of("year")
+ assert_datetime(new, 2000, 1, 1, 0, 0, 0, 0)
+
+
+def test_end_of_month_is_fluid():
+ d = pendulum.now()
+ assert isinstance(d.end_of("month"), pendulum.DateTime)
+
+
+def test_end_of_month():
+ d = pendulum.datetime(2000, 1, 1, 2, 3, 4).end_of("month")
+ new = d.end_of("month")
+ assert_datetime(new, 2000, 1, 31, 23, 59, 59)
+
+
+def test_end_of_month_from_last_day():
+ d = pendulum.datetime(2000, 1, 31, 2, 3, 4)
+ new = d.end_of("month")
+ assert_datetime(new, 2000, 1, 31, 23, 59, 59)
+
+
+def test_end_of_year_is_fluid():
+ d = pendulum.now()
+ assert isinstance(d.end_of("year"), pendulum.DateTime)
+
+
+def test_end_of_year_from_now():
+ d = pendulum.now().end_of("year")
+ new = d.end_of("year")
+ assert_datetime(new, d.year, 12, 31, 23, 59, 59, 999999)
+
+
+def test_end_of_year_from_first_day():
+ d = pendulum.datetime(2000, 1, 1, 1, 1, 1)
+ new = d.end_of("year")
+ assert_datetime(new, 2000, 12, 31, 23, 59, 59, 999999)
+
+
+def test_end_of_year_from_last_day():
+ d = pendulum.datetime(2000, 12, 31, 23, 59, 59, 999999)
+ new = d.end_of("year")
+ assert_datetime(new, 2000, 12, 31, 23, 59, 59, 999999)
+
+
+def test_start_of_decade_is_fluid():
+ d = pendulum.now()
+ assert isinstance(d.start_of("decade"), pendulum.DateTime)
+
+
+def test_start_of_decade_from_now():
+ d = pendulum.now()
+ new = d.start_of("decade")
+ assert_datetime(new, d.year - d.year % 10, 1, 1, 0, 0, 0, 0)
+
+
+def test_start_of_decade_from_first_day():
+ d = pendulum.datetime(2000, 1, 1, 1, 1, 1)
+ new = d.start_of("decade")
+ assert_datetime(new, 2000, 1, 1, 0, 0, 0, 0)
+
+
+def test_start_of_decade_from_last_day():
+ d = pendulum.datetime(2009, 12, 31, 23, 59, 59)
+ new = d.start_of("decade")
+ assert_datetime(new, 2000, 1, 1, 0, 0, 0, 0)
+
+
+def test_end_of_decade_is_fluid():
+ d = pendulum.now()
+ assert isinstance(d.end_of("decade"), pendulum.DateTime)
+
+
+def test_end_of_decade_from_now():
+ d = pendulum.now()
+ new = d.end_of("decade")
+ assert_datetime(new, d.year - d.year % 10 + 9, 12, 31, 23, 59, 59, 999999)
+
+
+def test_end_of_decade_from_first_day():
+ d = pendulum.datetime(2000, 1, 1, 1, 1, 1)
+ new = d.end_of("decade")
+ assert_datetime(new, 2009, 12, 31, 23, 59, 59, 999999)
+
+
+def test_end_of_decade_from_last_day():
+ d = pendulum.datetime(2009, 12, 31, 23, 59, 59, 999999)
+ new = d.end_of("decade")
+ assert_datetime(new, 2009, 12, 31, 23, 59, 59, 999999)
+
+
+def test_start_of_century_is_fluid():
+ d = pendulum.now()
+ assert isinstance(d.start_of("century"), pendulum.DateTime)
+
+
+def test_start_of_century_from_now():
+ d = pendulum.now()
+ new = d.start_of("century")
+ assert_datetime(new, d.year - d.year % 100 + 1, 1, 1, 0, 0, 0, 0)
+
+
+def test_start_of_century_from_first_day():
+ d = pendulum.datetime(2001, 1, 1, 1, 1, 1)
+ new = d.start_of("century")
+ assert_datetime(new, 2001, 1, 1, 0, 0, 0, 0)
+
+
+def test_start_of_century_from_last_day():
+ d = pendulum.datetime(2100, 12, 31, 23, 59, 59)
+ new = d.start_of("century")
+ assert_datetime(new, 2001, 1, 1, 0, 0, 0, 0)
+
+
+def test_end_of_century_is_fluid():
+ d = pendulum.now()
+ assert isinstance(d.end_of("century"), pendulum.DateTime)
+
+
+def test_end_of_century_from_now():
+ now = pendulum.now()
+ d = now.end_of("century")
+ assert_datetime(d, now.year - now.year % 100 + 100, 12, 31, 23, 59, 59, 999999)
+
+
+def test_end_of_century_from_first_day():
+ d = pendulum.datetime(2001, 1, 1, 1, 1, 1)
+ new = d.end_of("century")
+ assert_datetime(new, 2100, 12, 31, 23, 59, 59, 999999)
+
+
+def test_end_of_century_from_last_day():
+ d = pendulum.datetime(2100, 12, 31, 23, 59, 59, 999999)
+ new = d.end_of("century")
+ assert_datetime(new, 2100, 12, 31, 23, 59, 59, 999999)
+
+
+def test_average_is_fluid():
+ d = pendulum.now().average()
+ assert isinstance(d, pendulum.DateTime)
+
+
+def test_average_from_same():
+ d1 = pendulum.datetime(2000, 1, 31, 2, 3, 4)
+ d2 = pendulum.datetime(2000, 1, 31, 2, 3, 4).average(d1)
+ assert_datetime(d2, 2000, 1, 31, 2, 3, 4)
+
+
+def test_average_from_greater():
+ d1 = pendulum.datetime(2000, 1, 1, 1, 1, 1, tz="local")
+ d2 = pendulum.datetime(2009, 12, 31, 23, 59, 59, tz="local").average(d1)
+ assert_datetime(d2, 2004, 12, 31, 12, 30, 30)
+
+
+def test_average_from_lower():
+ d1 = pendulum.datetime(2009, 12, 31, 23, 59, 59, tz="local")
+ d2 = pendulum.datetime(2000, 1, 1, 1, 1, 1, tz="local").average(d1)
+ assert_datetime(d2, 2004, 12, 31, 12, 30, 30)
+
+
+def start_of_with_invalid_unit():
+ with pytest.raises(ValueError):
+ pendulum.now().start_of("invalid")
+
+
+def end_of_with_invalid_unit():
+ with pytest.raises(ValueError):
+ pendulum.now().end_of("invalid")
+
+
+def test_start_of_with_transition():
+ d = pendulum.datetime(2013, 10, 27, 23, 59, 59, tz="Europe/Paris")
+ assert d.offset == 3600
+ assert d.start_of("month").offset == 7200
+ assert d.start_of("day").offset == 7200
+ assert d.start_of("year").offset == 3600
+
+
+def test_end_of_with_transition():
+ d = pendulum.datetime(2013, 3, 31, tz="Europe/Paris")
+ assert d.offset == 3600
+ assert d.end_of("month").offset == 7200
+ assert d.end_of("day").offset == 7200
+ assert d.end_of("year").offset == 3600
diff --git a/tests/datetime/test_strings.py b/tests/datetime/test_strings.py
new file mode 100644
index 0000000..0de340d
--- /dev/null
+++ b/tests/datetime/test_strings.py
@@ -0,0 +1,141 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+
+def test_to_string():
+ d = pendulum.datetime(1975, 12, 25, 0, 0, 0, 0, tz="local")
+ assert str(d) == d.to_iso8601_string()
+ d = pendulum.datetime(1975, 12, 25, 0, 0, 0, 123456, tz="local")
+ assert str(d) == d.to_iso8601_string()
+
+
+def test_to_date_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16)
+
+ assert d.to_date_string() == "1975-12-25"
+
+
+def test_to_formatted_date_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16)
+
+ assert d.to_formatted_date_string() == "Dec 25, 1975"
+
+
+def test_to_timestring():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16)
+
+ assert d.to_time_string() == "14:15:16"
+
+
+def test_to_atom_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_atom_string() == "1975-12-25T14:15:16-05:00"
+
+
+def test_to_cookie_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_cookie_string() == "Thursday, 25-Dec-1975 14:15:16 EST"
+
+
+def test_to_iso8601_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_iso8601_string() == "1975-12-25T14:15:16-05:00"
+
+
+def test_to_iso8601_string_utc():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16)
+ assert d.to_iso8601_string() == "1975-12-25T14:15:16Z"
+
+
+def test_to_iso8601_extended_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, 123456, tz="local")
+ assert d.to_iso8601_string() == "1975-12-25T14:15:16.123456-05:00"
+
+
+def test_to_rfc822_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_rfc822_string() == "Thu, 25 Dec 75 14:15:16 -0500"
+
+
+def test_to_rfc850_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_rfc850_string() == "Thursday, 25-Dec-75 14:15:16 EST"
+
+
+def test_to_rfc1036_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_rfc1036_string() == "Thu, 25 Dec 75 14:15:16 -0500"
+
+
+def test_to_rfc1123_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_rfc1123_string() == "Thu, 25 Dec 1975 14:15:16 -0500"
+
+
+def test_to_rfc2822_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_rfc2822_string() == "Thu, 25 Dec 1975 14:15:16 -0500"
+
+
+def test_to_rfc3339_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_rfc3339_string() == "1975-12-25T14:15:16-05:00"
+
+
+def test_to_rfc3339_extended_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, 123456, tz="local")
+ assert d.to_rfc3339_string() == "1975-12-25T14:15:16.123456-05:00"
+
+
+def test_to_rss_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_rss_string() == "Thu, 25 Dec 1975 14:15:16 -0500"
+
+
+def test_to_w3c_string():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.to_w3c_string() == "1975-12-25T14:15:16-05:00"
+
+
+def test_to_string_invalid():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+
+ with pytest.raises(ValueError):
+ d._to_string("invalid")
+
+
+def test_repr():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ expected = f"DateTime(1975, 12, 25, 14, 15, 16, tzinfo={repr(d.tzinfo)})"
+ assert repr(d) == expected
+
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, 123456, tz="local")
+ expected = f"DateTime(1975, 12, 25, 14, 15, 16, 123456, tzinfo={repr(d.tzinfo)})"
+ assert repr(d) == expected
+
+
+def test_format_with_locale():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ expected = "jeudi 25e jour de décembre 1975 02:15:16 PM -05:00"
+ assert d.format("dddd Do [jour de] MMMM YYYY hh:mm:ss A Z", locale="fr") == expected
+
+
+def test_strftime():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.strftime("%d") == "25"
+
+
+def test_for_json():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="local")
+ assert d.for_json() == "1975-12-25T14:15:16-05:00"
+
+
+def test_format():
+ d = pendulum.datetime(1975, 12, 25, 14, 15, 16, tz="Europe/Paris")
+ assert f"{d}" == "1975-12-25T14:15:16+01:00"
+ assert f"{d:YYYY}" == "1975"
+ assert f"{d:%Y}" == "1975"
+ assert f"{d:%H:%M %d.%m.%Y}" == "14:15 25.12.1975"
diff --git a/tests/datetime/test_sub.py b/tests/datetime/test_sub.py
new file mode 100644
index 0000000..1e51977
--- /dev/null
+++ b/tests/datetime/test_sub.py
@@ -0,0 +1,251 @@
+from __future__ import annotations
+
+from datetime import timedelta
+
+import pytest
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_sub_years_positive():
+ assert pendulum.datetime(1975, 1, 1).subtract(years=1).year == 1974
+
+
+def test_sub_years_zero():
+ assert pendulum.datetime(1975, 1, 1).subtract(years=0).year == 1975
+
+
+def test_sub_years_negative():
+ assert pendulum.datetime(1975, 1, 1).subtract(years=-1).year == 1976
+
+
+def test_sub_months_positive():
+ assert pendulum.datetime(1975, 12, 1).subtract(months=1).month == 11
+
+
+def test_sub_months_zero():
+ assert pendulum.datetime(1975, 12, 1).subtract(months=0).month == 12
+
+
+def test_sub_months_negative():
+ assert pendulum.datetime(1975, 12, 1).subtract(months=-1).month == 1
+
+
+def test_sub_days_positive():
+ assert pendulum.datetime(1975, 5, 31).subtract(days=1).day == 30
+
+
+def test_sub_days_zero():
+ assert pendulum.datetime(1975, 5, 31).subtract(days=0).day == 31
+
+
+def test_sub_days_negative():
+ assert pendulum.datetime(1975, 5, 31).subtract(days=-1).day == 1
+
+
+def test_sub_weeks_positive():
+ assert pendulum.datetime(1975, 5, 21).subtract(weeks=1).day == 14
+
+
+def test_sub_weeks_zero():
+ assert pendulum.datetime(1975, 5, 21).subtract(weeks=0).day == 21
+
+
+def test_sub_weeks_negative():
+ assert pendulum.datetime(1975, 5, 21).subtract(weeks=-1).day == 28
+
+
+def test_sub_hours_positive():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(hours=1).hour == 23
+
+
+def test_sub_hours_zero():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(hours=0).hour == 0
+
+
+def test_sub_hours_negative():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(hours=-1).hour == 1
+
+
+def test_sub_minutes_positive():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(minutes=1).minute == 59
+
+
+def test_sub_minutes_zero():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(minutes=0).minute == 0
+
+
+def test_sub_minutes_negative():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(minutes=-1).minute == 1
+
+
+def test_sub_seconds_positive():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(seconds=1).second == 59
+
+
+def test_sub_seconds_zero():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(seconds=0).second == 0
+
+
+def test_sub_seconds_negative():
+ assert pendulum.datetime(1975, 5, 21, 0, 0, 0).subtract(seconds=-1).second == 1
+
+
+def test_subtract_timedelta():
+ delta = timedelta(days=6, seconds=16, microseconds=654321)
+ d = pendulum.datetime(2015, 3, 14, 3, 12, 15, 777777)
+
+ d = d - delta
+ assert d.day == 8
+ assert d.minute == 11
+ assert d.second == 59
+ assert d.microsecond == 123456
+
+
+def test_subtract_duration():
+ duration = pendulum.duration(
+ years=2, months=3, days=6, seconds=16, microseconds=654321
+ )
+ d = pendulum.datetime(2015, 3, 14, 3, 12, 15, 777777)
+
+ d = d - duration
+ assert d.year == 2012
+ assert d.month == 12
+ assert d.day == 8
+ assert d.hour == 3
+ assert d.minute == 11
+ assert d.second == 59
+ assert d.microsecond == 123456
+
+
+def test_subtract_time_to_new_transition_skipped():
+ dt = pendulum.datetime(2013, 3, 31, 3, 0, 0, 0, tz="Europe/Paris")
+
+ assert_datetime(dt, 2013, 3, 31, 3, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+ dt = dt.subtract(microseconds=1)
+
+ assert_datetime(dt, 2013, 3, 31, 1, 59, 59, 999999)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 3600
+ assert not dt.is_dst()
+
+ dt = pendulum.datetime(2013, 3, 10, 3, 0, 0, 0, tz="America/New_York")
+
+ assert_datetime(dt, 2013, 3, 10, 3, 0, 0, 0)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -4 * 3600
+ assert dt.is_dst()
+
+ dt = dt.subtract(microseconds=1)
+
+ assert_datetime(dt, 2013, 3, 10, 1, 59, 59, 999999)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -5 * 3600
+ assert not dt.is_dst()
+
+ dt = pendulum.datetime(1957, 4, 28, 3, 0, 0, 0, tz="America/New_York")
+
+ assert_datetime(dt, 1957, 4, 28, 3, 0, 0, 0)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -4 * 3600
+ assert dt.is_dst()
+
+ dt = dt.subtract(microseconds=1)
+
+ assert_datetime(dt, 1957, 4, 28, 1, 59, 59, 999999)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -5 * 3600
+ assert not dt.is_dst()
+
+
+def test_subtract_time_to_new_transition_skipped_big():
+ dt = pendulum.datetime(2013, 3, 31, 3, 0, 0, 0, tz="Europe/Paris")
+
+ assert_datetime(dt, 2013, 3, 31, 3, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+ dt = dt.subtract(days=1)
+
+ assert_datetime(dt, 2013, 3, 30, 3, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 3600
+ assert not dt.is_dst()
+
+
+def test_subtract_time_to_new_transition_repeated():
+ dt = pendulum.datetime(2013, 10, 27, 2, 0, 0, 0, tz="Europe/Paris")
+
+ assert_datetime(dt, 2013, 10, 27, 2, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 3600
+ assert not dt.is_dst()
+
+ dt = dt.subtract(microseconds=1)
+
+ assert_datetime(dt, 2013, 10, 27, 2, 59, 59, 999999)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+ dt = pendulum.datetime(2013, 11, 3, 1, 0, 0, 0, tz="America/New_York")
+
+ assert_datetime(dt, 2013, 11, 3, 1, 0, 0, 0)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -5 * 3600
+ assert not dt.is_dst()
+
+ dt = dt.subtract(microseconds=1)
+
+ assert_datetime(dt, 2013, 11, 3, 1, 59, 59, 999999)
+ assert dt.timezone_name == "America/New_York"
+ assert dt.offset == -4 * 3600
+ assert dt.is_dst()
+
+
+def test_subtract_time_to_new_transition_repeated_big():
+ dt = pendulum.datetime(2013, 10, 27, 2, 0, 0, 0, tz="Europe/Paris")
+
+ assert_datetime(dt, 2013, 10, 27, 2, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 3600
+ assert not dt.is_dst()
+
+ dt = dt.subtract(days=1)
+
+ assert_datetime(dt, 2013, 10, 26, 2, 0, 0, 0)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+
+def test_subtract_invalid_type():
+ d = pendulum.datetime(1975, 5, 21, 0, 0, 0)
+
+ with pytest.raises(TypeError):
+ d - "ab"
+
+ with pytest.raises(TypeError):
+ "ab" - d
+
+
+def test_subtract_negative_over_dls_transitioning_off():
+ just_before_dls_ends = pendulum.datetime(
+ 2019, 11, 3, 1, 30, tz="US/Pacific", fold=0
+ )
+ plus_10_hours = just_before_dls_ends + timedelta(hours=10)
+ minus_neg_10_hours = just_before_dls_ends - timedelta(hours=-10)
+
+ # 1:30-0700 becomes 10:30-0800
+ assert plus_10_hours.hour == 10
+ assert minus_neg_10_hours.hour == 10
+ assert just_before_dls_ends.is_dst()
+ assert not plus_10_hours.is_dst()
+ assert not minus_neg_10_hours.is_dst()
diff --git a/tests/datetime/test_timezone.py b/tests/datetime/test_timezone.py
new file mode 100644
index 0000000..343435d
--- /dev/null
+++ b/tests/datetime/test_timezone.py
@@ -0,0 +1,38 @@
+from __future__ import annotations
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_in_timezone():
+ d = pendulum.datetime(2015, 1, 15, 18, 15, 34)
+ now = pendulum.datetime(2015, 1, 15, 18, 15, 34)
+ assert d.timezone_name == "UTC"
+ assert_datetime(d, now.year, now.month, now.day, now.hour, now.minute)
+
+ d = d.in_timezone("Europe/Paris")
+ assert d.timezone_name == "Europe/Paris"
+ assert_datetime(d, now.year, now.month, now.day, now.hour + 1, now.minute)
+
+
+def test_in_tz():
+ d = pendulum.datetime(2015, 1, 15, 18, 15, 34)
+ now = pendulum.datetime(2015, 1, 15, 18, 15, 34)
+ assert d.timezone_name == "UTC"
+ assert_datetime(d, now.year, now.month, now.day, now.hour, now.minute)
+
+ d = d.in_tz("Europe/Paris")
+ assert d.timezone_name == "Europe/Paris"
+ assert_datetime(d, now.year, now.month, now.day, now.hour + 1, now.minute)
+
+
+def test_astimezone():
+ d = pendulum.datetime(2015, 1, 15, 18, 15, 34)
+ now = pendulum.datetime(2015, 1, 15, 18, 15, 34)
+ assert d.timezone_name == "UTC"
+ assert_datetime(d, now.year, now.month, now.day, now.hour, now.minute)
+
+ d = d.astimezone(pendulum.timezone("Europe/Paris"))
+ assert d.timezone_name == "Europe/Paris"
+ assert_datetime(d, now.year, now.month, now.day, now.hour + 1, now.minute)