Skip to content

Commit 664f7c9

Browse files
authored
Fix: Icon hourly data rounding issues (#2022)
1 parent 748783e commit 664f7c9

File tree

2 files changed

+20
-16
lines changed

2 files changed

+20
-16
lines changed

esmvalcore/cmor/_fixes/icon/icon.py

+18-14
Original file line numberDiff line numberDiff line change
@@ -270,25 +270,29 @@ def _fix_time(self, cube, cubes):
270270
new_t_unit = cf_units.Unit('days since 1850-01-01',
271271
calendar='proleptic_gregorian')
272272

273-
# New routine to convert time of daily and hourly data
274-
# The string %f (fraction of day) is not a valid format string
275-
# for datetime.strptime, so we have to convert it ourselves
276-
time_str = [str(x) for x in time_coord.points]
277-
278-
# First, extract date (year, month, day) from string
279-
# and convert it to datetime object
280-
year_month_day_str = pd.Series(time_str).str.extract(
281-
r'(\d*)\.?\d*', expand=False
282-
)
273+
# New routine to convert time of daily and hourly data. The string %f
274+
# (fraction of day) is not a valid format string for datetime.strptime,
275+
# so we have to convert it ourselves.
276+
time_str = pd.Series(time_coord.points, dtype=str)
277+
278+
# First, extract date (year, month, day) from string and convert it to
279+
# datetime object
280+
year_month_day_str = time_str.str.extract(r'(\d*)\.?\d*', expand=False)
283281
year_month_day = pd.to_datetime(year_month_day_str, format='%Y%m%d')
282+
284283
# Second, extract day fraction and convert it to timedelta object
285-
day_float_str = pd.Series(time_str).str.extract(
284+
day_float_str = time_str.str.extract(
286285
r'\d*(\.\d*)', expand=False
287286
).fillna('0.0')
288287
day_float = pd.to_timedelta(day_float_str.astype(float), unit='D')
289-
# Finally, add date and day fraction to get final datetime
290-
# and convert it to correct units
291-
new_datetimes = (year_month_day + day_float).dt.to_pydatetime()
288+
289+
# Finally, add date and day fraction to get final datetime and convert
290+
# it to correct units. Note: we also round to next second, otherwise
291+
# this results in times that are off by 1s (e.g., 13:59:59 instead of
292+
# 14:00:00).
293+
new_datetimes = (year_month_day + day_float).round(
294+
'S'
295+
).dt.to_pydatetime()
292296
new_dt_points = date2num(np.array(new_datetimes), new_t_unit)
293297

294298
time_coord.points = new_dt_points

tests/integration/cmor/_fixes/icon/test_icon.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1346,13 +1346,13 @@ def test_hourly_data(cubes_2d):
13461346
"""Test fix."""
13471347
fix = get_allvars_fix('Amon', 'tas')
13481348
for cube in cubes_2d:
1349-
cube.coord('time').points = [20220314.1]
1349+
cube.coord('time').points = [20041104.5833333]
13501350

13511351
fixed_cubes = fix.fix_metadata(cubes_2d)
13521352

13531353
cube = check_tas_metadata(fixed_cubes)
13541354
date = cube.coord('time').units.num2date(cube.coord('time').points)
1355-
np.testing.assert_array_equal(date, [datetime(2022, 3, 14, 2, 24)])
1355+
np.testing.assert_array_equal(date, [datetime(2004, 11, 4, 14, 0)])
13561356
assert cube.coord('time').bounds is None
13571357

13581358

0 commit comments

Comments
 (0)