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

Bas Couwenberg gitlab at salsa.debian.org
Mon Apr 20 18:46:00 BST 2020



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


Commits:
41185043 by Bas Couwenberg at 2020-04-20T19:30:01+02:00
New upstream version 1.1.2+ds
- - - - -


4 changed files:

- Changelog
- README.md
- cftime/_cftime.pyx
- test/test_cftime.py


Changes:

=====================================
Changelog
=====================================
@@ -1,3 +1,9 @@
+version 1.1.2 (release tag v1.1.2rel)
+=====================================
+ * change dayofwk and dayofyr attributes into properties (issue #158)
+ * fix for issue #165 (python datetime should be returned when
+   only_use_cftime_datimes=False).
+
 version 1.1.1.2 (release tag v1.1.1.2rel)
 =========================================
  * include pyproject.toml in MANIFEST.in so it gets 


=====================================
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).
 
+4/20/2020:  version 1.1.2 released.  Code optimization, fix logic so `only_use_cftime_datimes=False` works as expected (issues #158 and #165).
+
 3/16/2020:  version 1.1.1 released.  Fix bug in microsecond formatting, ensure identical num2date results if input is an array of times, or a single scalar ([issue #143](https://github.com/Unidata/cftime/issues/143)).
 
 2/12/2020:  version 1.1.0 released.  `cftime.datetime` instances are returned by default from `num2date`


=====================================
cftime/_cftime.pyx
=====================================
@@ -52,7 +52,7 @@ cdef int32_t* days_per_month_array = [
 _rop_lookup = {Py_LT: '__gt__', Py_LE: '__ge__', Py_EQ: '__eq__',
                Py_GT: '__lt__', Py_GE: '__le__', Py_NE: '__ne__'}
 
-__version__ = '1.1.1.2'
+__version__ = '1.1.2'
 
 # 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.
@@ -90,7 +90,7 @@ cpdef int32_t get_days_in_month(bint isleap, int month) nogil:
 
 
 class real_datetime(datetime_python):
-    """add dayofwk, dayofyr attributes to python datetime instance"""
+    """add dayofwk, dayofyr, daysinmonth attributes to python datetime instance"""
     @property
     def dayofwk(self):
         # 0=Monday, 6=Sunday
@@ -98,6 +98,9 @@ class real_datetime(datetime_python):
     @property
     def dayofyr(self):
         return self.timetuple().tm_yday
+    @property
+    def daysinmonth(self):
+        return get_days_in_month(_is_leap(self.year,'proleptic_gregorian'), self.month)
     nanosecond = 0 # workaround for pandas bug (cftime issue #77)
 
 # start of the gregorian calendar
@@ -325,12 +328,16 @@ def num2date(times,units,calendar='standard',\
             msg='zero not allowed as a reference year, does not exist in Julian or Gregorian calendars'
             raise ValueError(msg)
 
-    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)
-    else: # use python datetime module
+    use_python_datetime = False
+    if only_use_python_datetimes and not only_use_cftime_datetimes:
+        # only_use_cftime_datetimes takes precendence
+        use_python_datetime = True
+    if not only_use_python_datetimes and not only_use_cftime_datetimes and can_use_python_datetime:
+        # if only_use_cftimes_datetimes and only_use_python_datetimes are False
+        # return python datetime if possible.
+        use_python_datetime = True
+
+    if use_python_datetime: # use python datetime module
         isscalar = False
         try:
             times[0]
@@ -383,6 +390,11 @@ OverflowError in python datetime, probably because year < datetime.MINYEAR"""
             return dates[0]
         else:
             return np.reshape(np.array(dates), shape)
+    else: # use cftime datetime
+        cdftime = utime(units, calendar=calendar,
+                        only_use_cftime_datetimes=only_use_cftime_datetimes)
+        return cdftime.num2date(times)
+
 
 
 def date2index(dates, nctime, calendar=None, select='exact'):
@@ -584,7 +596,6 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=True,
     except:
         isscalar = True
 
-    is_real_dateime = False
     if calendar == 'proleptic_gregorian':
         # datetime.datetime does not support years < 1
         #if year < 0:
@@ -594,17 +605,14 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=True,
             if (year < 0).any(): # netcdftime issue #28
                datetime_type = DatetimeProlepticGregorian
             else:
-               is_real_datetime = True
                datetime_type = real_datetime
     elif calendar in ('standard', 'gregorian'):
         # return a 'real' datetime instance if calendar is proleptic
         # Gregorian or Gregorian and all dates are after the
         # Julian/Gregorian transition
         if ind_before and not only_use_cftime_datetimes:
-            is_real_datetime = True
             datetime_type = real_datetime
         else:
-            is_real_datetime = False
             datetime_type = DatetimeGregorian
     elif calendar == "julian":
         datetime_type = DatetimeJulian
@@ -623,16 +631,10 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=True,
                             zip(year, month, day, hour, minute, second,
                                 microsecond,dayofwk,dayofyr)])
         else:
-            if is_real_datetime:
-                return np.array([datetime_type(*args)
-                                 for args in
-                                 zip(year, month, day, hour, minute, second,
-                                     microsecond)])
-            else:
-                return np.array([datetime_type(*args)
-                                 for args in
-                                 zip(year, month, day, hour, minute, second,
-                                     microsecond,dayofwk,dayofyr)])
+            return np.array([datetime_type(*args)
+                             for args in
+                             zip(year, month, day, hour, minute, second,
+                                 microsecond)])
 
     else:
         if return_tuple:
@@ -640,13 +642,8 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=True,
                     minute[0], second[0], microsecond[0],
                     dayofwk[0], dayofyr[0])
         else:
-            if is_real_datetime:
-                return datetime_type(year[0], month[0], day[0], hour[0],
-                                     minute[0], second[0], microsecond[0])
-            else:
-                return datetime_type(year[0], month[0], day[0], hour[0],
-                                     minute[0], second[0], microsecond[0],
-                                     dayofwk[0], dayofyr[0])
+            return datetime_type(year[0], month[0], day[0], hour[0],
+                                 minute[0], second[0], microsecond[0])
 
 class utime:
 
@@ -1244,9 +1241,10 @@ 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, daysinmonth
+    cdef readonly int year, month, day, hour, minute
     cdef readonly int second, microsecond
     cdef readonly str calendar
+    cdef readonly int _dayofwk, _dayofyr
 
     # Python's datetime.datetime uses the proleptic Gregorian
     # calendar. This boolean is used to decide whether a
@@ -1254,27 +1252,56 @@ Gregorial calendar.
     # datetime.datetime.
     cdef readonly bint datetime_compatible
 
-    def __init__(self, int year, int month, int day, int hour=0, int minute=0, int second=0,
-                 int microsecond=0, int dayofwk=-1, int dayofyr=1):
-        """dayofyr set to 1 by default - otherwise time.strftime will complain"""
+    def __init__(self, int year, int month, int day, int hour=0, int minute=0,
+                       int second=0, int microsecond=0, int dayofwk=-1, 
+                       int dayofyr = -1):
 
         self.year = year
         self.month = month
         self.day = day
         self.hour = hour
         self.minute = minute
-        self.dayofwk = dayofwk # 0 is Monday, 6 is Sunday
-        self.dayofyr = dayofyr
         self.second = second
         self.microsecond = microsecond
         self.calendar = ""
-        self.daysinmonth = -1
         self.datetime_compatible = True
+        self._dayofwk = dayofwk
+        self._dayofyr = dayofyr
 
     @property
     def format(self):
         return '%Y-%m-%d %H:%M:%S'
 
+    @property
+    def dayofwk(self):
+        if self._dayofwk < 0:
+            jd = JulianDayFromDate(self,calendar=self.calendar)
+            year,month,day,hour,mn,sec,ms,dayofwk,dayofyr =\
+            DateFromJulianDay(jd,return_tuple=True,calendar=self.calendar)
+            # cache results for dayofwk, dayofyr
+            self._dayofwk = dayofwk
+            self._dayofyr = dayofyr
+            return dayofwk
+        else:
+            return self._dayofwk
+
+    @property
+    def dayofyr(self):
+        if self._dayofyr < 0:
+            jd = JulianDayFromDate(self,calendar=self.calendar)
+            year,month,day,hour,mn,sec,ms,dayofwk,dayofyr =\
+            DateFromJulianDay(jd,return_tuple=True,calendar=self.calendar)
+            # cache results for dayofwk, dayofyr
+            self._dayofwk = dayofwk
+            self._dayofyr = dayofyr
+            return dayofyr
+        else:
+            return self._dayofyr
+
+    @property
+    def daysinmonth(self):
+        return get_days_in_month(_is_leap(self.year,self.calendar), self.month)
+
     def strftime(self, format=None):
         """
         Return a string representing the date, controlled by an explicit format
@@ -1392,7 +1419,7 @@ Gregorial calendar.
     cdef _getstate(self):
         return (self.year, self.month, self.day, self.hour,
                 self.minute, self.second, self.microsecond,
-                self.dayofwk, self.dayofyr)
+                self._dayofwk, self._dayofyr)
 
     def __reduce__(self):
         """special method that allows instance to be pickled"""
@@ -1465,18 +1492,14 @@ but uses the "noleap" ("365_day") calendar.
         self.calendar = "noleap"
         self.datetime_compatible = False
         assert_valid_date(self, no_leap, False, has_year_zero=True)
-        # if dayofwk, dayofyr not set, calculate them.
-        if self.dayofwk < 0:
-            jd = JulianDayFromDate(self,calendar='365_day')
-            year,month,day,hour,mn,sec,ms,dayofwk,dayofyr =\
-            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))
 
+    @property
+    def daysinmonth(self):
+        return _dpm[self.month-1]
+
 @cython.embedsignature(True)
 cdef class DatetimeAllLeap(datetime):
     """
@@ -1488,18 +1511,14 @@ but uses the "all_leap" ("366_day") calendar.
         self.calendar = "all_leap"
         self.datetime_compatible = False
         assert_valid_date(self, all_leap, False, has_year_zero=True)
-        # if dayofwk, dayofyr not set, calculate them.
-        if self.dayofwk < 0:
-            jd = JulianDayFromDate(self,calendar='366_day')
-            year,month,day,hour,mn,sec,ms,dayofwk,dayofyr =\
-            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))
 
+    @property
+    def daysinmonth(self):
+        return _dpm_leap[self.month-1]
+
 @cython.embedsignature(True)
 cdef class Datetime360Day(datetime):
     """
@@ -1511,18 +1530,14 @@ but uses the "360_day" calendar.
         self.calendar = "360_day"
         self.datetime_compatible = False
         assert_valid_date(self, no_leap, False, has_year_zero=True, is_360_day=True)
-        # if dayofwk, dayofyr not set, calculate them.
-        if self.dayofwk < 0:
-            jd = JulianDayFromDate(self,calendar='360_day')
-            year,month,day,hour,mn,sec,ms,dayofwk,dayofyr =\
-            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))
 
+    @property
+    def daysinmonth(self):
+        return _dpm_360[self.month-1]
+
 @cython.embedsignature(True)
 cdef class DatetimeJulian(datetime):
     """
@@ -1534,14 +1549,6 @@ but uses the "julian" calendar.
         self.calendar = "julian"
         self.datetime_compatible = False
         assert_valid_date(self, is_leap_julian, False)
-        # if dayofwk, dayofyr not set, calculate them.
-        if self.dayofwk < 0:
-            jd = JulianDayFromDate(self,calendar='julian')
-            year,month,day,hour,mn,sec,ms,dayofwk,dayofyr =\
-            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))
@@ -1571,14 +1578,6 @@ a datetime.datetime instance or vice versa.
         else:
             self.datetime_compatible = False
         assert_valid_date(self, is_leap_gregorian, True)
-        # if dayofwk, dayofyr not set, calculate them.
-        if self.dayofwk < 0:
-            jd = JulianDayFromDate(self,calendar='gregorian')
-            year,month,day,hour,mn,sec,ms,dayofwk,dayofyr =\
-            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))
@@ -1606,14 +1605,6 @@ format, and calendar.
         self.calendar = "proleptic_gregorian"
         self.datetime_compatible = True
         assert_valid_date(self, is_leap_proleptic_gregorian, False)
-        # if dayofwk, dayofyr not set, calculate them.
-        if self.dayofwk < 0:
-            jd = JulianDayFromDate(self,calendar='proleptic_gregorian')
-            year,month,day,hour,mn,sec,ms,dayofwk,dayofyr =\
-            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,
@@ -1820,7 +1811,7 @@ cdef tuple add_timedelta(datetime dt, delta, bint (*is_leap)(int), bint julian_g
             day += delta_days
             delta_days = 0
 
-    return (year, month, day, hour, minute, second, microsecond, -1, 1)
+    return (year, month, day, hour, minute, second, microsecond, -1, -1)
 
 # Add a datetime.timedelta to a cftime.datetime instance with the 360_day calendar.
 #
@@ -1862,7 +1853,7 @@ cdef tuple add_timedelta_360_day(datetime dt, delta):
     year += (month - 1) // 12
     month = (month - 1) % 12 + 1
 
-    return (year, month, day, hour, minute, second, microsecond, -1, 1)
+    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


=====================================
test/test_cftime.py
=====================================
@@ -336,7 +336,7 @@ class cftimeTestCase(unittest.TestCase):
         # check datetime hash
         d1 = datetimex(1995, 1, 1)
         d2 = datetime(1995, 1, 1)
-        d3 = datetimex(2001, 2, 30)
+        d3 = datetimex(2001, 2, 20)
         assert hash(d1) == hash(d1)
         assert hash(d1) == hash(d2)
         assert hash(d1) != hash(d3)
@@ -744,6 +744,9 @@ class cftimeTestCase(unittest.TestCase):
         d = cftime.num2date(1261440000.015625,units)
         # on windows only 100 ms precision
         assert(str(d)[0:24] == '2009-12-22 00:00:00.0156')
+        # issue #165: make sure python datetime returned
+        d=num2date(0,units="seconds since 2000-01-01 00:00:00",only_use_cftime_datetimes=False)
+        assert isinstance(d, datetime)
 
 class TestDate2index(unittest.TestCase):
 
@@ -1533,5 +1536,13 @@ def test_replace_dayofyr_or_dayofwk_error(date_type, argument):
     with pytest.raises(ValueError):
         date_type(1, 1, 1).replace(**{argument: 3})
 
+
+def test_dayofyr_after_timedelta_addition(date_type):
+    initial_date = date_type(1, 1, 2)
+    date_after_timedelta_addition = initial_date + timedelta(days=1)
+    assert initial_date.dayofyr == 2
+    assert date_after_timedelta_addition.dayofyr == 3
+
+
 if __name__ == '__main__':
     unittest.main()



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

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/commit/41185043a3d8f81d5c08cb8c67388c790f932b71
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/20200420/61c7cecf/attachment-0001.html>


More information about the Pkg-grass-devel mailing list