diff --git a/docs/iris/src/userguide/interpolation_and_regridding.rst b/docs/iris/src/userguide/interpolation_and_regridding.rst index 0ce7b5c774..eb8ecc4aca 100644 --- a/docs/iris/src/userguide/interpolation_and_regridding.rst +++ b/docs/iris/src/userguide/interpolation_and_regridding.rst @@ -47,7 +47,7 @@ The specified coordinate must exist on the cube being interpolated! For example: are all examples of valid sample points. The values for coordinates that correspond to date/times can be supplied as -datetime.datetime or netcdftime.datetime instances, +datetime.datetime or cftime.datetime instances, e.g. ``[('time', datetime.datetime(2009, 11, 19, 10, 30))]``). Let's take the air temperature cube we've seen previously: diff --git a/docs/iris/src/whatsnew/contributions_2.1/newfeature_2018-May-23_cftime.txt b/docs/iris/src/whatsnew/contributions_2.1/newfeature_2018-May-23_cftime.txt new file mode 100644 index 0000000000..de74eeb8d6 --- /dev/null +++ b/docs/iris/src/whatsnew/contributions_2.1/newfeature_2018-May-23_cftime.txt @@ -0,0 +1,2 @@ +Iris updated its time-handling functionality from the netcdf4-python builtin netcdftime to the standalone module cftime. +cftime is entirely compatible with netcdftime. \ No newline at end of file diff --git a/lib/iris/analysis/__init__.py b/lib/iris/analysis/__init__.py index b48fab6baa..8cf1a5d2a7 100644 --- a/lib/iris/analysis/__init__.py +++ b/lib/iris/analysis/__init__.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2017, Met Office +# (C) British Crown Copyright 2010 - 2018, Met Office # # This file is part of Iris. # @@ -2181,7 +2181,7 @@ def interpolator(self, cube, coords): The values for coordinates that correspond to date/times may optionally be supplied as datetime.datetime or - netcdftime.datetime instances. + cftime.datetime instances. For example, for the callable returned by: `Linear().interpolator(cube, ['latitude', 'longitude'])`, @@ -2364,7 +2364,7 @@ def interpolator(self, cube, coords): The values for coordinates that correspond to date/times may optionally be supplied as datetime.datetime or - netcdftime.datetime instances. + cftime.datetime instances. For example, for the callable returned by: `Nearest().interpolator(cube, ['latitude', 'longitude'])`, diff --git a/lib/iris/coord_categorisation.py b/lib/iris/coord_categorisation.py index 0c6aadd3f7..3c3303441d 100644 --- a/lib/iris/coord_categorisation.py +++ b/lib/iris/coord_categorisation.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2016, Met Office +# (C) British Crown Copyright 2010 - 2018, Met Office # # This file is part of Iris. # @@ -173,7 +173,7 @@ def add_day_of_year(cube, coord, name='day_of_year'): (1..366 in leap years). """ - # Note: netcdftime.datetime objects return a normal tuple from timetuple(), + # Note: cftime.datetime objects return a normal tuple from timetuple(), # unlike datetime.datetime objects that return a namedtuple. # Index the time tuple (element 7 is day of year) instead of using named # element tm_yday. diff --git a/lib/iris/coords.py b/lib/iris/coords.py index 3b2b2f5eb1..e4a8eb35f2 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -32,7 +32,7 @@ import warnings import zlib -import netcdftime +import cftime import numpy as np import numpy.ma as ma @@ -267,13 +267,13 @@ def __common_cmp__(self, other, operator_method): operator.ge, operator.le): raise ValueError("Unexpected operator_method") - # Prevent silent errors resulting from missing netcdftime + # Prevent silent errors resulting from missing cftime # behaviour. - if (isinstance(other, netcdftime.datetime) or - (isinstance(self.point, netcdftime.datetime) and + if (isinstance(other, cftime.datetime) or + (isinstance(self.point, cftime.datetime) and not isinstance(other, iris.time.PartialDateTime))): raise TypeError('Cannot determine the order of ' - 'netcdftime.datetime objects') + 'cftime.datetime objects') if isinstance(other, Cell): # Cell vs Cell comparison for providing a strict sort order @@ -336,7 +336,7 @@ def __common_cmp__(self, other, operator_method): else: me = max(self.bound) - # Work around to handle netcdftime.datetime comparison, which + # Work around to handle cftime.datetime comparison, which # doesn't return NotImplemented on failure in some versions of the # library try: @@ -1127,7 +1127,7 @@ def cell(self, index): If `iris.FUTURE.cell_datetime_objects` is True, then this method will return Cell objects whose `points` and `bounds` attributes contain either datetime.datetime instances or - netcdftime.datetime instances (depending on the calendar). + cftime.datetime instances (depending on the calendar). .. deprecated:: 2.0.0 diff --git a/lib/iris/cube.py b/lib/iris/cube.py index 4de1ba93c7..9c42299c42 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -3640,7 +3640,7 @@ def interpolate(self, sample_points, scheme, collapse_scalar=True): A sequence of (coordinate, points) pairs over which to interpolate. The values for coordinates that correspond to dates or times may optionally be supplied as datetime.datetime or - netcdftime.datetime instances. + cftime.datetime instances. * scheme: The type of interpolation to use to interpolate from this :class:`~iris.cube.Cube` to the given sample points. The diff --git a/lib/iris/fileformats/nimrod_load_rules.py b/lib/iris/fileformats/nimrod_load_rules.py index 3fdf2d22c1..3dbb47cc35 100644 --- a/lib/iris/fileformats/nimrod_load_rules.py +++ b/lib/iris/fileformats/nimrod_load_rules.py @@ -23,7 +23,7 @@ import warnings import cf_units -import netcdftime +import cftime import numpy as np import iris @@ -76,9 +76,9 @@ def units(cube, field): def time(cube, field): """Add a time coord to the cube.""" - valid_date = netcdftime.datetime(field.vt_year, field.vt_month, - field.vt_day, field.vt_hour, - field.vt_minute, field.vt_second) + valid_date = cftime.datetime(field.vt_year, field.vt_month, + field.vt_day, field.vt_hour, + field.vt_minute, field.vt_second) point = TIME_UNIT.date2num(valid_date) bounds = None @@ -97,9 +97,9 @@ def time(cube, field): def reference_time(cube, field): """Add a 'reference time' to the cube, if present in the field.""" if field.dt_year != field.int_mdi: - data_date = netcdftime.datetime(field.dt_year, field.dt_month, - field.dt_day, field.dt_hour, - field.dt_minute) + data_date = cftime.datetime(field.dt_year, field.dt_month, + field.dt_day, field.dt_hour, + field.dt_minute) ref_time_coord = DimCoord(TIME_UNIT.date2num(data_date), standard_name='forecast_reference_time', diff --git a/lib/iris/fileformats/pp.py b/lib/iris/fileformats/pp.py index 3445255654..24dab953f0 100644 --- a/lib/iris/fileformats/pp.py +++ b/lib/iris/fileformats/pp.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2017, Met Office +# (C) British Crown Copyright 2010 - 2018, Met Office # # This file is part of Iris. # @@ -35,7 +35,7 @@ import cf_units import numpy as np import numpy.ma as ma -import netcdftime +import cftime from iris._deprecation import warn_deprecated from iris._lazy_data import as_concrete_data, as_lazy_data, is_lazy_data @@ -1425,8 +1425,8 @@ class PPField2(PPField): def _get_t1(self): if not hasattr(self, '_t1'): - self._t1 = netcdftime.datetime(self.lbyr, self.lbmon, self.lbdat, - self.lbhr, self.lbmin) + self._t1 = cftime.datetime(self.lbyr, self.lbmon, self.lbdat, + self.lbhr, self.lbmin) return self._t1 def _set_t1(self, dt): @@ -1440,14 +1440,14 @@ def _set_t1(self, dt): delattr(self, '_t1') t1 = property(_get_t1, _set_t1, None, - "A netcdftime.datetime object consisting of the lbyr, lbmon," + "A cftime.datetime object consisting of the lbyr, lbmon," " lbdat, lbhr, and lbmin attributes.") def _get_t2(self): if not hasattr(self, '_t2'): - self._t2 = netcdftime.datetime(self.lbyrd, self.lbmond, - self.lbdatd, self.lbhrd, - self.lbmind) + self._t2 = cftime.datetime(self.lbyrd, self.lbmond, + self.lbdatd, self.lbhrd, + self.lbmind) return self._t2 def _set_t2(self, dt): @@ -1461,7 +1461,7 @@ def _set_t2(self, dt): delattr(self, '_t2') t2 = property(_get_t2, _set_t2, None, - "A netcdftime.datetime object consisting of the lbyrd, " + "A cftime.datetime object consisting of the lbyrd, " "lbmond, lbdatd, lbhrd, and lbmind attributes.") @@ -1478,8 +1478,8 @@ class PPField3(PPField): def _get_t1(self): if not hasattr(self, '_t1'): - self._t1 = netcdftime.datetime(self.lbyr, self.lbmon, self.lbdat, - self.lbhr, self.lbmin, self.lbsec) + self._t1 = cftime.datetime(self.lbyr, self.lbmon, self.lbdat, + self.lbhr, self.lbmin, self.lbsec) return self._t1 def _set_t1(self, dt): @@ -1493,14 +1493,14 @@ def _set_t1(self, dt): delattr(self, '_t1') t1 = property(_get_t1, _set_t1, None, - "A netcdftime.datetime object consisting of the lbyr, lbmon," + "A cftime.datetime object consisting of the lbyr, lbmon," " lbdat, lbhr, lbmin, and lbsec attributes.") def _get_t2(self): if not hasattr(self, '_t2'): - self._t2 = netcdftime.datetime(self.lbyrd, self.lbmond, - self.lbdatd, self.lbhrd, - self.lbmind, self.lbsecd) + self._t2 = cftime.datetime(self.lbyrd, self.lbmond, + self.lbdatd, self.lbhrd, + self.lbmind, self.lbsecd) return self._t2 def _set_t2(self, dt): @@ -1514,7 +1514,7 @@ def _set_t2(self, dt): delattr(self, '_t2') t2 = property(_get_t2, _set_t2, None, - "A netcdftime.datetime object consisting of the lbyrd, " + "A cftime.datetime object consisting of the lbyrd, " "lbmond, lbdatd, lbhrd, lbmind, and lbsecd attributes.") diff --git a/lib/iris/fileformats/pp_load_rules.py b/lib/iris/fileformats/pp_load_rules.py index f709ac30c9..88a215546b 100644 --- a/lib/iris/fileformats/pp_load_rules.py +++ b/lib/iris/fileformats/pp_load_rules.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2017, Met Office +# (C) British Crown Copyright 2013 - 2018, Met Office # # This file is part of Iris. # @@ -295,7 +295,7 @@ def _collapse_degenerate_points_and_bounds(points, bounds=None, rtol=1.0e-7): All dimensions are tested, and if degenerate are reduced to length 1. Value equivalence is controlled by a tolerance, to avoid problems with - numbers from netcdftime.date2num, which has limited precision because of + numbers from cftime.date2num, which has limited precision because of the way it calculates with floats of days. Args: diff --git a/lib/iris/fileformats/pp_save_rules.py b/lib/iris/fileformats/pp_save_rules.py index 7fcc4ab639..84b4980647 100644 --- a/lib/iris/fileformats/pp_save_rules.py +++ b/lib/iris/fileformats/pp_save_rules.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2017, Met Office +# (C) British Crown Copyright 2017 - 2018, Met Office # # This file is part of Iris. # @@ -32,7 +32,7 @@ scalar_coord, vector_coord) from iris.util import is_regular, regular_step -import netcdftime +import cftime def _basic_coord_system_rules(cube, pp): @@ -129,7 +129,7 @@ def _general_time_rules(cube, pp): pp.lbtim.ia = 0 pp.lbtim.ib = 0 pp.t1 = time_coord.units.num2date(time_coord.points[0]) - pp.t2 = netcdftime.datetime(0, 0, 0) + pp.t2 = cftime.datetime(0, 0, 0) # Forecast. if (time_coord is not None and @@ -252,10 +252,10 @@ def _general_time_rules(cube, pp): pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0]) pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1]) if pp.t1.month == 12: - pp.t1 = netcdftime.datetime(pp.t1.year) + pp.t1 = cftime.datetime(pp.t1.year) else: - pp.t1 = netcdftime.datetime(pp.t1.year-1, 12, 1, 0, 0, 0) - pp.t2 = netcdftime.datetime(pp.t2.year, 3, 1, 0, 0, 0) + pp.t1 = cftime.datetime(pp.t1.year-1, 12, 1, 0, 0, 0) + pp.t2 = cftime.datetime(pp.t2.year, 3, 1, 0, 0, 0) _conditional_warning( time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1), "modified t1 for climatological seasonal mean") @@ -278,8 +278,8 @@ def _general_time_rules(cube, pp): # TODO: wut? pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0]) pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1]) - pp.t1 = netcdftime.datetime(pp.t1.year, 3, 1, 0, 0, 0) - pp.t2 = netcdftime.datetime(pp.t2.year, 6, 1, 0, 0, 0) + pp.t1 = cftime.datetime(pp.t1.year, 3, 1, 0, 0, 0) + pp.t2 = cftime.datetime(pp.t2.year, 6, 1, 0, 0, 0) _conditional_warning( time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1), "modified t1 for climatological seasonal mean") @@ -302,8 +302,8 @@ def _general_time_rules(cube, pp): # TODO: wut? pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0]) pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1]) - pp.t1 = netcdftime.datetime(pp.t1.year, 6, 1, 0, 0, 0) - pp.t2 = netcdftime.datetime(pp.t2.year, 9, 1, 0, 0, 0) + pp.t1 = cftime.datetime(pp.t1.year, 6, 1, 0, 0, 0) + pp.t2 = cftime.datetime(pp.t2.year, 9, 1, 0, 0, 0) _conditional_warning( time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1), "modified t1 for climatological seasonal mean") @@ -326,8 +326,8 @@ def _general_time_rules(cube, pp): # TODO: wut? pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0]) pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1]) - pp.t1 = netcdftime.datetime(pp.t1.year, 9, 1, 0, 0, 0) - pp.t2 = netcdftime.datetime(pp.t2.year, 12, 1, 0, 0, 0) + pp.t1 = cftime.datetime(pp.t1.year, 9, 1, 0, 0, 0) + pp.t2 = cftime.datetime(pp.t2.year, 12, 1, 0, 0, 0) _conditional_warning( time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1), "modified t1 for climatological seasonal mean") diff --git a/lib/iris/fileformats/um/_fast_load_structured_fields.py b/lib/iris/fileformats/um/_fast_load_structured_fields.py index 079a2d457d..5da80d523e 100644 --- a/lib/iris/fileformats/um/_fast_load_structured_fields.py +++ b/lib/iris/fileformats/um/_fast_load_structured_fields.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2017, Met Office +# (C) British Crown Copyright 2014 - 2018, Met Office # # This file is part of Iris. # @@ -28,7 +28,7 @@ import itertools -from netCDF4 import netcdftime +import cftime import numpy as np from iris._lazy_data import as_lazy_data, multidim_lazy_stack @@ -217,8 +217,8 @@ def _calculate_structure(self): arr_shape = arr.shape[:-1] extra_length = arr.shape[-1] # Flatten out the array apart from the last dimension, - # convert to netcdftime objects, then reshape back. - arr = np.array([netcdftime.datetime(*args) + # convert to cftime objects, then reshape back. + arr = np.array([cftime.datetime(*args) for args in arr.reshape(-1, extra_length)] ).reshape(arr_shape) vector_element_arrays_and_dims[name] = (arr, dims) diff --git a/lib/iris/pandas.py b/lib/iris/pandas.py index 780516f3ab..b4e1e9787a 100644 --- a/lib/iris/pandas.py +++ b/lib/iris/pandas.py @@ -28,7 +28,7 @@ import cf_units from cf_units import Unit -import netcdftime +import cftime import numpy as np import numpy.ma as ma import pandas @@ -59,7 +59,7 @@ def _add_iris_coord(cube, name, points, dim, calendar=None): # Convert datetime objects to Iris' current datetime representation. if points.dtype == object: - dt_types = (datetime.datetime, netcdftime.datetime) + dt_types = (datetime.datetime, cftime.datetime) if all([isinstance(i, dt_types) for i in points]): units = Unit("hours since epoch", calendar=calendar) points = units.date2num(points) diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 7ebe48ae03..64ce54c04c 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -37,8 +37,8 @@ import matplotlib.pyplot as plt from matplotlib.offsetbox import AnchoredText import matplotlib.ticker as mpl_ticker +import cftime import matplotlib.transforms as mpl_transforms -import netcdftime import numpy as np import numpy.ma as ma @@ -434,7 +434,7 @@ def _fixup_dates(coord, values): raise IrisError(msg) r = [nc_time_axis.CalendarDateTime( - netcdftime.datetime(*date), coord.units.calendar) + cftime.datetime(*date), coord.units.calendar) for date in dates] values = np.empty(len(r), dtype=object) values[:] = r diff --git a/lib/iris/tests/integration/plot/test_netcdftime.py b/lib/iris/tests/integration/plot/test_netcdftime.py index ea3cdd8d78..b4c19bd3e5 100644 --- a/lib/iris/tests/integration/plot/test_netcdftime.py +++ b/lib/iris/tests/integration/plot/test_netcdftime.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016 - 2017, Met Office +# (C) British Crown Copyright 2016 - 2018, Met Office # # This file is part of Iris. # @@ -26,7 +26,7 @@ # importing anything else import iris.tests as tests -import netcdftime +import cftime import numpy as np from iris.coords import AuxCoord @@ -51,8 +51,8 @@ def test_360_day_calendar(self): time_unit = Unit('days since 1970-01-01 00:00', calendar=calendar) time_coord = AuxCoord(np.arange(n), 'time', units=time_unit) times = [time_unit.num2date(point) for point in time_coord.points] - times = [netcdftime.datetime(atime.year, atime.month, atime.day, - atime.hour, atime.minute, atime.second) + times = [cftime.datetime(atime.year, atime.month, atime.day, + atime.hour, atime.minute, atime.second) for atime in times] expected_ydata = np.array([CalendarDateTime(time, calendar) for time in times]) diff --git a/lib/iris/tests/integration/test_PartialDateTime.py b/lib/iris/tests/integration/test_PartialDateTime.py index 207d89d150..706cf49f22 100644 --- a/lib/iris/tests/integration/test_PartialDateTime.py +++ b/lib/iris/tests/integration/test_PartialDateTime.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2017, Met Office +# (C) British Crown Copyright 2018, Met Office # # This file is part of Iris. # @@ -30,7 +30,7 @@ class Test(tests.IrisTest): @tests.skip_data - def test_netcdftime_interface(self): + def test_cftime_interface(self): # The `netcdf4` Python module introduced new calendar classes by v1.2.7 # This test is primarily of this interface, so the # final test assertion is simple. diff --git a/lib/iris/tests/test_pandas.py b/lib/iris/tests/test_pandas.py index 12b93addb7..aec432692a 100644 --- a/lib/iris/tests/test_pandas.py +++ b/lib/iris/tests/test_pandas.py @@ -27,9 +27,9 @@ import unittest import cf_units +import cftime import matplotlib.units import netCDF4 -import netcdftime import numpy as np @@ -101,21 +101,11 @@ def test_time_360(self): time_coord = DimCoord([0, 100.1, 200.2, 300.3, 400.4], long_name="time", units=time_unit) cube.add_dim_coord(time_coord, 0) - # TODO Remove this if statement as we move to cftime - if cf_units.__version__ > '1': - import cftime - expected_index = [cftime.Datetime360Day(2000, 1, 1, 0, 0), - cftime.Datetime360Day(2000, 4, 11, 2, 24), - cftime.Datetime360Day(2000, 7, 21, 4, 48), - cftime.Datetime360Day(2000, 11, 1, 7, 12), - cftime.Datetime360Day(2001, 2, 11, 9, 36)] - - else: - expected_index = [netcdftime.Datetime360Day(2000, 1, 1, 0, 0), - netcdftime.Datetime360Day(2000, 4, 11, 2, 24), - netcdftime.Datetime360Day(2000, 7, 21, 4, 48), - netcdftime.Datetime360Day(2000, 11, 1, 7, 12), - netcdftime.Datetime360Day(2001, 2, 11, 9, 36)] + expected_index = [cftime.Datetime360Day(2000, 1, 1, 0, 0), + cftime.Datetime360Day(2000, 4, 11, 2, 24), + cftime.Datetime360Day(2000, 7, 21, 4, 48), + cftime.Datetime360Day(2000, 11, 1, 7, 12), + cftime.Datetime360Day(2001, 2, 11, 9, 36)] series = iris.pandas.as_series(cube) self.assertArrayEqual(series, cube.data) @@ -248,16 +238,9 @@ def test_time_360(self): time_coord = DimCoord([100.1, 200.2], long_name="time", units=time_unit) cube.add_dim_coord(time_coord, 0) - # TODO: Remove this if statement once we move to exclusive cftime. - if cf_units.__version__ > '1': - # cf_units depends upon cftime, so we can safely assume we - # have it. - import cftime - expected_index = [cftime.Datetime360Day(2000, 4, 11, 2, 24), - cftime.Datetime360Day(2000, 7, 21, 4, 48)] - else: - expected_index = [netcdftime.Datetime360Day(2000, 4, 11, 2, 24), - netcdftime.Datetime360Day(2000, 7, 21, 4, 48)] + expected_index = [cftime.Datetime360Day(2000, 4, 11, 2, 24), + cftime.Datetime360Day(2000, 7, 21, 4, 48)] + expected_columns = [0, 1, 2, 3, 4] data_frame = iris.pandas.as_data_frame(cube) self.assertArrayEqual(data_frame, cube.data) @@ -355,14 +338,14 @@ def test_series_datetime_gregorian(self): tests.get_result_path(('pandas', 'as_cube', 'series_datetime_gregorian.cml'))) - def test_series_netcdftime_360(self): + def test_series_cftime_360(self): series = pandas.Series( [0, 1, 2, 3, 4], - index=[netcdftime.datetime(2001, 1, 1, 1, 1, 1), - netcdftime.datetime(2002, 2, 2, 2, 2, 2), - netcdftime.datetime(2003, 3, 3, 3, 3, 3), - netcdftime.datetime(2004, 4, 4, 4, 4, 4), - netcdftime.datetime(2005, 5, 5, 5, 5, 5)]) + index=[cftime.datetime(2001, 1, 1, 1, 1, 1), + cftime.datetime(2002, 2, 2, 2, 2, 2), + cftime.datetime(2003, 3, 3, 3, 3, 3), + cftime.datetime(2004, 4, 4, 4, 4, 4), + cftime.datetime(2005, 5, 5, 5, 5, 5)]) self.assertCML( iris.pandas.as_cube(series, calendars={0: cf_units.CALENDAR_360_DAY}), @@ -415,12 +398,12 @@ def test_data_frame_masked(self): tests.get_result_path(('pandas', 'as_cube', 'data_frame_masked.cml'))) - def test_data_frame_netcdftime_360(self): + def test_data_frame_cftime_360(self): data_frame = pandas.DataFrame( [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]], - index=[netcdftime.datetime(2001, 1, 1, 1, 1, 1), - netcdftime.datetime(2002, 2, 2, 2, 2, 2)], + index=[cftime.datetime(2001, 1, 1, 1, 1, 1), + cftime.datetime(2002, 2, 2, 2, 2, 2)], columns=[10, 11, 12, 13, 14]) self.assertCML( iris.pandas.as_cube( diff --git a/lib/iris/tests/test_pp_module.py b/lib/iris/tests/test_pp_module.py index acdaff158e..2c7ac0f1cf 100644 --- a/lib/iris/tests/test_pp_module.py +++ b/lib/iris/tests/test_pp_module.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2017, Met Office +# (C) British Crown Copyright 2013 - 2018, Met Office # # This file is part of Iris. # @@ -26,7 +26,7 @@ from types import GeneratorType import unittest -import netcdftime +import cftime from numpy.testing import assert_array_equal import iris.fileformats @@ -176,7 +176,8 @@ def test_lbproc_access(self): self.assertEqual(warn.call_count, 3) def test_t1_t2_access(self): - self.assertEqual(self.r[0].t1.timetuple(), netcdftime.datetime(1994, 12, 1, 0, 0).timetuple()) + self.assertEqual(self.r[0].t1.timetuple(), + cftime.datetime(1994, 12, 1, 0, 0).timetuple()) def test_save_single(self): temp_filename = iris.util.create_temp_filename(".pp") diff --git a/lib/iris/tests/unit/coords/test_Cell.py b/lib/iris/tests/unit/coords/test_Cell.py index 9a19962615..452fe12043 100644 --- a/lib/iris/tests/unit/coords/test_Cell.py +++ b/lib/iris/tests/unit/coords/test_Cell.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2017, Met Office +# (C) British Crown Copyright 2013 - 2018, Met Office # # This file is part of Iris. # @@ -26,7 +26,7 @@ import datetime import numpy as np -import netcdftime +import cftime from iris.coords import Cell from iris.tests import mock @@ -44,28 +44,28 @@ def assert_raises_on_comparison(self, cell, other, exception_type, regexp): with self.assertRaisesRegexp(exception_type, regexp): cell >= other - def test_netcdftime_cell(self): + def test_cftime_cell(self): # Check that cell comparison when the cell contains - # netcdftime.datetime objects raises an exception otherwise + # cftime.datetime objects raises an exception otherwise # this will fall back to id comparison producing unreliable # results. - cell = Cell(netcdftime.datetime(2010, 3, 21)) + cell = Cell(cftime.datetime(2010, 3, 21)) dt = mock.Mock(timetuple=mock.Mock()) self.assert_raises_on_comparison(cell, dt, TypeError, - 'determine the order of netcdftime') + 'determine the order of cftime') self.assert_raises_on_comparison(cell, 23, TypeError, - 'determine the order of netcdftime') + 'determine the order of cftime') self.assert_raises_on_comparison(cell, 'hello', TypeError, 'Unexpected type.*str') - def test_netcdftime_other(self): - # Check that cell comparison to a netcdftime.datetime object + def test_cftime_other(self): + # Check that cell comparison to a cftime.datetime object # raises an exception otherwise this will fall back to id comparison # producing unreliable results. - dt = netcdftime.datetime(2010, 3, 21) + dt = cftime.datetime(2010, 3, 21) cell = Cell(mock.Mock(timetuple=mock.Mock())) self.assert_raises_on_comparison(cell, dt, TypeError, - 'determine the order of netcdftime') + 'determine the order of cftime') def test_PartialDateTime_bounded_cell(self): # Check that bounded comparisions to a PartialDateTime @@ -81,7 +81,7 @@ def test_PartialDateTime_bounded_cell(self): def test_PartialDateTime_unbounded_cell(self): # Check that cell comparison works with PartialDateTimes. dt = PartialDateTime(month=6) - cell = Cell(netcdftime.datetime(2010, 3, 1)) + cell = Cell(cftime.datetime(2010, 3, 1)) self.assertLess(cell, dt) self.assertGreater(dt, cell) self.assertLessEqual(cell, dt) diff --git a/lib/iris/tests/unit/fileformats/pp_load_rules/test__all_other_rules.py b/lib/iris/tests/unit/fileformats/pp_load_rules/test__all_other_rules.py index 78b9dfa816..dc30769598 100644 --- a/lib/iris/tests/unit/fileformats/pp_load_rules/test__all_other_rules.py +++ b/lib/iris/tests/unit/fileformats/pp_load_rules/test__all_other_rules.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2017, Met Office +# (C) British Crown Copyright 2014 - 2018, Met Office # # This file is part of Iris. # @@ -28,7 +28,7 @@ import numpy as np from cf_units import Unit, CALENDAR_GREGORIAN, CALENDAR_360_DAY -from netcdftime import datetime as nc_datetime +from cftime import datetime as nc_datetime import cartopy.crs as ccrs import iris diff --git a/lib/iris/tests/unit/fileformats/pp_load_rules/test__convert_time_coords.py b/lib/iris/tests/unit/fileformats/pp_load_rules/test__convert_time_coords.py index f531a7a593..f00b39156f 100644 --- a/lib/iris/tests/unit/fileformats/pp_load_rules/test__convert_time_coords.py +++ b/lib/iris/tests/unit/fileformats/pp_load_rules/test__convert_time_coords.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2017, Met Office +# (C) British Crown Copyright 2014 - 2018, Met Office # # This file is part of Iris. # @@ -28,7 +28,7 @@ import iris.tests as tests from cf_units import Unit, CALENDAR_GREGORIAN -from netcdftime import datetime as nc_datetime +from cftime import datetime as nc_datetime import numpy as np from iris.coords import DimCoord, AuxCoord diff --git a/lib/iris/tests/unit/fileformats/pp_load_rules/test_convert.py b/lib/iris/tests/unit/fileformats/pp_load_rules/test_convert.py index aa406c8c4b..c1f8bdb891 100644 --- a/lib/iris/tests/unit/fileformats/pp_load_rules/test_convert.py +++ b/lib/iris/tests/unit/fileformats/pp_load_rules/test_convert.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2017, Met Office +# (C) British Crown Copyright 2013 - 2018, Met Office # # This file is part of Iris. # @@ -25,7 +25,7 @@ import iris.tests as tests import types -import netcdftime +import cftime import cf_units import numpy as np @@ -170,8 +170,8 @@ def test_365_calendar(self): f = mock.MagicMock(lbtim=SplittableInt(4, {'ia': 2, 'ib': 1, 'ic': 0}), lbyr=2013, lbmon=1, lbdat=1, lbhr=12, lbmin=0, lbsec=0, - t1=netcdftime.datetime(2013, 1, 1, 12, 0, 0), - t2=netcdftime.datetime(2013, 1, 2, 12, 0, 0), + t1=cftime.datetime(2013, 1, 1, 12, 0, 0), + t2=cftime.datetime(2013, 1, 2, 12, 0, 0), spec=PPField3) f.time_unit = six.create_bound_method(PPField3.time_unit, f) f.calendar = cf_units.CALENDAR_365_DAY diff --git a/lib/iris/tests/unit/fileformats/um/fast_load/test__convert_collation.py b/lib/iris/tests/unit/fileformats/um/fast_load/test__convert_collation.py index 78a9c5b8b5..d164bc5000 100644 --- a/lib/iris/tests/unit/fileformats/um/fast_load/test__convert_collation.py +++ b/lib/iris/tests/unit/fileformats/um/fast_load/test__convert_collation.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2017, Met Office +# (C) British Crown Copyright 2014 - 2018, Met Office # # This file is part of Iris. # @@ -24,7 +24,7 @@ import iris.tests as tests import cf_units -import netcdftime +import cftime import numpy as np from iris.fileformats.um._fast_load \ @@ -78,8 +78,8 @@ def _check_phenomenon(self, metadata, factory=None): def test_all_scalar(self): field = self._field() field.lbtim = 11 - field.t1 = netcdftime.datetime(1970, 1, 1, 18) - field.t2 = netcdftime.datetime(1970, 1, 1, 12) + field.t1 = cftime.datetime(1970, 1, 1, 18) + field.t2 = cftime.datetime(1970, 1, 1, 12) collation = mock.Mock(fields=[field], vector_dims_shape=(), element_arrays_and_dims={}) metadata = convert_collation(collation) @@ -99,10 +99,10 @@ def test_all_scalar(self): def test_vector_t1(self): field = self._field() field.lbtim = 11 - field.t2 = netcdftime.datetime(1970, 1, 1, 12) - t1 = ([netcdftime.datetime(1970, 1, 1, 18), - netcdftime.datetime(1970, 1, 2, 0), - netcdftime.datetime(1970, 1, 2, 6)], [0]) + field.t2 = cftime.datetime(1970, 1, 1, 12) + t1 = ([cftime.datetime(1970, 1, 1, 18), + cftime.datetime(1970, 1, 2, 0), + cftime.datetime(1970, 1, 2, 6)], [0]) collation = mock.Mock(fields=[field], vector_dims_shape=(3,), element_arrays_and_dims={'t1': t1}) metadata = convert_collation(collation) @@ -125,10 +125,10 @@ def test_vector_t1(self): def test_vector_t2(self): field = self._field() field.lbtim = 11 - field.t1 = netcdftime.datetime(1970, 1, 1, 18) - t2 = ([netcdftime.datetime(1970, 1, 1, 12), - netcdftime.datetime(1970, 1, 1, 15), - netcdftime.datetime(1970, 1, 1, 18)], [0]) + field.t1 = cftime.datetime(1970, 1, 1, 18) + t2 = ([cftime.datetime(1970, 1, 1, 12), + cftime.datetime(1970, 1, 1, 15), + cftime.datetime(1970, 1, 1, 18)], [0]) collation = mock.Mock(fields=[field], vector_dims_shape=(3,), element_arrays_and_dims={'t2': t2}) metadata = convert_collation(collation) @@ -153,8 +153,8 @@ def test_vector_t2(self): def test_vector_lbft(self): field = self._field() field.lbtim = 21 - field.t1 = netcdftime.datetime(1970, 1, 1, 12) - field.t2 = netcdftime.datetime(1970, 1, 1, 18) + field.t1 = cftime.datetime(1970, 1, 1, 12) + field.t2 = cftime.datetime(1970, 1, 1, 18) lbft = ([18, 15, 12], [0]) collation = mock.Mock(fields=[field], vector_dims_shape=(3,), element_arrays_and_dims={'lbft': lbft}) @@ -179,11 +179,11 @@ def test_vector_lbft(self): def test_vector_t1_and_t2(self): field = self._field() field.lbtim = 11 - t1 = ([netcdftime.datetime(1970, 1, 2, 6), - netcdftime.datetime(1970, 1, 2, 9), - netcdftime.datetime(1970, 1, 2, 12)], [1]) - t2 = ([netcdftime.datetime(1970, 1, 1, 12), - netcdftime.datetime(1970, 1, 2, 0)], [0]) + t1 = ([cftime.datetime(1970, 1, 2, 6), + cftime.datetime(1970, 1, 2, 9), + cftime.datetime(1970, 1, 2, 12)], [1]) + t2 = ([cftime.datetime(1970, 1, 1, 12), + cftime.datetime(1970, 1, 2, 0)], [0]) collation = mock.Mock(fields=[field], vector_dims_shape=(2, 3), element_arrays_and_dims={'t1': t1, 't2': t2}) metadata = convert_collation(collation) diff --git a/lib/iris/tests/unit/fileformats/um/fast_load_structured_fields/test_FieldCollation.py b/lib/iris/tests/unit/fileformats/um/fast_load_structured_fields/test_FieldCollation.py index bee4f84e97..12168a85d1 100644 --- a/lib/iris/tests/unit/fileformats/um/fast_load_structured_fields/test_FieldCollation.py +++ b/lib/iris/tests/unit/fileformats/um/fast_load_structured_fields/test_FieldCollation.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2017, Met Office +# (C) British Crown Copyright 2014 - 2018, Met Office # # This file is part of Iris. # @@ -28,7 +28,7 @@ import iris.tests as tests from iris._lazy_data import as_lazy_data -from netcdftime import datetime +from cftime import datetime import numpy as np from iris.fileformats.um._fast_load_structured_fields import FieldCollation diff --git a/lib/iris/tests/unit/plot/test__fixup_dates.py b/lib/iris/tests/unit/plot/test__fixup_dates.py index ef2f5bb11b..15d154d198 100644 --- a/lib/iris/tests/unit/plot/test__fixup_dates.py +++ b/lib/iris/tests/unit/plot/test__fixup_dates.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016, Met Office +# (C) British Crown Copyright 2016 - 2018, Met Office # # This file is part of Iris. # @@ -25,7 +25,7 @@ from cf_units import Unit import datetime -import netcdftime +import cftime from iris.coords import AuxCoord from iris.plot import _fixup_dates @@ -59,9 +59,9 @@ def test_360_day_calendar(self): unit = Unit('days since 2000-02-25 00:00:00', calendar='360_day') coord = AuxCoord([3, 4, 5], 'time', units=unit) result = _fixup_dates(coord, coord.points) - expected_datetimes = [netcdftime.datetime(2000, 2, 28), - netcdftime.datetime(2000, 2, 29), - netcdftime.datetime(2000, 2, 30)] + expected_datetimes = [cftime.datetime(2000, 2, 28), + cftime.datetime(2000, 2, 29), + cftime.datetime(2000, 2, 30)] self.assertArrayEqual([cdt.datetime for cdt in result], expected_datetimes) @@ -70,9 +70,9 @@ def test_365_day_calendar(self): unit = Unit('minutes since 2000-02-25 00:00:00', calendar='365_day') coord = AuxCoord([30, 60, 150], 'time', units=unit) result = _fixup_dates(coord, coord.points) - expected_datetimes = [netcdftime.datetime(2000, 2, 25, 0, 30), - netcdftime.datetime(2000, 2, 25, 1, 0), - netcdftime.datetime(2000, 2, 25, 2, 30)] + expected_datetimes = [cftime.datetime(2000, 2, 25, 0, 30), + cftime.datetime(2000, 2, 25, 1, 0), + cftime.datetime(2000, 2, 25, 2, 30)] self.assertArrayEqual([cdt.datetime for cdt in result], expected_datetimes) diff --git a/lib/iris/tests/unit/time/test_PartialDateTime.py b/lib/iris/tests/unit/time/test_PartialDateTime.py index b617eaaa8a..e70aef68fc 100644 --- a/lib/iris/tests/unit/time/test_PartialDateTime.py +++ b/lib/iris/tests/unit/time/test_PartialDateTime.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2018, Met Office # # This file is part of Iris. # @@ -27,7 +27,7 @@ import datetime import operator -import netcdftime +import cftime from iris.tests import mock from iris.time import PartialDateTime @@ -189,14 +189,14 @@ def setUp(self): self.op = operator.eq self.expected_value = EQ_EXPECTATIONS - def test_netcdftime_equal(self): + def test_cftime_equal(self): pdt = PartialDateTime(month=3, microsecond=2) - other = netcdftime.datetime(year=2013, month=3, day=20, second=2) + other = cftime.datetime(year=2013, month=3, day=20, second=2) self.assertTrue(pdt == other) - def test_netcdftime_not_equal(self): + def test_cftime_not_equal(self): pdt = PartialDateTime(month=3, microsecond=2) - other = netcdftime.datetime(year=2013, month=4, day=20, second=2) + other = cftime.datetime(year=2013, month=4, day=20, second=2) self.assertFalse(pdt == other) @@ -211,14 +211,14 @@ def setUp(self): self.op = operator.gt self.expected_value = GT_EXPECTATIONS - def test_netcdftime_greater(self): + def test_cftime_greater(self): pdt = PartialDateTime(month=3, microsecond=2) - other = netcdftime.datetime(year=2013, month=2, day=20, second=3) + other = cftime.datetime(year=2013, month=2, day=20, second=3) self.assertTrue(pdt > other) - def test_netcdftime_not_greater(self): + def test_cftime_not_greater(self): pdt = PartialDateTime(month=3, microsecond=2) - other = netcdftime.datetime(year=2013, month=3, day=20, second=3) + other = cftime.datetime(year=2013, month=3, day=20, second=3) self.assertFalse(pdt > other) diff --git a/lib/iris/time.py b/lib/iris/time.py index b57f7bb6a3..ef02b4dbfc 100644 --- a/lib/iris/time.py +++ b/lib/iris/time.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2018, Met Office # # This file is part of Iris. # @@ -35,7 +35,7 @@ class PartialDateTime(object): Comparisons are defined against any other class with all of the attributes: year, month, day, hour, minute, and second. Notably, this includes :class:`datetime.datetime` and - :class:`netcdftime.datetime`. Comparison also extends to the + :class:`cftime.datetime`. Comparison also extends to the microsecond attribute for classes, such as :class:`datetime.datetime`, which define it. diff --git a/requirements/core.txt b/requirements/core.txt index 4ed1b174e3..b2dbe52a05 100644 --- a/requirements/core.txt +++ b/requirements/core.txt @@ -4,10 +4,11 @@ # Without these, iris won't even import. cartopy +cf_units>=2 +cftime +dask>=0.17.1 matplotlib>=2 netcdf4<1.4 numpy>=1.14 -scipy # pyke (not pip installable) #conda: pyke -cf_units>=2 -dask>=0.17.1 +scipy