From 061f066afcad097f707ed51378ef1a7bd3710339 Mon Sep 17 00:00:00 2001 From: Ruth Comer Date: Thu, 14 Apr 2022 17:26:31 +0100 Subject: [PATCH 1/2] implement change_calendar --- cf_units/__init__.py | 25 +++++++++++++++ cf_units/tests/unit/unit/test_Unit.py | 45 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/cf_units/__init__.py b/cf_units/__init__.py index f6e72d61..3c36db02 100644 --- a/cf_units/__init__.py +++ b/cf_units/__init__.py @@ -1746,6 +1746,31 @@ def __ne__(self, other): """ return not self == other + def change_calendar(self, calendar): + """ + Returns a new unit with the requested calendar, modifying the reference + date if necessary. Only works with calenders that represent the real + world (standard, proleptic_gregorian, julian) and with short time + intervals (days or less). + + For example: + + >>> from cf_units import Unit + >>> u = Unit('days since 1500-01-01', calendar='proleptic_gregorian') + >>> u.change_calendar('standard') + Unit('days since 1499-12-23T00:00:00', calendar='standard') + + """ + if not self.is_time_reference(): + raise ValueError("unit is not a time reference") + + ref_date = self.num2date(0) + new_ref_date = ref_date.change_calendar(calendar) + time_units = self.origin.split(_OP_SINCE)[0] + new_origin = _OP_SINCE.join([time_units, new_ref_date.isoformat()]) + + return Unit(new_origin, calendar=calendar) + def convert(self, value, other, ctype=FLOAT64, inplace=False): """ Converts a single value or NumPy array of values from the current unit diff --git a/cf_units/tests/unit/unit/test_Unit.py b/cf_units/tests/unit/unit/test_Unit.py index 83e93788..9866d31d 100644 --- a/cf_units/tests/unit/unit/test_Unit.py +++ b/cf_units/tests/unit/unit/test_Unit.py @@ -31,6 +31,51 @@ def test_hash_replacement(self): self.assertEqual(u, expected) +class Test_change_calendar(unittest.TestCase): + def test_modern_standard_to_proleptic_gregorian(self): + u = Unit("hours since 1970-01-01 00:00:00", calendar="standard") + expected = Unit( + "hours since 1970-01-01 00:00:00", calendar="proleptic_gregorian" + ) + result = u.change_calendar("proleptic_gregorian") + self.assertEqual(result, expected) + + def test_ancient_standard_to_proleptic_gregorian(self): + u = Unit("hours since 1500-01-01 00:00:00", calendar="standard") + expected = Unit( + "hours since 1500-01-10 00:00:00", calendar="proleptic_gregorian" + ) + result = u.change_calendar("proleptic_gregorian") + self.assertEqual(result, expected) + + def test_no_change(self): + u = Unit("hours since 1500-01-01 00:00:00", calendar="standard") + result = u.change_calendar("standard") + self.assertEqual(result, u) + # Docstring states that a new unit is returned, so check these are not + # the same object. + self.assertIsNot(result, u) + + def test_long_time_interval(self): + u = Unit("months since 1970-01-01", calendar="standard") + with self.assertRaisesRegex(ValueError, "cannot be processed"): + u.change_calendar("proleptic_gregorian") + + def test_wrong_calendar(self): + u = Unit("days since 1900-01-01", calendar="360_day") + with self.assertRaisesRegex( + ValueError, "change_calendar only works for real-world calendars" + ): + u.change_calendar("standard") + + def test_non_time_unit(self): + u = Unit("m") + with self.assertRaisesRegex( + ValueError, "unit is not a time reference" + ): + u.change_calendar("standard") + + class Test_convert__calendar(unittest.TestCase): class MyStr(str): pass From 0186eb4f0412b4ff46ef5a6c6b180bb189ed5b37 Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Thu, 28 Apr 2022 08:39:29 +0100 Subject: [PATCH 2/2] typo! --- cf_units/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cf_units/__init__.py b/cf_units/__init__.py index 3c36db02..a62e93a5 100644 --- a/cf_units/__init__.py +++ b/cf_units/__init__.py @@ -1749,7 +1749,7 @@ def __ne__(self, other): def change_calendar(self, calendar): """ Returns a new unit with the requested calendar, modifying the reference - date if necessary. Only works with calenders that represent the real + date if necessary. Only works with calendars that represent the real world (standard, proleptic_gregorian, julian) and with short time intervals (days or less).