Skip to content

Commit

Permalink
Implement Unit.change_calendar (#235)
Browse files Browse the repository at this point in the history
* implement change_calendar

* typo!
  • Loading branch information
rcomer authored May 16, 2022
1 parent 26ec343 commit daa1660
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 0 deletions.
25 changes: 25 additions & 0 deletions cf_units/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 calendars 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
Expand Down
45 changes: 45 additions & 0 deletions cf_units/tests/unit/unit/test_Unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit daa1660

Please sign in to comment.