diff --git a/.gitignore b/.gitignore index b0426c4..9d6052a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /pyRFC3339-1.0.tar.gz /pyRFC3339-1.1.tar.gz +/pyrfc3339-2.0.1.tar.gz diff --git a/python-pyrfc3339.spec b/python-pyrfc3339.spec index 103cbe3..02bc08e 100644 --- a/python-pyrfc3339.spec +++ b/python-pyrfc3339.spec @@ -1,19 +1,17 @@ -%global srcname pyRFC3339 +%global srcname pyrfc3339 Name: python-pyrfc3339 -Version: 1.1 -Release: 20%{?dist} +Version: 2.0.1 +Release: 1%{?dist} Summary: Generate and parse RFC 3339 timestamps License: MIT URL: https://pypi.python.org/pypi/pyRFC3339 Source0: %{pypi_source} # release tarballs do not contain unit tests (pyrfc3339/tests/tests.py) -# https://raw.githubusercontent.com/kurtraschke/pyRFC3339/master/pyrfc3339/tests/tests.py -# v1.1: git commit e30cc155 -Source1: https://raw.githubusercontent.com/kurtraschke/pyRFC3339/e30cc1555adce0ecc7bd65509a2249d47e5a41b4/pyrfc3339/tests/tests.py -# basically the same as https://github.com/kurtraschke/pyRFC3339/pull/16 -Patch1: pyrfc3339-use-pytest-in-tests.patch +# https://github.com/kurtraschke/pyRFC3339/blob/master/pyrfc3339/tests/test_all.py +# v2.0.1: git commit 53c2d1587d3a +Source1: https://raw.githubusercontent.com/kurtraschke/pyRFC3339/53c2d1587d3aac1734ddd4d4006a815df2d80f36/pyrfc3339/tests/test_all.py BuildArch: noarch @@ -41,7 +39,6 @@ RFC 3339-compliant timestamps using Python datetime.datetime objects. %prep %autosetup -n %{srcname}-%{version} -N cp -a %{SOURCE1} . -%patch -P1 -p1 %build %pyproject_wheel @@ -51,12 +48,15 @@ cp -a %{SOURCE1} . %pyproject_save_files pyrfc3339 %check -%pytest -v tests.py +%pytest -v test_all.py %files -n python3-pyrfc3339 -f %{pyproject_files} %doc README.rst %changelog +* Fri Jan 03 2025 Felix Schwarz - 2.0.1-1 +- update to 2.0.1 + * Fri Jul 19 2024 Fedora Release Engineering - 1.1-20 - Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild diff --git a/sources b/sources index 8423dc9..bb55905 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (pyRFC3339-1.1.tar.gz) = 958b7761fab590aa42bb57a955c5d834441f717796a452b60df21663099dcf2fc046afe60f8157fd0f1edfd95c5e9c9c5349ab10ca4078d210fc63d848496a2f +SHA512 (pyrfc3339-2.0.1.tar.gz) = b1c6768842a8fc8917280264e4fa3e2e1223399184bd7639fbb12d3bd4755ea2cadfb291efe2bf1f3b7e89e50fcb1f89a46a9e81a94cca3a5ca0aa8c0b698c69 diff --git a/test_all.py b/test_all.py new file mode 100644 index 0000000..ed47f20 --- /dev/null +++ b/test_all.py @@ -0,0 +1,155 @@ +""" +Test suite for pyRFC3339. + +""" + +import unittest +import zoneinfo +from datetime import datetime, timezone +from zoneinfo import ZoneInfo + +from pyrfc3339 import generate, parse + + +class TestCore(unittest.TestCase): + """ + This test case contains tests to address cases not tested in the doctests, + as well as additional tests for end-to-end verification. + + """ + + def test_zero_offset(self) -> None: + """ + Both +00:00 and -00:00 are equivalent to the offset 'Z' (UTC). + + """ + timestamp = "2009-01-01T10:02:03+00:00" + dt = parse(timestamp) + self.assertEqual(dt.tzinfo, timezone.utc) + + timestamp = "2009-01-01T10:02:03-00:00" + dt = parse(timestamp) + self.assertEqual(dt.tzinfo, timezone.utc) + + def test_parse_microseconds(self) -> None: + """ + Test parsing timestamps with microseconds. + + """ + timestamp = "2009-01-01T10:02:03.25Z" + dt = parse(timestamp) + self.assertEqual(dt.microsecond, 250000) + + def test_generate_microseconds(self) -> None: + """ + Test generating timestamps with microseconds. + + """ + dt = datetime(2009, 1, 1, 10, 2, 3, 500000, tzinfo=timezone.utc) + timestamp = generate(dt, microseconds=True) + self.assertEqual(timestamp, "2009-01-01T10:02:03.500000Z") + + def test_mixed_case(self) -> None: + """ + Timestamps may use either 'T' or 't' and either 'Z' or 'z' + according to :RFC:`3339`. + + """ + dt1 = parse("2009-01-01t10:01:02z") + dt2 = datetime(2009, 1, 1, 10, 1, 2, tzinfo=timezone.utc) + + self.assertEqual(dt1, dt2) + + def test_parse_naive_utc(self) -> None: + """ + Test parsing a UTC timestamp to a naive datetime. + + """ + dt1 = parse("2009-01-01T10:01:02Z", produce_naive=True) + self.assertEqual(dt1.tzinfo, None) + + def test_parse_naive_local(self) -> None: + """ + Test that parsing a local timestamp to a naive datetime fails. + + """ + with self.assertRaises(ValueError): + parse("2009-01-01T10:01:02-04:00", produce_naive=True) + + def test_generate_utc_parse_utc(self) -> None: + """ + Generate a UTC timestamp and parse it into a UTC datetime. + + """ + dt1 = datetime.now(timezone.utc) + + dt2 = parse(generate(dt1, microseconds=True)) + self.assertEqual(dt1, dt2) + + def test_generate_local_parse_local(self) -> None: + """ + Generate a local timestamp and parse it into a local datetime. + + """ + eastern = ZoneInfo("US/Eastern") + dt1 = datetime.now(eastern) + dt2 = parse(generate(dt1, utc=False, microseconds=True), utc=False) + self.assertEqual(dt1, dt2) + + def test_generate_local_parse_utc(self) -> None: + """ + Generate a local timestamp and parse it into a UTC datetime. + + """ + eastern = ZoneInfo("US/Eastern") + dt1 = datetime.now(eastern) + dt2 = parse(generate(dt1, utc=False, microseconds=True)) + self.assertEqual(dt1, dt2) + + @unittest.skip("fails due to python/cpython#120713") + def test_three_digit_year(self) -> None: + dt = datetime(999, 1, 1, 0, 0, 0, tzinfo=timezone.utc) + self.assertEqual(generate(dt), "0999-01-01T00:00:00Z") + + +class TestExhaustiveRoundtrip(unittest.TestCase): + """ + This test case exhaustively tests parsing and generation by generating + a local RFC 3339 timestamp for every timezone supported by `zoneinfo`, + parsing that timestamp into a local datetime and a UTC datetime + and asserting that those represent the same instant. + + """ + + def setUp(self) -> None: + self.available_timezones = zoneinfo.available_timezones() + + def test_local_roundtrip(self) -> None: + """ + Generates a local datetime using the given timezone, + produces a local timestamp from the datetime, parses the timestamp + to a local datetime, and verifies that the two datetimes are equal. + + """ + for tz_name in self.available_timezones: + with self.subTest(tz=tz_name): + tzinfo = ZoneInfo(tz_name) + dt1 = datetime.now(tzinfo) + timestamp = generate(dt1, utc=False, microseconds=True) + dt2 = parse(timestamp, utc=False) + self.assertEqual(dt1, dt2) + + def test_utc_roundtrip(self) -> None: + """ + Generates a local datetime using the given timezone, + produces a local timestamp from the datetime, parses the timestamp + to a UTC datetime, and verifies that the two datetimes are equal. + + """ + for tz_name in self.available_timezones: + with self.subTest(tz=tz_name): + tzinfo = ZoneInfo(tz_name) + dt1 = datetime.now(tzinfo) + timestamp = generate(dt1, utc=False, microseconds=True) + dt2 = parse(timestamp) + self.assertEqual(dt1, dt2) diff --git a/tests.py b/tests.py deleted file mode 100644 index 831daab..0000000 --- a/tests.py +++ /dev/null @@ -1,170 +0,0 @@ -''' -Test suite for pyRFC3339. - -''' - -from datetime import datetime -from copy import deepcopy - -from pyrfc3339 import generate, parse -from pyrfc3339.utils import timezone -import pytz - -from nose.tools import eq_, raises - - -class TestCore(): - ''' - This test suite contains tests to address cases not tested in the doctests, - as well as additional tests for end-to-end verification. - - ''' - def test_timezone_rounding(self): - ''' - Test rounding of timezone values to the nearest second. - - ''' - eq_(timezone(5429), '+01:30') - eq_(timezone(5431), '+01:31') - - def test_zero_offset(self): - ''' - Both +00:00 and -00:00 are equivalent to the offset 'Z' (UTC). - - ''' - timestamp = '2009-01-01T10:02:03+00:00' - dt = parse(timestamp) - eq_(dt.tzinfo, pytz.utc) - - timestamp = '2009-01-01T10:02:03-00:00' - dt = parse(timestamp) - eq_(dt.tzinfo, pytz.utc) - - def test_deepcopy(self): - ''' - Tests that deepcopy works and doesn't crash - - ''' - timestamp = '2009-01-01T10:02:03+02:00' - dt = parse(timestamp) - deepcopy(dt) - - def test_parse_microseconds(self): - ''' - Test parsing timestamps with microseconds. - - ''' - timestamp = '2009-01-01T10:02:03.25Z' - dt = parse(timestamp) - eq_(dt.microsecond, 250000) - - def test_generate_microseconds(self): - ''' - Test generating timestamps with microseconds. - - ''' - dt = datetime(2009, 1, 1, 10, 2, 3, 500000, pytz.utc) - timestamp = generate(dt, microseconds=True) - eq_(timestamp, '2009-01-01T10:02:03.500000Z') - - def test_mixed_case(self): - ''' - Timestamps may use either 'T' or 't' and either 'Z' or 'z' - according to :RFC:`3339`. - - ''' - dt1 = parse('2009-01-01t10:01:02z') - dt2 = datetime(2009, 1, 1, 10, 1, 2, tzinfo=pytz.utc) - - eq_(dt1, dt2) - - def test_parse_naive_utc(self): - ''' - Test parsing a UTC timestamp to a naive datetime. - - ''' - dt1 = parse('2009-01-01T10:01:02Z', produce_naive=True) - eq_(dt1.tzinfo, None) - - @raises(ValueError) - def test_parse_naive_local(self): - ''' - Test that parsing a local timestamp to a naive datetime fails. - - ''' - parse('2009-01-01T10:01:02-04:00', produce_naive=True) - - def test_generate_utc_parse_utc(self): - ''' - Generate a UTC timestamp and parse it into a UTC datetime. - - ''' - dt1 = datetime.utcnow() - dt1 = dt1.replace(tzinfo=pytz.utc) - - dt2 = parse(generate(dt1, microseconds=True)) - eq_(dt1, dt2) - - def test_generate_local_parse_local(self): - ''' - Generate a local timestamp and parse it into a local datetime. - - ''' - eastern = pytz.timezone('US/Eastern') - dt1 = eastern.localize(datetime.utcnow()) - dt2 = parse(generate(dt1, utc=False, microseconds=True), utc=False) - eq_(dt1, dt2) - - def test_generate_local_parse_utc(self): - ''' - Generate a local timestamp and parse it into a UTC datetime. - - ''' - eastern = pytz.timezone('US/Eastern') - dt1 = eastern.localize(datetime.utcnow()) - dt2 = parse(generate(dt1, utc=False, microseconds=True)) - eq_(dt1, dt2) - - -class TestExhaustiveRoundtrip(): - ''' - This test suite exhaustively tests parsing and generation by generating - a local RFC 3339 timestamp for every timezone supported by pytz, - and parsing that timestamp into a local datetime and a UTC datetime. - ''' - - slow = True - - def test_local_roundtrip(self): - for tz_name in pytz.all_timezones: - yield self.local_roundtrip, tz_name - - def local_roundtrip(self, tz_name): - ''' - Generates a local datetime using the given timezone, - produces a local timestamp from the datetime, parses the timestamp - to a local datetime, and verifies that the two datetimes are equal. - - ''' - tzinfo = pytz.timezone(tz_name) - dt1 = tzinfo.localize(datetime.utcnow()) - timestamp = generate(dt1, utc=False, microseconds=True) - dt2 = parse(timestamp, utc=False) - eq_(dt1, dt2) - - def test_utc_roundtrip(self): - for tz_name in pytz.all_timezones: - yield self.utc_roundtrip, tz_name - - def utc_roundtrip(self, tz_name): - ''' - Generates a local datetime using the given timezone, - produces a local timestamp from the datetime, parses the timestamp - to a UTC datetime, and verifies that the two datetimes are equal. - - ''' - tzinfo = pytz.timezone(tz_name) - dt1 = tzinfo.localize(datetime.utcnow()) - timestamp = generate(dt1, utc=False, microseconds=True) - dt2 = parse(timestamp) - eq_(dt1, dt2)