[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