Skip to content

Commit

Permalink
Fix: Icon hourly data rounding issues (#2022)
Browse files Browse the repository at this point in the history
  • Loading branch information
BauerJul authored May 11, 2023
1 parent 748783e commit 664f7c9
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 16 deletions.
32 changes: 18 additions & 14 deletions esmvalcore/cmor/_fixes/icon/icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,25 +270,29 @@ def _fix_time(self, cube, cubes):
new_t_unit = cf_units.Unit('days since 1850-01-01',
calendar='proleptic_gregorian')

# New routine to convert time of daily and hourly data
# The string %f (fraction of day) is not a valid format string
# for datetime.strptime, so we have to convert it ourselves
time_str = [str(x) for x in time_coord.points]

# First, extract date (year, month, day) from string
# and convert it to datetime object
year_month_day_str = pd.Series(time_str).str.extract(
r'(\d*)\.?\d*', expand=False
)
# New routine to convert time of daily and hourly data. The string %f
# (fraction of day) is not a valid format string for datetime.strptime,
# so we have to convert it ourselves.
time_str = pd.Series(time_coord.points, dtype=str)

# First, extract date (year, month, day) from string and convert it to
# datetime object
year_month_day_str = time_str.str.extract(r'(\d*)\.?\d*', expand=False)
year_month_day = pd.to_datetime(year_month_day_str, format='%Y%m%d')

# Second, extract day fraction and convert it to timedelta object
day_float_str = pd.Series(time_str).str.extract(
day_float_str = time_str.str.extract(
r'\d*(\.\d*)', expand=False
).fillna('0.0')
day_float = pd.to_timedelta(day_float_str.astype(float), unit='D')
# Finally, add date and day fraction to get final datetime
# and convert it to correct units
new_datetimes = (year_month_day + day_float).dt.to_pydatetime()

# Finally, add date and day fraction to get final datetime and convert
# it to correct units. Note: we also round to next second, otherwise
# this results in times that are off by 1s (e.g., 13:59:59 instead of
# 14:00:00).
new_datetimes = (year_month_day + day_float).round(
'S'
).dt.to_pydatetime()
new_dt_points = date2num(np.array(new_datetimes), new_t_unit)

time_coord.points = new_dt_points
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/cmor/_fixes/icon/test_icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -1346,13 +1346,13 @@ def test_hourly_data(cubes_2d):
"""Test fix."""
fix = get_allvars_fix('Amon', 'tas')
for cube in cubes_2d:
cube.coord('time').points = [20220314.1]
cube.coord('time').points = [20041104.5833333]

fixed_cubes = fix.fix_metadata(cubes_2d)

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


Expand Down

0 comments on commit 664f7c9

Please sign in to comment.