summaryrefslogtreecommitdiffstats
path: root/tests/parsing
diff options
context:
space:
mode:
Diffstat (limited to 'tests/parsing')
-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
4 files changed, 1450 insertions, 0 deletions
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")