summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2023-01-05 10:38:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2023-01-05 10:39:17 +0000
commitd6d80a17444c90259c5bfdacb84c61e6bfece655 (patch)
tree157bff98bd572acf0b64cd5d478b0bdac87a37ae /tests
parentReleasing debian version 2.1.2-4. (diff)
downloadpendulum-d6d80a17444c90259c5bfdacb84c61e6bfece655.tar.xz
pendulum-d6d80a17444c90259c5bfdacb84c61e6bfece655.zip
Merging upstream version 3.0.0~a1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/conftest.py101
-rw-r--r--tests/date/__init__.py0
-rw-r--r--tests/date/test_add.py88
-rw-r--r--tests/date/test_behavior.py73
-rw-r--r--tests/date/test_comparison.py247
-rw-r--r--tests/date/test_construct.py16
-rw-r--r--tests/date/test_day_of_week_modifiers.py298
-rw-r--r--tests/date/test_diff.py365
-rw-r--r--tests/date/test_fluent_setters.py29
-rw-r--r--tests/date/test_getters.py87
-rw-r--r--tests/date/test_start_end_of.py252
-rw-r--r--tests/date/test_strings.py48
-rw-r--r--tests/date/test_sub.py90
-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
-rw-r--r--tests/duration/__init__.py0
-rw-r--r--tests/duration/test_add_sub.py54
-rw-r--r--tests/duration/test_arithmetic.py85
-rw-r--r--tests/duration/test_behavior.py21
-rw-r--r--tests/duration/test_construct.py99
-rw-r--r--tests/duration/test_in_methods.py28
-rw-r--r--tests/duration/test_in_words.py77
-rw-r--r--tests/duration/test_total_methods.py28
-rw-r--r--tests/fixtures/__init__.py0
-rw-r--r--tests/fixtures/tz/Parisbin0 -> 2971 bytes
-rw-r--r--tests/fixtures/tz/clock/etc/sysconfig/clock1
l---------tests/fixtures/tz/symlink/etc/localtime1
-rw-r--r--tests/fixtures/tz/symlink/usr/share/zoneinfo/Europe/Parisbin0 -> 2971 bytes
l---------tests/fixtures/tz/timezone_dir/etc/localtime1
-rw-r--r--tests/fixtures/tz/timezone_dir/etc/timezone/blank.md5
-rw-r--r--tests/fixtures/tz/timezone_dir/usr/share/zoneinfo/Europe/Parisbin0 -> 2971 bytes
-rw-r--r--tests/formatting/__init__.py0
-rw-r--r--tests/formatting/test_formatter.py263
-rw-r--r--tests/helpers/__init__.py0
-rw-r--r--tests/helpers/test_local_time.py31
-rw-r--r--tests/interval/__init__.py0
-rw-r--r--tests/interval/test_add_subtract.py49
-rw-r--r--tests/interval/test_arithmetic.py53
-rw-r--r--tests/interval/test_behavior.py54
-rw-r--r--tests/interval/test_construct.py121
-rw-r--r--tests/interval/test_hashing.py23
-rw-r--r--tests/interval/test_in_words.py70
-rw-r--r--tests/interval/test_range.py119
-rw-r--r--tests/localization/__init__.py0
-rw-r--r--tests/localization/test_cs.py109
-rw-r--r--tests/localization/test_da.py65
-rw-r--r--tests/localization/test_de.py65
-rw-r--r--tests/localization/test_es.py65
-rw-r--r--tests/localization/test_fa.py65
-rw-r--r--tests/localization/test_fo.py65
-rw-r--r--tests/localization/test_fr.py84
-rw-r--r--tests/localization/test_he.py65
-rw-r--r--tests/localization/test_id.py68
-rw-r--r--tests/localization/test_it.py85
-rw-r--r--tests/localization/test_ja.py68
-rw-r--r--tests/localization/test_ko.py65
-rw-r--r--tests/localization/test_lt.py68
-rw-r--r--tests/localization/test_nb.py84
-rw-r--r--tests/localization/test_nl.py83
-rw-r--r--tests/localization/test_nn.py84
-rw-r--r--tests/localization/test_pl.py109
-rw-r--r--tests/localization/test_ru.py86
-rw-r--r--tests/localization/test_sk.py112
-rw-r--r--tests/localization/test_sv.py86
-rw-r--r--tests/parsing/__init__.py0
-rw-r--r--tests/parsing/test_parse_iso8601.py465
-rw-r--r--tests/parsing/test_parsing.py687
-rw-r--r--tests/parsing/test_parsing_duration.py298
-rw-r--r--tests/test_helpers.py179
-rw-r--r--tests/test_main.py13
-rw-r--r--tests/test_parsing.py141
-rw-r--r--tests/testing/__init__.py0
-rw-r--r--tests/testing/test_time_travel.py85
-rw-r--r--tests/time/__init__.py0
-rw-r--r--tests/time/test_add.py78
-rw-r--r--tests/time/test_behavior.py49
-rw-r--r--tests/time/test_comparison.py185
-rw-r--r--tests/time/test_construct.py22
-rw-r--r--tests/time/test_diff.py350
-rw-r--r--tests/time/test_fluent_setters.py12
-rw-r--r--tests/time/test_strings.py39
-rw-r--r--tests/time/test_sub.py112
-rw-r--r--tests/tz/__init__.py0
-rw-r--r--tests/tz/test_helpers.py27
-rw-r--r--tests/tz/test_local_timezone.py52
-rw-r--r--tests/tz/test_timezone.py447
-rw-r--r--tests/tz/test_timezones.py16
103 files changed, 11435 insertions, 0 deletions
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..060e951
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,101 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+
+@pytest.fixture(autouse=True)
+def setup():
+ pendulum.set_local_timezone(pendulum.timezone("America/Toronto"))
+
+ yield
+
+ pendulum.set_locale("en")
+ pendulum.set_local_timezone()
+ pendulum.week_starts_at(pendulum.MONDAY)
+ pendulum.week_ends_at(pendulum.SUNDAY)
+
+
+def assert_datetime(
+ d, year, month, day, hour=None, minute=None, second=None, microsecond=None
+):
+ assert year == d.year
+ assert month == d.month
+ assert day == d.day
+
+ if hour is not None:
+ assert hour == d.hour
+
+ if minute is not None:
+ assert minute == d.minute
+
+ if second is not None:
+ assert second == d.second
+
+ if microsecond is not None:
+ assert microsecond == d.microsecond
+
+
+def assert_date(d, year, month, day):
+ assert year == d.year
+ assert month == d.month
+ assert day == d.day
+
+
+def assert_time(t, hour, minute, second, microsecond=None):
+ assert hour == t.hour
+ assert minute == t.minute
+ assert second == t.second
+
+ if microsecond is not None:
+ assert microsecond == t.microsecond
+
+
+def assert_duration(
+ dur,
+ years=None,
+ months=None,
+ weeks=None,
+ days=None,
+ hours=None,
+ minutes=None,
+ seconds=None,
+ microseconds=None,
+) -> None:
+ expected = {}
+ actual = {}
+
+ if years is not None:
+ expected["years"] = dur.years
+ actual["years"] = years
+
+ if months is not None:
+ expected["months"] = dur.months
+ actual["months"] = months
+
+ if weeks is not None:
+ expected["weeks"] = dur.weeks
+ actual["weeks"] = weeks
+
+ if days is not None:
+ expected["days"] = dur.remaining_days
+ actual["days"] = days
+
+ if hours is not None:
+ expected["hours"] = dur.hours
+ actual["hours"] = hours
+
+ if minutes is not None:
+ expected["minutes"] = dur.minutes
+ actual["minutes"] = minutes
+
+ if seconds is not None:
+ expected["seconds"] = dur.remaining_seconds
+ actual["seconds"] = seconds
+
+ if microseconds is not None:
+ expected["microseconds"] = dur.microseconds
+ actual["microseconds"] = microseconds
+
+ assert expected == actual
diff --git a/tests/date/__init__.py b/tests/date/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/date/__init__.py
diff --git a/tests/date/test_add.py b/tests/date/test_add.py
new file mode 100644
index 0000000..a435f46
--- /dev/null
+++ b/tests/date/test_add.py
@@ -0,0 +1,88 @@
+from __future__ import annotations
+
+from datetime import timedelta
+
+import pytest
+
+import pendulum
+
+from tests.conftest import assert_date
+
+
+def test_add_years_positive():
+ assert pendulum.date(1975, 1, 1).add(years=1).year == 1976
+
+
+def test_add_years_zero():
+ assert pendulum.date(1975, 1, 1).add(years=0).year == 1975
+
+
+def test_add_years_negative():
+ assert pendulum.date(1975, 1, 1).add(years=-1).year == 1974
+
+
+def test_add_months_positive():
+ assert pendulum.date(1975, 12, 1).add(months=1).month == 1
+
+
+def test_add_months_zero():
+ assert pendulum.date(1975, 12, 1).add(months=0).month == 12
+
+
+def test_add_months_negative():
+ assert pendulum.date(1975, 12, 1).add(months=-1).month == 11
+
+
+def test_add_month_with_overflow():
+ assert pendulum.Date(2012, 1, 31).add(months=1).month == 2
+
+
+def test_add_days_positive():
+ assert pendulum.Date(1975, 5, 31).add(days=1).day == 1
+
+
+def test_add_days_zero():
+ assert pendulum.Date(1975, 5, 31).add(days=0).day == 31
+
+
+def test_add_days_negative():
+ assert pendulum.Date(1975, 5, 31).add(days=-1).day == 30
+
+
+def test_add_weeks_positive():
+ assert pendulum.Date(1975, 5, 21).add(weeks=1).day == 28
+
+
+def test_add_weeks_zero():
+ assert pendulum.Date(1975, 5, 21).add(weeks=0).day == 21
+
+
+def test_add_weeks_negative():
+ assert pendulum.Date(1975, 5, 21).add(weeks=-1).day == 14
+
+
+def test_add_timedelta():
+ delta = timedelta(days=18)
+ d = pendulum.date(2015, 3, 14)
+
+ new = d + delta
+ assert isinstance(new, pendulum.Date)
+ assert_date(new, 2015, 4, 1)
+
+
+def test_add_duration():
+ duration = pendulum.duration(years=2, months=3, days=18)
+ d = pendulum.Date(2015, 3, 14)
+
+ new = d + duration
+ assert_date(new, 2017, 7, 2)
+
+
+def test_addition_invalid_type():
+ d = pendulum.date(2015, 3, 14)
+
+ with pytest.raises(TypeError):
+ d + 3
+
+ with pytest.raises(TypeError):
+ 3 + d
diff --git a/tests/date/test_behavior.py b/tests/date/test_behavior.py
new file mode 100644
index 0000000..98dd175
--- /dev/null
+++ b/tests/date/test_behavior.py
@@ -0,0 +1,73 @@
+from __future__ import annotations
+
+import pickle
+
+from datetime import date
+
+import pytest
+
+import pendulum
+
+
+@pytest.fixture()
+def p():
+ return pendulum.Date(2016, 8, 27)
+
+
+@pytest.fixture()
+def d():
+ return date(2016, 8, 27)
+
+
+def test_timetuple(p, d):
+ assert p.timetuple() == d.timetuple()
+
+
+def test_ctime(p, d):
+ assert p.ctime() == d.ctime()
+
+
+def test_isoformat(p, d):
+ assert p.isoformat() == d.isoformat()
+
+
+def test_toordinal(p, d):
+ assert p.toordinal() == d.toordinal()
+
+
+def test_weekday(p, d):
+ assert p.weekday() == d.weekday()
+
+
+def test_isoweekday(p, d):
+ assert p.isoweekday() == d.isoweekday()
+
+
+def test_isocalendar(p, d):
+ assert p.isocalendar() == d.isocalendar()
+
+
+def test_fromtimestamp():
+ assert pendulum.Date.fromtimestamp(0) == date.fromtimestamp(0)
+
+
+def test_fromordinal():
+ assert pendulum.Date.fromordinal(730120) == date.fromordinal(730120)
+
+
+def test_hash():
+ d1 = pendulum.Date(2016, 8, 27)
+ d2 = pendulum.Date(2016, 8, 27)
+ d3 = pendulum.Date(2016, 8, 28)
+
+ assert hash(d2) == hash(d1)
+ assert hash(d1) != hash(d3)
+
+
+def test_pickle():
+ d1 = pendulum.Date(2016, 8, 27)
+ s = pickle.dumps(d1)
+ d2 = pickle.loads(s)
+
+ assert isinstance(d2, pendulum.Date)
+ assert d2 == d1
diff --git a/tests/date/test_comparison.py b/tests/date/test_comparison.py
new file mode 100644
index 0000000..94bf224
--- /dev/null
+++ b/tests/date/test_comparison.py
@@ -0,0 +1,247 @@
+from __future__ import annotations
+
+from datetime import date
+
+import pendulum
+
+from tests.conftest import assert_date
+
+
+def test_equal_to_true():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 1)
+ d3 = date(2000, 1, 1)
+
+ assert d2 == d1
+ assert d3 == d1
+
+
+def test_equal_to_false():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 2)
+ d3 = date(2000, 1, 2)
+
+ assert d1 != d2
+ assert d1 != d3
+
+
+def test_not_equal_to_true():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 2)
+ d3 = date(2000, 1, 2)
+
+ assert d1 != d2
+ assert d1 != d3
+
+
+def test_not_equal_to_false():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 1)
+ d3 = date(2000, 1, 1)
+
+ assert d2 == d1
+ assert d3 == d1
+
+
+def test_not_equal_to_none():
+ d1 = pendulum.Date(2000, 1, 1)
+
+ assert d1 != None # noqa
+
+
+def test_greater_than_true():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(1999, 12, 31)
+ d3 = date(1999, 12, 31)
+
+ assert d1 > d2
+ assert d1 > d3
+
+
+def test_greater_than_false():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 2)
+ d3 = date(2000, 1, 2)
+
+ assert not d1 > d2
+ assert not d1 > d3
+
+
+def test_greater_than_or_equal_true():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(1999, 12, 31)
+ d3 = date(1999, 12, 31)
+
+ assert d1 >= d2
+ assert d1 >= d3
+
+
+def test_greater_than_or_equal_true_equal():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 1)
+ d3 = date(2000, 1, 1)
+
+ assert d1 >= d2
+ assert d1 >= d3
+
+
+def test_greater_than_or_equal_false():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 2)
+ d3 = date(2000, 1, 2)
+
+ assert not d1 >= d2
+ assert not d1 >= d3
+
+
+def test_less_than_true():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 2)
+ d3 = date(2000, 1, 2)
+
+ assert d1 < d2
+ assert d1 < d3
+
+
+def test_less_than_false():
+ d1 = pendulum.Date(2000, 1, 2)
+ d2 = pendulum.Date(2000, 1, 1)
+ d3 = date(2000, 1, 1)
+
+ assert not d1 < d2
+ assert not d1 < d3
+
+
+def test_less_than_or_equal_true():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 2)
+ d3 = date(2000, 1, 2)
+
+ assert d1 <= d2
+ assert d1 <= d3
+
+
+def test_less_than_or_equal_true_equal():
+ d1 = pendulum.Date(2000, 1, 1)
+ d2 = pendulum.Date(2000, 1, 1)
+ d3 = date(2000, 1, 1)
+
+ assert d1 <= d2
+ assert d1 <= d3
+
+
+def test_less_than_or_equal_false():
+ d1 = pendulum.Date(2000, 1, 2)
+ d2 = pendulum.Date(2000, 1, 1)
+ d3 = date(2000, 1, 1)
+
+ assert not d1 <= d2
+ assert not d1 <= d3
+
+
+def test_is_anniversary():
+ d = pendulum.Date.today()
+ 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.Date(1987, 4, 23)
+ d2 = pendulum.Date(2014, 9, 26)
+ d3 = pendulum.Date(2014, 4, 23)
+ assert not d2.is_anniversary(d1)
+ assert d3.is_anniversary(d1)
+
+
+def test_is_birthday(): # backward compatibility
+ d = pendulum.Date.today()
+ 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.Date(1987, 4, 23)
+ d2 = pendulum.Date(2014, 9, 26)
+ d3 = pendulum.Date(2014, 4, 23)
+ assert not d2.is_birthday(d1)
+ assert d3.is_birthday(d1)
+
+
+def test_closest():
+ instance = pendulum.Date(2015, 5, 28)
+ dt1 = pendulum.Date(2015, 5, 27)
+ dt2 = pendulum.Date(2015, 5, 30)
+ closest = instance.closest(dt1, dt2)
+ assert closest == dt1
+
+ closest = instance.closest(dt2, dt1)
+ assert closest == dt1
+
+
+def test_closest_with_date():
+ instance = pendulum.Date(2015, 5, 28)
+ dt1 = date(2015, 5, 27)
+ dt2 = date(2015, 5, 30)
+ closest = instance.closest(dt1, dt2)
+ assert isinstance(closest, pendulum.Date)
+ assert_date(closest, 2015, 5, 27)
+
+
+def test_closest_with_equals():
+ instance = pendulum.Date(2015, 5, 28)
+ dt1 = pendulum.Date(2015, 5, 28)
+ dt2 = pendulum.Date(2015, 5, 30)
+ closest = instance.closest(dt1, dt2)
+ assert closest == dt1
+
+
+def test_farthest():
+ instance = pendulum.Date(2015, 5, 28)
+ dt1 = pendulum.Date(2015, 5, 27)
+ dt2 = pendulum.Date(2015, 5, 30)
+ closest = instance.farthest(dt1, dt2)
+ assert closest == dt2
+
+ closest = instance.farthest(dt2, dt1)
+ assert closest == dt2
+
+
+def test_farthest_with_date():
+ instance = pendulum.Date(2015, 5, 28)
+ dt1 = date(2015, 5, 27)
+ dt2 = date(2015, 5, 30)
+ closest = instance.farthest(dt1, dt2)
+ assert isinstance(closest, pendulum.Date)
+ assert_date(closest, 2015, 5, 30)
+
+
+def test_farthest_with_equals():
+ instance = pendulum.Date(2015, 5, 28)
+ dt1 = pendulum.Date(2015, 5, 28)
+ dt2 = pendulum.Date(2015, 5, 30)
+ closest = instance.farthest(dt1, dt2)
+ assert closest == dt2
+
+
+def test_is_same_day():
+ dt1 = pendulum.Date(2015, 5, 28)
+ dt2 = pendulum.Date(2015, 5, 29)
+ dt3 = pendulum.Date(2015, 5, 28)
+ dt4 = date(2015, 5, 28)
+ dt5 = date(2015, 5, 29)
+
+ 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.Date.today()
+
+ assert dt1 != "test"
+ assert dt1 not in ["test"]
diff --git a/tests/date/test_construct.py b/tests/date/test_construct.py
new file mode 100644
index 0000000..5b6eb78
--- /dev/null
+++ b/tests/date/test_construct.py
@@ -0,0 +1,16 @@
+from __future__ import annotations
+
+from pendulum import Date
+from tests.conftest import assert_date
+
+
+def test_construct():
+ d = Date(2016, 10, 20)
+
+ assert_date(d, 2016, 10, 20)
+
+
+def test_today():
+ d = Date.today()
+
+ assert isinstance(d, Date)
diff --git a/tests/date/test_day_of_week_modifiers.py b/tests/date/test_day_of_week_modifiers.py
new file mode 100644
index 0000000..55aba55
--- /dev/null
+++ b/tests/date/test_day_of_week_modifiers.py
@@ -0,0 +1,298 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+from pendulum.exceptions import PendulumException
+from tests.conftest import assert_date
+
+
+def test_start_of_week():
+ d = pendulum.date(1980, 8, 7).start_of("week")
+ assert_date(d, 1980, 8, 4)
+
+
+def test_start_of_week_from_week_start():
+ d = pendulum.date(1980, 8, 4).start_of("week")
+ assert_date(d, 1980, 8, 4)
+
+
+def test_start_of_week_crossing_year_boundary():
+ d = pendulum.date(2014, 1, 1).start_of("week")
+ assert_date(d, 2013, 12, 30)
+
+
+def test_end_of_week():
+ d = pendulum.date(1980, 8, 7).end_of("week")
+ assert_date(d, 1980, 8, 10)
+
+
+def test_end_of_week_from_week_end():
+ d = pendulum.date(1980, 8, 10).end_of("week")
+ assert_date(d, 1980, 8, 10)
+
+
+def test_end_of_week_crossing_year_boundary():
+ d = pendulum.date(2013, 12, 31).end_of("week")
+ assert_date(d, 2014, 1, 5)
+
+
+def test_next():
+ d = pendulum.date(1975, 5, 21).next()
+ assert_date(d, 1975, 5, 28)
+
+
+def test_next_monday():
+ d = pendulum.date(1975, 5, 21).next(pendulum.MONDAY)
+ assert_date(d, 1975, 5, 26)
+
+
+def test_next_saturday():
+ d = pendulum.date(1975, 5, 21).next(6)
+ assert_date(d, 1975, 5, 24)
+
+
+def test_next_invalid():
+ dt = pendulum.date(1975, 5, 21)
+
+ with pytest.raises(ValueError):
+ dt.next(7)
+
+
+def test_previous():
+ d = pendulum.date(1975, 5, 21).previous()
+ assert_date(d, 1975, 5, 14)
+
+
+def test_previous_monday():
+ d = pendulum.date(1975, 5, 21).previous(pendulum.MONDAY)
+ assert_date(d, 1975, 5, 19)
+
+
+def test_previous_saturday():
+ d = pendulum.date(1975, 5, 21).previous(6)
+ assert_date(d, 1975, 5, 17)
+
+
+def test_previous_invalid():
+ dt = pendulum.date(1975, 5, 21)
+
+ with pytest.raises(ValueError):
+ dt.previous(7)
+
+
+def test_first_day_of_month():
+ d = pendulum.date(1975, 11, 21).first_of("month")
+ assert_date(d, 1975, 11, 1)
+
+
+def test_first_wednesday_of_month():
+ d = pendulum.date(1975, 11, 21).first_of("month", pendulum.WEDNESDAY)
+ assert_date(d, 1975, 11, 5)
+
+
+def test_first_friday_of_month():
+ d = pendulum.date(1975, 11, 21).first_of("month", 5)
+ assert_date(d, 1975, 11, 7)
+
+
+def test_last_day_of_month():
+ d = pendulum.date(1975, 12, 5).last_of("month")
+ assert_date(d, 1975, 12, 31)
+
+
+def test_last_tuesday_of_month():
+ d = pendulum.date(1975, 12, 1).last_of("month", pendulum.TUESDAY)
+ assert_date(d, 1975, 12, 30)
+
+
+def test_last_friday_of_month():
+ d = pendulum.date(1975, 12, 5).last_of("month", 5)
+ assert_date(d, 1975, 12, 26)
+
+
+def test_nth_of_month_outside_scope():
+ d = pendulum.date(1975, 6, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("month", 6, pendulum.MONDAY)
+
+
+def test_nth_of_month_outside_year():
+ d = pendulum.date(1975, 12, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("month", 55, pendulum.MONDAY)
+
+
+def test_nth_of_month_first():
+ d = pendulum.date(1975, 12, 5).nth_of("month", 1, pendulum.MONDAY)
+
+ assert_date(d, 1975, 12, 1)
+
+
+def test_2nd_monday_of_month():
+ d = pendulum.date(1975, 12, 5).nth_of("month", 2, pendulum.MONDAY)
+
+ assert_date(d, 1975, 12, 8)
+
+
+def test_3rd_wednesday_of_month():
+ d = pendulum.date(1975, 12, 5).nth_of("month", 3, 3)
+
+ assert_date(d, 1975, 12, 17)
+
+
+def test_first_day_of_quarter():
+ d = pendulum.date(1975, 11, 21).first_of("quarter")
+ assert_date(d, 1975, 10, 1)
+
+
+def test_first_wednesday_of_quarter():
+ d = pendulum.date(1975, 11, 21).first_of("quarter", pendulum.WEDNESDAY)
+ assert_date(d, 1975, 10, 1)
+
+
+def test_first_friday_of_quarter():
+ d = pendulum.date(1975, 11, 21).first_of("quarter", 5)
+ assert_date(d, 1975, 10, 3)
+
+
+def test_first_of_quarter_from_a_day_that_will_not_exist_in_the_first_month():
+ d = pendulum.date(2014, 5, 31).first_of("quarter")
+ assert_date(d, 2014, 4, 1)
+
+
+def test_last_day_of_quarter():
+ d = pendulum.date(1975, 8, 5).last_of("quarter")
+ assert_date(d, 1975, 9, 30)
+
+
+def test_last_tuesday_of_quarter():
+ d = pendulum.date(1975, 8, 5).last_of("quarter", pendulum.TUESDAY)
+ assert_date(d, 1975, 9, 30)
+
+
+def test_last_friday_of_quarter():
+ d = pendulum.date(1975, 8, 5).last_of("quarter", pendulum.FRIDAY)
+ assert_date(d, 1975, 9, 26)
+
+
+def test_last_day_of_quarter_that_will_not_exist_in_the_last_month():
+ d = pendulum.date(2014, 5, 31).last_of("quarter")
+ assert_date(d, 2014, 6, 30)
+
+
+def test_nth_of_quarter_outside_scope():
+ d = pendulum.date(1975, 1, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("quarter", 20, pendulum.MONDAY)
+
+
+def test_nth_of_quarter_outside_year():
+ d = pendulum.date(1975, 1, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("quarter", 55, pendulum.MONDAY)
+
+
+def test_nth_of_quarter_first():
+ d = pendulum.date(1975, 12, 5).nth_of("quarter", 1, pendulum.MONDAY)
+
+ assert_date(d, 1975, 10, 6)
+
+
+def test_nth_of_quarter_from_a_day_that_will_not_exist_in_the_first_month():
+ d = pendulum.date(2014, 5, 31).nth_of("quarter", 2, pendulum.MONDAY)
+ assert_date(d, 2014, 4, 14)
+
+
+def test_2nd_monday_of_quarter():
+ d = pendulum.date(1975, 8, 5).nth_of("quarter", 2, pendulum.MONDAY)
+ assert_date(d, 1975, 7, 14)
+
+
+def test_3rd_wednesday_of_quarter():
+ d = pendulum.date(1975, 8, 5).nth_of("quarter", 3, 3)
+ assert_date(d, 1975, 7, 16)
+
+
+def test_first_day_of_year():
+ d = pendulum.date(1975, 11, 21).first_of("year")
+ assert_date(d, 1975, 1, 1)
+
+
+def test_first_wednesday_of_year():
+ d = pendulum.date(1975, 11, 21).first_of("year", pendulum.WEDNESDAY)
+ assert_date(d, 1975, 1, 1)
+
+
+def test_first_friday_of_year():
+ d = pendulum.date(1975, 11, 21).first_of("year", 5)
+ assert_date(d, 1975, 1, 3)
+
+
+def test_last_day_of_year():
+ d = pendulum.date(1975, 8, 5).last_of("year")
+ assert_date(d, 1975, 12, 31)
+
+
+def test_last_tuesday_of_year():
+ d = pendulum.date(1975, 8, 5).last_of("year", pendulum.TUESDAY)
+ assert_date(d, 1975, 12, 30)
+
+
+def test_last_friday_of_year():
+ d = pendulum.date(1975, 8, 5).last_of("year", 5)
+ assert_date(d, 1975, 12, 26)
+
+
+def test_nth_of_year_outside_scope():
+ d = pendulum.date(1975, 1, 5)
+
+ with pytest.raises(PendulumException):
+ d.nth_of("year", 55, pendulum.MONDAY)
+
+
+def test_nth_of_year_first():
+ d = pendulum.date(1975, 12, 5).nth_of("year", 1, pendulum.MONDAY)
+
+ assert_date(d, 1975, 1, 6)
+
+
+def test_2nd_monday_of_year():
+ d = pendulum.date(1975, 8, 5).nth_of("year", 2, pendulum.MONDAY)
+ assert_date(d, 1975, 1, 13)
+
+
+def test_2rd_wednesday_of_year():
+ d = pendulum.date(1975, 8, 5).nth_of("year", 3, pendulum.WEDNESDAY)
+ assert_date(d, 1975, 1, 15)
+
+
+def test_7th_thursday_of_year():
+ d = pendulum.date(1975, 8, 31).nth_of("year", 7, pendulum.THURSDAY)
+ assert_date(d, 1975, 2, 13)
+
+
+def test_first_of_invalid_unit():
+ d = pendulum.date(1975, 8, 5)
+
+ with pytest.raises(ValueError):
+ d.first_of("invalid", 3)
+
+
+def test_last_of_invalid_unit():
+ d = pendulum.date(1975, 8, 5)
+
+ with pytest.raises(ValueError):
+ d.last_of("invalid", 3)
+
+
+def test_nth_of_invalid_unit():
+ d = pendulum.date(1975, 8, 5)
+
+ with pytest.raises(ValueError):
+ d.nth_of("invalid", 3, pendulum.MONDAY)
diff --git a/tests/date/test_diff.py b/tests/date/test_diff.py
new file mode 100644
index 0000000..56358cc
--- /dev/null
+++ b/tests/date/test_diff.py
@@ -0,0 +1,365 @@
+from __future__ import annotations
+
+from datetime import date
+
+import pytest
+
+import pendulum
+
+
+@pytest.fixture
+def today():
+ return pendulum.today().date()
+
+
+def test_diff_in_years_positive():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.add(years=1)).in_years() == 1
+
+
+def test_diff_in_years_negative_with_sign():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1), False).in_years() == -1
+
+
+def test_diff_in_years_negative_no_sign():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1)).in_years() == 1
+
+
+def test_diff_in_years_vs_default_now(today):
+ assert today.subtract(years=1).diff().in_years() == 1
+
+
+def test_diff_in_years_ensure_is_truncated():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.add(years=1).add(months=7)).in_years() == 1
+
+
+def test_diff_in_months_positive():
+ dt = pendulum.date(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.date(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.date(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1).add(months=1)).in_months() == 11
+
+
+def test_diff_in_months_vs_default_now(today):
+ assert today.subtract(years=1).diff().in_months() == 12
+
+
+def test_diff_in_months_ensure_is_truncated():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.add(months=1).add(days=16)).in_months() == 1
+
+
+def test_diff_in_days_positive():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.add(years=1)).in_days() == 366
+
+
+def test_diff_in_days_negative_with_sign():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1), False).in_days() == -365
+
+
+def test_diff_in_days_negative_no_sign():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1)).in_days() == 365
+
+
+def test_diff_in_days_vs_default_now(today):
+ assert today.subtract(weeks=1).diff().in_days() == 7
+
+
+def test_diff_in_weeks_positive():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.add(years=1)).in_weeks() == 52
+
+
+def test_diff_in_weeks_negative_with_sign():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1), False).in_weeks() == -52
+
+
+def test_diff_in_weeks_negative_no_sign():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.subtract(years=1)).in_weeks() == 52
+
+
+def test_diff_in_weeks_vs_default_now(today):
+ assert today.subtract(weeks=1).diff().in_weeks() == 1
+
+
+def test_diff_in_weeks_ensure_is_truncated():
+ dt = pendulum.date(2000, 1, 1)
+ assert dt.diff(dt.add(weeks=1).subtract(days=1)).in_weeks() == 0
+
+
+def test_diff_for_humans_now_and_day(today):
+ assert today.subtract(days=1).diff_for_humans() == "1 day ago"
+
+
+def test_diff_for_humans_now_and_days(today):
+ assert today.subtract(days=2).diff_for_humans() == "2 days ago"
+
+
+def test_diff_for_humans_now_and_nearly_week(today):
+ assert today.subtract(days=6).diff_for_humans() == "6 days ago"
+
+
+def test_diff_for_humans_now_and_week(today):
+ assert today.subtract(weeks=1).diff_for_humans() == "1 week ago"
+
+
+def test_diff_for_humans_now_and_weeks(today):
+ assert today.subtract(weeks=2).diff_for_humans() == "2 weeks ago"
+
+
+def test_diff_for_humans_now_and_nearly_month(today):
+ assert today.subtract(weeks=3).diff_for_humans() == "3 weeks ago"
+
+
+def test_diff_for_humans_now_and_month():
+ with pendulum.travel_to(pendulum.datetime(2016, 4, 1)):
+ today = pendulum.today().date()
+
+ assert today.subtract(weeks=4).diff_for_humans() == "4 weeks ago"
+ assert today.subtract(months=1).diff_for_humans() == "1 month ago"
+
+ with pendulum.travel_to(pendulum.datetime(2017, 3, 1)):
+ today = pendulum.today().date()
+
+ assert today.subtract(weeks=4).diff_for_humans() == "1 month ago"
+
+
+def test_diff_for_humans_now_and_months(today):
+ assert today.subtract(months=2).diff_for_humans() == "2 months ago"
+
+
+def test_diff_for_humans_now_and_nearly_year(today):
+ assert today.subtract(months=11).diff_for_humans() == "11 months ago"
+
+
+def test_diff_for_humans_now_and_year(today):
+ assert today.subtract(years=1).diff_for_humans() == "1 year ago"
+
+
+def test_diff_for_humans_now_and_years(today):
+ assert today.subtract(years=2).diff_for_humans() == "2 years ago"
+
+
+def test_diff_for_humans_now_and_future_day(today):
+ assert today.add(days=1).diff_for_humans() == "in 1 day"
+
+
+def test_diff_for_humans_now_and_future_days(today):
+ assert today.add(days=2).diff_for_humans() == "in 2 days"
+
+
+def test_diff_for_humans_now_and_nearly_future_week(today):
+ assert today.add(days=6).diff_for_humans() == "in 6 days"
+
+
+def test_diff_for_humans_now_and_future_week(today):
+ assert today.add(weeks=1).diff_for_humans() == "in 1 week"
+
+
+def test_diff_for_humans_now_and_future_weeks(today):
+ assert today.add(weeks=2).diff_for_humans() == "in 2 weeks"
+
+
+def test_diff_for_humans_now_and_nearly_future_month(today):
+ assert today.add(weeks=3).diff_for_humans() == "in 3 weeks"
+
+
+def test_diff_for_humans_now_and_future_month():
+ with pendulum.travel_to(pendulum.datetime(2016, 3, 1)):
+ today = pendulum.today("UTC").date()
+
+ assert today.add(weeks=4).diff_for_humans() == "in 4 weeks"
+ assert today.add(months=1).diff_for_humans() == "in 1 month"
+
+ with pendulum.travel_to(pendulum.datetime(2017, 3, 31)):
+ today = pendulum.today("UTC").date()
+
+ assert today.add(months=1).diff_for_humans() == "in 1 month"
+
+ with pendulum.travel_to(pendulum.datetime(2017, 4, 30)):
+ today = pendulum.today("UTC").date()
+
+ assert today.add(months=1).diff_for_humans() == "in 1 month"
+
+ with pendulum.travel_to(pendulum.datetime(2017, 1, 31)):
+ today = pendulum.today("UTC").date()
+
+ assert today.add(weeks=4).diff_for_humans() == "in 1 month"
+
+
+def test_diff_for_humans_now_and_future_months(today):
+ assert today.add(months=2).diff_for_humans() == "in 2 months"
+
+
+def test_diff_for_humans_now_and_nearly_future_year(today):
+ assert today.add(months=11).diff_for_humans() == "in 11 months"
+
+
+def test_diff_for_humans_now_and_future_year(today):
+ assert today.add(years=1).diff_for_humans() == "in 1 year"
+
+
+def test_diff_for_humans_now_and_future_years(today):
+ assert today.add(years=2).diff_for_humans() == "in 2 years"
+
+
+def test_diff_for_humans_other_and_day(today):
+ assert today.diff_for_humans(today.add(days=1)) == "1 day before"
+
+
+def test_diff_for_humans_other_and_days(today):
+ assert today.diff_for_humans(today.add(days=2)) == "2 days before"
+
+
+def test_diff_for_humans_other_and_nearly_week(today):
+ assert today.diff_for_humans(today.add(days=6)) == "6 days before"
+
+
+def test_diff_for_humans_other_and_week(today):
+ assert today.diff_for_humans(today.add(weeks=1)) == "1 week before"
+
+
+def test_diff_for_humans_other_and_weeks(today):
+ assert today.diff_for_humans(today.add(weeks=2)) == "2 weeks before"
+
+
+def test_diff_for_humans_other_and_nearly_month(today):
+ assert today.diff_for_humans(today.add(weeks=3)) == "3 weeks before"
+
+
+def test_diff_for_humans_other_and_month():
+ with pendulum.travel_to(pendulum.datetime(2016, 3, 1)):
+ today = pendulum.today().date()
+
+ assert today.diff_for_humans(today.add(weeks=4)) == "4 weeks before"
+ assert today.diff_for_humans(today.add(months=1)) == "1 month before"
+
+ with pendulum.travel_to(pendulum.datetime(2017, 3, 31)):
+ today = pendulum.today().date()
+
+ assert today.diff_for_humans(today.add(months=1)) == "1 month before"
+
+ with pendulum.travel_to(pendulum.datetime(2017, 4, 30)):
+ today = pendulum.today().date()
+
+ assert today.diff_for_humans(today.add(months=1)) == "1 month before"
+
+ with pendulum.travel_to(pendulum.datetime(2017, 1, 31)):
+ today = pendulum.today().date()
+
+ assert today.diff_for_humans(today.add(weeks=4)) == "1 month before"
+
+
+def test_diff_for_humans_other_and_months(today):
+ assert today.diff_for_humans(today.add(months=2)) == "2 months before"
+
+
+def test_diff_for_humans_other_and_nearly_year(today):
+ assert today.diff_for_humans(today.add(months=11)) == "11 months before"
+
+
+def test_diff_for_humans_other_and_year(today):
+ assert today.diff_for_humans(today.add(years=1)) == "1 year before"
+
+
+def test_diff_for_humans_other_and_years(today):
+ assert today.diff_for_humans(today.add(years=2)) == "2 years before"
+
+
+def test_diff_for_humans_other_and_future_day(today):
+ assert today.diff_for_humans(today.subtract(days=1)) == "1 day after"
+
+
+def test_diff_for_humans_other_and_future_days(today):
+ assert today.diff_for_humans(today.subtract(days=2)) == "2 days after"
+
+
+def test_diff_for_humans_other_and_nearly_future_week(today):
+ assert today.diff_for_humans(today.subtract(days=6)) == "6 days after"
+
+
+def test_diff_for_humans_other_and_future_week(today):
+ assert today.diff_for_humans(today.subtract(weeks=1)) == "1 week after"
+
+
+def test_diff_for_humans_other_and_future_weeks(today):
+ assert today.diff_for_humans(today.subtract(weeks=2)) == "2 weeks after"
+
+
+def test_diff_for_humans_other_and_nearly_future_month(today):
+ assert today.diff_for_humans(today.subtract(weeks=3)) == "3 weeks after"
+
+
+def test_diff_for_humans_other_and_future_month():
+ with pendulum.travel_to(pendulum.datetime(2016, 3, 1)):
+ today = pendulum.today().date()
+
+ assert today.diff_for_humans(today.subtract(weeks=4)) == "4 weeks after"
+ assert today.diff_for_humans(today.subtract(months=1)) == "1 month after"
+
+ with pendulum.travel_to(pendulum.datetime(2017, 2, 28)):
+ today = pendulum.today().date()
+
+ assert today.diff_for_humans(today.subtract(weeks=4)) == "1 month after"
+
+
+def test_diff_for_humans_other_and_future_months(today):
+ assert today.diff_for_humans(today.subtract(months=2)) == "2 months after"
+
+
+def test_diff_for_humans_other_and_nearly_future_year(today):
+ assert today.diff_for_humans(today.subtract(months=11)) == "11 months after"
+
+
+def test_diff_for_humans_other_and_future_year(today):
+ assert today.diff_for_humans(today.subtract(years=1)) == "1 year after"
+
+
+def test_diff_for_humans_other_and_future_years(today):
+ assert today.diff_for_humans(today.subtract(years=2)) == "2 years after"
+
+
+def test_diff_for_humans_absolute_days(today):
+ assert today.diff_for_humans(today.subtract(days=2), True) == "2 days"
+ assert today.diff_for_humans(today.add(days=2), True) == "2 days"
+
+
+def test_diff_for_humans_absolute_weeks(today):
+ assert today.diff_for_humans(today.subtract(weeks=2), True) == "2 weeks"
+ assert today.diff_for_humans(today.add(weeks=2), True) == "2 weeks"
+
+
+def test_diff_for_humans_absolute_months(today):
+ assert today.diff_for_humans(today.subtract(months=2), True) == "2 months"
+ assert today.diff_for_humans(today.add(months=2), True) == "2 months"
+
+
+def test_diff_for_humans_absolute_years(today):
+ assert today.diff_for_humans(today.subtract(years=1), True) == "1 year"
+ assert today.diff_for_humans(today.add(years=1), True) == "1 year"
+
+
+def test_subtraction():
+ d = pendulum.date(2016, 7, 5)
+ future_dt = date(2016, 7, 6)
+ future = d.add(days=1)
+
+ assert (future - d).total_seconds() == 86400
+ assert (future_dt - d).total_seconds() == 86400
diff --git a/tests/date/test_fluent_setters.py b/tests/date/test_fluent_setters.py
new file mode 100644
index 0000000..c76cc2f
--- /dev/null
+++ b/tests/date/test_fluent_setters.py
@@ -0,0 +1,29 @@
+from __future__ import annotations
+
+import pendulum
+
+from tests.conftest import assert_date
+
+
+def test_fluid_year_setter():
+ d = pendulum.Date(2016, 10, 20)
+ new = d.set(year=1995)
+
+ assert_date(new, 1995, 10, 20)
+ assert new.year == 1995
+
+
+def test_fluid_month_setter():
+ d = pendulum.Date(2016, 7, 2)
+ new = d.set(month=11)
+
+ assert new.month == 11
+ assert d.month == 7
+
+
+def test_fluid_day_setter():
+ d = pendulum.Date(2016, 7, 2)
+ new = d.set(day=9)
+
+ assert new.day == 9
+ assert d.day == 2
diff --git a/tests/date/test_getters.py b/tests/date/test_getters.py
new file mode 100644
index 0000000..a492794
--- /dev/null
+++ b/tests/date/test_getters.py
@@ -0,0 +1,87 @@
+from __future__ import annotations
+
+import pendulum
+
+
+def test_year():
+ d = pendulum.Date(1234, 5, 6)
+ assert d.year == 1234
+
+
+def test_month():
+ d = pendulum.Date(1234, 5, 6)
+ assert d.month == 5
+
+
+def test_day():
+ d = pendulum.Date(1234, 5, 6)
+ assert d.day == 6
+
+
+def test_day_of_week():
+ d = pendulum.Date(2012, 5, 7)
+ assert d.day_of_week == pendulum.MONDAY
+
+
+def test_day_of_year():
+ d = pendulum.Date(2015, 12, 31)
+ assert d.day_of_year == 365
+ d = pendulum.Date(2016, 12, 31)
+ assert d.day_of_year == 366
+
+
+def test_days_in_month():
+ d = pendulum.Date(2012, 5, 7)
+ assert d.days_in_month == 31
+
+
+def test_age():
+ d = pendulum.Date.today()
+ assert d.age == 0
+ assert d.add(years=1).age == -1
+ assert d.subtract(years=1).age == 1
+
+
+def test_is_leap_year():
+ assert pendulum.Date(2012, 1, 1).is_leap_year()
+ assert not pendulum.Date(2011, 1, 1).is_leap_year()
+
+
+def test_is_long_year():
+ assert pendulum.Date(2015, 1, 1).is_long_year()
+ assert not pendulum.Date(2016, 1, 1).is_long_year()
+
+
+def test_week_of_month():
+ assert pendulum.Date(2012, 9, 30).week_of_month == 5
+ assert pendulum.Date(2012, 9, 28).week_of_month == 5
+ assert pendulum.Date(2012, 9, 20).week_of_month == 4
+ assert pendulum.Date(2012, 9, 8).week_of_month == 2
+ assert pendulum.Date(2012, 9, 1).week_of_month == 1
+ assert pendulum.date(2020, 1, 1).week_of_month == 1
+ assert pendulum.date(2020, 1, 7).week_of_month == 2
+ assert pendulum.date(2020, 1, 14).week_of_month == 3
+
+
+def test_week_of_year_first_week():
+ assert pendulum.Date(2012, 1, 1).week_of_year == 52
+ assert pendulum.Date(2012, 1, 2).week_of_year == 1
+
+
+def test_week_of_year_last_week():
+ assert pendulum.Date(2012, 12, 30).week_of_year == 52
+ assert pendulum.Date(2012, 12, 31).week_of_year == 1
+
+
+def test_is_future():
+ d = pendulum.Date.today()
+ assert not d.is_future()
+ d = d.add(days=1)
+ assert d.is_future()
+
+
+def test_is_past():
+ d = pendulum.Date.today()
+ assert not d.is_past()
+ d = d.subtract(days=1)
+ assert d.is_past()
diff --git a/tests/date/test_start_end_of.py b/tests/date/test_start_end_of.py
new file mode 100644
index 0000000..f1b4412
--- /dev/null
+++ b/tests/date/test_start_end_of.py
@@ -0,0 +1,252 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+from pendulum import Date
+from tests.conftest import assert_date
+
+
+def test_start_of_day():
+ d = Date.today()
+ new = d.start_of("day")
+ assert isinstance(new, Date)
+ assert_date(new, d.year, d.month, d.day)
+
+
+def test_end_of_day():
+ d = Date.today()
+ new = d.end_of("day")
+ assert isinstance(new, Date)
+ assert_date(new, d.year, d.month, d.day)
+
+
+def test_start_of_week():
+ d = Date(2016, 10, 20)
+ new = d.start_of("week")
+ assert isinstance(new, Date)
+ assert_date(new, d.year, d.month, 17)
+
+
+def test_end_of_week():
+ d = Date(2016, 10, 20)
+ new = d.end_of("week")
+ assert isinstance(new, Date)
+ assert_date(new, d.year, d.month, 23)
+
+
+def test_start_of_month_is_fluid():
+ d = Date.today()
+ assert isinstance(d.start_of("month"), Date)
+
+
+def test_start_of_month_from_now():
+ d = Date.today()
+ new = d.start_of("month")
+ assert_date(new, d.year, d.month, 1)
+
+
+def test_start_of_month_from_last_day():
+ d = Date(2000, 1, 31)
+ new = d.start_of("month")
+ assert_date(new, 2000, 1, 1)
+
+
+def test_start_of_year_is_fluid():
+ d = Date.today()
+ new = d.start_of("year")
+ assert isinstance(new, Date)
+
+
+def test_start_of_year_from_now():
+ d = Date.today()
+ new = d.start_of("year")
+ assert_date(new, d.year, 1, 1)
+
+
+def test_start_of_year_from_first_day():
+ d = Date(2000, 1, 1)
+ new = d.start_of("year")
+ assert_date(new, 2000, 1, 1)
+
+
+def test_start_of_year_from_last_day():
+ d = Date(2000, 12, 31)
+ new = d.start_of("year")
+ assert_date(new, 2000, 1, 1)
+
+
+def test_end_of_month_is_fluid():
+ d = Date.today()
+ assert isinstance(d.end_of("month"), Date)
+
+
+def test_end_of_month_from_now():
+ d = Date.today().start_of("month")
+ new = d.start_of("month")
+ assert_date(new, d.year, d.month, 1)
+
+
+def test_end_of_month():
+ d = Date(2000, 1, 1).end_of("month")
+ new = d.end_of("month")
+ assert_date(new, 2000, 1, 31)
+
+
+def test_end_of_month_from_last_day():
+ d = Date(2000, 1, 31)
+ new = d.end_of("month")
+ assert_date(new, 2000, 1, 31)
+
+
+def test_end_of_year_is_fluid():
+ d = Date.today()
+ assert isinstance(d.end_of("year"), Date)
+
+
+def test_end_of_year_from_now():
+ d = Date.today().end_of("year")
+ new = d.end_of("year")
+ assert_date(new, d.year, 12, 31)
+
+
+def test_end_of_year_from_first_day():
+ d = Date(2000, 1, 1)
+ new = d.end_of("year")
+ assert_date(new, 2000, 12, 31)
+
+
+def test_end_of_year_from_last_day():
+ d = Date(2000, 12, 31)
+ new = d.end_of("year")
+ assert_date(new, 2000, 12, 31)
+
+
+def test_start_of_decade_is_fluid():
+ d = Date.today()
+ assert isinstance(d.start_of("decade"), Date)
+
+
+def test_start_of_decade_from_now():
+ d = Date.today()
+ new = d.start_of("decade")
+ assert_date(new, d.year - d.year % 10, 1, 1)
+
+
+def test_start_of_decade_from_first_day():
+ d = Date(2000, 1, 1)
+ new = d.start_of("decade")
+ assert_date(new, 2000, 1, 1)
+
+
+def test_start_of_decade_from_last_day():
+ d = Date(2009, 12, 31)
+ new = d.start_of("decade")
+ assert_date(new, 2000, 1, 1)
+
+
+def test_end_of_decade_is_fluid():
+ d = Date.today()
+ assert isinstance(d.end_of("decade"), Date)
+
+
+def test_end_of_decade_from_now():
+ d = Date.today()
+ new = d.end_of("decade")
+ assert_date(new, d.year - d.year % 10 + 9, 12, 31)
+
+
+def test_end_of_decade_from_first_day():
+ d = Date(2000, 1, 1)
+ new = d.end_of("decade")
+ assert_date(new, 2009, 12, 31)
+
+
+def test_end_of_decade_from_last_day():
+ d = Date(2009, 12, 31)
+ new = d.end_of("decade")
+ assert_date(new, 2009, 12, 31)
+
+
+def test_start_of_century_is_fluid():
+ d = Date.today()
+ assert isinstance(d.start_of("century"), Date)
+
+
+def test_start_of_century_from_now():
+ d = Date.today()
+ new = d.start_of("century")
+ assert_date(new, d.year - d.year % 100 + 1, 1, 1)
+
+
+def test_start_of_century_from_first_day():
+ d = Date(2001, 1, 1)
+ new = d.start_of("century")
+ assert_date(new, 2001, 1, 1)
+
+
+def test_start_of_century_from_last_day():
+ d = Date(2100, 12, 31)
+ new = d.start_of("century")
+ assert_date(new, 2001, 1, 1)
+
+
+def test_end_of_century_is_fluid():
+ d = Date.today()
+ assert isinstance(d.end_of("century"), Date)
+
+
+def test_end_of_century_from_now():
+ now = Date.today()
+ d = now.end_of("century")
+ assert_date(d, now.year - now.year % 100 + 100, 12, 31)
+
+
+def test_end_of_century_from_first_day():
+ d = Date(2001, 1, 1)
+ new = d.end_of("century")
+ assert_date(new, 2100, 12, 31)
+
+
+def test_end_of_century_from_last_day():
+ d = Date(2100, 12, 31)
+ new = d.end_of("century")
+ assert_date(new, 2100, 12, 31)
+
+
+def test_average_is_fluid():
+ d = Date.today().average()
+ assert isinstance(d, Date)
+
+
+def test_average_from_same():
+ d1 = pendulum.date(2000, 1, 31)
+ d2 = pendulum.date(2000, 1, 31).average(d1)
+ assert_date(d2, 2000, 1, 31)
+
+
+def test_average_from_greater():
+ d1 = pendulum.date(2000, 1, 1)
+ d2 = pendulum.date(2009, 12, 31).average(d1)
+ assert_date(d2, 2004, 12, 31)
+
+
+def test_average_from_lower():
+ d1 = pendulum.date(2009, 12, 31)
+ d2 = pendulum.date(2000, 1, 1).average(d1)
+ assert_date(d2, 2004, 12, 31)
+
+
+def test_start_of():
+ d = pendulum.date(2013, 3, 31)
+
+ with pytest.raises(ValueError):
+ d.start_of("invalid")
+
+
+def test_end_of_invalid_unit():
+ d = pendulum.date(2013, 3, 31)
+
+ with pytest.raises(ValueError):
+ d.end_of("invalid")
diff --git a/tests/date/test_strings.py b/tests/date/test_strings.py
new file mode 100644
index 0000000..f373581
--- /dev/null
+++ b/tests/date/test_strings.py
@@ -0,0 +1,48 @@
+from __future__ import annotations
+
+import pendulum
+
+
+def test_to_string():
+ d = pendulum.Date(2016, 10, 16)
+ assert str(d) == "2016-10-16"
+
+
+def test_to_date_string():
+ d = pendulum.Date(1975, 12, 25)
+ assert d.to_date_string() == "1975-12-25"
+
+
+def test_to_formatted_date_string():
+ d = pendulum.Date(1975, 12, 25)
+ assert d.to_formatted_date_string() == "Dec 25, 1975"
+
+
+def test_repr():
+ d = pendulum.Date(1975, 12, 25)
+
+ assert repr(d) == "Date(1975, 12, 25)"
+ assert d.__repr__() == "Date(1975, 12, 25)"
+
+
+def test_format_with_locale():
+ d = pendulum.Date(1975, 12, 25)
+ expected = "jeudi 25e jour de décembre 1975"
+ assert d.format("dddd Do [jour de] MMMM YYYY", locale="fr") == expected
+
+
+def test_strftime():
+ d = pendulum.Date(1975, 12, 25)
+ assert d.strftime("%d") == "25"
+
+
+def test_for_json():
+ d = pendulum.Date(1975, 12, 25)
+ assert d.for_json() == "1975-12-25"
+
+
+def test_format():
+ d = pendulum.Date(1975, 12, 25)
+ assert f"{d}" == "1975-12-25"
+ assert f"{d:YYYY}" == "1975"
+ assert f"{d:%Y}" == "1975"
diff --git a/tests/date/test_sub.py b/tests/date/test_sub.py
new file mode 100644
index 0000000..33855a4
--- /dev/null
+++ b/tests/date/test_sub.py
@@ -0,0 +1,90 @@
+from __future__ import annotations
+
+from datetime import datetime
+from datetime import timedelta
+
+import pytest
+
+import pendulum
+
+from tests.conftest import assert_date
+
+
+def test_subtract_years_positive():
+ assert pendulum.date(1975, 1, 1).subtract(years=1).year == 1974
+
+
+def test_subtract_years_zero():
+ assert pendulum.date(1975, 1, 1).subtract(years=0).year == 1975
+
+
+def test_subtract_years_negative():
+ assert pendulum.date(1975, 1, 1).subtract(years=-1).year == 1976
+
+
+def test_subtract_months_positive():
+ assert pendulum.date(1975, 1, 1).subtract(months=1).month == 12
+
+
+def test_subtract_months_zero():
+ assert pendulum.date(1975, 12, 1).subtract(months=0).month == 12
+
+
+def test_subtract_months_negative():
+ assert pendulum.date(1975, 11, 1).subtract(months=-1).month == 12
+
+
+def test_subtract_days_positive():
+ assert pendulum.Date(1975, 6, 1).subtract(days=1).day == 31
+
+
+def test_subtract_days_zero():
+ assert pendulum.Date(1975, 5, 31).subtract(days=0).day == 31
+
+
+def test_subtract_days_negative():
+ assert pendulum.Date(1975, 5, 30).subtract(days=-1).day == 31
+
+
+def test_subtract_days_max():
+ delta = pendulum.now() - pendulum.instance(datetime.min)
+ assert pendulum.now().subtract(days=delta.days - 1).year == 1
+
+
+def test_subtract_weeks_positive():
+ assert pendulum.Date(1975, 5, 28).subtract(weeks=1).day == 21
+
+
+def test_subtract_weeks_zero():
+ assert pendulum.Date(1975, 5, 21).subtract(weeks=0).day == 21
+
+
+def test_subtract_weeks_negative():
+ assert pendulum.Date(1975, 5, 14).subtract(weeks=-1).day == 21
+
+
+def test_subtract_timedelta():
+ delta = timedelta(days=18)
+ d = pendulum.date(2015, 3, 14)
+
+ new = d - delta
+ assert isinstance(new, pendulum.Date)
+ assert_date(new, 2015, 2, 24)
+
+
+def test_subtract_duration():
+ delta = pendulum.duration(years=2, months=3, days=18)
+ d = pendulum.date(2015, 3, 14)
+
+ new = d - delta
+ assert_date(new, 2012, 11, 26)
+
+
+def test_addition_invalid_type():
+ d = pendulum.date(2015, 3, 14)
+
+ with pytest.raises(TypeError):
+ d - "ab"
+
+ with pytest.raises(TypeError):
+ "ab" - d
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)
diff --git a/tests/duration/__init__.py b/tests/duration/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/duration/__init__.py
diff --git a/tests/duration/test_add_sub.py b/tests/duration/test_add_sub.py
new file mode 100644
index 0000000..193a493
--- /dev/null
+++ b/tests/duration/test_add_sub.py
@@ -0,0 +1,54 @@
+from __future__ import annotations
+
+from datetime import timedelta
+
+import pendulum
+
+from tests.conftest import assert_duration
+
+
+def test_add_interval():
+ p1 = pendulum.duration(days=23, seconds=32)
+ p2 = pendulum.duration(days=12, seconds=30)
+
+ p = p1 + p2
+ assert_duration(p, 0, 0, 5, 0, 0, 1, 2)
+
+
+def test_add_timedelta():
+ p1 = pendulum.duration(days=23, seconds=32)
+ p2 = timedelta(days=12, seconds=30)
+
+ p = p1 + p2
+ assert_duration(p, 0, 0, 5, 0, 0, 1, 2)
+
+
+def test_add_unsupported():
+ p = pendulum.duration(days=23, seconds=32)
+ assert NotImplemented == p.__add__(5)
+
+
+def test_sub_interval():
+ p1 = pendulum.duration(days=23, seconds=32)
+ p2 = pendulum.duration(days=12, seconds=28)
+
+ p = p1 - p2
+ assert_duration(p, 0, 0, 1, 4, 0, 0, 4)
+
+
+def test_sub_timedelta():
+ p1 = pendulum.duration(days=23, seconds=32)
+ p2 = timedelta(days=12, seconds=28)
+
+ p = p1 - p2
+ assert_duration(p, 0, 0, 1, 4, 0, 0, 4)
+
+
+def test_sub_unsupported():
+ p = pendulum.duration(days=23, seconds=32)
+ assert NotImplemented == p.__sub__(5)
+
+
+def test_neg():
+ p = pendulum.duration(days=23, seconds=32)
+ assert_duration(-p, 0, 0, -3, -2, 0, 0, -32)
diff --git a/tests/duration/test_arithmetic.py b/tests/duration/test_arithmetic.py
new file mode 100644
index 0000000..cba4d39
--- /dev/null
+++ b/tests/duration/test_arithmetic.py
@@ -0,0 +1,85 @@
+from __future__ import annotations
+
+import pendulum
+
+from tests.conftest import assert_duration
+
+
+def test_multiply():
+ it = pendulum.duration(days=6, seconds=34, microseconds=522222)
+ mul = it * 2
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 1, 5, 0, 1, 9, 44444)
+
+ it = pendulum.duration(days=6, seconds=34, microseconds=522222)
+ mul = 2 * it
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 1, 5, 0, 1, 9, 44444)
+
+ it = pendulum.duration(
+ years=2, months=3, weeks=4, days=6, seconds=34, microseconds=522222
+ )
+ mul = 2 * it
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 4, 6, 9, 5, 0, 1, 9, 44444)
+
+
+def test_divide():
+ it = pendulum.duration(days=2, seconds=34, microseconds=522222)
+ mul = it / 2
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 1, 0, 0, 17, 261111)
+
+ it = pendulum.duration(days=2, seconds=35, microseconds=522222)
+ mul = it / 2
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 1, 0, 0, 17, 761111)
+
+ it = pendulum.duration(days=2, seconds=35, microseconds=522222)
+ mul = it / 1.1
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 1, 19, 38, 43, 202020)
+
+ it = pendulum.duration(years=2, months=4, days=2, seconds=35, microseconds=522222)
+ mul = it / 2
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 1, 2, 0, 1, 0, 0, 17, 761111)
+
+ it = pendulum.duration(years=2, months=4, days=2, seconds=35, microseconds=522222)
+ mul = it / 2.0
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 1, 2, 0, 1, 0, 0, 17, 761111)
+
+
+def test_floor_divide():
+ it = pendulum.duration(days=2, seconds=34, microseconds=522222)
+ mul = it // 2
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 1, 0, 0, 17, 261111)
+
+ it = pendulum.duration(days=2, seconds=35, microseconds=522222)
+ mul = it // 3
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 0, 16, 0, 11, 840740)
+
+ it = pendulum.duration(years=2, months=4, days=2, seconds=34, microseconds=522222)
+ mul = it // 2
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 1, 2, 0, 1, 0, 0, 17, 261111)
+
+ it = pendulum.duration(years=2, months=4, days=2, seconds=35, microseconds=522222)
+ mul = it // 3
+
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 1, 0, 0, 16, 0, 11, 840740)
diff --git a/tests/duration/test_behavior.py b/tests/duration/test_behavior.py
new file mode 100644
index 0000000..a97bbde
--- /dev/null
+++ b/tests/duration/test_behavior.py
@@ -0,0 +1,21 @@
+from __future__ import annotations
+
+import pickle
+
+from datetime import timedelta
+
+import pendulum
+
+
+def test_pickle():
+ it = pendulum.duration(days=3, seconds=2456, microseconds=123456)
+ s = pickle.dumps(it)
+ it2 = pickle.loads(s)
+
+ assert it == it2
+
+
+def test_comparison_to_timedelta():
+ duration = pendulum.duration(days=3)
+
+ assert duration < timedelta(days=4)
diff --git a/tests/duration/test_construct.py b/tests/duration/test_construct.py
new file mode 100644
index 0000000..aaa5390
--- /dev/null
+++ b/tests/duration/test_construct.py
@@ -0,0 +1,99 @@
+from __future__ import annotations
+
+from datetime import timedelta
+
+import pytest
+
+import pendulum
+
+from pendulum.duration import AbsoluteDuration
+from tests.conftest import assert_duration
+
+
+def test_defaults():
+ pi = pendulum.duration()
+ assert_duration(pi, 0, 0, 0, 0, 0, 0, 0)
+
+
+def test_years():
+ pi = pendulum.duration(years=2)
+ assert_duration(pi, years=2, weeks=0)
+ assert pi.days == 730
+ assert pi.total_seconds() == 63072000
+
+
+def test_months():
+ pi = pendulum.duration(months=3)
+ assert_duration(pi, months=3, weeks=0)
+ assert pi.days == 90
+ assert pi.total_seconds() == 7776000
+
+
+def test_weeks():
+ pi = pendulum.duration(days=365)
+ assert_duration(pi, weeks=52)
+
+ pi = pendulum.duration(days=13)
+ assert_duration(pi, weeks=1)
+
+
+def test_days():
+ pi = pendulum.duration(days=6)
+ assert_duration(pi, 0, 0, 0, 6, 0, 0, 0)
+
+ pi = pendulum.duration(days=16)
+ assert_duration(pi, 0, 0, 2, 2, 0, 0, 0)
+
+
+def test_hours():
+ pi = pendulum.duration(seconds=3600 * 3)
+ assert_duration(pi, 0, 0, 0, 0, 3, 0, 0)
+
+
+def test_minutes():
+ pi = pendulum.duration(seconds=60 * 3)
+ assert_duration(pi, 0, 0, 0, 0, 0, 3, 0)
+
+ pi = pendulum.duration(seconds=60 * 3 + 12)
+ assert_duration(pi, 0, 0, 0, 0, 0, 3, 12)
+
+
+def test_all():
+ pi = pendulum.duration(
+ years=2, months=3, days=1177, seconds=7284, microseconds=1000000
+ )
+ assert_duration(pi, 2, 3, 168, 1, 2, 1, 25)
+ assert pi.days == 1997
+ assert pi.seconds == 7285
+
+
+def test_absolute_interval():
+ pi = AbsoluteDuration(days=-1177, seconds=-7284, microseconds=-1000001)
+ assert_duration(pi, 0, 0, 168, 1, 2, 1, 25)
+ assert pi.microseconds == 1
+ assert pi.invert
+
+
+def test_invert():
+ pi = pendulum.duration(days=1177, seconds=7284, microseconds=1000000)
+ assert not pi.invert
+
+ pi = pendulum.duration(days=-1177, seconds=-7284, microseconds=-1000000)
+ assert pi.invert
+
+
+def test_as_timedelta():
+ pi = pendulum.duration(seconds=3456.123456)
+ assert_duration(pi, 0, 0, 0, 0, 0, 57, 36, 123456)
+ delta = pi.as_timedelta()
+ assert isinstance(delta, timedelta)
+ assert delta.total_seconds() == 3456.123456
+ assert delta.seconds == 3456
+
+
+def test_float_years_and_months():
+ with pytest.raises(ValueError):
+ pendulum.duration(years=1.5)
+
+ with pytest.raises(ValueError):
+ pendulum.duration(months=1.5)
diff --git a/tests/duration/test_in_methods.py b/tests/duration/test_in_methods.py
new file mode 100644
index 0000000..4527257
--- /dev/null
+++ b/tests/duration/test_in_methods.py
@@ -0,0 +1,28 @@
+from __future__ import annotations
+
+import pendulum
+
+
+def test_in_weeks():
+ it = pendulum.duration(days=17)
+ assert it.in_weeks() == 2
+
+
+def test_in_days():
+ it = pendulum.duration(days=3)
+ assert it.in_days() == 3
+
+
+def test_in_hours():
+ it = pendulum.duration(days=3, minutes=72)
+ assert it.in_hours() == 73
+
+
+def test_in_minutes():
+ it = pendulum.duration(minutes=6, seconds=72)
+ assert it.in_minutes() == 7
+
+
+def test_in_seconds():
+ it = pendulum.duration(seconds=72)
+ assert it.in_seconds() == 72
diff --git a/tests/duration/test_in_words.py b/tests/duration/test_in_words.py
new file mode 100644
index 0000000..c0a1a1f
--- /dev/null
+++ b/tests/duration/test_in_words.py
@@ -0,0 +1,77 @@
+from __future__ import annotations
+
+import pendulum
+
+
+def test_week():
+ assert pendulum.duration(days=364).in_words() == "52 weeks"
+ assert pendulum.duration(days=7).in_words() == "1 week"
+
+
+def test_week_to_string():
+ assert str(pendulum.duration(days=364)) == "52 weeks"
+ assert str(pendulum.duration(days=7)) == "1 week"
+
+
+def test_weeks_and_day():
+ assert pendulum.duration(days=365).in_words() == "52 weeks 1 day"
+
+
+def test_all():
+ pi = pendulum.duration(
+ years=2, months=3, days=1177, seconds=7284, microseconds=1000000
+ )
+
+ expected = "2 years 3 months 168 weeks 1 day 2 hours 1 minute 25 seconds"
+ assert pi.in_words() == expected
+
+
+def test_in_french():
+ pi = pendulum.duration(
+ years=2, months=3, days=1177, seconds=7284, microseconds=1000000
+ )
+
+ expected = "2 ans 3 mois 168 semaines 1 jour 2 heures 1 minute 25 secondes"
+ assert pi.in_words(locale="fr") == expected
+
+
+def test_repr():
+ pi = pendulum.duration(
+ years=2, months=3, days=1177, seconds=7284, microseconds=1000000
+ )
+
+ expected = (
+ "Duration(years=2, months=3, weeks=168, days=1, hours=2, minutes=1, seconds=25)"
+ )
+ assert repr(pi) == expected
+
+
+def test_singular_negative_values():
+ pi = pendulum.duration(days=-1)
+
+ assert pi.in_words() == "-1 day"
+
+
+def test_separator():
+ pi = pendulum.duration(days=1177, seconds=7284, microseconds=1000000)
+
+ expected = "168 weeks, 1 day, 2 hours, 1 minute, 25 seconds"
+ assert pi.in_words(separator=", ") == expected
+
+
+def test_subseconds():
+ pi = pendulum.duration(microseconds=123456)
+
+ assert pi.in_words() == "0.12 second"
+
+
+def test_subseconds_with_seconds():
+ pi = pendulum.duration(seconds=12, microseconds=123456)
+
+ assert pi.in_words() == "12 seconds"
+
+
+def test_duration_with_all_zero_values():
+ pi = pendulum.duration()
+
+ assert pi.in_words() == "0 microseconds"
diff --git a/tests/duration/test_total_methods.py b/tests/duration/test_total_methods.py
new file mode 100644
index 0000000..aabcd6c
--- /dev/null
+++ b/tests/duration/test_total_methods.py
@@ -0,0 +1,28 @@
+from __future__ import annotations
+
+import pendulum
+
+
+def test_in_weeks():
+ it = pendulum.duration(days=17)
+ assert round(it.total_weeks(), 2) == 2.43
+
+
+def test_in_days():
+ it = pendulum.duration(days=3)
+ assert it.total_days() == 3
+
+
+def test_in_hours():
+ it = pendulum.duration(days=3, minutes=72)
+ assert it.total_hours() == 73.2
+
+
+def test_in_minutes():
+ it = pendulum.duration(minutes=6, seconds=72)
+ assert it.total_minutes() == 7.2
+
+
+def test_in_seconds():
+ it = pendulum.duration(seconds=72, microseconds=123456)
+ assert it.total_seconds() == 72.123456
diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/fixtures/__init__.py
diff --git a/tests/fixtures/tz/Paris b/tests/fixtures/tz/Paris
new file mode 100644
index 0000000..cf6e2e2
--- /dev/null
+++ b/tests/fixtures/tz/Paris
Binary files differ
diff --git a/tests/fixtures/tz/clock/etc/sysconfig/clock b/tests/fixtures/tz/clock/etc/sysconfig/clock
new file mode 100644
index 0000000..285e68b
--- /dev/null
+++ b/tests/fixtures/tz/clock/etc/sysconfig/clock
@@ -0,0 +1 @@
+ZONE="/usr/share/zoneinfo/Europe/Zurich"
diff --git a/tests/fixtures/tz/symlink/etc/localtime b/tests/fixtures/tz/symlink/etc/localtime
new file mode 120000
index 0000000..d9726ef
--- /dev/null
+++ b/tests/fixtures/tz/symlink/etc/localtime
@@ -0,0 +1 @@
+../usr/share/zoneinfo/Europe/Paris \ No newline at end of file
diff --git a/tests/fixtures/tz/symlink/usr/share/zoneinfo/Europe/Paris b/tests/fixtures/tz/symlink/usr/share/zoneinfo/Europe/Paris
new file mode 100644
index 0000000..cf6e2e2
--- /dev/null
+++ b/tests/fixtures/tz/symlink/usr/share/zoneinfo/Europe/Paris
Binary files differ
diff --git a/tests/fixtures/tz/timezone_dir/etc/localtime b/tests/fixtures/tz/timezone_dir/etc/localtime
new file mode 120000
index 0000000..d9726ef
--- /dev/null
+++ b/tests/fixtures/tz/timezone_dir/etc/localtime
@@ -0,0 +1 @@
+../usr/share/zoneinfo/Europe/Paris \ No newline at end of file
diff --git a/tests/fixtures/tz/timezone_dir/etc/timezone/blank.md b/tests/fixtures/tz/timezone_dir/etc/timezone/blank.md
new file mode 100644
index 0000000..a3a9320
--- /dev/null
+++ b/tests/fixtures/tz/timezone_dir/etc/timezone/blank.md
@@ -0,0 +1,5 @@
+# Blank file
+
+Necessary for environments, which not handle empty folder synchronization well, to be sure the parent folder is created.
+
+The `/etc/timezone` folder is necessary to ensure that the package not fail if that folder exists.
diff --git a/tests/fixtures/tz/timezone_dir/usr/share/zoneinfo/Europe/Paris b/tests/fixtures/tz/timezone_dir/usr/share/zoneinfo/Europe/Paris
new file mode 100644
index 0000000..cf6e2e2
--- /dev/null
+++ b/tests/fixtures/tz/timezone_dir/usr/share/zoneinfo/Europe/Paris
Binary files differ
diff --git a/tests/formatting/__init__.py b/tests/formatting/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/formatting/__init__.py
diff --git a/tests/formatting/test_formatter.py b/tests/formatting/test_formatter.py
new file mode 100644
index 0000000..76c7a7f
--- /dev/null
+++ b/tests/formatting/test_formatter.py
@@ -0,0 +1,263 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+from pendulum.formatting import Formatter
+from pendulum.locales.locale import Locale
+
+
+@pytest.fixture(autouse=True)
+def setup():
+ Locale._cache["dummy"] = {}
+
+ yield
+
+ del Locale._cache["dummy"]
+
+
+def test_year_tokens():
+ d = pendulum.datetime(2009, 1, 14, 15, 25, 50, 123456)
+ f = Formatter()
+
+ assert f.format(d, "YYYY") == "2009"
+ assert f.format(d, "YY") == "09"
+ assert f.format(d, "Y") == "2009"
+
+
+def test_quarter_tokens():
+ f = Formatter()
+ d = pendulum.datetime(1985, 1, 4)
+ assert f.format(d, "Q") == "1"
+
+ d = pendulum.datetime(2029, 8, 1)
+ assert f.format(d, "Q") == "3"
+
+ d = pendulum.datetime(1985, 1, 4)
+ assert f.format(d, "Qo") == "1st"
+
+ d = pendulum.datetime(2029, 8, 1)
+ assert f.format(d, "Qo") == "3rd"
+
+ d = pendulum.datetime(1985, 1, 4)
+ assert f.format(d, "Qo", locale="fr") == "1er"
+
+ d = pendulum.datetime(2029, 8, 1)
+ assert f.format(d, "Qo", locale="fr") == "3e"
+
+
+def test_month_tokens():
+ f = Formatter()
+ d = pendulum.datetime(2016, 3, 24)
+ assert f.format(d, "MM") == "03"
+ assert f.format(d, "M") == "3"
+
+ assert f.format(d, "MMM") == "Mar"
+ assert f.format(d, "MMMM") == "March"
+ assert f.format(d, "Mo") == "3rd"
+
+ assert f.format(d, "MMM", locale="fr") == "mars"
+ assert f.format(d, "MMMM", locale="fr") == "mars"
+ assert f.format(d, "Mo", locale="fr") == "3e"
+
+
+def test_day_tokens():
+ f = Formatter()
+ d = pendulum.datetime(2016, 3, 7)
+ assert f.format(d, "DD") == "07"
+ assert f.format(d, "D") == "7"
+
+ assert f.format(d, "Do") == "7th"
+ assert f.format(d.first_of("month"), "Do") == "1st"
+
+ assert f.format(d, "Do", locale="fr") == "7e"
+ assert f.format(d.first_of("month"), "Do", locale="fr") == "1er"
+
+
+def test_day_of_year():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28)
+ assert f.format(d, "DDDD") == "241"
+ assert f.format(d, "DDD") == "241"
+ assert f.format(d.start_of("year"), "DDDD") == "001"
+ assert f.format(d.start_of("year"), "DDD") == "1"
+
+ assert f.format(d, "DDDo") == "241st"
+ assert f.format(d.add(days=3), "DDDo") == "244th"
+
+ assert f.format(d, "DDDo", locale="fr") == "241e"
+ assert f.format(d.add(days=3), "DDDo", locale="fr") == "244e"
+
+
+def test_week_of_year():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28)
+
+ assert f.format(d, "wo") == "34th"
+
+
+def test_day_of_week():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28)
+ assert f.format(d, "d") == "0"
+
+ assert f.format(d, "dd") == "Su"
+ assert f.format(d, "ddd") == "Sun"
+ assert f.format(d, "dddd") == "Sunday"
+
+ assert f.format(d, "dd", locale="fr") == "di"
+ assert f.format(d, "ddd", locale="fr") == "dim."
+ assert f.format(d, "dddd", locale="fr") == "dimanche"
+
+ assert f.format(d, "do") == "0th"
+
+
+def test_day_of_iso_week():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28)
+ assert f.format(d, "E") == "7"
+
+
+def test_am_pm():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 23)
+ assert f.format(d, "A") == "PM"
+ assert f.format(d.set(hour=11), "A") == "AM"
+
+
+def test_hour():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7)
+ assert f.format(d, "H") == "7"
+ assert f.format(d, "HH") == "07"
+
+ d = pendulum.datetime(2016, 8, 28, 0)
+ assert f.format(d, "h") == "12"
+ assert f.format(d, "hh") == "12"
+
+
+def test_minute():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7, 3)
+ assert f.format(d, "m") == "3"
+ assert f.format(d, "mm") == "03"
+
+
+def test_second():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6)
+ assert f.format(d, "s") == "6"
+ assert f.format(d, "ss") == "06"
+
+
+def test_fractional_second():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456)
+ assert f.format(d, "S") == "1"
+ assert f.format(d, "SS") == "12"
+ assert f.format(d, "SSS") == "123"
+ assert f.format(d, "SSSS") == "1234"
+ assert f.format(d, "SSSSS") == "12345"
+ assert f.format(d, "SSSSSS") == "123456"
+
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 0)
+ assert f.format(d, "S") == "0"
+ assert f.format(d, "SS") == "00"
+ assert f.format(d, "SSS") == "000"
+ assert f.format(d, "SSSS") == "0000"
+ assert f.format(d, "SSSSS") == "00000"
+ assert f.format(d, "SSSSSS") == "000000"
+
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123)
+ assert f.format(d, "S") == "0"
+ assert f.format(d, "SS") == "00"
+ assert f.format(d, "SSS") == "000"
+ assert f.format(d, "SSSS") == "0001"
+ assert f.format(d, "SSSSS") == "00012"
+ assert f.format(d, "SSSSSS") == "000123"
+
+
+def test_timezone():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456, tz="Europe/Paris")
+ assert f.format(d, "zz") == "CEST"
+ assert f.format(d, "z") == "Europe/Paris"
+
+ d = pendulum.datetime(2016, 1, 28, 7, 3, 6, 123456, tz="Europe/Paris")
+ assert f.format(d, "zz") == "CET"
+ assert f.format(d, "z") == "Europe/Paris"
+
+
+def test_timezone_offset():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456, tz="Europe/Paris")
+ assert f.format(d, "ZZ") == "+0200"
+ assert f.format(d, "Z") == "+02:00"
+
+ d = pendulum.datetime(2016, 1, 28, 7, 3, 6, 123456, tz="Europe/Paris")
+ assert f.format(d, "ZZ") == "+0100"
+ assert f.format(d, "Z") == "+01:00"
+
+ d = pendulum.datetime(2016, 1, 28, 7, 3, 6, 123456, tz="America/Guayaquil")
+ assert f.format(d, "ZZ") == "-0500"
+ assert f.format(d, "Z") == "-05:00"
+
+
+def test_timestamp():
+ f = Formatter()
+ d = pendulum.datetime(1970, 1, 1)
+ assert f.format(d, "X") == "0"
+ assert f.format(d.add(days=1), "X") == "86400"
+
+
+def test_timestamp_milliseconds():
+ f = Formatter()
+ d = pendulum.datetime(1970, 1, 1)
+ assert f.format(d, "x") == "0"
+ assert f.format(d.add(days=1), "x") == "86400000"
+ assert f.format(d.add(days=1, microseconds=129123), "x") == "86400129"
+
+
+def test_date_formats():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456)
+ assert f.format(d, "LT") == "7:03 AM"
+ assert f.format(d, "LTS") == "7:03:06 AM"
+ assert f.format(d, "L") == "08/28/2016"
+ assert f.format(d, "LL") == "August 28, 2016"
+ assert f.format(d, "LLL") == "August 28, 2016 7:03 AM"
+ assert f.format(d, "LLLL") == "Sunday, August 28, 2016 7:03 AM"
+
+ assert f.format(d, "LT", locale="fr") == "07:03"
+ assert f.format(d, "LTS", locale="fr") == "07:03:06"
+ assert f.format(d, "L", locale="fr") == "28/08/2016"
+ assert f.format(d, "LL", locale="fr") == "28 août 2016"
+ assert f.format(d, "LLL", locale="fr") == "28 août 2016 07:03"
+ assert f.format(d, "LLLL", locale="fr") == "dimanche 28 août 2016 07:03"
+
+
+def test_escape():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28)
+ assert f.format(d, r"[YYYY] YYYY \[YYYY\]") == "YYYY 2016 [2016]"
+ assert f.format(d, r"\D D \\D") == "D 28 \\28"
+
+
+def test_date_formats_missing():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456)
+
+ assert f.format(d, "LT", locale="dummy") == "7:03 AM"
+ assert f.format(d, "LTS", locale="dummy") == "7:03:06 AM"
+ assert f.format(d, "L", locale="dummy") == "08/28/2016"
+ assert f.format(d, "LL", locale="dummy") == "August 28, 2016"
+ assert f.format(d, "LLL", locale="dummy") == "August 28, 2016 7:03 AM"
+ assert f.format(d, "LLLL", locale="dummy") == "Sunday, August 28, 2016 7:03 AM"
+
+
+def test_unknown_token():
+ f = Formatter()
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456)
+
+ assert f.format(d, "J") == "J"
diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/helpers/__init__.py
diff --git a/tests/helpers/test_local_time.py b/tests/helpers/test_local_time.py
new file mode 100644
index 0000000..0563107
--- /dev/null
+++ b/tests/helpers/test_local_time.py
@@ -0,0 +1,31 @@
+from __future__ import annotations
+
+import pendulum
+
+from pendulum.helpers import local_time
+
+
+def test_local_time_positive_integer():
+ d = pendulum.datetime(2016, 8, 7, 12, 34, 56, 123456)
+
+ t = local_time(d.int_timestamp, 0, d.microsecond)
+ assert d.year == t[0]
+ assert d.month == t[1]
+ assert d.day == t[2]
+ assert d.hour == t[3]
+ assert d.minute == t[4]
+ assert d.second == t[5]
+ assert d.microsecond == t[6]
+
+
+def test_local_time_negative_integer():
+ d = pendulum.datetime(1951, 8, 7, 12, 34, 56, 123456)
+
+ t = local_time(d.int_timestamp, 0, d.microsecond)
+ assert d.year == t[0]
+ assert d.month == t[1]
+ assert d.day == t[2]
+ assert d.hour == t[3]
+ assert d.minute == t[4]
+ assert d.second == t[5]
+ assert d.microsecond == t[6]
diff --git a/tests/interval/__init__.py b/tests/interval/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/interval/__init__.py
diff --git a/tests/interval/test_add_subtract.py b/tests/interval/test_add_subtract.py
new file mode 100644
index 0000000..88525a3
--- /dev/null
+++ b/tests/interval/test_add_subtract.py
@@ -0,0 +1,49 @@
+from __future__ import annotations
+
+import pendulum
+
+
+def test_dst_add():
+ start = pendulum.datetime(2017, 3, 7, tz="America/Toronto")
+ end = start.add(days=6)
+ period = end - start
+ new_end = start + period
+
+ assert new_end == end
+
+
+def test_dst_add_non_variable_units():
+ start = pendulum.datetime(2013, 3, 31, 1, 30, tz="Europe/Paris")
+ end = start.add(hours=1)
+ period = end - start
+ new_end = start + period
+
+ assert new_end == end
+
+
+def test_dst_subtract():
+ start = pendulum.datetime(2017, 3, 7, tz="America/Toronto")
+ end = start.add(days=6)
+ period = end - start
+ new_start = end - period
+
+ assert new_start == start
+
+
+def test_naive_subtract():
+ start = pendulum.naive(2013, 3, 31, 1, 30)
+ end = start.add(hours=1)
+ period = end - start
+ new_end = start + period
+
+ assert new_end == end
+
+
+def test_negative_difference_subtract():
+ start = pendulum.datetime(2018, 5, 28, 12, 34, 56, 123456)
+ end = pendulum.datetime(2018, 1, 1)
+
+ period = end - start
+ new_end = start + period
+
+ assert new_end == end
diff --git a/tests/interval/test_arithmetic.py b/tests/interval/test_arithmetic.py
new file mode 100644
index 0000000..e5ba01f
--- /dev/null
+++ b/tests/interval/test_arithmetic.py
@@ -0,0 +1,53 @@
+from __future__ import annotations
+
+import pendulum
+
+from tests.conftest import assert_duration
+
+
+def test_multiply():
+ dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56)
+ dt2 = dt1.add(days=6, seconds=34)
+ it = pendulum.interval(dt1, dt2)
+ mul = it * 2
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 1, 5, 0, 1, 8)
+
+ dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56)
+ dt2 = dt1.add(days=6, seconds=34)
+ it = pendulum.interval(dt1, dt2)
+ mul = it * 2
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 1, 5, 0, 1, 8)
+
+
+def test_divide():
+ dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56)
+ dt2 = dt1.add(days=2, seconds=34)
+ it = pendulum.interval(dt1, dt2)
+ mul = it / 2
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 1, 0, 0, 17)
+
+ dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56)
+ dt2 = dt1.add(days=2, seconds=35)
+ it = pendulum.interval(dt1, dt2)
+ mul = it / 2
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 1, 0, 0, 17)
+
+
+def test_floor_divide():
+ dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56)
+ dt2 = dt1.add(days=2, seconds=34)
+ it = pendulum.interval(dt1, dt2)
+ mul = it // 2
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 1, 0, 0, 17)
+
+ dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56)
+ dt2 = dt1.add(days=2, seconds=35)
+ it = pendulum.interval(dt1, dt2)
+ mul = it // 3
+ assert isinstance(mul, pendulum.Duration)
+ assert_duration(mul, 0, 0, 0, 0, 16, 0, 11)
diff --git a/tests/interval/test_behavior.py b/tests/interval/test_behavior.py
new file mode 100644
index 0000000..b5e057a
--- /dev/null
+++ b/tests/interval/test_behavior.py
@@ -0,0 +1,54 @@
+from __future__ import annotations
+
+import pickle
+
+from datetime import timedelta
+
+import pendulum
+
+
+def test_pickle():
+ dt1 = pendulum.datetime(2016, 11, 18)
+ dt2 = pendulum.datetime(2016, 11, 20)
+
+ p = pendulum.interval(dt1, dt2)
+ s = pickle.dumps(p)
+ p2 = pickle.loads(s)
+
+ assert p.start == p2.start
+ assert p.end == p2.end
+ assert p.invert == p2.invert
+
+ p = pendulum.interval(dt2, dt1)
+ s = pickle.dumps(p)
+ p2 = pickle.loads(s)
+
+ assert p.start == p2.start
+ assert p.end == p2.end
+ assert p.invert == p2.invert
+
+ p = pendulum.interval(dt2, dt1, True)
+ s = pickle.dumps(p)
+ p2 = pickle.loads(s)
+
+ assert p.start == p2.start
+ assert p.end == p2.end
+ assert p.invert == p2.invert
+
+
+def test_comparison_to_timedelta():
+ dt1 = pendulum.datetime(2016, 11, 18)
+ dt2 = pendulum.datetime(2016, 11, 20)
+
+ period = dt2 - dt1
+
+ assert period < timedelta(days=4)
+
+
+def test_equality_to_timedelta():
+ dt1 = pendulum.datetime(2016, 11, 18)
+ dt2 = pendulum.datetime(2016, 11, 20)
+
+ period = dt2 - dt1
+
+ assert period == timedelta(days=2)
diff --git a/tests/interval/test_construct.py b/tests/interval/test_construct.py
new file mode 100644
index 0000000..024e741
--- /dev/null
+++ b/tests/interval/test_construct.py
@@ -0,0 +1,121 @@
+from __future__ import annotations
+
+from datetime import datetime
+
+import pendulum
+
+from tests.conftest import assert_datetime
+
+
+def test_with_datetimes():
+ dt1 = datetime(2000, 1, 1)
+ dt2 = datetime(2000, 1, 31)
+ p = pendulum.interval(dt1, dt2)
+
+ assert isinstance(p.start, pendulum.DateTime)
+ assert isinstance(p.end, pendulum.DateTime)
+ assert_datetime(p.start, 2000, 1, 1)
+ assert_datetime(p.end, 2000, 1, 31)
+
+
+def test_with_pendulum():
+ dt1 = pendulum.DateTime(2000, 1, 1)
+ dt2 = pendulum.DateTime(2000, 1, 31)
+ p = pendulum.interval(dt1, dt2)
+
+ assert_datetime(p.start, 2000, 1, 1)
+ assert_datetime(p.end, 2000, 1, 31)
+
+
+def test_inverted():
+ dt1 = pendulum.DateTime(2000, 1, 1)
+ dt2 = pendulum.DateTime(2000, 1, 31)
+ p = pendulum.interval(dt2, dt1)
+
+ assert_datetime(p.start, 2000, 1, 31)
+ assert_datetime(p.end, 2000, 1, 1)
+
+
+def test_inverted_and_absolute():
+ dt1 = pendulum.DateTime(2000, 1, 1)
+ dt2 = pendulum.DateTime(2000, 1, 31)
+ p = pendulum.interval(dt2, dt1, True)
+
+ assert_datetime(p.start, 2000, 1, 1)
+ assert_datetime(p.end, 2000, 1, 31)
+
+
+def test_accuracy():
+ dt1 = pendulum.DateTime(2000, 11, 20)
+ dt2 = pendulum.DateTime(2000, 11, 25)
+ dt3 = pendulum.DateTime(2016, 11, 5)
+ p1 = pendulum.interval(dt1, dt3)
+ p2 = pendulum.interval(dt2, dt3)
+
+ assert p1.years == 15
+ assert p1.in_years() == 15
+ assert p1.months == 11
+ assert p1.in_months() == 191
+ assert p1.days == 5829
+ assert p1.remaining_days == 2
+ assert p1.in_days() == 5829
+
+ assert p2.years == 15
+ assert p2.in_years() == 15
+ assert p2.months == 11
+ assert p2.in_months() == 191
+ assert p2.days == 5824
+ assert p2.remaining_days == 4
+ assert p2.in_days() == 5824
+
+
+def test_dst_transition():
+ start = pendulum.datetime(2017, 3, 7, tz="America/Toronto")
+ end = start.add(days=6)
+ period = end - start
+
+ assert period.days == 5
+ assert period.seconds == 82800
+
+ assert period.remaining_days == 6
+ assert period.hours == 0
+ assert period.remaining_seconds == 0
+
+ assert period.in_days() == 6
+ assert period.in_hours() == 5 * 24 + 23
+
+
+def test_timedelta_behavior():
+ dt1 = pendulum.DateTime(2000, 11, 20, 1)
+ dt2 = pendulum.DateTime(2000, 11, 25, 2)
+ dt3 = pendulum.DateTime(2016, 11, 5, 3)
+
+ p1 = pendulum.interval(dt1, dt3)
+ p2 = pendulum.interval(dt2, dt3)
+ it1 = p1.as_timedelta()
+ it2 = p2.as_timedelta()
+
+ assert it1.total_seconds() == p1.total_seconds()
+ assert it2.total_seconds() == p2.total_seconds()
+ assert it1.days == p1.days
+ assert it2.days == p2.days
+ assert it1.seconds == p1.seconds
+ assert it2.seconds == p2.seconds
+ assert it1.microseconds == p1.microseconds
+ assert it2.microseconds == p2.microseconds
+
+
+def test_different_timezones_same_time():
+ dt1 = pendulum.datetime(2013, 3, 31, 1, 30, tz="Europe/Paris")
+ dt2 = pendulum.datetime(2013, 4, 1, 1, 30, tz="Europe/Paris")
+ period = dt2 - dt1
+
+ assert period.in_words() == "1 day"
+ assert period.in_hours() == 23
+
+ dt1 = pendulum.datetime(2013, 3, 31, 1, 30, tz="Europe/Paris")
+ dt2 = pendulum.datetime(2013, 4, 1, 1, 30, tz="America/Toronto")
+ period = dt2 - dt1
+
+ assert period.in_words() == "1 day 5 hours"
+ assert period.in_hours() == 29
diff --git a/tests/interval/test_hashing.py b/tests/interval/test_hashing.py
new file mode 100644
index 0000000..c18502f
--- /dev/null
+++ b/tests/interval/test_hashing.py
@@ -0,0 +1,23 @@
+from __future__ import annotations
+
+import pendulum
+
+
+def test_periods_with_same_duration_and_different_dates():
+ day1 = pendulum.DateTime(2018, 1, 1)
+ day2 = pendulum.DateTime(2018, 1, 2)
+ day3 = pendulum.DateTime(2018, 1, 2)
+
+ period1 = day2 - day1
+ period2 = day3 - day2
+
+ assert period1 != period2
+ assert len({period1, period2}) == 2
+
+
+def test_periods_with_same_dates():
+ period1 = pendulum.DateTime(2018, 1, 2) - pendulum.DateTime(2018, 1, 1)
+ period2 = pendulum.DateTime(2018, 1, 2) - pendulum.DateTime(2018, 1, 1)
+
+ assert period1 == period2
+ assert len({period1, period2}) == 1
diff --git a/tests/interval/test_in_words.py b/tests/interval/test_in_words.py
new file mode 100644
index 0000000..410e11f
--- /dev/null
+++ b/tests/interval/test_in_words.py
@@ -0,0 +1,70 @@
+from __future__ import annotations
+
+import pendulum
+
+
+def test_week():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(start=start_date, end=start_date.add(weeks=1))
+ assert period.in_words() == "1 week"
+
+
+def test_week_and_day():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(start=start_date, end=start_date.add(weeks=1, days=1))
+ assert period.in_words() == "1 week 1 day"
+
+
+def test_all():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(
+ start=start_date,
+ end=start_date.add(years=1, months=1, days=1, seconds=1, microseconds=1),
+ )
+ assert period.in_words() == "1 year 1 month 1 day 1 second"
+
+
+def test_in_french():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(
+ start=start_date,
+ end=start_date.add(years=1, months=1, days=1, seconds=1, microseconds=1),
+ )
+ assert period.in_words(locale="fr") == "1 an 1 mois 1 jour 1 seconde"
+
+
+def test_singular_negative_values():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(start=start_date, end=start_date.subtract(days=1))
+ assert period.in_words() == "-1 day"
+
+
+def test_separator():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(
+ start=start_date,
+ end=start_date.add(years=1, months=1, days=1, seconds=1, microseconds=1),
+ )
+ assert period.in_words(separator=", ") == "1 year, 1 month, 1 day, 1 second"
+
+
+def test_subseconds():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(
+ start=start_date, end=start_date.add(microseconds=123456)
+ )
+ assert period.in_words() == "0.12 second"
+
+
+def test_subseconds_with_seconds():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(
+ start=start_date, end=start_date.add(seconds=12, microseconds=123456)
+ )
+ assert period.in_words() == "12 seconds"
+
+
+def test_zero_period():
+ start_date = pendulum.datetime(2012, 1, 1)
+ period = pendulum.interval(start=start_date, end=start_date)
+ assert period.in_words() == "0 microseconds"
diff --git a/tests/interval/test_range.py b/tests/interval/test_range.py
new file mode 100644
index 0000000..28fe1ff
--- /dev/null
+++ b/tests/interval/test_range.py
@@ -0,0 +1,119 @@
+from __future__ import annotations
+
+import pendulum
+
+from pendulum.interval import Interval
+from tests.conftest import assert_datetime
+
+
+def test_range():
+ dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37)
+ dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37)
+
+ p = Interval(dt1, dt2)
+ r = list(p.range("days"))
+
+ assert len(r) == 31
+ assert_datetime(r[0], 2000, 1, 1, 12, 45, 37)
+ assert_datetime(r[-1], 2000, 1, 31, 12, 45, 37)
+
+
+def test_range_no_overflow():
+ dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37)
+ dt2 = pendulum.datetime(2000, 1, 31, 11, 45, 37)
+
+ p = Interval(dt1, dt2)
+ r = list(p.range("days"))
+
+ assert len(r) == 30
+ assert_datetime(r[0], 2000, 1, 1, 12, 45, 37)
+ assert_datetime(r[-1], 2000, 1, 30, 12, 45, 37)
+
+
+def test_range_inverted():
+ dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37)
+ dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37)
+
+ p = Interval(dt2, dt1)
+ r = list(p.range("days"))
+
+ assert len(r) == 31
+ assert_datetime(r[-1], 2000, 1, 1, 12, 45, 37)
+ assert_datetime(r[0], 2000, 1, 31, 12, 45, 37)
+
+
+def test_iter():
+ dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37)
+ dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37)
+
+ p = Interval(dt1, dt2)
+ i = 0 # noqa: SIM113 (suggests use of enumerate)
+ for dt in p:
+ assert isinstance(dt, pendulum.DateTime)
+ i += 1
+
+ assert i == 31
+
+
+def test_contains():
+ dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37)
+ dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37)
+
+ p = pendulum.interval(dt1, dt2)
+ dt = pendulum.datetime(2000, 1, 7)
+ assert dt in p
+
+
+def test_not_contains():
+ dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37)
+ dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37)
+
+ p = pendulum.interval(dt1, dt2)
+ dt = pendulum.datetime(2000, 1, 1, 11, 45, 37)
+ assert dt not in p
+
+
+def test_contains_with_datetime():
+ dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37)
+ dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37)
+
+ p = pendulum.interval(dt1, dt2)
+ dt = pendulum.datetime(2000, 1, 7)
+ assert dt in p
+
+
+def test_range_months_overflow():
+ dt1 = pendulum.datetime(2016, 1, 30, tz="America/Sao_Paulo")
+ dt2 = dt1.add(months=4)
+
+ p = pendulum.interval(dt1, dt2)
+ r = list(p.range("months"))
+
+ assert_datetime(r[0], 2016, 1, 30, 0, 0, 0)
+ assert_datetime(r[-1], 2016, 5, 30, 0, 0, 0)
+
+
+def test_range_with_dst():
+ dt1 = pendulum.datetime(2016, 10, 14, tz="America/Sao_Paulo")
+ dt2 = dt1.add(weeks=1)
+
+ p = pendulum.interval(dt1, dt2)
+ r = list(p.range("days"))
+
+ assert_datetime(r[0], 2016, 10, 14, 0, 0, 0)
+ assert_datetime(r[2], 2016, 10, 16, 1, 0, 0)
+ assert_datetime(r[-1], 2016, 10, 21, 0, 0, 0)
+
+
+def test_range_amount():
+ dt1 = pendulum.datetime(2016, 10, 14, tz="America/Sao_Paulo")
+ dt2 = dt1.add(weeks=1)
+
+ p = pendulum.interval(dt1, dt2)
+ r = list(p.range("days", 2))
+
+ assert len(r) == 4
+ assert_datetime(r[0], 2016, 10, 14, 0, 0, 0)
+ assert_datetime(r[1], 2016, 10, 16, 1, 0, 0)
+ assert_datetime(r[2], 2016, 10, 18, 0, 0, 0)
+ assert_datetime(r[3], 2016, 10, 20, 0, 0, 0)
diff --git a/tests/localization/__init__.py b/tests/localization/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/localization/__init__.py
diff --git a/tests/localization/test_cs.py b/tests/localization/test_cs.py
new file mode 100644
index 0000000..71b8340
--- /dev/null
+++ b/tests/localization/test_cs.py
@@ -0,0 +1,109 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "cs"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "pár vteřin zpět"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "pár vteřin zpět"
+
+ d = pendulum.now().subtract(seconds=20)
+ assert d.diff_for_humans(locale=locale) == "před 20 sekundami"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "před 1 minutou"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "před 2 minutami"
+
+ d = pendulum.now().subtract(minutes=5)
+ assert d.diff_for_humans(locale=locale) == "před 5 minutami"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "před 1 hodinou"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "před 2 hodinami"
+
+ d = pendulum.now().subtract(hours=5)
+ assert d.diff_for_humans(locale=locale) == "před 5 hodinami"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "před 1 dnem"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "před 2 dny"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "před 1 týdnem"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "před 2 týdny"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "před 1 měsícem"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "před 2 měsíci"
+
+ d = pendulum.now().subtract(months=5)
+ assert d.diff_for_humans(locale=locale) == "před 5 měsíci"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "před 1 rokem"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "před 2 lety"
+
+ d = pendulum.now().subtract(years=5)
+ assert d.diff_for_humans(locale=locale) == "před 5 lety"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "za pár vteřin"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "pár vteřin po"
+ assert d2.diff_for_humans(d, locale=locale) == "pár vteřin zpět"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "pár vteřin"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "pár vteřin"
+
+ d = pendulum.now().add(seconds=20)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "20 sekund po"
+ assert d2.diff_for_humans(d, locale=locale) == "20 sekund zpět"
+
+ d = pendulum.now().add(seconds=10)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, True, locale=locale) == "pár vteřin"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "11 sekund"
+
+
+def test_format():
+ d = pendulum.datetime(2016, 8, 29, 7, 3, 6, 123456)
+ assert d.format("dddd", locale=locale) == "pondělí"
+ assert d.format("ddd", locale=locale) == "po"
+ assert d.format("MMMM", locale=locale) == "srpna"
+ assert d.format("MMM", locale=locale) == "srp"
+ assert d.format("A", locale=locale) == "dop."
+ assert d.format("Qo", locale=locale) == "3."
+ assert d.format("Mo", locale=locale) == "8."
+ assert d.format("Do", locale=locale) == "29."
+
+ assert d.format("LT", locale=locale) == "7:03"
+ assert d.format("LTS", locale=locale) == "7:03:06"
+ assert d.format("L", locale=locale) == "29. 8. 2016"
+ assert d.format("LL", locale=locale) == "29. srpna, 2016"
+ assert d.format("LLL", locale=locale) == "29. srpna, 2016 7:03"
+ assert d.format("LLLL", locale=locale) == "pondělí, 29. srpna, 2016 7:03"
diff --git a/tests/localization/test_da.py b/tests/localization/test_da.py
new file mode 100644
index 0000000..b08adfe
--- /dev/null
+++ b/tests/localization/test_da.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "da"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 sekund siden"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 sekunder siden"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 minut siden"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 minutter siden"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 time siden"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 timer siden"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 dag siden"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 dage siden"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 uge siden"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 uger siden"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 måned siden"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 måneder siden"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 år siden"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 år siden"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "om 1 sekund"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 sekund efter"
+ assert d2.diff_for_humans(d, locale=locale) == "1 sekund før"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekunder"
diff --git a/tests/localization/test_de.py b/tests/localization/test_de.py
new file mode 100644
index 0000000..9c72b79
--- /dev/null
+++ b/tests/localization/test_de.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "de"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "vor 1 Sekunde"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "vor 2 Sekunden"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "vor 1 Minute"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "vor 2 Minuten"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "vor 1 Stunde"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "vor 2 Stunden"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "vor 1 Tag"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "vor 2 Tagen"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "vor 1 Woche"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "vor 2 Wochen"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "vor 1 Monat"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "vor 2 Monaten"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "vor 1 Jahr"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "vor 2 Jahren"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "in 1 Sekunde"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 Sekunde später"
+ assert d2.diff_for_humans(d, locale=locale) == "1 Sekunde zuvor"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 Sekunde"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 Sekunden"
diff --git a/tests/localization/test_es.py b/tests/localization/test_es.py
new file mode 100644
index 0000000..747ec6f
--- /dev/null
+++ b/tests/localization/test_es.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "es"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "hace unos segundos"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "hace unos segundos"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "hace 1 minuto"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "hace 2 minutos"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "hace 1 hora"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "hace 2 horas"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "hace 1 día"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "hace 2 días"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "hace 1 semana"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "hace 2 semanas"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "hace 1 mes"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "hace 2 meses"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "hace 1 año"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "hace 2 años"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "dentro de unos segundos"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "unos segundos después"
+ assert d2.diff_for_humans(d, locale=locale) == "unos segundos antes"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "unos segundos"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "unos segundos"
diff --git a/tests/localization/test_fa.py b/tests/localization/test_fa.py
new file mode 100644
index 0000000..39d2e4a
--- /dev/null
+++ b/tests/localization/test_fa.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "fa"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "1 ثانیه پیش"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "2 ثانیه پیش"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1 دقیقه پیش"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2 دقیقه پیش"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1 ساعت پیش"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2 ساعت پیش"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1 روز پیش"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2 روز پیش"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1 هفته پیش"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2 هفته پیش"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1 ماه پیش"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2 ماه پیش"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1 سال پیش"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2 سال پیش"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "1 ثانیه بعد"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 ثانیه پس از"
+ assert d2.diff_for_humans(d, locale=locale) == "1 ثانیه پیش از"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 ثانیه"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 ثانیه"
diff --git a/tests/localization/test_fo.py b/tests/localization/test_fo.py
new file mode 100644
index 0000000..f451553
--- /dev/null
+++ b/tests/localization/test_fo.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "fo"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "1 sekund síðan"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "2 sekund síðan"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1 minutt síðan"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2 minuttir síðan"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1 tími síðan"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2 tímar síðan"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1 dagur síðan"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2 dagar síðan"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1 vika síðan"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2 vikur síðan"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1 mánað síðan"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2 mánaðir síðan"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1 ár síðan"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2 ár síðan"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "um 1 sekund"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 sekund aftaná"
+ assert d2.diff_for_humans(d, locale=locale) == "1 sekund áðrenn"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekundir"
diff --git a/tests/localization/test_fr.py b/tests/localization/test_fr.py
new file mode 100644
index 0000000..1cfef5b
--- /dev/null
+++ b/tests/localization/test_fr.py
@@ -0,0 +1,84 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "fr"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "il y a quelques secondes"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "il y a quelques secondes"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "il y a 1 minute"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "il y a 2 minutes"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "il y a 1 heure"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "il y a 2 heures"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "il y a 1 jour"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "il y a 2 jours"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "il y a 1 semaine"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "il y a 2 semaines"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "il y a 1 mois"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "il y a 2 mois"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "il y a 1 an"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "il y a 2 ans"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "dans quelques secondes"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "quelques secondes après"
+ assert d2.diff_for_humans(d, locale=locale) == "quelques secondes avant"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "quelques secondes"
+ assert (
+ d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "quelques secondes"
+ )
+
+
+def test_format():
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456)
+ assert d.format("dddd", locale=locale) == "dimanche"
+ assert d.format("ddd", locale=locale) == "dim."
+ assert d.format("MMMM", locale=locale) == "août"
+ assert d.format("MMM", locale=locale) == "août"
+ assert d.format("A", locale=locale) == "AM"
+ assert d.format("Do", locale=locale) == "28e"
+
+ assert d.format("LT", locale=locale) == "07:03"
+ assert d.format("LTS", locale=locale) == "07:03:06"
+ assert d.format("L", locale=locale) == "28/08/2016"
+ assert d.format("LL", locale=locale) == "28 août 2016"
+ assert d.format("LLL", locale=locale) == "28 août 2016 07:03"
+ assert d.format("LLLL", locale=locale) == "dimanche 28 août 2016 07:03"
diff --git a/tests/localization/test_he.py b/tests/localization/test_he.py
new file mode 100644
index 0000000..6186ef2
--- /dev/null
+++ b/tests/localization/test_he.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "he"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "לפני כמה שניות"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "לפני כמה שניות"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "לפני דקה"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "לפני שתי דקות"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "לפני שעה"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "לפני שעתיים"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "לפני יום 1"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "לפני יומיים"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "לפני שבוע"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "לפני שבועיים"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "לפני חודש"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "לפני חודשיים"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "לפני שנה"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "לפני שנתיים"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "תוך כמה שניות"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "בעוד כמה שניות"
+ assert d2.diff_for_humans(d, locale=locale) == "כמה שניות קודם"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "כמה שניות"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "כמה שניות"
diff --git a/tests/localization/test_id.py b/tests/localization/test_id.py
new file mode 100644
index 0000000..3dd316c
--- /dev/null
+++ b/tests/localization/test_id.py
@@ -0,0 +1,68 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "id"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "beberapa detik yang lalu"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "beberapa detik yang lalu"
+
+ d = pendulum.now().subtract(seconds=21)
+ assert d.diff_for_humans(locale=locale) == "21 detik yang lalu"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1 menit yang lalu"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2 menit yang lalu"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1 jam yang lalu"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2 jam yang lalu"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1 hari yang lalu"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2 hari yang lalu"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1 minggu yang lalu"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2 minggu yang lalu"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1 bulan yang lalu"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2 bulan yang lalu"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1 tahun yang lalu"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2 tahun yang lalu"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "dalam beberapa detik"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "beberapa detik kemudian"
+ assert d2.diff_for_humans(d, locale=locale) == "beberapa detik yang lalu"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "beberapa detik"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "beberapa detik"
diff --git a/tests/localization/test_it.py b/tests/localization/test_it.py
new file mode 100644
index 0000000..1918a2b
--- /dev/null
+++ b/tests/localization/test_it.py
@@ -0,0 +1,85 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "it"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "alcuni secondi fa"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "alcuni secondi fa"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1 minuto fa"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2 minuti fa"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1 ora fa"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2 ore fa"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1 giorno fa"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2 giorni fa"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1 settimana fa"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2 settimane fa"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1 mese fa"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2 mesi fa"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1 anno fa"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2 anni fa"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "in alcuni secondi"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "alcuni secondi dopo"
+ assert d2.diff_for_humans(d, locale=locale) == "alcuni secondi prima"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "alcuni secondi"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "alcuni secondi"
+
+
+def test_format():
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456)
+ assert d.format("dddd", locale=locale) == "domenica"
+ assert d.format("ddd", locale=locale) == "dom"
+ assert d.format("MMMM", locale=locale) == "agosto"
+ assert d.format("MMM", locale=locale) == "ago"
+ assert d.format("A", locale=locale) == "AM"
+
+ assert d.format("LT", locale=locale) == "7:03"
+ assert d.format("LTS", locale=locale) == "7:03:06"
+ assert d.format("L", locale=locale) == "28/08/2016"
+ assert d.format("LL", locale=locale) == "28 agosto 2016"
+ assert d.format("LLL", locale=locale) == "28 agosto 2016 alle 7:03"
+ assert d.format("LLLL", locale=locale) == "domenica, 28 agosto 2016 alle 7:03"
+
+ assert d.format("Do", locale=locale) == "28°"
+ d = pendulum.datetime(2019, 1, 1, 7, 3, 6, 123456)
+ assert d.format("Do", locale=locale) == "1°"
diff --git a/tests/localization/test_ja.py b/tests/localization/test_ja.py
new file mode 100644
index 0000000..82457fd
--- /dev/null
+++ b/tests/localization/test_ja.py
@@ -0,0 +1,68 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "ja"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "数秒 前に"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "数秒 前に"
+
+ d = pendulum.now().subtract(seconds=21)
+ assert d.diff_for_humans(locale=locale) == "21 秒前"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1 分前"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2 分前"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1 時間前"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2 時間前"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1 日前"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2 日前"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1 週間前"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2 週間前"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1 か月前"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2 か月前"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1 年前"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2 年前"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "今から 数秒"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "数秒 後"
+ assert d2.diff_for_humans(d, locale=locale) == "数秒 前"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "数秒"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "数秒"
diff --git a/tests/localization/test_ko.py b/tests/localization/test_ko.py
new file mode 100644
index 0000000..e33ca25
--- /dev/null
+++ b/tests/localization/test_ko.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "ko"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "1초 전"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "2초 전"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1분 전"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2분 전"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1시간 전"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2시간 전"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1일 전"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2일 전"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1주 전"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2주 전"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1개월 전"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2개월 전"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1년 전"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2년 전"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "1초 후"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1초 뒤"
+ assert d2.diff_for_humans(d, locale=locale) == "1초 앞"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1초"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2초"
diff --git a/tests/localization/test_lt.py b/tests/localization/test_lt.py
new file mode 100644
index 0000000..71de1ac
--- /dev/null
+++ b/tests/localization/test_lt.py
@@ -0,0 +1,68 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "lt"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "prieš 1 sekundę"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "prieš 2 sekundes"
+
+ d = pendulum.now().subtract(seconds=21)
+ assert d.diff_for_humans(locale=locale) == "prieš 21 sekundę"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "prieš 1 minutę"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "prieš 2 minutes"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "prieš 1 valandą"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "prieš 2 valandas"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "prieš 1 dieną"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "prieš 2 dienas"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "prieš 1 savaitę"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "prieš 2 savaites"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "prieš 1 mėnesį"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "prieš 2 mėnesius"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "prieš 1 metus"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "prieš 2 metus"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "po 1 sekundės"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "po 1 sekundės"
+ assert d2.diff_for_humans(d, locale=locale) == "1 sekundę nuo dabar"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 sekundė"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekundės"
diff --git a/tests/localization/test_nb.py b/tests/localization/test_nb.py
new file mode 100644
index 0000000..3f696e5
--- /dev/null
+++ b/tests/localization/test_nb.py
@@ -0,0 +1,84 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "nb"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 sekund siden"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 sekunder siden"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 minutt siden"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 minutter siden"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 time siden"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 timer siden"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 dag siden"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 dager siden"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 uke siden"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 uker siden"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 måned siden"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 måneder siden"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 år siden"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 år siden"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "om 1 sekund"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 sekund etter"
+ assert d2.diff_for_humans(d, locale=locale) == "1 sekund før"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekunder"
+
+
+def test_format():
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456)
+ assert d.format("dddd", locale=locale) == "søndag"
+ assert d.format("ddd", locale=locale) == "søn."
+ assert d.format("MMMM", locale=locale) == "august"
+ assert d.format("MMM", locale=locale) == "aug."
+ assert d.format("A", locale=locale) == "a.m."
+ assert d.format("Qo", locale=locale) == "3."
+ assert d.format("Mo", locale=locale) == "8."
+ assert d.format("Do", locale=locale) == "28."
+
+ assert d.format("LT", locale=locale) == "07:03"
+ assert d.format("LTS", locale=locale) == "07:03:06"
+ assert d.format("L", locale=locale) == "28.08.2016"
+ assert d.format("LL", locale=locale) == "28. august 2016"
+ assert d.format("LLL", locale=locale) == "28. august 2016 07:03"
+ assert d.format("LLLL", locale=locale) == "søndag 28. august 2016 07:03"
diff --git a/tests/localization/test_nl.py b/tests/localization/test_nl.py
new file mode 100644
index 0000000..68227ec
--- /dev/null
+++ b/tests/localization/test_nl.py
@@ -0,0 +1,83 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "nl"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "enkele seconden geleden"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "enkele seconden geleden"
+
+ d = pendulum.now().subtract(seconds=22)
+ assert d.diff_for_humans(locale=locale) == "22 seconden geleden"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1 minuut geleden"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2 minuten geleden"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1 uur geleden"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2 uur geleden"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1 dag geleden"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2 dagen geleden"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1 week geleden"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2 weken geleden"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1 maand geleden"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2 maanden geleden"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1 jaar geleden"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2 jaar geleden"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "over enkele seconden"
+
+ d = pendulum.now().add(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "over 1 week"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "enkele seconden later"
+ assert d2.diff_for_humans(d, locale=locale) == "enkele seconden eerder"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "enkele seconden"
+ assert (
+ d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "enkele seconden"
+ )
+
+
+def test_format():
+ d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456)
+ assert d.format("dddd", locale=locale) == "zondag"
+ assert d.format("ddd", locale=locale) == "zo"
+ assert d.format("MMMM", locale=locale) == "augustus"
+ assert d.format("MMM", locale=locale) == "aug."
+ assert d.format("A", locale=locale) == "a.m."
+ assert d.format("Do", locale=locale) == "28e"
diff --git a/tests/localization/test_nn.py b/tests/localization/test_nn.py
new file mode 100644
index 0000000..d4b8099
--- /dev/null
+++ b/tests/localization/test_nn.py
@@ -0,0 +1,84 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "nn"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 sekund sidan"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 sekund sidan"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 minutt sidan"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 minutt sidan"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 time sidan"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 timar sidan"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 dag sidan"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 dagar sidan"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 veke sidan"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 veker sidan"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 månad sidan"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 månadar sidan"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "for 1 år sidan"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "for 2 år sidan"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "om 1 sekund"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 sekund etter"
+ assert d2.diff_for_humans(d, locale=locale) == "1 sekund før"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekund"
+
+
+def test_format():
+ d = pendulum.datetime(2016, 8, 29, 7, 3, 6, 123456)
+ assert d.format("dddd", locale=locale) == "måndag"
+ assert d.format("ddd", locale=locale) == "mån."
+ assert d.format("MMMM", locale=locale) == "august"
+ assert d.format("MMM", locale=locale) == "aug."
+ assert d.format("A", locale=locale) == "formiddag"
+ assert d.format("Qo", locale=locale) == "3."
+ assert d.format("Mo", locale=locale) == "8."
+ assert d.format("Do", locale=locale) == "29."
+
+ assert d.format("LT", locale=locale) == "07:03"
+ assert d.format("LTS", locale=locale) == "07:03:06"
+ assert d.format("L", locale=locale) == "29.08.2016"
+ assert d.format("LL", locale=locale) == "29. august 2016"
+ assert d.format("LLL", locale=locale) == "29. august 2016 07:03"
+ assert d.format("LLLL", locale=locale) == "måndag 29. august 2016 07:03"
diff --git a/tests/localization/test_pl.py b/tests/localization/test_pl.py
new file mode 100644
index 0000000..2b6e707
--- /dev/null
+++ b/tests/localization/test_pl.py
@@ -0,0 +1,109 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "pl"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "kilka sekund temu"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "kilka sekund temu"
+
+ d = pendulum.now().subtract(seconds=20)
+ assert d.diff_for_humans(locale=locale) == "20 sekund temu"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1 minutę temu"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2 minuty temu"
+
+ d = pendulum.now().subtract(minutes=5)
+ assert d.diff_for_humans(locale=locale) == "5 minut temu"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1 godzinę temu"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2 godziny temu"
+
+ d = pendulum.now().subtract(hours=5)
+ assert d.diff_for_humans(locale=locale) == "5 godzin temu"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1 dzień temu"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2 dni temu"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1 tydzień temu"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2 tygodnie temu"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1 miesiąc temu"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2 miesiące temu"
+
+ d = pendulum.now().subtract(months=5)
+ assert d.diff_for_humans(locale=locale) == "5 miesięcy temu"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1 rok temu"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2 lata temu"
+
+ d = pendulum.now().subtract(years=5)
+ assert d.diff_for_humans(locale=locale) == "5 lat temu"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "za kilka sekund"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "kilka sekund po"
+ assert d2.diff_for_humans(d, locale=locale) == "kilka sekund przed"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "kilka sekund"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "kilka sekund"
+
+ d = pendulum.now().add(seconds=20)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "20 sekund po"
+ assert d2.diff_for_humans(d, locale=locale) == "20 sekund przed"
+
+ d = pendulum.now().add(seconds=10)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, True, locale=locale) == "kilka sekund"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "11 sekund"
+
+
+def test_format():
+ d = pendulum.datetime(2016, 8, 29, 7, 3, 6, 123456)
+ assert d.format("dddd", locale=locale) == "poniedziałek"
+ assert d.format("ddd", locale=locale) == "pon."
+ assert d.format("MMMM", locale=locale) == "sierpnia"
+ assert d.format("MMM", locale=locale) == "sie"
+ assert d.format("A", locale=locale) == "AM"
+ assert d.format("Qo", locale=locale) == "3"
+ assert d.format("Mo", locale=locale) == "8"
+ assert d.format("Do", locale=locale) == "29"
+
+ assert d.format("LT", locale=locale) == "07:03"
+ assert d.format("LTS", locale=locale) == "07:03:06"
+ assert d.format("L", locale=locale) == "29.08.2016"
+ assert d.format("LL", locale=locale) == "29 sierpnia 2016"
+ assert d.format("LLL", locale=locale) == "29 sierpnia 2016 07:03"
+ assert d.format("LLLL", locale=locale) == "poniedziałek, 29 sierpnia 2016 07:03"
diff --git a/tests/localization/test_ru.py b/tests/localization/test_ru.py
new file mode 100644
index 0000000..be0e645
--- /dev/null
+++ b/tests/localization/test_ru.py
@@ -0,0 +1,86 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "ru"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "1 секунду назад"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "2 секунды назад"
+
+ d = pendulum.now().subtract(seconds=5)
+ assert d.diff_for_humans(locale=locale) == "5 секунд назад"
+
+ d = pendulum.now().subtract(seconds=21)
+ assert d.diff_for_humans(locale=locale) == "21 секунду назад"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "1 минуту назад"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "2 минуты назад"
+
+ d = pendulum.now().subtract(minutes=5)
+ assert d.diff_for_humans(locale=locale) == "5 минут назад"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "1 час назад"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "2 часа назад"
+
+ d = pendulum.now().subtract(hours=5)
+ assert d.diff_for_humans(locale=locale) == "5 часов назад"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "1 день назад"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "2 дня назад"
+
+ d = pendulum.now().subtract(days=5)
+ assert d.diff_for_humans(locale=locale) == "5 дней назад"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "1 неделю назад"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "2 недели назад"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "1 месяц назад"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "2 месяца назад"
+
+ d = pendulum.now().subtract(months=5)
+ assert d.diff_for_humans(locale=locale) == "5 месяцев назад"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "1 год назад"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "2 года назад"
+
+ d = pendulum.now().subtract(years=5)
+ assert d.diff_for_humans(locale=locale) == "5 лет назад"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "через 1 секунду"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 секунда после"
+ assert d2.diff_for_humans(d, locale=locale) == "1 секунда до"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 секунда"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 секунды"
diff --git a/tests/localization/test_sk.py b/tests/localization/test_sk.py
new file mode 100644
index 0000000..5553e7f
--- /dev/null
+++ b/tests/localization/test_sk.py
@@ -0,0 +1,112 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "sk"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "pred 1 sekundou"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "o 1 sekundu"
+
+ d = pendulum.now().add(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "o 2 sekundy"
+
+ d = pendulum.now().add(seconds=5)
+ assert d.diff_for_humans(locale=locale) == "o 5 sekúnd"
+
+ d = pendulum.now().subtract(seconds=20)
+ assert d.diff_for_humans(locale=locale) == "pred 20 sekundami"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "pred 1 minútou"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "pred 2 minútami"
+
+ d = pendulum.now().add(minutes=5)
+ assert d.diff_for_humans(locale=locale) == "o 5 minút"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "pred 1 hodinou"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "pred 2 hodinami"
+
+ d = pendulum.now().subtract(hours=5)
+ assert d.diff_for_humans(locale=locale) == "pred 5 hodinami"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "pred 1 dňom"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "pred 2 dňami"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "pred 1 týždňom"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "pred 2 týždňami"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "pred 1 mesiacom"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "pred 2 mesiacmi"
+
+ d = pendulum.now().subtract(months=5)
+ assert d.diff_for_humans(locale=locale) == "pred 5 mesiacmi"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "pred 1 rokom"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "pred 2 rokmi"
+
+ d = pendulum.now().subtract(years=5)
+ assert d.diff_for_humans(locale=locale) == "pred 5 rokmi"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 sekunda po"
+ assert d2.diff_for_humans(d, locale=locale) == "1 sekunda pred"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 sekunda"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekundy"
+
+ d = pendulum.now().add(seconds=20)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "20 sekúnd po"
+ assert d2.diff_for_humans(d, locale=locale) == "20 sekúnd pred"
+
+ d = pendulum.now().add(seconds=10)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, True, locale=locale) == "10 sekúnd"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "11 sekúnd"
+
+
+def test_format():
+ d = pendulum.datetime(2016, 8, 29, 7, 3, 6, 123456)
+ assert d.format("dddd", locale=locale) == "pondelok"
+ assert d.format("ddd", locale=locale) == "po"
+ assert d.format("MMMM", locale=locale) == "augusta"
+ assert d.format("MMM", locale=locale) == "aug"
+ assert d.format("A", locale=locale) == "AM"
+ assert d.format("Qo", locale=locale) == "3"
+ assert d.format("Mo", locale=locale) == "8"
+ assert d.format("Do", locale=locale) == "29"
+
+ assert d.format("LT", locale=locale) == "07:03"
+ assert d.format("LTS", locale=locale) == "07:03:06"
+ assert d.format("L", locale=locale) == "29.08.2016"
+ assert d.format("LL", locale=locale) == "29. augusta 2016"
+ assert d.format("LLL", locale=locale) == "29. augusta 2016 07:03"
+ assert d.format("LLLL", locale=locale) == "pondelok, 29. augusta 2016 07:03"
diff --git a/tests/localization/test_sv.py b/tests/localization/test_sv.py
new file mode 100644
index 0000000..e0e4e65
--- /dev/null
+++ b/tests/localization/test_sv.py
@@ -0,0 +1,86 @@
+from __future__ import annotations
+
+import pendulum
+
+locale = "sv"
+
+
+def test_diff_for_humans():
+ with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True):
+ diff_for_humans()
+
+
+def diff_for_humans():
+ d = pendulum.now().subtract(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "för 1 sekund sedan"
+
+ d = pendulum.now().subtract(seconds=2)
+ assert d.diff_for_humans(locale=locale) == "för 2 sekunder sedan"
+
+ d = pendulum.now().subtract(seconds=5)
+ assert d.diff_for_humans(locale=locale) == "för 5 sekunder sedan"
+
+ d = pendulum.now().subtract(seconds=21)
+ assert d.diff_for_humans(locale=locale) == "för 21 sekunder sedan"
+
+ d = pendulum.now().subtract(minutes=1)
+ assert d.diff_for_humans(locale=locale) == "för 1 minut sedan"
+
+ d = pendulum.now().subtract(minutes=2)
+ assert d.diff_for_humans(locale=locale) == "för 2 minuter sedan"
+
+ d = pendulum.now().subtract(minutes=5)
+ assert d.diff_for_humans(locale=locale) == "för 5 minuter sedan"
+
+ d = pendulum.now().subtract(hours=1)
+ assert d.diff_for_humans(locale=locale) == "för 1 timme sedan"
+
+ d = pendulum.now().subtract(hours=2)
+ assert d.diff_for_humans(locale=locale) == "för 2 timmar sedan"
+
+ d = pendulum.now().subtract(hours=5)
+ assert d.diff_for_humans(locale=locale) == "för 5 timmar sedan"
+
+ d = pendulum.now().subtract(days=1)
+ assert d.diff_for_humans(locale=locale) == "för 1 dag sedan"
+
+ d = pendulum.now().subtract(days=2)
+ assert d.diff_for_humans(locale=locale) == "för 2 dagar sedan"
+
+ d = pendulum.now().subtract(days=5)
+ assert d.diff_for_humans(locale=locale) == "för 5 dagar sedan"
+
+ d = pendulum.now().subtract(weeks=1)
+ assert d.diff_for_humans(locale=locale) == "för 1 vecka sedan"
+
+ d = pendulum.now().subtract(weeks=2)
+ assert d.diff_for_humans(locale=locale) == "för 2 veckor sedan"
+
+ d = pendulum.now().subtract(months=1)
+ assert d.diff_for_humans(locale=locale) == "för 1 månad sedan"
+
+ d = pendulum.now().subtract(months=2)
+ assert d.diff_for_humans(locale=locale) == "för 2 månader sedan"
+
+ d = pendulum.now().subtract(months=5)
+ assert d.diff_for_humans(locale=locale) == "för 5 månader sedan"
+
+ d = pendulum.now().subtract(years=1)
+ assert d.diff_for_humans(locale=locale) == "för 1 år sedan"
+
+ d = pendulum.now().subtract(years=2)
+ assert d.diff_for_humans(locale=locale) == "för 2 år sedan"
+
+ d = pendulum.now().subtract(years=5)
+ assert d.diff_for_humans(locale=locale) == "för 5 år sedan"
+
+ d = pendulum.now().add(seconds=1)
+ assert d.diff_for_humans(locale=locale) == "om 1 sekund"
+
+ d = pendulum.now().add(seconds=1)
+ d2 = pendulum.now()
+ assert d.diff_for_humans(d2, locale=locale) == "1 sekund efter"
+ assert d2.diff_for_humans(d, locale=locale) == "1 sekund innan"
+
+ assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund"
+ assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekunder"
diff --git a/tests/parsing/__init__.py b/tests/parsing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/parsing/__init__.py
diff --git a/tests/parsing/test_parse_iso8601.py b/tests/parsing/test_parse_iso8601.py
new file mode 100644
index 0000000..0047791
--- /dev/null
+++ b/tests/parsing/test_parse_iso8601.py
@@ -0,0 +1,465 @@
+from __future__ import annotations
+
+from datetime import date
+from datetime import datetime
+from datetime import time
+
+import pytest
+
+from pendulum.parsing import parse_iso8601
+
+try:
+ from pendulum.parsing._extension import TZFixedOffset as FixedTimezone
+except ImportError:
+ from pendulum.tz.timezone import FixedTimezone
+
+
+def test_parse_iso8601():
+ # Date
+ assert date(2016, 1, 1) == parse_iso8601("2016")
+ assert date(2016, 10, 1) == parse_iso8601("2016-10")
+ assert date(2016, 10, 6) == parse_iso8601("2016-10-06")
+ assert date(2016, 10, 6) == parse_iso8601("20161006")
+
+ # Time
+ assert time(20, 16, 10, 0) == parse_iso8601("201610")
+
+ # Datetime
+ assert datetime(2016, 10, 6, 12, 34, 56, 123456) == parse_iso8601(
+ "2016-10-06T12:34:56.123456"
+ )
+ assert datetime(2016, 10, 6, 12, 34, 56, 123000) == parse_iso8601(
+ "2016-10-06T12:34:56.123"
+ )
+ assert datetime(2016, 10, 6, 12, 34, 56, 123) == parse_iso8601(
+ "2016-10-06T12:34:56.000123"
+ )
+ assert datetime(2016, 10, 6, 12, 0, 0, 0) == parse_iso8601("2016-10-06T12")
+ assert datetime(2016, 10, 6, 12, 34, 56, 0) == parse_iso8601("2016-10-06T123456")
+ assert datetime(2016, 10, 6, 12, 34, 56, 123456) == parse_iso8601(
+ "2016-10-06T123456.123456"
+ )
+ assert datetime(2016, 10, 6, 12, 34, 56, 123456) == parse_iso8601(
+ "20161006T123456.123456"
+ )
+ assert datetime(2016, 10, 6, 12, 34, 56, 123456) == parse_iso8601(
+ "20161006 123456.123456"
+ )
+
+ # Datetime with offset
+ assert datetime(
+ 2016, 10, 6, 12, 34, 56, 123456, FixedTimezone(19800)
+ ) == parse_iso8601("2016-10-06T12:34:56.123456+05:30")
+ assert datetime(
+ 2016, 10, 6, 12, 34, 56, 123456, FixedTimezone(19800)
+ ) == parse_iso8601("2016-10-06T12:34:56.123456+0530")
+ assert datetime(
+ 2016, 10, 6, 12, 34, 56, 123456, FixedTimezone(-19800)
+ ) == parse_iso8601("2016-10-06T12:34:56.123456-05:30")
+ assert datetime(
+ 2016, 10, 6, 12, 34, 56, 123456, FixedTimezone(-19800)
+ ) == parse_iso8601("2016-10-06T12:34:56.123456-0530")
+ assert datetime(
+ 2016, 10, 6, 12, 34, 56, 123456, FixedTimezone(18000)
+ ) == parse_iso8601("2016-10-06T12:34:56.123456+05")
+ assert datetime(
+ 2016, 10, 6, 12, 34, 56, 123456, FixedTimezone(-18000)
+ ) == parse_iso8601("2016-10-06T12:34:56.123456-05")
+ assert datetime(
+ 2016, 10, 6, 12, 34, 56, 123456, FixedTimezone(-18000)
+ ) == parse_iso8601("20161006T123456,123456-05")
+ assert datetime(
+ 2016, 10, 6, 12, 34, 56, 123456, FixedTimezone(+19800)
+ ) == parse_iso8601("2016-10-06T12:34:56.123456789+05:30")
+
+ # Ordinal date
+ assert date(2012, 1, 7) == parse_iso8601("2012-007")
+ assert date(2012, 1, 7) == parse_iso8601("2012007")
+ assert date(2017, 3, 20) == parse_iso8601("2017-079")
+
+ # Week date
+ assert date(2012, 1, 30) == parse_iso8601("2012-W05")
+ assert date(2008, 9, 27) == parse_iso8601("2008-W39-6")
+ assert date(2010, 1, 3) == parse_iso8601("2009-W53-7")
+ assert date(2008, 12, 29) == parse_iso8601("2009-W01-1")
+
+ # Week date wth time
+ assert datetime(2008, 9, 27, 9, 0, 0, 0) == parse_iso8601("2008-W39-6T09")
+
+
+def test_parse_ios8601_invalid():
+ # Invalid month
+ with pytest.raises(ValueError):
+ parse_iso8601("20161306T123456")
+
+ # Invalid day
+ with pytest.raises(ValueError):
+ parse_iso8601("20161033T123456")
+
+ # Invalid day for month
+ with pytest.raises(ValueError):
+ parse_iso8601("20161131T123456")
+
+ # Invalid hour
+ with pytest.raises(ValueError):
+ parse_iso8601("20161006T243456")
+
+ # Invalid minute
+ with pytest.raises(ValueError):
+ parse_iso8601("20161006T126056")
+
+ # Invalid second
+ with pytest.raises(ValueError):
+ parse_iso8601("20161006T123460")
+
+ # Extraneous separator
+ with pytest.raises(ValueError):
+ parse_iso8601("20140203 04:05:.123456")
+ with pytest.raises(ValueError):
+ parse_iso8601("2009-05-19 14:")
+
+ # Invalid ordinal
+ with pytest.raises(ValueError):
+ parse_iso8601("2009367")
+ with pytest.raises(ValueError):
+ parse_iso8601("2009-367")
+ with pytest.raises(ValueError):
+ parse_iso8601("2015-366")
+ with pytest.raises(ValueError):
+ parse_iso8601("2015-000")
+
+ # Invalid date
+ with pytest.raises(ValueError):
+ parse_iso8601("2009-")
+
+ # Invalid time
+ with pytest.raises(ValueError):
+ parse_iso8601("2009-05-19T14:3924")
+ with pytest.raises(ValueError):
+ parse_iso8601("2010-02-18T16.5:23.35:48")
+ with pytest.raises(ValueError):
+ parse_iso8601("2010-02-18T16:23.35:48.45")
+ with pytest.raises(ValueError):
+ parse_iso8601("2010-02-18T16:23.33.600")
+
+ # Invalid offset
+ with pytest.raises(ValueError):
+ parse_iso8601("2009-05-19 14:39:22+063")
+ with pytest.raises(ValueError):
+ parse_iso8601("2009-05-19 14:39:22+06a00")
+ with pytest.raises(ValueError):
+ parse_iso8601("2009-05-19 14:39:22+0:6:00")
+
+ # Missing time separator
+ with pytest.raises(ValueError):
+ parse_iso8601("2009-05-1914:39")
+
+ # Invalid week date
+ with pytest.raises(ValueError):
+ parse_iso8601("2012-W63")
+ with pytest.raises(ValueError):
+ parse_iso8601("2012-W12-9")
+ with pytest.raises(ValueError):
+ parse_iso8601("2012W12-3") # Missing separator
+ with pytest.raises(ValueError):
+ parse_iso8601("2012-W123") # Missing separator
+
+
+def test_parse_ios8601_duration():
+ text = "P2Y3M4DT5H6M7S"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 2
+ assert parsed.months == 3
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 4
+ assert parsed.hours == 5
+ assert parsed.minutes == 6
+ assert parsed.remaining_seconds == 7
+ assert parsed.microseconds == 0
+
+ text = "P1Y2M3DT4H5M6.5S"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 4
+ assert parsed.minutes == 5
+ assert parsed.remaining_seconds == 6
+ assert parsed.microseconds == 500000
+
+ text = "P1Y2M3DT4H5M6,5S"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 4
+ assert parsed.minutes == 5
+ assert parsed.remaining_seconds == 6
+ assert parsed.microseconds == 500000
+
+ text = "P1Y2M3D"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1Y2M3.5D"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1Y2M3,5D"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "PT4H54M6.5S"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 4
+ assert parsed.minutes == 54
+ assert parsed.remaining_seconds == 6
+ assert parsed.microseconds == 500000
+
+ text = "PT4H54M6,5S"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 4
+ assert parsed.minutes == 54
+ assert parsed.remaining_seconds == 6
+ assert parsed.microseconds == 500000
+
+ text = "P1Y"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1.5Y"
+ with pytest.raises(ValueError):
+ parse_iso8601(text)
+
+ text = "P1,5Y"
+ with pytest.raises(ValueError):
+ parse_iso8601(text)
+
+ text = "P1M"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 1
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1.5M"
+ with pytest.raises(ValueError):
+ parse_iso8601(text)
+
+ text = "P1,5M"
+ with pytest.raises(ValueError):
+ parse_iso8601(text)
+
+ text = "P1W"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 1
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1.5W"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 1
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1,5W"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 1
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1D"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 1
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1.5D"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 1
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1,5D"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 1
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "PT1H"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 1
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "PT1.5H"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 1
+ assert parsed.minutes == 30
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "PT1,5H"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 1
+ assert parsed.minutes == 30
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ # Double digit with 0
+ text = "P2Y30M4DT5H6M7S"
+ parsed = parse_iso8601(text)
+
+ assert parsed.years == 2
+ assert parsed.months == 30
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 4
+ assert parsed.hours == 5
+ assert parsed.minutes == 6
+ assert parsed.remaining_seconds == 7
+ assert parsed.microseconds == 0
+
+ # No P operator
+ with pytest.raises(ValueError):
+ parse_iso8601("2Y3M4DT5H6M7S")
+
+ # Week and other units combined
+ with pytest.raises(ValueError):
+ parse_iso8601("P1Y2W")
+
+ # Invalid units order
+ with pytest.raises(ValueError):
+ parse_iso8601("P1S")
+
+ with pytest.raises(ValueError):
+ parse_iso8601("P1D1S")
+
+ with pytest.raises(ValueError):
+ parse_iso8601("1Y2M3D1SPT1M")
+
+ with pytest.raises(ValueError):
+ parse_iso8601("P1Y2M3D2MT1S")
+
+ with pytest.raises(ValueError):
+ parse_iso8601("P2M3D1ST1Y1M")
+
+ with pytest.raises(ValueError):
+ parse_iso8601("P1Y2M2MT3D1S")
+
+ with pytest.raises(ValueError):
+ parse_iso8601("P1D1Y1M")
+
+ with pytest.raises(ValueError):
+ parse_iso8601("PT1S1H")
+
+ # Invalid
+ with pytest.raises(ValueError):
+ parse_iso8601("P1Dasdfasdf")
+
+ # Invalid fractional
+ with pytest.raises(ValueError):
+ parse_iso8601("P2Y3M4DT5.5H6M7S")
diff --git a/tests/parsing/test_parsing.py b/tests/parsing/test_parsing.py
new file mode 100644
index 0000000..35dcf86
--- /dev/null
+++ b/tests/parsing/test_parsing.py
@@ -0,0 +1,687 @@
+from __future__ import annotations
+
+import datetime
+
+import pytest
+
+import pendulum
+
+from pendulum.parsing import ParserError
+from pendulum.parsing import parse
+
+
+def test_y():
+ text = "2016"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 1
+ assert parsed.day == 1
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_ym():
+ text = "2016-10"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 1
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_ymd():
+ text = "2016-10-06"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 6
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_ymd_one_character():
+ text = "2016-2-6"
+
+ parsed = parse(text, strict=False)
+
+ assert parsed.year == 2016
+ assert parsed.month == 2
+ assert parsed.day == 6
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_ymd_hms():
+ text = "2016-10-06 12:34:56"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 6
+ assert parsed.hour == 12
+ assert parsed.minute == 34
+ assert parsed.second == 56
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2016-10-06 12:34:56.123456"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 6
+ assert parsed.hour == 12
+ assert parsed.minute == 34
+ assert parsed.second == 56
+ assert parsed.microsecond == 123456
+ assert parsed.tzinfo is None
+
+
+def test_rfc_3339():
+ text = "2016-10-06T12:34:56+05:30"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 6
+ assert parsed.hour == 12
+ assert parsed.minute == 34
+ assert parsed.second == 56
+ assert parsed.microsecond == 0
+ assert parsed.utcoffset().total_seconds() == 19800
+
+
+def test_rfc_3339_extended():
+ text = "2016-10-06T12:34:56.123456+05:30"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 6
+ assert parsed.hour == 12
+ assert parsed.minute == 34
+ assert parsed.second == 56
+ assert parsed.microsecond == 123456
+ assert parsed.utcoffset().total_seconds() == 19800
+
+ text = "2016-10-06T12:34:56.000123+05:30"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 6
+ assert parsed.hour == 12
+ assert parsed.minute == 34
+ assert parsed.second == 56
+ assert parsed.microsecond == 123
+ assert parsed.utcoffset().total_seconds() == 19800
+
+
+def test_rfc_3339_extended_nanoseconds():
+ text = "2016-10-06T12:34:56.123456789+05:30"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 6
+ assert parsed.hour == 12
+ assert parsed.minute == 34
+ assert parsed.second == 56
+ assert parsed.microsecond == 123456
+ assert parsed.utcoffset().total_seconds() == 19800
+
+
+def test_iso_8601_date():
+ text = "2012"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 1
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012-05-03"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 5
+ assert parsed.day == 3
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "20120503"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 5
+ assert parsed.day == 3
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012-05"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 5
+ assert parsed.day == 1
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_iso8601_datetime():
+ text = "2016-10-01T14"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 1
+ assert parsed.hour == 14
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2016-10-01T14:30"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 1
+ assert parsed.hour == 14
+ assert parsed.minute == 30
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "20161001T14"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 1
+ assert parsed.hour == 14
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "20161001T1430"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 1
+ assert parsed.hour == 14
+ assert parsed.minute == 30
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "20161001T1430+0530"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 1
+ assert parsed.hour == 14
+ assert parsed.minute == 30
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.utcoffset().total_seconds() == 19800
+
+ text = "20161001T1430,4+0530"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 10
+ assert parsed.day == 1
+ assert parsed.hour == 14
+ assert parsed.minute == 30
+ assert parsed.second == 0
+ assert parsed.microsecond == 400000
+ assert parsed.utcoffset().total_seconds() == 19800
+
+ text = "2008-09-03T20:56:35.450686+01"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2008
+ assert parsed.month == 9
+ assert parsed.day == 3
+ assert parsed.hour == 20
+ assert parsed.minute == 56
+ assert parsed.second == 35
+ assert parsed.microsecond == 450686
+ assert parsed.utcoffset().total_seconds() == 3600
+
+
+def test_iso8601_week_number():
+ text = "2012-W05"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 30
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012W05"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 30
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ # Long Year
+ text = "2015W53"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2015
+ assert parsed.month == 12
+ assert parsed.day == 28
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012-W05-5"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 2
+ assert parsed.day == 3
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012W055"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 2
+ assert parsed.day == 3
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2009-W53-7"
+ parsed = parse(text)
+
+ assert parsed.year == 2010
+ assert parsed.month == 1
+ assert parsed.day == 3
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2009-W01-1"
+ parsed = parse(text)
+
+ assert parsed.year == 2008
+ assert parsed.month == 12
+ assert parsed.day == 29
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_iso8601_week_number_with_time():
+ text = "2012-W05T09"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 30
+ assert parsed.hour == 9
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012W05T09"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 30
+ assert parsed.hour == 9
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012-W05-5T09"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 2
+ assert parsed.day == 3
+ assert parsed.hour == 9
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012W055T09"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 2
+ assert parsed.day == 3
+ assert parsed.hour == 9
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_iso8601_ordinal():
+ text = "2012-007"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 7
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "2012007"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 7
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_iso8601_time():
+ now = pendulum.datetime(2015, 11, 12)
+
+ text = "201205"
+
+ parsed = parse(text, now=now)
+
+ assert parsed.year == 2015
+ assert parsed.month == 11
+ assert parsed.day == 12
+ assert parsed.hour == 20
+ assert parsed.minute == 12
+ assert parsed.second == 5
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "20:12:05"
+
+ parsed = parse(text, now=now)
+
+ assert parsed.year == 2015
+ assert parsed.month == 11
+ assert parsed.day == 12
+ assert parsed.hour == 20
+ assert parsed.minute == 12
+ assert parsed.second == 5
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "20:12:05.123456"
+
+ parsed = parse(text, now=now)
+
+ assert parsed.year == 2015
+ assert parsed.month == 11
+ assert parsed.day == 12
+ assert parsed.hour == 20
+ assert parsed.minute == 12
+ assert parsed.second == 5
+ assert parsed.microsecond == 123456
+ assert parsed.tzinfo is None
+
+
+def test_iso8601_ordinal_invalid():
+ text = "2012-007-05"
+
+ with pytest.raises(ParserError):
+ parse(text)
+
+
+def test_exact():
+ text = "2012"
+
+ parsed = parse(text, exact=True)
+
+ assert isinstance(parsed, datetime.date)
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 1
+
+ text = "2012-03"
+
+ parsed = parse(text, exact=True)
+
+ assert isinstance(parsed, datetime.date)
+ assert parsed.year == 2012
+ assert parsed.month == 3
+ assert parsed.day == 1
+
+ text = "2012-03-13"
+
+ parsed = parse(text, exact=True)
+
+ assert isinstance(parsed, datetime.date)
+ assert parsed.year == 2012
+ assert parsed.month == 3
+ assert parsed.day == 13
+
+ text = "2012W055"
+
+ parsed = parse(text, exact=True)
+
+ assert isinstance(parsed, datetime.date)
+ assert parsed.year == 2012
+ assert parsed.month == 2
+ assert parsed.day == 3
+
+ text = "2012007"
+
+ parsed = parse(text, exact=True)
+
+ assert isinstance(parsed, datetime.date)
+ assert parsed.year == 2012
+ assert parsed.month == 1
+ assert parsed.day == 7
+
+ text = "20:12:05"
+
+ parsed = parse(text, exact=True)
+
+ assert isinstance(parsed, datetime.time)
+ assert parsed.hour == 20
+ assert parsed.minute == 12
+ assert parsed.second == 5
+ assert parsed.microsecond == 0
+
+
+def test_edge_cases():
+ text = "2013-11-1"
+
+ parsed = parse(text, strict=False)
+ assert parsed.year == 2013
+ assert parsed.month == 11
+ assert parsed.day == 1
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "10-01-01"
+
+ parsed = parse(text, strict=False)
+ assert parsed.year == 2010
+ assert parsed.month == 1
+ assert parsed.day == 1
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "31-01-01"
+
+ parsed = parse(text, strict=False)
+ assert parsed.year == 2031
+ assert parsed.month == 1
+ assert parsed.day == 1
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+ text = "32-01-01"
+
+ parsed = parse(text, strict=False)
+ assert parsed.year == 2032
+ assert parsed.month == 1
+ assert parsed.day == 1
+ assert parsed.hour == 0
+ assert parsed.minute == 0
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_strict():
+ text = "4 Aug 2015 - 11:20 PM"
+
+ with pytest.raises(ParserError):
+ parse(text)
+
+ parsed = parse(text, strict=False)
+ assert parsed.year == 2015
+ assert parsed.month == 8
+ assert parsed.day == 4
+ assert parsed.hour == 23
+ assert parsed.minute == 20
+ assert parsed.second == 0
+ assert parsed.microsecond == 0
+ assert parsed.tzinfo is None
+
+
+def test_invalid():
+ text = "201610T"
+
+ with pytest.raises(ParserError):
+ parse(text)
+
+ text = "2012-W54"
+
+ with pytest.raises(ParserError):
+ parse(text)
+
+ text = "2012-W13-8"
+
+ with pytest.raises(ParserError):
+ parse(text)
+
+ # W53 in normal year (not long)
+ text = "2017W53"
+
+ with pytest.raises(ParserError):
+ parse(text)
+
+
+def test_exif_edge_case():
+ text = "2016:12:26 15:45:28"
+
+ parsed = parse(text)
+
+ assert parsed.year == 2016
+ assert parsed.month == 12
+ assert parsed.day == 26
+ assert parsed.hour == 15
+ assert parsed.minute == 45
+ assert parsed.second == 28
diff --git a/tests/parsing/test_parsing_duration.py b/tests/parsing/test_parsing_duration.py
new file mode 100644
index 0000000..ab8b992
--- /dev/null
+++ b/tests/parsing/test_parsing_duration.py
@@ -0,0 +1,298 @@
+from __future__ import annotations
+
+import pytest
+
+from pendulum.parsing import ParserError
+from pendulum.parsing import parse
+
+
+def test_parse_duration():
+ text = "P2Y3M4DT5H6M7S"
+ parsed = parse(text)
+
+ assert parsed.years == 2
+ assert parsed.months == 3
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 4
+ assert parsed.hours == 5
+ assert parsed.minutes == 6
+ assert parsed.remaining_seconds == 7
+ assert parsed.microseconds == 0
+
+ text = "P1Y2M3DT4H5M6.5S"
+ parsed = parse(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 4
+ assert parsed.minutes == 5
+ assert parsed.remaining_seconds == 6
+ assert parsed.microseconds == 500000
+
+ text = "P1Y2M3DT4H5M6,5S"
+ parsed = parse(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 4
+ assert parsed.minutes == 5
+ assert parsed.remaining_seconds == 6
+ assert parsed.microseconds == 500000
+
+ text = "P1Y2M3D"
+ parsed = parse(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1Y2M3.5D"
+ parsed = parse(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1Y2M3,5D"
+ parsed = parse(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 2
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "PT4H54M6.5S"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 4
+ assert parsed.minutes == 54
+ assert parsed.remaining_seconds == 6
+ assert parsed.microseconds == 500000
+
+ text = "PT4H54M6,5S"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 4
+ assert parsed.minutes == 54
+ assert parsed.remaining_seconds == 6
+ assert parsed.microseconds == 500000
+
+ text = "P1Y"
+ parsed = parse(text)
+
+ assert parsed.years == 1
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1.5Y"
+ with pytest.raises(ParserError):
+ parse(text)
+
+ text = "P1,5Y"
+ with pytest.raises(ParserError):
+ parse(text)
+
+ text = "P1M"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 1
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1.5M"
+ with pytest.raises(ParserError):
+ parse(text)
+
+ text = "P1,5M"
+ with pytest.raises(ParserError):
+ parse(text)
+
+ text = "P1W"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 1
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1.5W"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 1
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1,5W"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 1
+ assert parsed.remaining_days == 3
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1D"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 1
+ assert parsed.hours == 0
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1.5D"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 1
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "P1,5D"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 1
+ assert parsed.hours == 12
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "PT1H"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 1
+ assert parsed.minutes == 0
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "PT1.5H"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 1
+ assert parsed.minutes == 30
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+ text = "PT1,5H"
+ parsed = parse(text)
+
+ assert parsed.years == 0
+ assert parsed.months == 0
+ assert parsed.weeks == 0
+ assert parsed.remaining_days == 0
+ assert parsed.hours == 1
+ assert parsed.minutes == 30
+ assert parsed.remaining_seconds == 0
+ assert parsed.microseconds == 0
+
+
+def test_parse_duration_no_operator():
+ with pytest.raises(ParserError):
+ parse("2Y3M4DT5H6M7S")
+
+
+def test_parse_duration_weeks_combined():
+ with pytest.raises(ParserError):
+ parse("P1Y2W")
+
+
+def test_parse_duration_invalid_order():
+ with pytest.raises(ParserError):
+ parse("P1S")
+
+ with pytest.raises(ParserError):
+ parse("P1D1S")
+
+ with pytest.raises(ParserError):
+ parse("1Y2M3D1SPT1M")
+
+ with pytest.raises(ParserError):
+ parse("P1Y2M3D2MT1S")
+
+ with pytest.raises(ParserError):
+ parse("P2M3D1ST1Y1M")
+
+ with pytest.raises(ParserError):
+ parse("P1Y2M2MT3D1S")
+
+ with pytest.raises(ParserError):
+ parse("P1D1Y1M")
+
+ with pytest.raises(ParserError):
+ parse("PT1S1H")
+
+
+def test_parse_duration_invalid():
+ with pytest.raises(ParserError):
+ parse("P1Dasdfasdf")
+
+
+def test_parse_duration_fraction_only_allowed_on_last_component():
+ with pytest.raises(ParserError):
+ parse("P2Y3M4DT5.5H6M7S")
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
new file mode 100644
index 0000000..e3daeac
--- /dev/null
+++ b/tests/test_helpers.py
@@ -0,0 +1,179 @@
+from __future__ import annotations
+
+from datetime import datetime
+
+import pytest
+import pytz
+
+import pendulum
+
+from pendulum import timezone
+from pendulum.helpers import days_in_year
+from pendulum.helpers import precise_diff
+from pendulum.helpers import week_day
+
+
+def assert_diff(
+ diff, years=0, months=0, days=0, hours=0, minutes=0, seconds=0, microseconds=0
+):
+ assert diff.years == years
+ assert diff.months == months
+ assert diff.days == days
+ assert diff.hours == hours
+ assert diff.minutes == minutes
+ assert diff.seconds == seconds
+ assert diff.microseconds == microseconds
+
+
+def test_precise_diff():
+ dt1 = datetime(2003, 3, 1, 0, 0, 0)
+ dt2 = datetime(2003, 1, 31, 23, 59, 59)
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(diff, months=-1, seconds=-1)
+
+ diff = precise_diff(dt2, dt1)
+ assert_diff(diff, months=1, seconds=1)
+
+ dt1 = datetime(2012, 3, 1, 0, 0, 0)
+ dt2 = datetime(2012, 1, 31, 23, 59, 59)
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(diff, months=-1, seconds=-1)
+
+ diff = precise_diff(dt2, dt1)
+ assert_diff(diff, months=1, seconds=1)
+
+ dt1 = datetime(2001, 1, 1)
+ dt2 = datetime(2003, 9, 17, 20, 54, 47, 282310)
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(
+ diff,
+ years=2,
+ months=8,
+ days=16,
+ hours=20,
+ minutes=54,
+ seconds=47,
+ microseconds=282310,
+ )
+
+ dt1 = datetime(2017, 2, 17, 16, 5, 45, 123456)
+ dt2 = datetime(2018, 2, 17, 16, 5, 45, 123256)
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(
+ diff, months=11, days=30, hours=23, minutes=59, seconds=59, microseconds=999800
+ )
+
+ # DST
+ tz = timezone("America/Toronto")
+ dt1 = tz.datetime(2017, 3, 7)
+ dt2 = tz.datetime(2017, 3, 13)
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(diff, days=6, hours=0)
+
+
+def test_precise_diff_timezone():
+ paris = pendulum.timezone("Europe/Paris")
+ toronto = pendulum.timezone("America/Toronto")
+
+ dt1 = paris.datetime(2013, 3, 31, 1, 30)
+ dt2 = paris.datetime(2013, 4, 1, 1, 30)
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(diff, days=1, hours=0)
+
+ dt2 = toronto.datetime(2013, 4, 1, 1, 30)
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(diff, days=1, hours=5)
+
+ # pytz
+ paris = pytz.timezone("Europe/Paris")
+ toronto = pytz.timezone("America/Toronto")
+
+ dt1 = paris.localize(datetime(2013, 3, 31, 1, 30))
+ dt2 = paris.localize(datetime(2013, 4, 1, 1, 30))
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(diff, days=1, hours=0)
+
+ dt2 = toronto.localize(datetime(2013, 4, 1, 1, 30))
+
+ diff = precise_diff(dt1, dt2)
+ assert_diff(diff, days=1, hours=5)
+
+ # Issue238
+ dt1 = timezone("UTC").datetime(2018, 6, 20, 1, 30)
+ dt2 = timezone("Europe/Paris").datetime(2018, 6, 20, 3, 40) # UTC+2
+ diff = precise_diff(dt1, dt2)
+ assert_diff(diff, minutes=10)
+
+
+def test_week_day():
+ assert week_day(2017, 6, 2) == 5
+ assert week_day(2017, 1, 1) == 7
+
+
+def test_days_in_years():
+ assert days_in_year(2017) == 365
+ assert days_in_year(2016) == 366
+
+
+def test_locale():
+ dt = pendulum.datetime(2000, 11, 10, 12, 34, 56, 123456)
+ pendulum.set_locale("fr")
+
+ assert pendulum.get_locale() == "fr"
+
+ assert dt.format("MMMM") == "novembre"
+ assert dt.date().format("MMMM") == "novembre"
+
+
+def test_set_locale_invalid():
+ with pytest.raises(ValueError):
+ pendulum.set_locale("invalid")
+
+
+@pytest.mark.parametrize(
+ "locale", ["DE", "pt-BR", "pt-br", "PT-br", "PT-BR", "pt_br", "PT_BR", "PT_BR"]
+)
+def test_set_locale_malformed_locale(locale):
+ pendulum.set_locale(locale)
+
+ pendulum.set_locale("en")
+
+
+def test_week_starts_at():
+ pendulum.week_starts_at(pendulum.SATURDAY)
+
+ dt = pendulum.now().start_of("week")
+ assert dt.day_of_week == pendulum.SATURDAY
+ assert dt.date().day_of_week == pendulum.SATURDAY
+
+
+def test_week_starts_at_invalid_value():
+ with pytest.raises(ValueError):
+ pendulum.week_starts_at(-1)
+
+ with pytest.raises(ValueError):
+ pendulum.week_starts_at(11)
+
+
+def test_week_ends_at():
+ pendulum.week_ends_at(pendulum.SATURDAY)
+
+ dt = pendulum.now().end_of("week")
+ assert dt.day_of_week == pendulum.SATURDAY
+ assert dt.date().day_of_week == pendulum.SATURDAY
+
+
+def test_week_ends_at_invalid_value():
+ with pytest.raises(ValueError):
+ pendulum.week_ends_at(-1)
+
+ with pytest.raises(ValueError):
+ pendulum.week_ends_at(11)
diff --git a/tests/test_main.py b/tests/test_main.py
new file mode 100644
index 0000000..1710bf2
--- /dev/null
+++ b/tests/test_main.py
@@ -0,0 +1,13 @@
+from __future__ import annotations
+
+import pytz
+
+from pendulum import _safe_timezone
+from pendulum.tz.timezone import Timezone
+
+
+def test_safe_timezone_with_tzinfo_objects():
+ tz = _safe_timezone(pytz.timezone("Europe/Paris"))
+
+ assert isinstance(tz, Timezone)
+ assert tz.name == "Europe/Paris"
diff --git a/tests/test_parsing.py b/tests/test_parsing.py
new file mode 100644
index 0000000..0e5308c
--- /dev/null
+++ b/tests/test_parsing.py
@@ -0,0 +1,141 @@
+from __future__ import annotations
+
+import pendulum
+
+from tests.conftest import assert_date
+from tests.conftest import assert_datetime
+from tests.conftest import assert_duration
+from tests.conftest import assert_time
+
+
+def test_parse():
+ text = "2016-10-16T12:34:56.123456+01:30"
+
+ dt = pendulum.parse(text)
+
+ assert isinstance(dt, pendulum.DateTime)
+ assert_datetime(dt, 2016, 10, 16, 12, 34, 56, 123456)
+ assert dt.tz.name == "+01:30"
+ assert dt.offset == 5400
+
+ text = "2016-10-16"
+
+ dt = pendulum.parse(text)
+
+ assert isinstance(dt, pendulum.DateTime)
+ assert_datetime(dt, 2016, 10, 16, 0, 0, 0, 0)
+ assert dt.offset == 0
+
+ with pendulum.travel_to(pendulum.datetime(2015, 11, 12), freeze=True):
+ text = "12:34:56.123456"
+
+ dt = pendulum.parse(text)
+
+ assert isinstance(dt, pendulum.DateTime)
+ assert_datetime(dt, 2015, 11, 12, 12, 34, 56, 123456)
+ assert dt.offset == 0
+
+
+def test_parse_with_timezone():
+ text = "2016-10-16T12:34:56.123456"
+
+ dt = pendulum.parse(text, tz="Europe/Paris")
+ assert_datetime(dt, 2016, 10, 16, 12, 34, 56, 123456)
+ assert dt.tz.name == "Europe/Paris"
+ assert dt.offset == 7200
+
+
+def test_parse_exact():
+ text = "2016-10-16T12:34:56.123456+01:30"
+
+ dt = pendulum.parse(text, exact=True)
+
+ assert isinstance(dt, pendulum.DateTime)
+ assert_datetime(dt, 2016, 10, 16, 12, 34, 56, 123456)
+ assert dt.offset == 5400
+
+ text = "2016-10-16"
+
+ dt = pendulum.parse(text, exact=True)
+
+ assert isinstance(dt, pendulum.Date)
+ assert_date(dt, 2016, 10, 16)
+
+ text = "12:34:56.123456"
+
+ dt = pendulum.parse(text, exact=True)
+
+ assert isinstance(dt, pendulum.Time)
+ assert_time(dt, 12, 34, 56, 123456)
+
+ text = "13:00"
+
+ dt = pendulum.parse(text, exact=True)
+
+ assert isinstance(dt, pendulum.Time)
+ assert_time(dt, 13, 0, 0)
+
+
+def test_parse_duration():
+ text = "P2Y3M4DT5H6M7S"
+
+ duration = pendulum.parse(text)
+
+ assert isinstance(duration, pendulum.Duration)
+ assert_duration(duration, 2, 3, 0, 4, 5, 6, 7)
+
+ text = "P2W"
+
+ duration = pendulum.parse(text)
+
+ assert isinstance(duration, pendulum.Duration)
+ assert_duration(duration, 0, 0, 2, 0, 0, 0, 0)
+
+
+def test_parse_interval():
+ text = "2008-05-11T15:30:00Z/P1Y2M10DT2H30M"
+
+ period = pendulum.parse(text)
+
+ assert isinstance(period, pendulum.Interval)
+ assert_datetime(period.start, 2008, 5, 11, 15, 30, 0, 0)
+ assert period.start.offset == 0
+ assert_datetime(period.end, 2009, 7, 21, 18, 0, 0, 0)
+ assert period.end.offset == 0
+
+ text = "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
+
+ period = pendulum.parse(text)
+
+ assert isinstance(period, pendulum.Interval)
+ assert_datetime(period.start, 2007, 3, 1, 13, 0, 0, 0)
+ assert period.start.offset == 0
+ assert_datetime(period.end, 2008, 5, 11, 15, 30, 0, 0)
+ assert period.end.offset == 0
+
+ text = "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"
+
+ period = pendulum.parse(text)
+
+ assert isinstance(period, pendulum.Interval)
+ assert_datetime(period.start, 2007, 3, 1, 13, 0, 0, 0)
+ assert period.start.offset == 0
+ assert_datetime(period.end, 2008, 5, 11, 15, 30, 0, 0)
+ assert period.end.offset == 0
+
+
+def test_parse_now():
+ dt = pendulum.parse("now")
+
+ assert dt.timezone_name == "America/Toronto"
+
+ mock_now = pendulum.yesterday()
+
+ with pendulum.travel_to(mock_now, freeze=True):
+ assert pendulum.parse("now") == mock_now
+
+
+def test_parse_with_utc_timezone():
+ dt = pendulum.parse("2020-02-05T20:05:37.364951Z")
+
+ assert dt.to_iso8601_string() == "2020-02-05T20:05:37.364951Z"
diff --git a/tests/testing/__init__.py b/tests/testing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/testing/__init__.py
diff --git a/tests/testing/test_time_travel.py b/tests/testing/test_time_travel.py
new file mode 100644
index 0000000..dd496e3
--- /dev/null
+++ b/tests/testing/test_time_travel.py
@@ -0,0 +1,85 @@
+from __future__ import annotations
+
+from time import sleep
+from typing import TYPE_CHECKING
+
+import pytest
+
+import pendulum
+
+from pendulum.utils._compat import PYPY
+
+if TYPE_CHECKING:
+ from typing import Generator
+
+
+@pytest.fixture(autouse=True)
+def setup() -> Generator[None, None, None]:
+ pendulum.travel_back()
+
+ yield
+
+ pendulum.travel_back()
+
+
+@pytest.mark.skipif(PYPY, reason="Time travelling not available on PyPy")
+def test_travel() -> None:
+ now = pendulum.now()
+
+ pendulum.travel(minutes=5)
+
+ assert pendulum.now().diff_for_humans(now) == "5 minutes after"
+
+
+@pytest.mark.skipif(PYPY, reason="Time travelling not available on PyPy")
+def test_travel_with_frozen_time() -> None:
+ pendulum.travel(minutes=5, freeze=True)
+
+ now = pendulum.now()
+
+ sleep(0.01)
+
+ assert now == pendulum.now()
+
+
+@pytest.mark.skipif(PYPY, reason="Time travelling not available on PyPy")
+def test_travel_to() -> None:
+ dt = pendulum.datetime(2022, 1, 19, tz="local")
+
+ pendulum.travel_to(dt)
+
+ assert pendulum.now().date() == dt.date()
+
+
+@pytest.mark.skipif(PYPY, reason="Time travelling not available on PyPy")
+def test_freeze() -> None:
+ pendulum.freeze()
+
+ pendulum.travel(minutes=5)
+
+ assert pendulum.now() == pendulum.now()
+
+ pendulum.travel_back()
+
+ pendulum.travel(minutes=5)
+
+ now = pendulum.now()
+
+ sleep(0.01)
+
+ assert now != pendulum.now()
+
+ pendulum.freeze()
+
+ assert pendulum.now() == pendulum.now()
+
+ pendulum.travel_back()
+
+ with pendulum.freeze():
+ assert pendulum.now() == pendulum.now()
+
+ now = pendulum.now()
+
+ sleep(0.01)
+
+ assert now != pendulum.now()
diff --git a/tests/time/__init__.py b/tests/time/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/time/__init__.py
diff --git a/tests/time/test_add.py b/tests/time/test_add.py
new file mode 100644
index 0000000..7075ebe
--- /dev/null
+++ b/tests/time/test_add.py
@@ -0,0 +1,78 @@
+from __future__ import annotations
+
+from datetime import timedelta
+
+import pytest
+
+import pendulum
+
+
+def test_add_hours_positive():
+ assert pendulum.time(12, 34, 56).add(hours=1).hour == 13
+
+
+def test_add_hours_zero():
+ assert pendulum.time(12, 34, 56).add(hours=0).hour == 12
+
+
+def test_add_hours_negative():
+ assert pendulum.time(12, 34, 56).add(hours=-1).hour == 11
+
+
+def test_add_minutes_positive():
+ assert pendulum.time(12, 34, 56).add(minutes=1).minute == 35
+
+
+def test_add_minutes_zero():
+ assert pendulum.time(12, 34, 56).add(minutes=0).minute == 34
+
+
+def test_add_minutes_negative():
+ assert pendulum.time(12, 34, 56).add(minutes=-1).minute == 33
+
+
+def test_add_seconds_positive():
+ assert pendulum.time(12, 34, 56).add(seconds=1).second == 57
+
+
+def test_add_seconds_zero():
+ assert pendulum.time(12, 34, 56).add(seconds=0).second == 56
+
+
+def test_add_seconds_negative():
+ assert pendulum.time(12, 34, 56).add(seconds=-1).second == 55
+
+
+def test_add_timedelta():
+ delta = timedelta(seconds=45, microseconds=123456)
+ d = pendulum.time(3, 12, 15, 654321)
+
+ d = d.add_timedelta(delta)
+ assert d.minute == 13
+ assert d.second == 0
+ assert d.microsecond == 777777
+
+ d = pendulum.time(3, 12, 15, 654321)
+
+ d = d + delta
+ assert d.minute == 13
+ assert d.second == 0
+ assert d.microsecond == 777777
+
+
+def test_add_timedelta_with_days():
+ delta = timedelta(days=3, seconds=45, microseconds=123456)
+ d = pendulum.time(3, 12, 15, 654321)
+
+ with pytest.raises(TypeError):
+ d.add_timedelta(delta)
+
+
+def test_addition_invalid_type():
+ d = pendulum.time(3, 12, 15, 654321)
+
+ with pytest.raises(TypeError):
+ d + 3
+
+ with pytest.raises(TypeError):
+ 3 + d
diff --git a/tests/time/test_behavior.py b/tests/time/test_behavior.py
new file mode 100644
index 0000000..0071c94
--- /dev/null
+++ b/tests/time/test_behavior.py
@@ -0,0 +1,49 @@
+from __future__ import annotations
+
+import pickle
+
+from datetime import time
+
+import pytest
+
+import pendulum
+
+from pendulum import Time
+
+
+@pytest.fixture()
+def p():
+ return pendulum.Time(12, 34, 56, 123456, tzinfo=pendulum.timezone("Europe/Paris"))
+
+
+@pytest.fixture()
+def d():
+ return time(12, 34, 56, 123456, tzinfo=pendulum.timezone("Europe/Paris"))
+
+
+def test_hash(p, d):
+ assert hash(d) == hash(p)
+ dt1 = Time(12, 34, 57, 123456)
+
+ assert hash(p) != hash(dt1)
+
+
+def test_pickle():
+ dt1 = Time(12, 34, 56, 123456)
+ s = pickle.dumps(dt1)
+ dt2 = pickle.loads(s)
+
+ assert dt2 == dt1
+
+
+def test_utcoffset(p, d):
+ assert d.utcoffset() == p.utcoffset()
+
+
+def test_dst(p, d):
+ assert d.dst() == p.dst()
+
+
+def test_tzname(p, d):
+ assert d.tzname() == p.tzname()
+ assert Time(12, 34, 56, 123456).tzname() == time(12, 34, 56, 123456).tzname()
diff --git a/tests/time/test_comparison.py b/tests/time/test_comparison.py
new file mode 100644
index 0000000..f1ef275
--- /dev/null
+++ b/tests/time/test_comparison.py
@@ -0,0 +1,185 @@
+from __future__ import annotations
+
+from datetime import time
+
+import pendulum
+
+from tests.conftest import assert_time
+
+
+def test_equal_to_true():
+ t1 = pendulum.time(1, 2, 3)
+ t2 = pendulum.time(1, 2, 3)
+ t3 = time(1, 2, 3)
+
+ assert t1 == t2
+ assert t1 == t3
+
+
+def test_equal_to_false():
+ t1 = pendulum.time(1, 2, 3)
+ t2 = pendulum.time(1, 2, 4)
+ t3 = time(1, 2, 4)
+
+ assert t1 != t2
+ assert t1 != t3
+
+
+def test_not_equal_to_none():
+ t1 = pendulum.time(1, 2, 3)
+
+ assert t1 != None # noqa
+
+
+def test_greater_than_true():
+ t1 = pendulum.time(1, 2, 3)
+ t2 = pendulum.time(1, 2, 2)
+ t3 = time(1, 2, 2)
+
+ assert t1 > t2
+ assert t1 > t3
+
+
+def test_greater_than_false():
+ t1 = pendulum.time(1, 2, 2)
+ t2 = pendulum.time(1, 2, 3)
+ t3 = time(1, 2, 3)
+
+ assert not t1 > t2
+ assert not t1 > t3
+
+
+def test_greater_than_or_equal_true():
+ t1 = pendulum.time(1, 2, 3)
+ t2 = pendulum.time(1, 2, 2)
+ t3 = time(1, 2, 2)
+
+ assert t1 >= t2
+ assert t1 >= t3
+
+
+def test_greater_than_or_equal_true_equal():
+ t1 = pendulum.time(1, 2, 3)
+ t2 = pendulum.time(1, 2, 3)
+ t3 = time(1, 2, 3)
+
+ assert t1 >= t2
+ assert t1 >= t3
+
+
+def test_greater_than_or_equal_false():
+ t1 = pendulum.time(1, 2, 2)
+ t2 = pendulum.time(1, 2, 3)
+ t3 = time(1, 2, 3)
+
+ assert not t1 >= t2
+ assert not t1 >= t3
+
+
+def test_less_than_true():
+ t1 = pendulum.time(1, 2, 2)
+ t2 = pendulum.time(1, 2, 3)
+ t3 = time(1, 2, 3)
+
+ assert t1 < t2
+ assert t1 < t3
+
+
+def test_less_than_false():
+ t1 = pendulum.time(1, 2, 3)
+ t2 = pendulum.time(1, 2, 2)
+ t3 = time(1, 2, 2)
+
+ assert not t1 < t2
+ assert not t1 < t3
+
+
+def test_less_than_or_equal_true():
+ t1 = pendulum.time(1, 2, 2)
+ t2 = pendulum.time(1, 2, 3)
+ t3 = time(1, 2, 3)
+
+ assert t1 <= t2
+ assert t1 <= t3
+
+
+def test_less_than_or_equal_true_equal():
+ t1 = pendulum.time(1, 2, 3)
+ t2 = pendulum.time(1, 2, 3)
+ t3 = time(1, 2, 3)
+
+ assert t1 <= t2
+ assert t1 <= t3
+
+
+def test_less_than_or_equal_false():
+ t1 = pendulum.time(1, 2, 3)
+ t2 = pendulum.time(1, 2, 2)
+ t3 = time(1, 2, 2)
+
+ assert not t1 <= t2
+ assert not t1 <= t3
+
+
+def test_closest():
+ instance = pendulum.time(12, 34, 56)
+ t1 = pendulum.time(12, 34, 54)
+ t2 = pendulum.time(12, 34, 59)
+ closest = instance.closest(t1, t2)
+ assert t1 == closest
+
+ closest = instance.closest(t2, t1)
+ assert t1 == closest
+
+
+def test_closest_with_time():
+ instance = pendulum.time(12, 34, 56)
+ t1 = pendulum.time(12, 34, 54)
+ t2 = pendulum.time(12, 34, 59)
+ closest = instance.closest(t1, t2)
+
+ assert_time(closest, 12, 34, 54)
+
+
+def test_closest_with_equals():
+ instance = pendulum.time(12, 34, 56)
+ t1 = pendulum.time(12, 34, 56)
+ t2 = pendulum.time(12, 34, 59)
+ closest = instance.closest(t1, t2)
+ assert t1 == closest
+
+
+def test_farthest():
+ instance = pendulum.time(12, 34, 56)
+ t1 = pendulum.time(12, 34, 54)
+ t2 = pendulum.time(12, 34, 59)
+ farthest = instance.farthest(t1, t2)
+ assert t2 == farthest
+
+ farthest = instance.farthest(t2, t1)
+ assert t2 == farthest
+
+
+def test_farthest_with_time():
+ instance = pendulum.time(12, 34, 56)
+ t1 = pendulum.time(12, 34, 54)
+ t2 = pendulum.time(12, 34, 59)
+ farthest = instance.farthest(t1, t2)
+
+ assert_time(farthest, 12, 34, 59)
+
+
+def test_farthest_with_equals():
+ instance = pendulum.time(12, 34, 56)
+ t1 = pendulum.time(12, 34, 56)
+ t2 = pendulum.time(12, 34, 59)
+
+ farthest = instance.farthest(t1, t2)
+ assert t2 == farthest
+
+
+def test_comparison_to_unsupported():
+ t1 = pendulum.now().time()
+
+ assert t1 != "test"
+ assert t1 not in ["test"]
diff --git a/tests/time/test_construct.py b/tests/time/test_construct.py
new file mode 100644
index 0000000..7d81b37
--- /dev/null
+++ b/tests/time/test_construct.py
@@ -0,0 +1,22 @@
+from __future__ import annotations
+
+import pendulum
+
+from tests.conftest import assert_time
+
+
+def test_init():
+ t = pendulum.time(12, 34, 56, 123456)
+
+ assert_time(t, 12, 34, 56, 123456)
+
+
+def test_init_with_missing_values():
+ t = pendulum.time(12, 34, 56)
+ assert_time(t, 12, 34, 56, 0)
+
+ t = pendulum.time(12, 34)
+ assert_time(t, 12, 34, 0, 0)
+
+ t = pendulum.time(12)
+ assert_time(t, 12, 0, 0, 0)
diff --git a/tests/time/test_diff.py b/tests/time/test_diff.py
new file mode 100644
index 0000000..629a058
--- /dev/null
+++ b/tests/time/test_diff.py
@@ -0,0 +1,350 @@
+from __future__ import annotations
+
+import pendulum
+
+from pendulum import Time
+
+
+def test_diff_in_hours_positive():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.add(hours=2).add(seconds=3672)).in_hours() == 3
+
+
+def test_diff_in_hours_negative_with_sign():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.subtract(hours=2).add(seconds=3600), False).in_hours() == -1
+
+
+def test_diff_in_hours_negative_no_sign():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.subtract(hours=2).add(seconds=3600)).in_hours() == 1
+
+
+def test_diff_in_hours_vs_default_now():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(hours=2).diff().in_hours() == 2
+
+
+def test_diff_in_hours_ensure_is_truncated():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.add(hours=2).add(seconds=5401)).in_hours() == 3
+
+
+def test_diff_in_minutes_positive():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.add(hours=1).add(minutes=2)).in_minutes() == 62
+
+
+def test_diff_in_minutes_positive_big():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.add(hours=25).add(minutes=2)).in_minutes() == 62
+
+
+def test_diff_in_minutes_negative_with_sign():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.subtract(hours=1).add(minutes=2), False).in_minutes() == -58
+
+
+def test_diff_in_minutes_negative_no_sign():
+ dt = Time(12, 34, 56)
+ 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.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(hours=1).diff().in_minutes() == 60
+
+
+def test_diff_in_minutes_ensure_is_truncated():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.add(minutes=1).add(seconds=59)).in_minutes() == 1
+
+
+def test_diff_in_seconds_positive():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.add(minutes=1).add(seconds=2)).in_seconds() == 62
+
+
+def test_diff_in_seconds_positive_big():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.add(hours=2).add(seconds=2)).in_seconds() == 7202
+
+
+def test_diff_in_seconds_negative_with_sign():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.subtract(minutes=1).add(seconds=2), False).in_seconds() == -58
+
+
+def test_diff_in_seconds_negative_no_sign():
+ dt = Time(12, 34, 56)
+ 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.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(hours=1).diff().in_seconds() == 3600
+
+
+def test_diff_in_seconds_ensure_is_truncated():
+ dt = Time(12, 34, 56)
+ assert dt.diff(dt.add(seconds=1.9)).in_seconds() == 1
+
+
+def test_diff_for_humans_now_and_second():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans() == "a few seconds ago"
+
+
+def test_diff_for_humans_now_and_seconds():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert 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.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(seconds=59).diff_for_humans() == "59 seconds ago"
+
+
+def test_diff_for_humans_now_and_minute():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(minutes=1).diff_for_humans() == "1 minute ago"
+
+
+def test_diff_for_humans_now_and_minutes():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(minutes=2).diff_for_humans() == "2 minutes ago"
+
+
+def test_diff_for_humans_now_and_nearly_hour():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(minutes=59).diff_for_humans() == "59 minutes ago"
+
+
+def test_diff_for_humans_now_and_hour():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(hours=1).diff_for_humans() == "1 hour ago"
+
+
+def test_diff_for_humans_now_and_hours():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.subtract(hours=2).diff_for_humans() == "2 hours ago"
+
+
+def test_diff_for_humans_now_and_future_second():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert 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.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert 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.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.add(seconds=59).diff_for_humans() == "in 59 seconds"
+
+
+def test_diff_for_humans_now_and_future_minute():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.add(minutes=1).diff_for_humans() == "in 1 minute"
+
+
+def test_diff_for_humans_now_and_future_minutes():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert 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.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.add(minutes=59).diff_for_humans() == "in 59 minutes"
+
+
+def test_diff_for_humans_now_and_future_hour():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.add(hours=1).diff_for_humans() == "in 1 hour"
+
+
+def test_diff_for_humans_now_and_future_hours():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.add(hours=2).diff_for_humans() == "in 2 hours"
+
+
+def test_diff_for_humans_other_and_second():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(seconds=1)) == "a few seconds before"
+
+
+def test_diff_for_humans_other_and_seconds():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(seconds=2)) == "a few seconds before"
+
+
+def test_diff_for_humans_other_and_nearly_minute():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(seconds=59)) == "59 seconds before"
+
+
+def test_diff_for_humans_other_and_minute():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(minutes=1)) == "1 minute before"
+
+
+def test_diff_for_humans_other_and_minutes():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(minutes=2)) == "2 minutes before"
+
+
+def test_diff_for_humans_other_and_nearly_hour():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(minutes=59)) == "59 minutes before"
+
+
+def test_diff_for_humans_other_and_hour():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(hours=1)) == "1 hour before"
+
+
+def test_diff_for_humans_other_and_hours():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(hours=2)) == "2 hours before"
+
+
+def test_diff_for_humans_other_and_future_second():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(seconds=1)) == "a few seconds after"
+
+
+def test_diff_for_humans_other_and_future_seconds():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(seconds=2)) == "a few seconds after"
+
+
+def test_diff_for_humans_other_and_nearly_future_minute():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(seconds=59)) == "59 seconds after"
+
+
+def test_diff_for_humans_other_and_future_minute():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(minutes=1)) == "1 minute after"
+
+
+def test_diff_for_humans_other_and_future_minutes():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(minutes=2)) == "2 minutes after"
+
+
+def test_diff_for_humans_other_and_nearly_future_hour():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(minutes=59)) == "59 minutes after"
+
+
+def test_diff_for_humans_other_and_future_hour():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(hours=1)) == "1 hour after"
+
+
+def test_diff_for_humans_other_and_future_hours():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(hours=2)) == "2 hours after"
+
+
+def test_diff_for_humans_absolute_seconds():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(seconds=59), True) == "59 seconds"
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(seconds=59), True) == "59 seconds"
+
+
+def test_diff_for_humans_absolute_minutes():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(minutes=30), True) == "30 minutes"
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(minutes=30), True) == "30 minutes"
+
+
+def test_diff_for_humans_absolute_hours():
+ with pendulum.travel_to(pendulum.today().at(12, 34, 56)):
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.subtract(hours=3), True) == "3 hours"
+ now = pendulum.now().time()
+
+ assert now.diff_for_humans(now.add(hours=3), True) == "3 hours"
diff --git a/tests/time/test_fluent_setters.py b/tests/time/test_fluent_setters.py
new file mode 100644
index 0000000..a678e56
--- /dev/null
+++ b/tests/time/test_fluent_setters.py
@@ -0,0 +1,12 @@
+from __future__ import annotations
+
+from pendulum import Time
+from tests.conftest import assert_time
+
+
+def test_replace():
+ t = Time(12, 34, 56, 123456)
+ t = t.replace(1, 2, 3, 654321)
+
+ assert isinstance(t, Time)
+ assert_time(t, 1, 2, 3, 654321)
diff --git a/tests/time/test_strings.py b/tests/time/test_strings.py
new file mode 100644
index 0000000..db3c9cd
--- /dev/null
+++ b/tests/time/test_strings.py
@@ -0,0 +1,39 @@
+from __future__ import annotations
+
+from pendulum import Time
+
+
+def test_to_string():
+ d = Time(1, 2, 3)
+ assert str(d) == "01:02:03"
+ d = Time(1, 2, 3, 123456)
+ assert str(d) == "01:02:03.123456"
+
+
+def test_repr():
+ d = Time(1, 2, 3)
+ assert repr(d) == "Time(1, 2, 3)"
+
+ d = Time(1, 2, 3, 123456)
+ assert repr(d) == "Time(1, 2, 3, 123456)"
+
+
+def test_format_with_locale():
+ d = Time(14, 15, 16)
+ assert d.format("hh:mm:ss A", locale="fr") == "02:15:16 PM"
+
+
+def test_strftime():
+ d = Time(14, 15, 16)
+ assert d.strftime("%H") == "14"
+
+
+def test_for_json():
+ d = Time(14, 15, 16)
+ assert d.for_json() == "14:15:16"
+
+
+def test_format():
+ d = Time(14, 15, 16)
+ assert f"{d}" == "14:15:16"
+ assert f"{d:mm}" == "15"
diff --git a/tests/time/test_sub.py b/tests/time/test_sub.py
new file mode 100644
index 0000000..1a957ad
--- /dev/null
+++ b/tests/time/test_sub.py
@@ -0,0 +1,112 @@
+from __future__ import annotations
+
+from datetime import time
+from datetime import timedelta
+
+import pytest
+import pytz
+
+import pendulum
+
+from pendulum import Time
+from tests.conftest import assert_duration
+
+
+def test_sub_hours_positive():
+ assert Time(0, 0, 0).subtract(hours=1).hour == 23
+
+
+def test_sub_hours_zero():
+ assert Time(0, 0, 0).subtract(hours=0).hour == 0
+
+
+def test_sub_hours_negative():
+ assert Time(0, 0, 0).subtract(hours=-1).hour == 1
+
+
+def test_sub_minutes_positive():
+ assert Time(0, 0, 0).subtract(minutes=1).minute == 59
+
+
+def test_sub_minutes_zero():
+ assert Time(0, 0, 0).subtract(minutes=0).minute == 0
+
+
+def test_sub_minutes_negative():
+ assert Time(0, 0, 0).subtract(minutes=-1).minute == 1
+
+
+def test_sub_seconds_positive():
+ assert Time(0, 0, 0).subtract(seconds=1).second == 59
+
+
+def test_sub_seconds_zero():
+ assert Time(0, 0, 0).subtract(seconds=0).second == 0
+
+
+def test_sub_seconds_negative():
+ assert Time(0, 0, 0).subtract(seconds=-1).second == 1
+
+
+def test_subtract_timedelta():
+ delta = timedelta(seconds=16, microseconds=654321)
+ d = Time(3, 12, 15, 777777)
+
+ d = d.subtract_timedelta(delta)
+ assert d.minute == 11
+ assert d.second == 59
+ assert d.microsecond == 123456
+
+ d = Time(3, 12, 15, 777777)
+
+ d = d - delta
+ assert d.minute == 11
+ assert d.second == 59
+ assert d.microsecond == 123456
+
+
+def test_add_timedelta_with_days():
+ delta = timedelta(days=3, seconds=45, microseconds=123456)
+ d = Time(3, 12, 15, 654321)
+
+ with pytest.raises(TypeError):
+ d.subtract_timedelta(delta)
+
+
+def test_subtract_invalid_type():
+ d = Time(0, 0, 0)
+
+ with pytest.raises(TypeError):
+ d - "ab"
+
+ with pytest.raises(TypeError):
+ "ab" - d
+
+
+def test_subtract_time():
+ t = Time(12, 34, 56)
+ t1 = Time(1, 1, 1)
+ t2 = time(1, 1, 1)
+ t3 = time(1, 1, 1, tzinfo=pytz.timezone("Europe/Paris"))
+
+ diff = t - t1
+ assert isinstance(diff, pendulum.Duration)
+ assert_duration(diff, 0, hours=11, minutes=33, seconds=55)
+
+ diff = t1 - t
+ assert isinstance(diff, pendulum.Duration)
+ assert_duration(diff, 0, hours=-11, minutes=-33, seconds=-55)
+
+ diff = t - t2
+ assert isinstance(diff, pendulum.Duration)
+ assert_duration(diff, 0, hours=11, minutes=33, seconds=55)
+
+ diff = t2 - t
+ assert isinstance(diff, pendulum.Duration)
+ assert_duration(diff, 0, hours=-11, minutes=-33, seconds=-55)
+
+ with pytest.raises(TypeError):
+ t - t3
+
+ with pytest.raises(TypeError):
+ t3 - t
diff --git a/tests/tz/__init__.py b/tests/tz/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/tz/__init__.py
diff --git a/tests/tz/test_helpers.py b/tests/tz/test_helpers.py
new file mode 100644
index 0000000..edec6fd
--- /dev/null
+++ b/tests/tz/test_helpers.py
@@ -0,0 +1,27 @@
+from __future__ import annotations
+
+import pytest
+
+from pendulum.tz import timezone
+from pendulum.tz.exceptions import InvalidTimezone
+from pendulum.tz.timezone import FixedTimezone
+from pendulum.tz.timezone import Timezone
+
+
+def test_timezone_with_name():
+ tz = timezone("Europe/Paris")
+
+ assert isinstance(tz, Timezone)
+ assert tz.name == "Europe/Paris"
+
+
+def test_timezone_with_invalid_name():
+ with pytest.raises(InvalidTimezone):
+ timezone("Invalid")
+
+
+def test_timezone_with_offset():
+ tz = timezone(-19800)
+
+ assert isinstance(tz, FixedTimezone)
+ assert tz.name == "-05:30"
diff --git a/tests/tz/test_local_timezone.py b/tests/tz/test_local_timezone.py
new file mode 100644
index 0000000..b8eff6d
--- /dev/null
+++ b/tests/tz/test_local_timezone.py
@@ -0,0 +1,52 @@
+from __future__ import annotations
+
+import os
+import sys
+
+import pytest
+
+from pendulum.tz.local_timezone import _get_unix_timezone
+from pendulum.tz.local_timezone import _get_windows_timezone
+
+
+@pytest.mark.skipif(
+ sys.platform == "win32", reason="Test only available for UNIX systems"
+)
+def test_unix_symlink():
+ # A ZONE setting in the target path of a symbolic linked localtime,
+ # f ex systemd distributions
+ local_path = os.path.join(os.path.split(__file__)[0], "..")
+ tz = _get_unix_timezone(_root=os.path.join(local_path, "fixtures", "tz", "symlink"))
+
+ assert tz.name == "Europe/Paris"
+
+
+@pytest.mark.skipif(
+ sys.platform == "win32", reason="Test only available for UNIX systems"
+)
+def test_unix_clock():
+ # A ZONE setting in the target path of a symbolic linked localtime,
+ # f ex systemd distributions
+ local_path = os.path.join(os.path.split(__file__)[0], "..")
+ tz = _get_unix_timezone(_root=os.path.join(local_path, "fixtures", "tz", "clock"))
+
+ assert tz.name == "Europe/Zurich"
+
+
+@pytest.mark.skipif(sys.platform != "win32", reason="Test only available for Windows")
+def test_windows_timezone():
+ timezone = _get_windows_timezone()
+
+ assert timezone is not None
+
+
+@pytest.mark.skipif(
+ sys.platform == "win32", reason="Test only available for UNIX systems"
+)
+def test_unix_etc_timezone_dir():
+ # Should not fail if `/etc/timezone` is a folder
+ local_path = os.path.join(os.path.split(__file__)[0], "..")
+ root_path = os.path.join(local_path, "fixtures", "tz", "timezone_dir")
+ tz = _get_unix_timezone(_root=root_path)
+
+ assert tz.name == "Europe/Paris"
diff --git a/tests/tz/test_timezone.py b/tests/tz/test_timezone.py
new file mode 100644
index 0000000..655267d
--- /dev/null
+++ b/tests/tz/test_timezone.py
@@ -0,0 +1,447 @@
+from __future__ import annotations
+
+from datetime import datetime
+from datetime import timedelta
+
+import pytest
+
+import pendulum
+
+from pendulum import timezone
+from pendulum.tz import fixed_timezone
+from pendulum.tz.exceptions import AmbiguousTime
+from pendulum.tz.exceptions import NonExistingTime
+from pendulum.utils._compat import zoneinfo
+from tests.conftest import assert_datetime
+
+
+@pytest.fixture(autouse=True)
+def setup():
+ pendulum.tz._tz_cache = {}
+
+ yield
+
+ pendulum.tz._tz_cache = {}
+
+
+def test_basic_convert():
+ dt = datetime(2016, 6, 1, 12, 34, 56, 123456, fold=1)
+ tz = timezone("Europe/Paris")
+ dt = tz.convert(dt)
+
+ assert dt.year == 2016
+ assert dt.month == 6
+ assert dt.day == 1
+ assert dt.hour == 12
+ assert dt.minute == 34
+ assert dt.second == 56
+ assert dt.microsecond == 123456
+ assert dt.tzinfo.name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=7200)
+ assert dt.tzinfo.dst(dt) == timedelta(seconds=3600)
+
+
+def test_skipped_time_with_pre_rule():
+ dt = datetime(2013, 3, 31, 2, 30, 45, 123456, fold=0)
+ tz = timezone("Europe/Paris")
+ dt = tz.convert(dt)
+
+ assert dt.year == 2013
+ assert dt.month == 3
+ assert dt.day == 31
+ assert dt.hour == 1
+ assert dt.minute == 30
+ assert dt.second == 45
+ assert dt.microsecond == 123456
+ assert dt.tzinfo.name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=3600)
+ assert dt.tzinfo.dst(dt) == timedelta()
+
+
+def test_skipped_time_with_post_rule():
+ dt = datetime(2013, 3, 31, 2, 30, 45, 123456, fold=1)
+ tz = timezone("Europe/Paris")
+ dt = tz.convert(dt)
+
+ assert dt.year == 2013
+ assert dt.month == 3
+ assert dt.day == 31
+ assert dt.hour == 3
+ assert dt.minute == 30
+ assert dt.second == 45
+ assert dt.microsecond == 123456
+ assert dt.tzinfo.name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=7200)
+ assert dt.tzinfo.dst(dt) == timedelta(seconds=3600)
+
+
+def test_skipped_time_with_error():
+ dt = datetime(2013, 3, 31, 2, 30, 45, 123456)
+ tz = timezone("Europe/Paris")
+ with pytest.raises(NonExistingTime):
+ tz.convert(dt, raise_on_unknown_times=True)
+
+
+def test_repeated_time():
+ dt = datetime(2013, 10, 27, 2, 30, 45, 123456, fold=1)
+ tz = timezone("Europe/Paris")
+ dt = tz.convert(dt)
+
+ assert dt.year == 2013
+ assert dt.month == 10
+ assert dt.day == 27
+ assert dt.hour == 2
+ assert dt.minute == 30
+ assert dt.second == 45
+ assert dt.microsecond == 123456
+ assert dt.tzinfo.name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=3600)
+ assert dt.tzinfo.dst(dt) == timedelta()
+
+
+def test_repeated_time_pre_rule():
+ dt = datetime(2013, 10, 27, 2, 30, 45, 123456, fold=0)
+ tz = timezone("Europe/Paris")
+ dt = tz.convert(dt)
+
+ assert dt.year == 2013
+ assert dt.month == 10
+ assert dt.day == 27
+ assert dt.hour == 2
+ assert dt.minute == 30
+ assert dt.second == 45
+ assert dt.microsecond == 123456
+ assert dt.tzinfo.name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=7200)
+ assert dt.tzinfo.dst(dt) == timedelta(seconds=3600)
+
+
+def test_repeated_time_with_error():
+ dt = datetime(2013, 10, 27, 2, 30, 45, 123456)
+ tz = timezone("Europe/Paris")
+ with pytest.raises(AmbiguousTime):
+ tz.convert(dt, raise_on_unknown_times=True)
+
+
+def test_pendulum_create_basic():
+ dt = pendulum.datetime(2016, 6, 1, 12, 34, 56, 123456, tz="Europe/Paris")
+
+ assert_datetime(dt, 2016, 6, 1, 12, 34, 56, 123456)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.offset == 7200
+ assert dt.is_dst()
+
+
+def test_pendulum_create_skipped():
+ dt = pendulum.datetime(2013, 3, 31, 2, 30, 45, 123456, tz="Europe/Paris")
+
+ assert isinstance(dt, pendulum.DateTime)
+ assert_datetime(dt, 2013, 3, 31, 3, 30, 45, 123456)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=7200)
+ assert dt.tzinfo.dst(dt) == timedelta(seconds=3600)
+
+
+def test_pendulum_create_skipped_with_pre_rule():
+ dt = pendulum.datetime(2013, 3, 31, 2, 30, 45, 123456, tz="Europe/Paris", fold=0)
+
+ assert_datetime(dt, 2013, 3, 31, 1, 30, 45, 123456)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=3600)
+ assert dt.tzinfo.dst(dt) == timedelta()
+
+
+def test_pendulum_create_skipped_with_error():
+ with pytest.raises(NonExistingTime):
+ pendulum.datetime(
+ 2013,
+ 3,
+ 31,
+ 2,
+ 30,
+ 45,
+ 123456,
+ tz="Europe/Paris",
+ raise_on_unknown_times=True,
+ )
+
+
+def test_pendulum_create_repeated():
+ dt = pendulum.datetime(2013, 10, 27, 2, 30, 45, 123456, tz="Europe/Paris")
+
+ assert_datetime(dt, 2013, 10, 27, 2, 30, 45, 123456)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=3600)
+ assert dt.tzinfo.dst(dt) == timedelta()
+
+
+def test_pendulum_create_repeated_with_pre_rule():
+ dt = pendulum.datetime(
+ 2013,
+ 10,
+ 27,
+ 2,
+ 30,
+ 45,
+ 123456,
+ tz="Europe/Paris",
+ fold=0,
+ )
+
+ assert_datetime(dt, 2013, 10, 27, 2, 30, 45, 123456)
+ assert dt.timezone_name == "Europe/Paris"
+ assert dt.tzinfo.utcoffset(dt) == timedelta(seconds=7200)
+ assert dt.tzinfo.dst(dt) == timedelta(seconds=3600)
+
+
+def test_pendulum_create_repeated_with_error():
+ with pytest.raises(AmbiguousTime):
+ pendulum.datetime(
+ 2013,
+ 10,
+ 27,
+ 2,
+ 30,
+ 45,
+ 123456,
+ tz="Europe/Paris",
+ raise_on_unknown_times=True,
+ )
+
+
+def test_convert_accept_pendulum_instance():
+ dt = pendulum.datetime(2016, 8, 7, 12, 53, 54)
+ tz = timezone("Europe/Paris")
+ new = tz.convert(dt)
+
+ assert isinstance(new, pendulum.DateTime)
+ assert_datetime(new, 2016, 8, 7, 14, 53, 54)
+
+
+def test_utcoffset():
+ tz = pendulum.timezone("America/Guayaquil")
+ utcoffset = tz.utcoffset(pendulum.now("UTC"))
+ assert utcoffset == timedelta(0, -18000)
+
+
+def test_utcoffset_pre_transition():
+ tz = pendulum.timezone("America/Chicago")
+ utcoffset = tz.utcoffset(datetime(1883, 11, 18))
+ assert utcoffset == timedelta(days=-1, seconds=65364)
+
+
+def test_dst():
+ tz = pendulum.timezone("Europe/Amsterdam")
+ dst = tz.dst(datetime(1940, 7, 1))
+ native_tz = zoneinfo.ZoneInfo("Europe/Amsterdam")
+
+ assert dst == native_tz.dst(datetime(1940, 7, 1))
+
+
+def test_short_timezones_should_not_modify_time():
+ tz = pendulum.timezone("EST")
+ dt = tz.datetime(2017, 6, 15, 14, 0, 0)
+
+ assert dt.year == 2017
+ assert dt.month == 6
+ assert dt.day == 15
+ assert dt.hour == 14
+ assert dt.minute == 0
+ assert dt.second == 0
+
+ tz = pendulum.timezone("HST")
+ dt = tz.datetime(2017, 6, 15, 14, 0, 0)
+
+ assert dt.year == 2017
+ assert dt.month == 6
+ assert dt.day == 15
+ assert dt.hour == 14
+ assert dt.minute == 0
+ assert dt.second == 0
+
+
+def test_after_last_transition():
+ tz = pendulum.timezone("Europe/Paris")
+ dt = tz.datetime(2135, 6, 15, 14, 0, 0)
+
+ assert dt.year == 2135
+ assert dt.month == 6
+ assert dt.day == 15
+ assert dt.hour == 14
+ assert dt.minute == 0
+ assert dt.second == 0
+ assert dt.microsecond == 0
+
+
+@pytest.mark.skip(
+ reason="zoneinfo does not currently support POSIX transition rules to go beyond the last fixed transition."
+)
+def test_on_last_transition():
+ tz = pendulum.timezone("Europe/Paris")
+ dt = pendulum.naive(2037, 10, 25, 2, 30)
+ dt = tz.convert(dt, dst_rule=pendulum.POST_TRANSITION)
+
+ assert dt.year == 2037
+ assert dt.month == 10
+ assert dt.day == 25
+ assert dt.hour == 2
+ assert dt.minute == 30
+ assert dt.second == 0
+ assert dt.microsecond == 0
+ assert dt.utcoffset().total_seconds() == 3600
+
+ dt = pendulum.naive(2037, 10, 25, 2, 30)
+ dt = tz.convert(dt, dst_rule=pendulum.PRE_TRANSITION)
+
+ assert dt.year == 2037
+ assert dt.month == 10
+ assert dt.day == 25
+ assert dt.hour == 2
+ assert dt.minute == 30
+ assert dt.second == 0
+ assert dt.microsecond == 0
+ assert dt.utcoffset().total_seconds() == 7200
+
+
+def test_convert_fold_attribute_is_honored():
+ tz = pendulum.timezone("US/Eastern")
+ dt = datetime(2014, 11, 2, 1, 30)
+
+ new = tz.convert(dt)
+ assert new.strftime("%z") == "-0400"
+
+ new = tz.convert(dt.replace(fold=1))
+ assert new.strftime("%z") == "-0500"
+
+
+def test_utcoffset_fold_attribute_is_honored():
+ tz = pendulum.timezone("US/Eastern")
+ dt = datetime(2014, 11, 2, 1, 30)
+
+ offset = tz.utcoffset(dt)
+
+ assert offset.total_seconds() == -4 * 3600
+
+ offset = tz.utcoffset(dt.replace(fold=1))
+
+ assert offset.total_seconds() == -5 * 3600
+
+
+def test_dst_fold_attribute_is_honored():
+ tz = pendulum.timezone("US/Eastern")
+ dt = datetime(2014, 11, 2, 1, 30)
+
+ offset = tz.dst(dt)
+
+ assert offset.total_seconds() == 3600
+
+ offset = tz.dst(dt.replace(fold=1))
+
+ assert offset.total_seconds() == 0
+
+
+def test_tzname_fold_attribute_is_honored():
+ tz = pendulum.timezone("US/Eastern")
+ dt = datetime(2014, 11, 2, 1, 30)
+
+ name = tz.tzname(dt)
+
+ assert name == "EDT"
+
+ name = tz.tzname(dt.replace(fold=1))
+
+ assert name == "EST"
+
+
+def test_constructor_fold_attribute_is_honored():
+ tz = pendulum.timezone("US/Eastern")
+ dt = datetime(2014, 11, 2, 1, 30, tzinfo=tz)
+
+ assert dt.strftime("%z") == "-0400"
+
+ dt = datetime(2014, 11, 2, 1, 30, tzinfo=tz, fold=1)
+
+ assert dt.strftime("%z") == "-0500"
+
+
+def test_datetime():
+ tz = timezone("Europe/Paris")
+
+ dt = tz.datetime(2013, 3, 24, 1, 30)
+ assert dt.year == 2013
+ assert dt.month == 3
+ assert dt.day == 24
+ assert dt.hour == 1
+ assert dt.minute == 30
+ assert dt.second == 0
+ assert dt.microsecond == 0
+
+ dt = tz.datetime(2013, 3, 31, 2, 30)
+ assert dt.year == 2013
+ assert dt.month == 3
+ assert dt.day == 31
+ assert dt.hour == 3
+ assert dt.minute == 30
+ assert dt.second == 0
+ assert dt.microsecond == 0
+
+
+def test_fixed_timezone():
+ tz = fixed_timezone(19800)
+ tz2 = fixed_timezone(18000)
+ dt = datetime(2016, 11, 26, tzinfo=tz)
+
+ assert tz2.utcoffset(dt).total_seconds() == 18000
+ assert tz2.dst(dt) == timedelta()
+
+
+def test_just_before_last_transition():
+ tz = pendulum.timezone("Asia/Shanghai")
+ dt = datetime(1991, 4, 20, 1, 49, 8, fold=0)
+ dt = tz.convert(dt)
+
+ epoch = datetime(1970, 1, 1, tzinfo=timezone("UTC"))
+ expected = (dt - epoch).total_seconds()
+ assert expected == 672079748.0
+
+
+@pytest.mark.skip(
+ reason="zoneinfo does not currently support POSIX transition rules to go beyond the last fixed transition."
+)
+def test_timezones_are_extended():
+ tz = pendulum.timezone("Europe/Paris")
+ dt = tz.convert(pendulum.naive(2134, 2, 13, 1))
+
+ assert_datetime(dt, 2134, 2, 13, 1)
+ assert dt.utcoffset().total_seconds() == 3600
+ assert dt.dst() == timedelta()
+
+ dt = tz.convert(pendulum.naive(2134, 3, 28, 2, 30))
+
+ assert_datetime(dt, 2134, 3, 28, 3, 30)
+ assert dt.utcoffset().total_seconds() == 7200
+ assert dt.dst() == timedelta(seconds=3600)
+
+ dt = tz.convert(pendulum.naive(2134, 7, 11, 2, 30))
+
+ assert_datetime(dt, 2134, 7, 11, 2, 30)
+ assert dt.utcoffset().total_seconds() == 7200
+ assert dt.dst() == timedelta(seconds=3600)
+
+ dt = tz.convert(pendulum.naive(2134, 10, 31, 2, 30, fold=0))
+
+ assert_datetime(dt, 2134, 10, 31, 2, 30)
+ assert dt.utcoffset().total_seconds() == 7200
+ assert dt.dst() == timedelta(seconds=3600)
+
+ dt = tz.convert(pendulum.naive(2134, 10, 31, 2, 30))
+
+ assert_datetime(dt, 2134, 10, 31, 2, 30)
+ assert dt.utcoffset().total_seconds() == 3600
+ assert dt.dst() == timedelta()
+
+
+def test_repr():
+ tz = timezone("Europe/Paris")
+
+ assert repr(tz) == "Timezone('Europe/Paris')"
diff --git a/tests/tz/test_timezones.py b/tests/tz/test_timezones.py
new file mode 100644
index 0000000..200ed09
--- /dev/null
+++ b/tests/tz/test_timezones.py
@@ -0,0 +1,16 @@
+from __future__ import annotations
+
+import pytest
+
+import pendulum
+
+
+def test_timezones():
+ zones = pendulum.timezones()
+
+ assert "America/Argentina/Buenos_Aires" in zones
+
+
+@pytest.mark.parametrize("zone", list(pendulum.timezones()))
+def test_timezones_are_loadable(zone):
+ pendulum.timezone(zone)