diff --git a/compliance_checker/cf/cf_1_6.py b/compliance_checker/cf/cf_1_6.py index 7fc3ef6b..d4752bfd 100644 --- a/compliance_checker/cf/cf_1_6.py +++ b/compliance_checker/cf/cf_1_6.py @@ -2221,7 +2221,7 @@ def check_grid_coordinates(self, ds): alt = ( "{} has no coordinate associated with a variable identified as true latitude/longitude; " - + "its coordinate variable should also share a subset of {}'s dimensions" + "its coordinate variable should also share a subset of {}'s dimensions" ) # Make sure we can find latitude and its dimensions are a subset diff --git a/compliance_checker/cf/cf_1_9.py b/compliance_checker/cf/cf_1_9.py index 0e6d9cda..c499448c 100644 --- a/compliance_checker/cf/cf_1_9.py +++ b/compliance_checker/cf/cf_1_9.py @@ -1,5 +1,6 @@ from netCDF4 import Dataset +from compliance_checker import cfutil from compliance_checker.base import BaseCheck, TestCtx from compliance_checker.cf.cf_1_8 import CF1_8Check from compliance_checker.cf.util import VariableReferenceError, reference_attr_variables @@ -13,6 +14,31 @@ def __init__(self, options=None): super(CF1_9Check, self).__init__(options) self.section_titles.update({"5.8": "ยง5.8 Domain Variables"}) + def check_calendar(self, ds): + # IMPLEMENTATION CONFORMANCE 4.4.1 RECOMMENDED CF 1.9 + super(CF1_9Check, self).check_calendar.__doc__ + prev_return = super(CF1_9Check, self).check_calendar(ds) + time_var_candidate_name = cfutil.get_time_variable(ds) + time_var_name = ( + time_var_candidate_name + if time_var_candidate_name in self._find_coord_vars(ds) + else None + ) + # most datasets should have a time coordinate variable + test_ctx = self.get_test_ctx( + BaseCheck.HIGH, self.section_titles["4.4"], time_var_name + ) + if time_var_name is None: + return prev_return + + test_ctx.assert_true( + hasattr(ds.variables[time_var_name], "calendar"), + f'Time coordinate variable "{time_var_name}" ' + "should have a calendar attribute", + ) + prev_return.append(test_ctx.to_result()) + return prev_return + def check_domain_variables(self, ds: Dataset): # Domain variables should have coordinates attribute, but should have # scalar dimensions diff --git a/compliance_checker/tests/test_cf.py b/compliance_checker/tests/test_cf.py index 5a9db06f..388053b1 100644 --- a/compliance_checker/tests/test_cf.py +++ b/compliance_checker/tests/test_cf.py @@ -2997,6 +2997,30 @@ class TestCF1_9(BaseTestCase): def setUp(self): self.cf = CF1_9Check() + def test_time_variable_has_calendar(self): + # TEST CONFORMANCE 4.4.1 RECOMMENDED CF 1.9 + dataset = MockTimeSeries() + results = self.cf.check_calendar(dataset) + assert ( + results[0].msgs[0] == 'Time coordinate variable "time" should have a ' + "calendar attribute" + ) + # FIXME: NetCDF files shouldn't normally be modified so we can usually + # depend on cached results. Here we need to recreate the checker + # instance in order to not have previous results included pass condition + self.cf = CF1_9Check() + dataset.variables["time"].calendar = "standard" + results = self.cf.check_calendar(dataset) + # no time coordinate present, i.e. there is no time variable name with + # the same name as the time dimension name. + self.cf = CF1_9Check() + dataset = MockTimeSeries() + dataset.variables["time2"] = dataset.variables["time"] + del dataset.variables["time"] + results = self.cf.check_calendar(dataset) + # results array should be empty as no time coordinate variable detected + assert not results + def test_domain(self): dataset = MockTimeSeries() domain_var = dataset.createVariable("domain", "c", ())