[Git][debian-gis-team/cftime][upstream] New upstream version 1.2.0+ds

Bas Couwenberg gitlab at salsa.debian.org
Mon Jul 6 04:48:30 BST 2020



Bas Couwenberg pushed to branch upstream at Debian GIS Project / cftime


Commits:
2153605f by Bas Couwenberg at 2020-07-06T05:42:01+02:00
New upstream version 1.2.0+ds
- - - - -


8 changed files:

- .travis.yml
- Changelog
- README.md
- cftime/__init__.py
- cftime/_cftime.pyx
- docs/api.rst
- docs/installing.rst
- test/test_cftime.py


Changes:

=====================================
.travis.yml
=====================================
@@ -9,8 +9,8 @@ notifications:
 matrix:
   fast_finish: true
   include:
-    - name: "python-2.7"
-      env: PY=2.7
+#   - name: "python-2.7"
+#     env: PY=2.7
     - name: "python-3.7"
       env: PY=3.7
     - name: "python-3.8"
@@ -20,6 +20,10 @@ matrix:
     - name: "docs"
       env: PY=3
 
+#jobs:
+#  allow_failures:
+#    - env: PY=2.7
+
 before_install:
   # Build the conda testing environment.
   - >


=====================================
Changelog
=====================================
@@ -1,3 +1,19 @@
+version 1.2.0 (release tag v1.2.0rel)
+=====================================
+ * Return the default values of dayofwk and dayofyr when calendar
+   is '' (issue #173).
+ * fix treatment of masked arrays in num2date and date2num (issue #175).
+   Also make sure masked arrays are output from num2date/date2num if
+   masked arrays are input.
+ * Where possible, use timedelta arithmetic to decode times exactly within
+   num2date (issue #171).
+ * Make taking the difference between two cftime datetimes to produce a
+   timedelta exact to the microsecond; depending on the units encoding,
+   this enables date2num to be exact as well (issue #109).
+ * utime.date2num/utime.num2date now just call module level functions.
+   JulianDayFromDate/DateFromJulianDay no longer used internally (PR #180).
+
+
 version 1.1.3 (release tag v1.1.3rel)
 =====================================
  * add isoformat method for compatibility with python datetime (issue #152).


=====================================
README.md
=====================================
@@ -12,6 +12,8 @@ Time-handling functionality from netcdf4-python
 ## News
 For details on the latest updates, see the [Changelog](https://github.com/Unidata/cftime/blob/master/Changelog).
 
+7/06/2020:  version 1.2.0 released. New microsecond accurate algorithm for date2num/num2date contributed by @spencerkclark. Bugs fixed in masked array handling.
+
 5/12/2020:  version 1.1.3 released.  Add isoformat method for compatibility with python datetime (issue #152).
  Make 'standard' default calendar for cftime.datetime so that dayofwk,dayofyr methods don't fail (issue #169).
 


=====================================
cftime/__init__.py
=====================================
@@ -1,4 +1,4 @@
-from ._cftime import utime, JulianDayFromDate, DateFromJulianDay
+from ._cftime import utime, JulianDayFromDate, DateFromJulianDay, UNIT_CONVERSION_FACTORS
 from ._cftime import _parse_date, date2index, time2index
 from ._cftime import datetime, real_datetime
 from ._cftime import DatetimeNoLeap, DatetimeAllLeap, Datetime360Day, DatetimeJulian, \


=====================================
cftime/_cftime.pyx
=====================================
The diff for this file was not included because it is too large.

=====================================
docs/api.rst
=====================================
@@ -2,5 +2,5 @@ API
 ===
 
 .. automodule:: cftime
-   :members: datetime, date2num, num2date, num2pydate, date2index, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian
+   :members: datetime, date2num, num2date, num2pydate, date2index, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian, DateFromJulianDay, time2index
    :show-inheritance:


=====================================
docs/installing.rst
=====================================
@@ -4,7 +4,7 @@ Installation
 Required dependencies
 ---------------------
 
-- Python 2.7, 3.4, 3.5, or 3.6
+- Python: >3.2
 - `numpy <http://www.numpy.org/>`__ (1.7 or later)
 
 


=====================================
test/test_cftime.py
=====================================
@@ -5,7 +5,7 @@ import operator
 import sys
 import unittest
 from collections import namedtuple
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, MINYEAR
 
 import numpy as np
 import pytest
@@ -17,7 +17,7 @@ from cftime import real_datetime
 from cftime import (DateFromJulianDay, Datetime360Day, DatetimeAllLeap,
                     DatetimeGregorian, DatetimeJulian, DatetimeNoLeap,
                     DatetimeProlepticGregorian, JulianDayFromDate, _parse_date,
-                    date2index, date2num, num2date, utime)
+                    date2index, date2num, num2date, utime, UNIT_CONVERSION_FACTORS)
 
 try:
     from datetime import timezone
@@ -115,11 +115,8 @@ class cftimeTestCase(unittest.TestCase):
         t1 = self.cdftime_mixed.date2num(d1)
         d2 = datetime(1582, 10, 4, 18, tzinfo=est)
         t2 = self.cdftime_mixed.date2num(d2)
-        d3 = d2.replace(tzinfo=None)
-        t3 = self.cdftime_mixed.date2num(d3)
         assert_almost_equal(t1, 13865687.0)
-        assert_almost_equal(t2, 13865687.0)
-        assert_almost_equal(t3, 13865682.0)
+        assert_almost_equal(t2, 13865682.0) # est is 5 hours behind utc
 
     def test_tz_naive(self):
         """testing cftime"""
@@ -380,7 +377,7 @@ class cftimeTestCase(unittest.TestCase):
         ntimes = 1001
         verbose = True # print out max error diagnostics
         for calendar in calendars:
-            eps = 100.
+            eps = 1.
             units = 'microseconds since 2000-01-30 01:01:01'
             microsecs1 = date2num(dateref,units,calendar=calendar)
             maxerr = 0
@@ -397,7 +394,7 @@ class cftimeTestCase(unittest.TestCase):
                 print('calendar = %s max abs err (microsecs) = %s eps = %s' % \
                      (calendar,maxerr,eps))
             units = 'milliseconds since 1800-01-30 01:01:01'
-            eps = 0.1
+            eps = 0.001
             millisecs1 = date2num(dateref,units,calendar=calendar)
             maxerr = 0.
             for n in range(ntimes):
@@ -412,7 +409,7 @@ class cftimeTestCase(unittest.TestCase):
             if verbose:
                 print('calendar = %s max abs err (millisecs) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 1.e-3
+            eps = 1.e-5
             units = 'seconds since 0001-01-30 01:01:01'
             secs1 = date2num(dateref,units,calendar=calendar)
             maxerr = 0.
@@ -428,7 +425,7 @@ class cftimeTestCase(unittest.TestCase):
             if verbose:
                 print('calendar = %s max abs err (secs) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 1.e-5
+            eps = 1.e-6
             units = 'minutes since 0001-01-30 01:01:01'
             mins1 = date2num(dateref,units,calendar=calendar)
             maxerr = 0.
@@ -445,7 +442,7 @@ class cftimeTestCase(unittest.TestCase):
             if verbose:
                 print('calendar = %s max abs err (mins) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 1.e-6
+            eps = 1.e-8
             units = 'hours since 0001-01-30 01:01:01'
             hrs1 = date2num(dateref,units,calendar=calendar)
             maxerr = 0.
@@ -462,7 +459,7 @@ class cftimeTestCase(unittest.TestCase):
             if verbose:
                 print('calendar = %s max abs err (hours) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 1.e-8
+            eps = 1.e-9
             units = 'days since 0001-01-30 01:01:01'
             days1 = date2num(dateref,units,calendar=calendar)
             maxerr = 0.
@@ -697,6 +694,20 @@ class cftimeTestCase(unittest.TestCase):
         d2 = real_datetime(1582,10,4,12)
         assert (d1.dayofwk == d2.dayofwk == 0)
         assert (d1.dayofyr == d2.dayofyr == 277)
+
+        # issue 173: Return the default values of dayofwk and dayofyr
+        # when calendar is ''
+        d1 = datetimex(1582, 10, 4, 12, calendar='')
+        assert (d1.dayofwk == d1.dayofyr == -1)
+        d1 = datetimex(2020, 5, 20, calendar='')
+        assert (d1.dayofwk == d1.dayofyr == -1)
+        d1 = datetimex(2020, 5, 20, dayofwk=-2, dayofyr=-3, calendar='')
+        assert (d1.dayofwk == -2)
+        assert (d1.dayofyr == -3)
+        d1 = datetimex(2020, 5, 20, dayofwk=8, dayofyr=9, calendar='')
+        assert (d1.dayofwk == 8)
+        assert (d1.dayofyr == 9)
+        
         # issue 71: negative reference years
         # https://coastwatch.pfeg.noaa.gov/erddap/convert/time.html
         # gives 2446433 (365 days more - is it counting year 0?)
@@ -758,6 +769,42 @@ class cftimeTestCase(unittest.TestCase):
         # to fail.
         c = cftime.datetime(*cftime._parse_date('7480-01-01 00:00:00'))
         assert(c.strftime() == '7480-01-01 00:00:00')
+        # issue #175: masked values not treated properly in num2date
+        times = np.ma.masked_array([-3956.7499999995343,-999999999999],mask=[False,True])
+        units='days since 1858-11-17 00:00:00'
+        dates = num2date(times, units=units, calendar='standard',\
+        only_use_cftime_datetimes=False, only_use_python_datetimes=True)
+        test = dates == np.ma.masked_array([datetime(1848, 1, 17, 6, 0, 0, 40), None],mask=[0,1])
+        assert(test.all())
+        dates = num2date(times, units=units, calendar='standard')
+        assert(str(dates)=='[cftime.DatetimeGregorian(1848, 1, 17, 6, 0, 0, 40) --]')
+#  check that time range of 200,000 + years can be represented accurately
+        calendar='standard'
+        _MAX_INT64 = np.iinfo("int64").max
+        refdate = DatetimeGregorian(292277,10,24,0,0,1)
+        for unit in ['microseconds','milliseconds','seconds']:
+            units = '%s since 01-01-01' % unit
+            time = 292471*365*86400*(1000000//int(UNIT_CONVERSION_FACTORS[unit])) + 1000000//int(UNIT_CONVERSION_FACTORS[unit])
+            date = num2date(time,units,calendar=calendar)
+            assert(date == refdate)
+            # check round-trip
+            time2 = date2num(date,units,calendar=calendar)
+            date2 = num2date(time2,units,calendar=calendar)
+            # windows doesn't have a longdouble type (it defaults to float64)
+            if np.finfo(np.longdouble).precision == 18:
+                assert(date2 == refdate)
+# microsecond roundtrip accuracy preserved over time ranges of 286 years
+# (float64 can only represent integers exactly up to 2**53-1)
+        refdate=DatetimeGregorian(286,6,3,23,47,34,740992)
+        for unit in ['microseconds','milliseconds','seconds','hours','days']:
+            units = '%s since 01-01-01' % unit
+            time = (2**53 - 1)*(1/UNIT_CONVERSION_FACTORS[unit]) + 1/UNIT_CONVERSION_FACTORS[unit]
+            date = num2date(time,units,calendar=calendar)
+            assert(date == refdate)
+            # check round-trip
+            time2 = date2num(date,units,calendar=calendar)
+            date2 = num2date(time2,units,calendar=calendar)
+            assert(date2 == refdate)
 
 class TestDate2index(unittest.TestCase):
 
@@ -1139,6 +1186,10 @@ class DateTime(unittest.TestCase):
         self.assertEqual(self.date1_365_day.replace(minute=3).minute, 3)
         self.assertEqual(self.date1_365_day.replace(second=3).second, 3)
         self.assertEqual(self.date1_365_day.replace(microsecond=3).microsecond, 3)
+        d = datetimex(2000, 2, 3, calendar='noleap')
+        self.assertEqual(d.replace(year=1999).calendar, 'noleap')
+        d = datetimex(2000, 2, 3, calendar='')
+        self.assertEqual(d.replace(year=1999).calendar, '')
 
     def test_pickling(self):
         "Test reversibility of pickling."
@@ -1315,13 +1366,13 @@ class issue57TestCase(unittest.TestCase):
         for datestr in ("days since2017-05-01 ", "dayssince 2017-05-01 00:00", "days snce 2017-05-01 00:00", "days_since_2017-05-01 00:00",
             "days_since_2017-05-01_00:00"):
             self.assertRaises(
-                ValueError, cftime._cftime._dateparse, datestr)
+                ValueError, cftime._cftime._dateparse, datestr, 'standard')
 
             self.assertRaises(
-                ValueError, cftime._cftime.num2date, 1, datestr)
+                ValueError, cftime._cftime.num2date, 1, datestr, 'standard')
 
             self.assertRaises(
-                ValueError, cftime._cftime.date2num, datetime(1900, 1, 1, 0), datestr)
+                ValueError, cftime._cftime.date2num, datetime(1900, 1, 1, 0), datestr, 'standard')
 
 
 _DATE_TYPES = [DatetimeNoLeap, DatetimeAllLeap, DatetimeJulian, Datetime360Day,
@@ -1514,7 +1565,8 @@ def test_num2date_only_use_cftime_datetimes_post_gregorian(
 
 
 def test_repr():
-    expected = 'cftime.datetime(2000-01-01 00:00:00)'
+    #expected = 'cftime.datetime(2000-01-01 00:00:00)'
+    expected = 'cftime.datetime(2000, 1, 1, 0, 0, 0, 0)'
     assert repr(datetimex(2000, 1, 1)) == expected
 
 
@@ -1555,5 +1607,272 @@ def test_dayofyr_after_timedelta_addition(date_type):
     assert date_after_timedelta_addition.dayofyr == 3
 
 
+def test_exact_datetime_difference(date_type):
+    b = date_type(2000, 1, 2, 0, 0, 0, 5)
+    a = date_type(2000, 1, 2)
+    result = b - a
+    expected = timedelta(microseconds=5)
+    assert result == expected
+
+
+_SHAPES = [(4, ), (2, 2)]
+_MICROSECOND_UNITS = ["microseconds", "microsecond", "microsec", "microsecs"]
+_MILLISECOND_UNITS = ["milliseconds", "millisecond", "millisec", "millisecs", "ms"]
+_SECOND_UNITS = ["seconds", "second", "sec", "secs", "s"]
+_MINUTE_UNITS = ["minutes", "minute", "min", "mins"]
+_HOUR_UNITS = ["hours", "hour", "hr", "hrs", "h"]
+_DAY_UNITS = ["day", "days", "d"]
+_MONTH_UNITS = ["month", "months"]
+_DTYPES = [np.dtype("int64"), np.dtype("float64")]
+_STANDARD_CALENDARS = [
+    "standard",
+    "gregorian",
+    "proleptic_gregorian"
+]
+_REAL_WORLD_CALENDARS = [
+    "julian",
+    "standard",
+    "gregorian",
+    "proleptic_gregorian"
+]
+_ARTIFICIAL_CALENDARS = ["noleap", "all_leap", "360_day"]
+
+
+ at pytest.fixture(params=_SHAPES)
+def shape(request):
+    return request.param
+
+
+ at pytest.fixture(params=_DTYPES)
+def dtype(request):
+    return request.param
+
+
+ at pytest.fixture(params=list(_EXPECTED_DATE_TYPES.keys()))
+def calendar(request):
+    return request.param
+
+
+ at pytest.mark.parametrize("unit", _MICROSECOND_UNITS)
+def test_num2date_microsecond_units(calendar, unit, shape, dtype):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 1, 1, 0, 0, 0, 1),
+                         date_type(2000, 1, 1, 0, 0, 0, 2),
+                         date_type(2000, 1, 1, 0, 0, 0, 3),
+                         date_type(2000, 1, 1, 0, 0, 0, 4)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "{} since 2000-01-01".format(unit)
+    result = num2date(numeric_times, units=units, calendar=calendar)
+    np.testing.assert_equal(result, expected)
+
+
+ at pytest.mark.parametrize("unit", _MILLISECOND_UNITS)
+def test_num2date_millisecond_units(calendar, unit, shape, dtype):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 1, 1, 0, 0, 0, 1000),
+                         date_type(2000, 1, 1, 0, 0, 0, 2000),
+                         date_type(2000, 1, 1, 0, 0, 0, 3000),
+                         date_type(2000, 1, 1, 0, 0, 0, 4000)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "{} since 2000-01-01".format(unit)
+    result = num2date(numeric_times, units=units, calendar=calendar)
+    np.testing.assert_equal(result, expected)
+
+
+ at pytest.mark.parametrize("unit", _SECOND_UNITS)
+def test_num2date_second_units(calendar, unit, shape, dtype):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 1, 1, 0, 0, 1, 0),
+                         date_type(2000, 1, 1, 0, 0, 2, 0),
+                         date_type(2000, 1, 1, 0, 0, 3, 0),
+                         date_type(2000, 1, 1, 0, 0, 4, 0)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "{} since 2000-01-01".format(unit)
+    result = num2date(numeric_times, units=units, calendar=calendar)
+    np.testing.assert_equal(result, expected)
+
+
+ at pytest.mark.parametrize("unit", _MINUTE_UNITS)
+def test_num2date_minute_units(calendar, unit, shape, dtype):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 1, 1, 0, 1, 0, 0),
+                         date_type(2000, 1, 1, 0, 2, 0, 0),
+                         date_type(2000, 1, 1, 0, 3, 0, 0),
+                         date_type(2000, 1, 1, 0, 4, 0, 0)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "{} since 2000-01-01".format(unit)
+    result = num2date(numeric_times, units=units, calendar=calendar)
+    np.testing.assert_equal(result, expected)
+
+
+ at pytest.mark.parametrize("unit", _HOUR_UNITS)
+def test_num2date_hour_units(calendar, unit, shape, dtype):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 1, 1, 1, 0, 0, 0),
+                         date_type(2000, 1, 1, 2, 0, 0, 0),
+                         date_type(2000, 1, 1, 3, 0, 0, 0),
+                         date_type(2000, 1, 1, 4, 0, 0, 0)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "{} since 2000-01-01".format(unit)
+    result = num2date(numeric_times, units=units, calendar=calendar)
+    np.testing.assert_equal(result, expected)
+
+
+ at pytest.mark.parametrize("unit", _DAY_UNITS)
+def test_num2date_day_units(calendar, unit, shape, dtype):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 1, 2, 0, 0, 0, 0),
+                         date_type(2000, 1, 3, 0, 0, 0, 0),
+                         date_type(2000, 1, 4, 0, 0, 0, 0),
+                         date_type(2000, 1, 5, 0, 0, 0, 0)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "{} since 2000-01-01".format(unit)
+    result = num2date(numeric_times, units=units, calendar=calendar)
+    np.testing.assert_equal(result, expected)
+
+
+ at pytest.mark.parametrize("unit", _MONTH_UNITS)
+def test_num2date_month_units(calendar, unit, shape, dtype):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 2, 1, 0, 0, 0, 0),
+                         date_type(2000, 3, 1, 0, 0, 0, 0),
+                         date_type(2000, 4, 1, 0, 0, 0, 0),
+                         date_type(2000, 5, 1, 0, 0, 0, 0)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "{} since 2000-01-01".format(unit)
+
+    if calendar != "360_day":
+        with pytest.raises(ValueError):
+            num2date(numeric_times, units=units, calendar=calendar)
+    else:
+        result = num2date(numeric_times, units=units, calendar=calendar)
+        np.testing.assert_equal(result, expected)
+
+
+def test_num2date_only_use_python_datetimes(calendar, shape, dtype):
+    date_type = real_datetime
+    expected = np.array([date_type(2000, 1, 2, 0, 0, 0, 0),
+                         date_type(2000, 1, 3, 0, 0, 0, 0),
+                         date_type(2000, 1, 4, 0, 0, 0, 0),
+                         date_type(2000, 1, 5, 0, 0, 0, 0)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "days since 2000-01-01"
+    if calendar not in _STANDARD_CALENDARS:
+        with pytest.raises(ValueError):
+            num2date(numeric_times, units=units, calendar=calendar,
+                           only_use_python_datetimes=True,
+                           only_use_cftime_datetimes=False)
+    else:
+        result = num2date(numeric_times, units=units, calendar=calendar,
+                                only_use_python_datetimes=True,
+                                only_use_cftime_datetimes=False)
+        np.testing.assert_equal(result, expected)
+
+
+def test_num2date_use_pydatetime_if_possible(calendar, shape, dtype):
+    if calendar not in _STANDARD_CALENDARS:
+        date_type = _EXPECTED_DATE_TYPES[calendar]
+    else:
+        date_type = real_datetime
+
+    expected = np.array([date_type(2000, 1, 2, 0, 0, 0, 0),
+                         date_type(2000, 1, 3, 0, 0, 0, 0),
+                         date_type(2000, 1, 4, 0, 0, 0, 0),
+                         date_type(2000, 1, 5, 0, 0, 0, 0)]).reshape(shape)
+    numeric_times = np.array([1, 2, 3, 4]).reshape(shape).astype(dtype)
+    units = "days since 2000-01-01"
+    result = num2date(numeric_times, units=units, calendar=calendar,
+                            only_use_python_datetimes=False,
+                            only_use_cftime_datetimes=False)
+    np.testing.assert_equal(result, expected)
+
+
+ at pytest.mark.parametrize(
+    ["standard_calendar", "breakpoint"],
+    [("proleptic_gregorian", "{}-12-31T23:59:59.999999".format(MINYEAR - 1)),
+     ("gregorian", "1582-10-15"),
+     ("standard", "1582-10-15")]
+)
+def test_num2date_only_use_python_datetimes_invalid_basedate(
+    standard_calendar,
+    breakpoint
+):
+    units = "days since {}".format(breakpoint)
+    numeric_times = np.array([1, 2, 3, 4])
+    with pytest.raises(ValueError):
+        num2date(
+            numeric_times,
+            units=units,
+            calendar=standard_calendar,
+            only_use_python_datetimes=True,
+            only_use_cftime_datetimes=False
+        )
+
+
+ at pytest.mark.parametrize("real_world_calendar", _REAL_WORLD_CALENDARS)
+def test_num2date_invalid_zero_reference_year(real_world_calendar):
+    units = "days since 0000-01-01"
+    numeric_times = np.array([1, 2, 3, 4])
+    with pytest.raises(ValueError, match="zero not allowed as a reference"):
+        num2date(
+            numeric_times,
+            units=units,
+            calendar=real_world_calendar
+        )
+
+
+ at pytest.mark.parametrize("artificial_calendar", _ARTIFICIAL_CALENDARS)
+def test_num2date_valid_zero_reference_year(artificial_calendar):
+    units = "days since 0000-01-01"
+    numeric_times = np.array([1, 2, 3, 4])
+    num2date(numeric_times, units=units, calendar=artificial_calendar)
+
+
+def test_num2date_masked_array(calendar):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 1, 1, 1, 0, 0, 0),
+                         date_type(2000, 1, 1, 2, 0, 0, 0),
+                         date_type(2000, 1, 1, 3, 0, 0, 0),
+                         date_type(2000, 1, 1, 4, 0, 0, 0)])
+    mask = [False, False, True, False]
+    expected = np.ma.masked_array(expected, mask=mask)
+    numeric_times = np.ma.masked_array([1, 2, 3, 4], mask=mask)
+    units = "hours since 2000-01-01"
+    result = num2date(numeric_times, units=units, calendar=calendar)
+    np.testing.assert_equal(result, expected)
+
+
+def test_num2date_out_of_range():
+    numeric_times = 12 * np.array([200000, 400000, 600000])
+    units = "months since 2000-01-01"
+    with pytest.raises(OverflowError, match="time values outside range of 64 bit signed integers"):
+        num2date(numeric_times, units=units, calendar="360_day")
+
+
+def test_num2date_list_input(calendar):
+    date_type = _EXPECTED_DATE_TYPES[calendar]
+    expected = np.array([date_type(2000, 1, 1, 1, 0, 0, 0),
+                         date_type(2000, 1, 1, 2, 0, 0, 0),
+                         date_type(2000, 1, 1, 3, 0, 0, 0),
+                         date_type(2000, 1, 1, 4, 0, 0, 0)])
+    numeric_times = [1, 2, 3, 4]
+    units = "hours since 2000-01-01"
+    result = num2date(numeric_times, units=units, calendar=calendar)
+    np.testing.assert_equal(result, expected)
+
+
+def test_num2date_integer_upcast_required():
+    numeric_times = np.array([30, 60, 90, 120], dtype=np.int32)
+    units = "minutes since 2000-01-01"
+    expected = np.array([
+        Datetime360Day(2000, 1, 1, 0, 30, 0),
+        Datetime360Day(2000, 1, 1, 1, 0, 0),
+        Datetime360Day(2000, 1, 1, 1, 30, 0),
+        Datetime360Day(2000, 1, 1, 2, 0, 0)
+    ])
+    result = num2date(numeric_times, units=units, calendar="360_day")
+    np.testing.assert_equal(result, expected)
+
+
 if __name__ == '__main__':
     unittest.main()



View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/commit/2153605fbdd4574b6fd22eab81c430dac0db4424

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/commit/2153605fbdd4574b6fd22eab81c430dac0db4424
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20200706/28bd4c55/attachment-0001.html>


More information about the Pkg-grass-devel mailing list