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

[PR] time type conversion to timedelta if timedelta64 is given #660

Merged
merged 6 commits into from
Jun 10, 2024
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
54 changes: 51 additions & 3 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@
"standard_name": "time",
},
)
time_hourly_dt = xr.DataArray(
data=np.array(
[
"2000-01-01T00:00:00.000000000",
"2000-01-01T01:00:00.000000000",
"2000-01-01T02:00:00.000000000",
],
dtype="datetime64[ns]",
),
dims=["time"],
attrs={
"axis": "T",
"long_name": "time",
"standard_name": "time",
},
)
time_subhourly = xr.DataArray(
data=np.array(
[
Expand Down Expand Up @@ -189,6 +205,30 @@
"xcdat_bounds": "True",
},
)
time_bnds_hourly_dt = xr.DataArray(
name="time_bnds",
data=np.array(
[
[
"2000-01-01T00:00:00.000000000",
"2000-01-01T01:00:00.000000000",
],
[
"2000-01-01T01:00:00.000000000",
"2000-01-01T02:00:00.000000000",
],
[
"2000-01-01T02:00:00.000000000",
"2000-01-01T03:00:00.000000000",
],
],
dtype="datetime64[ns]",
),
dims=["time", "bnds"],
attrs={
"xcdat_bounds": "True",
},
)
time_bnds_subhourly = xr.DataArray(
name="time_bnds",
data=np.array(
Expand Down Expand Up @@ -495,7 +535,8 @@ def generate_dataset(


def generate_dataset_by_frequency(
freq: Literal["subhour", "hour", "day", "month", "year"] = "month"
freq: Literal["subhour", "hour", "day", "month", "year"] = "month",
obj_type: Literal["cftime", "datetime"] = "cftime",
) -> xr.Dataset:
"""Generates a dataset for a given temporal frequency.

Expand Down Expand Up @@ -523,8 +564,15 @@ def generate_dataset_by_frequency(
time = time_daily.copy()
time_bnds = time_bnds_daily.copy()
elif freq == "hour":
time = time_hourly.copy()
time_bnds = time_bnds_hourly.copy()
# Test cftime and datetime. datetime subtraction results in
# dtype=timedelta64[ns] objects, which need to be converted to Pandas
# TimeDelta objects to use the `.seconds` time component.
if obj_type == "cftime":
time = time_hourly.copy()
time_bnds = time_bnds_hourly.copy()
else:
time = time_hourly_dt.copy()
time_bnds = time_bnds_hourly_dt.copy()
elif freq == "subhour":
time = time_subhourly.copy()
time_bnds = time_bnds_subhourly.copy()
Expand Down
15 changes: 15 additions & 0 deletions tests/test_bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,21 @@ def test_add_bounds_for_time_coords_with_different_frequencies(self):
assert monthly_bounds.identical(ds_monthly_with_bnds)
assert yearly_bounds.identical(ds_yearly_with_bnds)

def test_add_bounds_for_hourly_time_coords_as_datetime_objects(self):
# get reference datasets
ds_hrly_with_bnds = generate_dataset_by_frequency("hour", "datetime")

# drop bounds for testing
ds_hrly_wo_bnds = ds_hrly_with_bnds.drop_vars("time_bnds")

# test adding bounds
hourly_bounds = ds_hrly_wo_bnds.bounds.add_time_bounds(
method="freq", freq="hour"
)

# ensure identical
assert hourly_bounds.identical(ds_hrly_with_bnds)

def test_add_monthly_bounds_for_end_of_month_set_to_true(self):
ds_with_bnds = self.ds_with_bnds.copy()

Expand Down
11 changes: 9 additions & 2 deletions xcdat/bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ def _get_bounds_keys(self, axis: CFAxisKey) -> List[str]:

return list(set(keys))

def _create_time_bounds(
def _create_time_bounds( # noqa: C901
self,
time: xr.DataArray,
freq: Optional[Literal["year", "month", "day", "hour"]] = None,
Expand Down Expand Up @@ -601,9 +601,16 @@ def _create_time_bounds(
elif freq == "day":
time_bnds = self._create_daily_time_bounds(timesteps, obj_type)
elif freq == "hour":
# Determine the daily frequency for generating time bounds.
# Determine the daily frequency for generating time bounds.
if daily_subfreq is None:
diff = time.values[1] - time.values[0]

# Arrays with `dtype="timedelta64[ns]"` must be converted to
# pandas timedelta objects in order to access the `.seconds`
# time component.
if isinstance(diff, np.timedelta64):
diff = pd.to_timedelta(diff)

hrs = diff.seconds / 3600
daily_subfreq = int(24 / hrs) # type: ignore

Expand Down
Loading