Skip to content

Commit fca2902

Browse files
authored
Merge pull request #193 from csiro-coasts/fix-190/detect-more-time-coordinates
Detect more time coordinates
2 parents bc3ff2b + 430d5ba commit fca2902

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

docs/releases/development.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ Next release (in development)
2020
Only the first frame is used to generate the clim to avoid loading more data than required.
2121
The new clim parameter allows users to specify the data limits explicitly if this is insufficient
2222
(:pr:`191`).
23+
* Detect time coordinates with correct dtype and some standard attributes even without a `units` attribute.
24+
Variables with `units` are preferred
25+
(:issue:`190`, :pr:`193`).

src/emsarray/conventions/_base.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -296,17 +296,31 @@ def time_coordinate(self) -> xarray.DataArray:
296296
----------
297297
.. [1] `CF Conventions v1.10, 4.4 Time Coordinate <https://cfconventions.org/Data/cf-conventions/cf-conventions-1.10/cf-conventions.html#time-coordinate>`_
298298
"""
299+
# First look for a datetime64 variable with a 'units' field in the encoding
299300
for name in self.dataset.variables.keys():
300301
variable = self.dataset[name]
301-
# xarray will automatically decode all time variables
302-
# and move the 'units' attribute over to encoding to store this change.
303-
if 'units' in variable.encoding:
304-
units = variable.encoding['units']
305-
# A time variable must have units of the form '<units> since <epoc>'
306-
if 'since' in units:
307-
# The variable must now be a numpy datetime
308-
if variable.dtype.type == numpy.datetime64:
302+
# The variable must be a numpy datetime
303+
if variable.dtype.type == numpy.datetime64:
304+
# xarray will automatically decode all time variables
305+
# and move the 'units' attribute over to encoding to store this change.
306+
if 'units' in variable.encoding:
307+
units = variable.encoding['units']
308+
# A time variable must have units of the form '<units> since <epoc>'
309+
if 'since' in units:
309310
return variable
311+
312+
# Next, look for any datetime64 variable with an appropriate attribute
313+
for name in self.dataset.variables.keys():
314+
variable = self.dataset[name]
315+
if variable.dtype.type == numpy.datetime64:
316+
possible_attributes = {
317+
'coordinate_type': 'time',
318+
'standard_name': 'time',
319+
'axis': 'T',
320+
}
321+
if any(variable.attrs.get(name, None) == value for name, value in possible_attributes.items()):
322+
return variable
323+
310324
raise NoSuchCoordinateError("Could not find time coordinate in dataset")
311325

312326
@cached_property

tests/conventions/test_base.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,47 @@ def apply_clip_mask(self, clip_mask: xarray.Dataset, work_dir: Pathish) -> xarra
146146
return masking.mask_grid_dataset(self.dataset, clip_mask, work_dir)
147147

148148

149-
def test_time_coordinate(datasets: pathlib.Path) -> None:
149+
def test_time_coordinate_with_units(datasets: pathlib.Path) -> None:
150150
dataset = xarray.open_dataset(datasets / 'times.nc')
151151
SimpleConvention(dataset).bind()
152152
time_coordinate = dataset.ems.time_coordinate
153153
assert time_coordinate.name == 'time'
154154
xarray.testing.assert_equal(time_coordinate, dataset['time'])
155155

156156

157+
def test_time_coordinate_no_units(datasets: pathlib.Path) -> None:
158+
dataset = xarray.Dataset({
159+
'time': xarray.DataArray(
160+
data=pandas.date_range('2025-09-08', '2025-10-08'),
161+
attrs={'coordinate_type': 'time', 'standard_name': 'time'},
162+
),
163+
})
164+
165+
SimpleConvention(dataset).bind()
166+
time_coordinate = dataset.ems.time_coordinate
167+
assert time_coordinate.name == 'time'
168+
xarray.testing.assert_equal(time_coordinate, dataset['time'])
169+
170+
171+
def test_time_coordinate_with_units_first(datasets: pathlib.Path) -> None:
172+
dataset = xarray.Dataset({
173+
'time_no_units': xarray.DataArray(
174+
data=pandas.date_range('2025-09-08', '2025-10-08'),
175+
attrs={'coordinate_type': 'time', 'standard_name': 'time'},
176+
),
177+
'time_with_units': xarray.DataArray(
178+
data=pandas.date_range('2025-09-08', '2025-10-08'),
179+
attrs={'coordinate_type': 'time', 'standard_name': 'time'},
180+
),
181+
})
182+
183+
dataset['time_with_units'].encoding['units'] = 'days since 1970-01-01'
184+
185+
SimpleConvention(dataset).bind()
186+
time_coordinate = dataset.ems.time_coordinate
187+
assert time_coordinate.name == 'time_with_units'
188+
189+
157190
def test_time_coordinate_missing() -> None:
158191
dataset = xarray.Dataset()
159192
SimpleConvention(dataset).bind()

0 commit comments

Comments
 (0)