Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Unit.change_calendar #235

Merged
merged 2 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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