[Git][debian-gis-team/cftime][upstream] 2 commits: New upstream version 1.1.0

Bas Couwenberg gitlab at salsa.debian.org
Fri Feb 14 06:08:41 GMT 2020



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


Commits:
aec29138 by Bas Couwenberg at 2020-02-14T06:45:42+01:00
New upstream version 1.1.0
- - - - -
3b9efa37 by Bas Couwenberg at 2020-02-14T06:54:23+01:00
New upstream version 1.1.0+ds
- - - - -


11 changed files:

- .appveyor.yml
- .gitignore
- .travis.yml
- + Changelog
- README.md
- cftime/__init__.py
- cftime/_cftime.pyx
- docs/api.rst
- + docs/index.html
- setup.py
- test/test_cftime.py


Changes:

=====================================
.appveyor.yml
=====================================
@@ -1,16 +1,17 @@
 environment:
-  CONDA_INSTALL_LOCN: C:\\Miniconda36-x64
+  PYTHON: "C:\\myminiconda3"
   matrix:
-    - TARGET_ARCH: x64
-      NPY: 1.16
-      PY: 3.6
 
     - TARGET_ARCH: x64
-      NPY: 1.16
+      NPY: 1.17
       PY: 3.7
 
-platform:
-    - x64
+    - TARGET_ARCH: x64
+      NPY: 1.17
+      PY: 3.8
+
+init:
+  - "ECHO %PYTHON_VERSION% %MINICONDA%"
 
 install:
   # If there is a newer build queued for the same PR, cancel this one.
@@ -24,18 +25,19 @@ install:
         throw "There are newer queued builds for this pull request, failing early." }
 
   # Add path, activate `conda` and update conda.
-  - cmd: call %CONDA_INSTALL_LOCN%\Scripts\activate.bat
-  - cmd: conda config --set always_yes yes --set changeps1 no --set show_channel_urls true
-  - cmd: conda update conda
-  - cmd: conda config --add channels conda-forge --force
-  - cmd: conda config --set channel_priority strict
-  - cmd: set PYTHONUNBUFFERED=1
-  - cmd: conda install conda-build vs2008_express_vc_python_patch
-  - cmd: call setup_x64
-
-  - cmd: conda.exe create --name TEST python=%PY% numpy=%NPY% cython pip pytest pytest-cov
-  - cmd: conda info --all
-  - cmd: conda activate TEST
+
+  - set URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe"
+  - curl -fsS -o miniconda3.exe %URL%
+  - start /wait "" miniconda3.exe /InstallationType=JustMe /RegisterPython=0 /S /D=%PYTHON%
+  - "set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
+  - call %PYTHON%\Scripts\activate
+  - conda config --set always_yes yes --set changeps1 no --set show_channel_urls true
+  - conda config --add channels conda-forge --force
+  - set PYTHONUNBUFFERED=1
+  - conda install conda-build vs2008_express_vc_python_patch
+  - call setup_x64
+  - conda create --name TEST python=%PY% numpy=%NPY% cython pip pytest pytest-cov
+  - conda activate TEST
 
 # Skip .NET project specific build phase.
 build: off


=====================================
.gitignore
=====================================
@@ -4,6 +4,5 @@
 build/
 *.egg?
 *.egg-info
-_build/
 __pycache__
 .pytest_cache/


=====================================
.travis.yml
=====================================
@@ -8,8 +8,8 @@ notifications:
 
 env:
   - PYTHON_VERSION=2.7
-  - PYTHON_VERSION=3.6
   - PYTHON_VERSION=3.7
+  - PYTHON_VERSION=3.8
 
 matrix:
   include:


=====================================
Changelog
=====================================
@@ -0,0 +1,29 @@
+version 1.1.0 (not yet released)
+================================
+
+ * improved exceptions for time differences (issue #128, PR #131).
+
+ * fix intersphinx entries (issue #133, PR #133)
+
+ * make only_use_cftime_datetimes=True by default, so cftime datetime
+   instances are returned by default by num2date (instead of returning python
+   datetime instances where possible). Issue #136, PR #135.
+
+ * Add daysinmonth attribute (issue #137, PR #138).
+
+ * If only_use_python_datetimes=True and only_use_cftime_datetimes=False,
+   num2date only returns python datetime instances and raises an exception
+   if this is not possible.  num2pydate convenience function added which just calls
+   num2date with only_use_python_datetimes=True and
+   only_use_cftime_datetimes=False.
+   Remove positive times check, raise ValueError if python datetime
+   tries to compute a date before MINYEAR (issue #134, PR #139)
+
+ * Fix for fractional seconds in reference date in units string (issue #140,
+   PR # 141).
+
+version 1.0.4.2 release
+=======================
+
+ * fix for issue #126 (date2num error when converting a DatetimeProlepticGregorian 
+   object). PR #127.


=====================================
README.md
=====================================
@@ -10,6 +10,14 @@ Time-handling functionality from netcdf4-python
 [![Commits Status](https://img.shields.io/github/commits-since/UniData/cftime/latest.svg)](https://github.com/UniData/cftime/commits/master)
 
 ## News
+For details on the latest updates, see the [Changelog](https://github.com/Unidata/cftime/blob/master/Changelog).
+
+2/12/2019:  version 1.1.0 released.  `cftime.datetime` instances are returned by default from `num2date`
+(instead of returning python datetime instances where possible ([issue #136](https://github.com/Unidata/cftime/issues/136)).  `num2pydate`
+convenience function added (always returns python datetime instances, [issue #134](https://github.com/Unidata/cftime/issues/134)). Fix for
+fraction seconds in reference date string ([issue #140](https://github.com/Unidata/cftime/issues/140)). Added `daysinmonth` attribute 
+([issue #137](https://github.com/Unidata/cftime/issues/137)).
+
 10/25/2019:  version 1.0.4.2 released (fix for [issue #126](https://github.com/Unidata/cftime/issues/126)).
 
 10/21/2019:  version 1.0.4 released.


=====================================
cftime/__init__.py
=====================================
@@ -5,5 +5,5 @@ from ._cftime import DatetimeNoLeap, DatetimeAllLeap, Datetime360Day, DatetimeJu
                          DatetimeGregorian, DatetimeProlepticGregorian
 from ._cftime import microsec_units, millisec_units, \
                          sec_units, hr_units, day_units, min_units
-from ._cftime import num2date, date2num, date2index
+from ._cftime import num2date, date2num, date2index, num2pydate
 from ._cftime import __version__


=====================================
cftime/_cftime.pyx
=====================================
@@ -4,6 +4,7 @@ Performs conversions of netCDF time coordinate data to/from datetime objects.
 
 from cpython.object cimport (PyObject_RichCompare, Py_LT, Py_LE, Py_EQ,
                              Py_NE, Py_GT, Py_GE)
+from numpy cimport int64_t, int32_t
 import cython
 import numpy as np
 import re
@@ -28,7 +29,7 @@ month_units =    ['month', 'months'] # only allowed for 360_day calendar
 _units = microsec_units+millisec_units+sec_units+min_units+hr_units+day_units
 # supported calendars. Includes synonyms ('standard'=='gregorian',
 # '366_day'=='all_leap','365_day'=='noleap')
-# see http://cfconventions.org/cf-conventions/cf-conventions.html#calendar 
+# see http://cfconventions.org/cf-conventions/cf-conventions.html#calendar
 # for definitions.
 _calendars = ['standard', 'gregorian', 'proleptic_gregorian',
               'noleap', 'julian', 'all_leap', '365_day', '366_day', '360_day']
@@ -39,11 +40,19 @@ cdef int[12] _dpm_360  = [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30]
 # Same as above, but SUM of previous months (no leap years).
 cdef int[13] _spm_365day = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
 cdef int[13] _spm_366day = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
+
+# Slightly more performant cython lookups than a 2D table
+# The first 12 entries correspond to month lengths for non-leap years.
+# The remaining 12 entries give month lengths for leap years
+cdef int32_t* days_per_month_array = [
+    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+    31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+
 # Reverse operator lookup for datetime.__richcmp__
 _rop_lookup = {Py_LT: '__gt__', Py_LE: '__ge__', Py_EQ: '__eq__',
                Py_GT: '__lt__', Py_GE: '__le__', Py_NE: '__ne__'}
 
-__version__ = '1.0.4.2'
+__version__ = '1.1.0'
 
 # Adapted from http://delete.me.uk/2005/03/iso8601.html
 # Note: This regex ensures that all ISO8601 timezone formats are accepted - but, due to legacy support for other timestrings, not all incorrect formats can be rejected.
@@ -57,8 +66,31 @@ ISO8601_REGEX = re.compile(r"(?P<year>[+-]?[0-9]{1,4})(-(?P<month>[0-9]{1,2})(-(
 TIMEZONE_REGEX = re.compile(
     "(?P<prefix>[+-])(?P<hours>[0-9]{2})(?:(?::(?P<minutes1>[0-9]{2}))|(?P<minutes2>[0-9]{2}))?")
 
+
+# Taken from pandas ccalendar.pyx
+ at cython.wraparound(False)
+ at cython.boundscheck(False)
+cpdef int32_t get_days_in_month(bint isleap, int month) nogil:
+    """
+    Return the number of days in the given month of the given year.
+    Parameters
+    ----------
+    leap : int [0,1]
+    month : int
+
+    Returns
+    -------
+    days_in_month : int
+    Notes
+    -----
+    Assumes that the arguments are valid.  Passing a month not between 1 and 12
+    risks a segfault.
+    """
+    return days_per_month_array[12 * isleap + month - 1]
+
+
 class real_datetime(datetime_python):
-    """add dayofwk and dayofyr attributes to python datetime instance"""
+    """add dayofwk, dayofyr attributes to python datetime instance"""
     @property
     def dayofwk(self):
         # 0=Monday, 6=Sunday
@@ -72,7 +104,7 @@ class real_datetime(datetime_python):
 gregorian = real_datetime(1582,10,15)
 
 def _datesplit(timestr):
-    """split a time string into two components, units and the remainder 
+    """split a time string into two components, units and the remainder
     after 'since'
     """
     try:
@@ -97,12 +129,14 @@ def _dateparse(timestr):
     year, month, day, hour, minute, second, microsecond, utc_offset =\
         _parse_date( isostring.strip() )
     if year >= MINYEAR:
-        basedate = real_datetime(year, month, day, hour, minute, second)
+        basedate = real_datetime(year, month, day, hour, minute, second,
+                microsecond)
         # subtract utc_offset from basedate time instance (which is timezone naive)
         basedate -= timedelta(days=utc_offset/1440.)
     else:
         if not utc_offset:
-            basedate = datetime(year, month, day, hour, minute, second)
+            basedate = datetime(year, month, day, hour, minute, second,
+                    microsecond)
         else:
             raise ValueError('cannot use utc_offset for reference years <= 0')
     return basedate
@@ -215,8 +249,19 @@ def date2num(dates,units,calendar='standard'):
             return cdftime.date2num(dates)
 
 
-def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
-    """num2date(times,units,calendar='standard')
+def num2pydate(times,units,calendar='standard'):
+    """num2pydate(times,units,calendar='standard')
+    Always returns python datetime.datetime
+    objects and raise an error if this is not possible.
+
+    Same as
+    num2date(times,units,calendar,only_use_cftime_datetimes=False,only_use_python_datetimes=True)
+    """
+    return num2date(times,units,calendar,only_use_cftime_datetimes=False,only_use_python_datetimes=True)
+
+def num2date(times,units,calendar='standard',\
+             only_use_cftime_datetimes=True,only_use_python_datetimes=False):
+    """num2date(times,units,calendar='standard',only_use_cftime_datetimes=True,only_use_python_datetimes=False)
 
     Return datetime objects given numeric time values. The units
     of the numeric time values are described by the `units` argument
@@ -238,40 +283,54 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
     'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'`.
     Default is `'standard'`, which is a mixed Julian/Gregorian calendar.
 
-    **`only_use_cftime_datetimes`**: if False (default), datetime.datetime
+    **`only_use_cftime_datetimes`**: if False, python datetime.datetime
     objects are returned from num2date where possible; if True dates which
-    subclass cftime.datetime are returned for all calendars.
+    subclass cftime.datetime are returned for all calendars. Default `True`.
+
+    **`only_use_python_datetimes`**: always return python datetime.datetime
+    objects and raise an error if this is not possible. Ignored unless
+    `only_use_cftime_datetimes=False`. Default `False`.
 
     returns a datetime instance, or an array of datetime instances with
     approximately 100 microsecond accuracy.
 
-    ***Note***: The datetime instances returned are 'real' python datetime
+    ***Note***: If only_use_cftime_datetimes=False and
+    use_only_python_datetimes=False, the datetime instances
+    returned are 'real' python datetime
     objects if `calendar='proleptic_gregorian'`, or
     `calendar='standard'` or `'gregorian'`
     and the date is after the breakpoint between the Julian and
-    Gregorian calendars (1582-10-15). Otherwise, they are 'phony' datetime
-    objects which support some but not all the methods of 'real' python
+    Gregorian calendars (1582-10-15). Otherwise, they are ctime.datetime
+    objects which support some but not all the methods of native python
     datetime objects. The datetime instances
     do not contain a time-zone offset, even if the specified `units`
     contains one.
     """
     calendar = calendar.lower()
     basedate = _dateparse(units)
+
+    can_use_python_datetime=\
+      ((calendar == 'proleptic_gregorian' and basedate.year >= MINYEAR) or \
+       (calendar in ['gregorian','standard'] and basedate > gregorian))
+    if not only_use_cftime_datetimes and only_use_python_datetimes:
+        if not can_use_python_datetime:
+            msg='illegal calendar or reference date for python datetime'
+            raise ValueError(msg)
+
     (unit, ignore) = _datesplit(units)
+
     # real-world calendars limited to positive reference years.
     if calendar in ['julian', 'standard', 'gregorian', 'proleptic_gregorian']:
         if basedate.year == 0:
             msg='zero not allowed as a reference year, does not exist in Julian or Gregorian calendars'
             raise ValueError(msg)
 
-    postimes =  (np.asarray(times) > 0).all() # netcdf4-python issue #659
-    if only_use_cftime_datetimes:
+    if only_use_cftime_datetimes or not \
+       (only_use_python_datetimes and can_use_python_datetime):
         cdftime = utime(units, calendar=calendar,
                         only_use_cftime_datetimes=only_use_cftime_datetimes)
         return cdftime.num2date(times)
-    elif postimes and ((calendar == 'proleptic_gregorian' and basedate.year >= MINYEAR) or \
-       (calendar in ['gregorian','standard'] and basedate > gregorian)):
-        # use python datetime module,
+    else: # use python datetime module
         isscalar = False
         try:
             times[0]
@@ -313,15 +372,17 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
                 msecs = np.round(msecsd - secs*1.e6)
                 td = timedelta(days=days,seconds=secs,microseconds=msecs)
                 # add time delta to base date.
-                date = basedate + td
+                try:
+                    date = basedate + td
+                except OverflowError:
+                    msg="""
+OverflowError in python datetime, probably because year < datetime.MINYEAR"""
+                    raise ValueError(msg)
                 dates.append(date)
         if isscalar:
             return dates[0]
         else:
             return np.reshape(np.array(dates), shape)
-    else: # use cftime for other calendars
-        cdftime = utime(units,calendar=calendar)
-        return cdftime.num2date(times)
 
 
 def date2index(dates, nctime, calendar=None, select='exact'):
@@ -435,7 +496,7 @@ def JulianDayFromDate(date, calendar='standard'):
     else:
         return jd
 
-def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=False,
+def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=True,
                       return_tuple=False):
     """
 
@@ -451,10 +512,9 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=False,
 
     If only_use_cftime_datetimes is set to True, then cftime.datetime
     objects are returned for all calendars.  Otherwise the datetime object is a
-    'real' datetime object if the date falls in the Gregorian calendar
+    native python datetime object if the date falls in the Gregorian calendar
     (i.e. calendar='proleptic_gregorian', or  calendar = 'standard'/'gregorian'
-    and the date is after 1582-10-15).  In all other cases a 'phony' datetime
-    objects are used, which are actually instances of cftime.datetime.
+    and the date is after 1582-10-15).
     """
 
     julian = np.atleast_1d(np.array(JD, dtype=np.longdouble))
@@ -640,16 +700,15 @@ leap year if it is divisible by 4.
 The C{L{num2date}} and C{L{date2num}} class methods can used to convert datetime
 instances to/from the specified time units using the specified calendar.
 
-The datetime instances returned by C{num2date} are 'real' python datetime
+The datetime instances returned by C{num2date} are native python datetime
 objects if the date falls in the Gregorian calendar (i.e.
 C{calendar='proleptic_gregorian', 'standard'} or C{'gregorian'} and
-the date is after 1582-10-15). Otherwise, they are 'phony' datetime
+the date is after 1582-10-15). Otherwise, they are native datetime
 objects which are actually instances of C{L{cftime.datetime}}.  This is
 because the python datetime module cannot handle the weird dates in some
 calendars (such as C{'360_day'} and C{'all_leap'}) which don't exist in any real
 world calendar.
 
-
 Example usage:
 
 >>> from cftime import utime
@@ -690,7 +749,7 @@ it should be noted that udunits treats 0 AD as identical to 1 AD."
     """
 
     def __init__(self, unit_string, calendar='standard',
-                 only_use_cftime_datetimes=False):
+                 only_use_cftime_datetimes=True):
         """
 @param unit_string: a string of the form
 C{'time-units since <time-origin>'} defining the time units.
@@ -723,9 +782,9 @@ are:
  Proleptic Julian calendar, extended to dates after 1582-10-5. A year is a
  leap year if it is divisible by 4.
 
- at keyword only_use_cftime_datetimes: if False (default), datetime.datetime
+ at keyword only_use_cftime_datetimes: if False, datetime.datetime
 objects are returned from num2date where possible; if True dates which subclass
-cftime.datetime are returned for all calendars.
+cftime.datetime are returned for all calendars. Default True.
 
 @returns: A class instance which may be used for converting times from netCDF
 units to datetime objects.
@@ -823,10 +882,10 @@ units to datetime objects.
         Works for scalars, sequences and numpy arrays.
         Returns a scalar if input is a scalar, else returns a numpy array.
 
-        The datetime instances returned by C{num2date} are 'real' python datetime
+        The datetime instances returned by C{num2date} are native python datetime
         objects if the date falls in the Gregorian calendar (i.e.
         C{calendar='proleptic_gregorian'}, or C{calendar = 'standard'/'gregorian'} and
-        the date is after 1582-10-15). Otherwise, they are 'phony' datetime
+        the date is after 1582-10-15). Otherwise, they are cftime.datetime
         objects which are actually instances of cftime.datetime.  This is
         because the python datetime module cannot handle the weird dates in some
         calendars (such as C{'360_day'} and C{'all_leap'}) which
@@ -1184,7 +1243,7 @@ The base class implementing most methods of datetime classes that
 mimic datetime.datetime but support calendars other than the proleptic
 Gregorial calendar.
     """
-    cdef readonly int year, month, day, hour, minute, dayofwk, dayofyr
+    cdef readonly int year, month, day, hour, minute, dayofwk, dayofyr, daysinmonth
     cdef readonly int second, microsecond
     cdef readonly str calendar
 
@@ -1208,7 +1267,7 @@ Gregorial calendar.
         self.second = second
         self.microsecond = microsecond
         self.calendar = ""
-
+        self.daysinmonth = -1
         self.datetime_compatible = True
 
     @property
@@ -1368,7 +1427,12 @@ Gregorial calendar.
             elif isinstance(other, datetime_python):
                 # datetime - real_datetime
                 if not dt.datetime_compatible:
-                    raise ValueError("cannot compute the time difference between dates with different calendars")
+                    msg="""
+Cannot compute the time difference between dates with different calendars.
+One of the datetime objects may have been converted to a native python
+datetime instance.  Try using only_use_cftime_datetimes=True when creating the
+datetime object."""
+                    raise ValueError(msg)
                 return dt._to_real_datetime() - other
             elif isinstance(other, timedelta):
                 # datetime - timedelta
@@ -1379,7 +1443,12 @@ Gregorial calendar.
             if isinstance(self, datetime_python):
                 # real_datetime - datetime
                 if not other.datetime_compatible:
-                    raise ValueError("cannot compute the time difference between dates with different calendars")
+                    msg="""
+Cannot compute the time difference between dates with different calendars.
+One of the datetime objects may have been converted to a native python
+datetime instance.  Try using only_use_cftime_datetimes=True when creating the
+datetime object."""
+                    raise ValueError(msg)
                 return self - other._to_real_datetime()
             else:
                 return NotImplemented
@@ -1402,6 +1471,7 @@ but uses the "noleap" ("365_day") calendar.
             DateFromJulianDay(jd,return_tuple=True,calendar='365_day')
             self.dayofwk = dayofwk
             self.dayofyr = dayofyr
+        self.daysinmonth = _dpm[self.month-1]
 
     cdef _add_timedelta(self, delta):
         return DatetimeNoLeap(*add_timedelta(self, delta, no_leap, False))
@@ -1424,6 +1494,7 @@ but uses the "all_leap" ("366_day") calendar.
             DateFromJulianDay(jd,return_tuple=True,calendar='366_day')
             self.dayofwk = dayofwk
             self.dayofyr = dayofyr
+        self.daysinmonth = _dpm_leap[self.month-1]
 
     cdef _add_timedelta(self, delta):
         return DatetimeAllLeap(*add_timedelta(self, delta, all_leap, False))
@@ -1446,6 +1517,7 @@ but uses the "360_day" calendar.
             DateFromJulianDay(jd,return_tuple=True,calendar='360_day')
             self.dayofwk = dayofwk
             self.dayofyr = dayofyr
+        self.daysinmonth = 30
 
     cdef _add_timedelta(self, delta):
         return Datetime360Day(*add_timedelta_360_day(self, delta))
@@ -1468,6 +1540,7 @@ but uses the "julian" calendar.
             DateFromJulianDay(jd,return_tuple=True,calendar='julian')
             self.dayofwk = dayofwk
             self.dayofyr = dayofyr
+        self.daysinmonth = get_days_in_month(_is_leap(self.year, self.calendar), self.month)
 
     cdef _add_timedelta(self, delta):
         return DatetimeJulian(*add_timedelta(self, delta, is_leap_julian, False))
@@ -1504,6 +1577,7 @@ a datetime.datetime instance or vice versa.
             DateFromJulianDay(jd,return_tuple=True,calendar='gregorian')
             self.dayofwk = dayofwk
             self.dayofyr = dayofyr
+        self.daysinmonth = get_days_in_month(_is_leap(self.year, self.calendar), self.month)
 
     cdef _add_timedelta(self, delta):
         return DatetimeGregorian(*add_timedelta(self, delta, is_leap_gregorian, True))
@@ -1518,9 +1592,9 @@ Supports timedelta operations by overloading + and -.
 
 Has strftime, timetuple, replace, __repr__, and __str__ methods. The
 format of the string produced by __str__ is controlled by self.format
-(default %Y-%m-%d %H:%M:%S). Supports comparisons with other phony
+(default %Y-%m-%d %H:%M:%S). Supports comparisons with other
 datetime instances using the same calendar; comparison with
-datetime.datetime instances is possible for cftime.datetime
+native python datetime instances is possible for cftime.datetime
 instances using 'gregorian' and 'proleptic_gregorian' calendars.
 
 Instance variables are year,month,day,hour,minute,second,microsecond,dayofwk,dayofyr,
@@ -1538,6 +1612,7 @@ format, and calendar.
             DateFromJulianDay(jd,return_tuple=True,calendar='proleptic_gregorian')
             self.dayofwk = dayofwk
             self.dayofyr = dayofyr
+        self.daysinmonth = get_days_in_month(_is_leap(self.year, self.calendar), self.month)
 
     cdef _add_timedelta(self, delta):
         return DatetimeProlepticGregorian(*add_timedelta(self, delta,
@@ -1789,7 +1864,7 @@ cdef tuple add_timedelta_360_day(datetime dt, delta):
     return (year, month, day, hour, minute, second, microsecond, -1, 1)
 
 # Calendar calculations base on calcals.c by David W. Pierce
-# http://meteora.ucsd.edu/~pierce/calcalcs 
+# http://meteora.ucsd.edu/~pierce/calcalcs
 
 cdef _is_leap(int year, calendar):
     cdef int tyear


=====================================
docs/api.rst
=====================================
@@ -2,6 +2,5 @@ API
 ===
 
 .. automodule:: cftime
-   :members: datetime, date2num, date2index, num2date, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian
+   :members: datetime, date2num, num2date, num2pydate, date2index, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian
    :show-inheritance:
-   :noindex:
\ No newline at end of file


=====================================
docs/index.html
=====================================
@@ -0,0 +1 @@
+<meta http-equiv="refresh" content="0; url=./_build/html/index.html" />


=====================================
setup.py
=====================================
@@ -2,6 +2,7 @@ from __future__ import print_function
 
 import os
 import sys
+import numpy
 
 from setuptools import Command, Extension, setup
 
@@ -88,7 +89,8 @@ if any([arg in CMDS_NOCYTHONIZE for arg in sys.argv]):
 else:
     extension = Extension('{}._{}'.format(NAME, NAME),
                           sources=[CYTHON_FNAME],
-                          define_macros=DEFINE_MACROS)
+                          define_macros=DEFINE_MACROS,
+                          include_dirs=[numpy.get_include(),])
     ext_modules = [extension]
     if cythonize:
         ext_modules = cythonize(extension,


=====================================
test/test_cftime.py
=====================================
@@ -440,7 +440,8 @@ class cftimeTestCase(unittest.TestCase):
                 err = np.abs(mins1 - mins2)
                 maxerr = max(err,maxerr)
                 assert(err < eps)
-                assert(date1.strftime(dateformat) == date2.strftime(dateformat))
+                diff = abs(date1-date2)
+                assert(diff.microseconds < 100)
             if verbose:
                 print('calendar = %s max abs err (mins) = %s eps = %s' % \
                      (calendar,maxerr,eps))
@@ -456,7 +457,8 @@ class cftimeTestCase(unittest.TestCase):
                 err = np.abs(hrs1 - hrs2)
                 maxerr = max(err,maxerr)
                 assert(err < eps)
-                assert(date1.strftime(dateformat) == date2.strftime(dateformat))
+                diff = abs(date1-date2)
+                assert(diff.microseconds < 100)
             if verbose:
                 print('calendar = %s max abs err (hours) = %s eps = %s' % \
                      (calendar,maxerr,eps))
@@ -472,7 +474,8 @@ class cftimeTestCase(unittest.TestCase):
                 err = np.abs(days1 - days2)
                 maxerr = max(err,maxerr)
                 assert(err < eps)
-                assert(date1.strftime(dateformat) == date2.strftime(dateformat))
+                diff = abs(date1-date2)
+                assert(diff.microseconds < 100)
             if verbose:
                 print('calendar = %s max abs err (days) = %s eps = %s' % \
                      (calendar,maxerr,eps))
@@ -616,6 +619,13 @@ class cftimeTestCase(unittest.TestCase):
         assert (d.month == 1)
         assert (d.day == 1)
         assert (d.hour == 0)
+        # cftime issue #134
+        d = num2date(-657071, units, calendar='proleptic_gregorian',
+                     only_use_cftime_datetimes=False,only_use_python_datetimes=True)
+        assert(d==datetime(1,1,1,0))
+        self.assertRaises(ValueError, num2date, \
+        -657072, units, calendar='proleptic_gregorian',
+                     only_use_cftime_datetimes=False,only_use_python_datetimes=True)
         # issue 685: wrong time zone conversion
         # 'The following times all refer to the same moment: "18:30Z", "22:30+04", "1130-0700", and "15:00-03:30'
         # (https://en.wikipedia.org/w/index.php?title=ISO_8601&oldid=787811367#Time_offsets_from_UTC)
@@ -715,6 +725,10 @@ class cftimeTestCase(unittest.TestCase):
         d = cftime.DatetimeProlepticGregorian(1, 1, 1)
         assert(cftime.date2num(d, 'days since 0001-01-01',\
             'proleptic_gregorian') == 0.0)
+        # issue #140 (fractional seconds in reference date)
+        d = datetime.strptime('2018-01-23 09:27:10.950000',"%Y-%m-%d %H:%M:%S.%f")
+        units = 'seconds since 2018-01-23 09:31:42.94'
+        assert(cftime.date2num(d, units) == -271.99)
 
 class TestDate2index(unittest.TestCase):
 
@@ -895,8 +909,9 @@ class TestDate2index(unittest.TestCase):
                  datetime(1995, 11, 25, 18, 7, 59, 999999)]
         times2 = date2num(dates, units)
         dates2 = num2date(times2, units)
-        for date, date2 in zip(dates, dates2):
-            assert_equal(date, date2)
+        datediff = abs(dates-dates2)
+        for diff in datediff:
+            assert(diff.microseconds < 100) # tolerance of 100 ms
 
     def test_issue444(self):
         # make sure integer overflow not causing error in
@@ -1488,6 +1503,16 @@ def test_dayofwk_after_replace(date_type):
     assert result == expected
 
 
+def test_daysinmonth_non_leap(date_type, month, days_per_month_non_leap_year):
+    date = date_type(1, month, 1)
+    assert date.daysinmonth == days_per_month_non_leap_year
+
+
+def test_daysinmonth_leap(date_type, month, days_per_month_leap_year):
+    date = date_type(2000, month, 1)
+    assert date.daysinmonth == days_per_month_leap_year
+
+
 @pytest.mark.parametrize('argument', ['dayofyr', 'dayofwk'])
 def test_replace_dayofyr_or_dayofwk_error(date_type, argument):
     with pytest.raises(ValueError):



View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/compare/71e924ac5b654426ca9465932e0c2748a0285dd8...3b9efa37703d58d35db66d9394c753043b881b50

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/compare/71e924ac5b654426ca9465932e0c2748a0285dd8...3b9efa37703d58d35db66d9394c753043b881b50
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/20200214/0b50542d/attachment-0001.html>


More information about the Pkg-grass-devel mailing list