Repository: esmvalgroup/esmvalcore
Issue: Bug in ACCESS1-0 calender fix that modifies the time point values unintentionally.

https://github.com/ESMValGroup/ESMValCore/blob/2fbe3e55e07672c52a968fd1d2d9d65d081c8866/esmvalcore/cmor/_fixes/cmip5/access1_0.py#L34

This fix changes merely the name of the calendar, but not the time points. Consequently, the time points are interpreted differently:

```Python
cube = iris.load_cube('tas_Amon_ACCESS1-0_historical_r1i1p1_185001-200512.nc')
time = cube.coord('time')   
print(time)                                                                                                                                                                                        
DimCoord([1850-01-16 12:00:00, 1850-02-15 00:00:00, 1850-03-16 12:00:00, ...,
       2005-10-16 12:00:00, 2005-11-16 00:00:00, 2005-12-16 12:00:00], bounds=[[1850-01-01 00:00:00, 1850-02-01 00:00:00],
       [1850-02-01 00:00:00, 1850-03-01 00:00:00],
       [1850-03-01 00:00:00, 1850-04-01 00:00:00],
       ...,
       [2005-10-01 00:00:00, 2005-11-01 00:00:00],
       [2005-11-01 00:00:00, 2005-12-01 00:00:00],
       [2005-12-01 00:00:00, 2006-01-01 00:00:00]], standard_name='time', calendar='proleptic_gregorian', long_name='time', var_name='time')

# Apply the faulty fix
time.units = Unit(time.units.name, calendar='gregorian')
print(time)                                                                                                                                                                                        
DimCoord([1850-01-14 12:00:00, 1850-02-13 00:00:00, 1850-03-14 12:00:00, ...,
       2005-10-14 12:00:00, 2005-11-14 00:00:00, 2005-12-14 12:00:00], bounds=[[1849-12-30 00:00:00, 1850-01-30 00:00:00],
       [1850-01-30 00:00:00, 1850-02-27 00:00:00],
       [1850-02-27 00:00:00, 1850-03-30 00:00:00],
       ...,
       [2005-09-29 00:00:00, 2005-10-30 00:00:00],
       [2005-10-30 00:00:00, 2005-11-29 00:00:00],
       [2005-11-29 00:00:00, 2005-12-30 00:00:00]], standard_name='time', calendar='gregorian', long_name='time', var_name='time')
```

Note the comment to [this answer](https://stackoverflow.com/a/53373130/6012085) and a related discussion [here](https://github.com/SciTools/iris/issues/3089)

I suppose we need to change not just the name of the calendar, but also the time points. However, 
```Python
time.convert_units(Unit('days since 1850-01-01', calendar='gregorian'))
ValueError: Unable to convert from 'Unit('days since 0001-01-01', calendar='proleptic_gregorian')' to 'Unit('days since 1850-01-01', calendar='gregorian')'.
```
doesn't work, so we might need to either rebuild the coordinate completely, using num2date and date2num as proposed in the question [here](https://stackoverflow.com/questions/52702433/convert-calendar-using-iris), or maybe use something like [convertdate](https://pypi.org/project/convertdate/).

@katjaweigel

Changed files:
tests/integration/cmor/_fixes/cmip5/test_access1_3.py, tests/integration/cmor/_fixes/cmip5/test_access1_0.py, esmvalcore/cmor/_fixes/cmip5/access1_0.py, esmvalcore/cmor/_fixes/cmip5/access1_3.py

diff --git a/esmvalcore/cmor/_fixes/cmip5/access1_0.py b/esmvalcore/cmor/_fixes/cmip5/access1_0.py
index d9a6cd2ab..4fa4da755 100644
--- a/esmvalcore/cmor/_fixes/cmip5/access1_0.py
+++ b/esmvalcore/cmor/_fixes/cmip5/access1_0.py
@@ -31,7 +31,10 @@ class AllVars(Fix):
             except iris.exceptions.CoordinateNotFoundError:
                 continue
             else:
-                time.units = Unit(time.units.name, 'gregorian')
+                if time.units.calendar == 'proleptic_gregorian':
+                    time.convert_units(Unit("days since 1850-01-01",
+                                            calendar='proleptic_gregorian'))
+                    time.units = Unit(time.units.name, 'gregorian')
         return cubes
 
 
diff --git a/esmvalcore/cmor/_fixes/cmip5/access1_3.py b/esmvalcore/cmor/_fixes/cmip5/access1_3.py
index dd5cc90d2..f02618166 100644
--- a/esmvalcore/cmor/_fixes/cmip5/access1_3.py
+++ b/esmvalcore/cmor/_fixes/cmip5/access1_3.py
@@ -33,5 +33,8 @@ class AllVars(Fix):
             except iris.exceptions.CoordinateNotFoundError:
                 continue
             else:
-                time.units = Unit(time.units.name, 'gregorian')
+                if time.units.calendar == 'proleptic_gregorian':
+                    time.convert_units(Unit("days since 1850-01-01",
+                                            calendar='proleptic_gregorian'))
+                    time.units = Unit(time.units.name, 'gregorian')
         return cubes
diff --git a/tests/integration/cmor/_fixes/cmip5/test_access1_0.py b/tests/integration/cmor/_fixes/cmip5/test_access1_0.py
index c0cf54d7e..f56bf48cc 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_access1_0.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_access1_0.py
@@ -1,9 +1,10 @@
 """Test Access1-0 fixes."""
 import unittest
+from datetime import datetime
 
 import pytest
-from cf_units import Unit
-from iris.coords import AuxCoord
+from cf_units import Unit, date2num, num2date
+from iris.coords import AuxCoord, DimCoord
 from iris.cube import Cube, CubeList
 
 from esmvalcore.cmor._fixes.cmip5.access1_0 import AllVars, Cl
@@ -16,10 +17,19 @@ class TestAllVars(unittest.TestCase):
 
     def setUp(self):
         """Prepare tests."""
-        self.cube = Cube([1.0], var_name='co2', units='J')
-        self.cube.add_aux_coord(
-            AuxCoord(0, 'time', 'time', 'time',
-                     Unit('days since 1850-01-01', 'julian')))
+        self.cube = Cube([1.0, 2.0], var_name='co2', units='J')
+        reference_dates = [
+            datetime(300, 1, 16, 12),  # e.g. piControl
+            datetime(1850, 1, 16, 12)  # e.g. historical
+        ]
+        esgf_time_units = {
+            'unit': 'days since 0001-01-01',
+            'calendar': 'proleptic_gregorian'
+        }
+        time_points = date2num(reference_dates, **esgf_time_units)
+        self.cube.add_dim_coord(
+            DimCoord(time_points, 'time', 'time', 'time',
+                     Unit(**esgf_time_units)), data_dim=0)
         self.fix = AllVars(None)
 
     def test_get(self):
@@ -31,7 +41,11 @@ class TestAllVars(unittest.TestCase):
     def test_fix_metadata(self):
         """Test fix for bad calendar."""
         cube = self.fix.fix_metadata([self.cube])[0]
-        self.assertEqual(cube.coord('time').units.calendar, 'gregorian')
+        time = cube.coord('time')
+        dates = num2date(time.points, time.units.name, time.units.calendar)
+        self.assertEqual(time.units.calendar, 'gregorian')
+        self.assertEqual(dates[0].strftime('%Y%m%d%H%M'), ' 30001161200')
+        self.assertEqual(dates[1].strftime('%Y%m%d%H%M'), '185001161200')
 
     def test_fix_metadata_if_not_time(self):
         """Test calendar fix do not fail if no time coord present."""
diff --git a/tests/integration/cmor/_fixes/cmip5/test_access1_3.py b/tests/integration/cmor/_fixes/cmip5/test_access1_3.py
index 6635382c0..435782e5b 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_access1_3.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_access1_3.py
@@ -1,8 +1,9 @@
 """Test fixes for ACCESS1-3."""
 import unittest
+from datetime import datetime
 
-from cf_units import Unit
-from iris.coords import AuxCoord
+from cf_units import Unit, date2num, num2date
+from iris.coords import DimCoord
 from iris.cube import Cube
 
 from esmvalcore.cmor._fixes.cmip5.access1_0 import Cl as BaseCl
@@ -15,10 +16,19 @@ class TestAllVars(unittest.TestCase):
 
     def setUp(self):
         """Prepare tests."""
-        self.cube = Cube([1.0], var_name='co2', units='J')
-        self.cube.add_aux_coord(
-            AuxCoord(0, 'time', 'time', 'time',
-                     Unit('days since 1850-01-01', 'julian')))
+        self.cube = Cube([1.0, 2.0], var_name='co2', units='J')
+        reference_dates = [
+            datetime(300, 1, 16, 12),  # e.g. piControl
+            datetime(1850, 1, 16, 12)  # e.g. historical
+        ]
+        esgf_time_units = {
+            'unit': 'days since 0001-01-01',
+            'calendar': 'proleptic_gregorian'
+        }
+        time_points = date2num(reference_dates, **esgf_time_units)
+        self.cube.add_dim_coord(
+            DimCoord(time_points, 'time', 'time', 'time',
+                     Unit(**esgf_time_units)), data_dim=0)
         self.fix = AllVars(None)
 
     def test_get(self):
@@ -28,9 +38,13 @@ class TestAllVars(unittest.TestCase):
             [AllVars(None)])
 
     def test_fix_metadata(self):
-        """Test calendar fix."""
+        """Test fix for bad calendar."""
         cube = self.fix.fix_metadata([self.cube])[0]
-        self.assertEqual(cube.coord('time').units.calendar, 'gregorian')
+        time = cube.coord('time')
+        dates = num2date(time.points, time.units.name, time.units.calendar)
+        self.assertEqual(time.units.calendar, 'gregorian')
+        self.assertEqual(dates[0].strftime('%Y%m%d%H%M'), ' 30001161200')
+        self.assertEqual(dates[1].strftime('%Y%m%d%H%M'), '185001161200')
 
     def test_fix_metadata_if_not_time(self):
         """Test calendar fix do not fail if no time coord present."""