[Git][debian-gis-team/cftime][upstream] New upstream version 1.4.1+ds
Bas Couwenberg
gitlab at salsa.debian.org
Tue Feb 2 05:52:24 GMT 2021
Bas Couwenberg pushed to branch upstream at Debian GIS Project / cftime
Commits:
f7635189 by Bas Couwenberg at 2021-02-02T06:14:08+01:00
New upstream version 1.4.1+ds
- - - - -
6 changed files:
- Changelog
- README.md
- docs/api.rst
- src/cftime/_cftime.pyx
- src/cftime/_cftime_legacy.pyx
- test/test_cftime.py
Changes:
=====================================
Changelog
=====================================
@@ -1,3 +1,12 @@
+
+version 1.4.1 (release tag v1.4.1.rel)
+======================================
+ * Restore use of calendar-specific sub-classes in `cftime.num2date`,
+ `cftime.datetime.__add__`, and `cftime.datetime.__sub__`. The use of them
+ will be removed in a later release.
+ * add 'fromordinal' static method to create a cftime.datetime instance
+ from a julian day ordinal and calendar (inverse of 'toordinal').
+
version 1.4.0 (release tag v1.4.0.rel)
======================================
* `cftime.date2num` will now always return an array of integers, if the units
=====================================
README.md
=====================================
@@ -11,6 +11,12 @@ Time-handling functionality from netcdf4-python
## News
For details on the latest updates, see the [Changelog](https://github.com/Unidata/cftime/blob/master/Changelog).
+2/2/2021: Version 1.4.1 released. Restore use of calendar-specific subclasses
+in `cftime.num2date`, `cftime.datetime.__add__`, and `cftime.datetime.__sub__`.
+The use of this will be removed in a later release.
+Add 'fromordinal' static method to create a cftime.datetime instance
+from a julian day ordinal and calendar (inverse of 'toordinal').
+
2/1/2021: Version 1.4.0 released. License changed to MIT (GPL'ed code replaced).
Roundtrip accuracy improved for units other than microseconds. Added
cftime.datetime.toordinal method, returns integer julian day number.
=====================================
docs/api.rst
=====================================
@@ -2,5 +2,5 @@ API
===
.. automodule:: cftime
- :members: datetime, date2num, num2date, num2pydate, date2index, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian, DateFromJulianDay, time2index
+ :members: datetime, date2num, num2date, num2pydate, date2index, time2index
:show-inheritance:
=====================================
src/cftime/_cftime.pyx
=====================================
@@ -41,7 +41,7 @@ cdef int[13] _cumdayspermonth_leap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 27
_rop_lookup = {Py_LT: '__gt__', Py_LE: '__ge__', Py_EQ: '__eq__',
Py_GT: '__lt__', Py_GE: '__le__', Py_NE: '__ne__'}
-__version__ = '1.4.0'
+__version__ = '1.4.1'
# 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.
@@ -295,6 +295,18 @@ UNIT_CONVERSION_FACTORS = {
"months": 30 * 86400 * 1000000
}
+DATE_TYPES = {
+ "proleptic_gregorian": DatetimeProlepticGregorian,
+ "standard": DatetimeGregorian,
+ "noleap": DatetimeNoLeap,
+ "365_day": DatetimeNoLeap,
+ "all_leap": DatetimeAllLeap,
+ "366_day": DatetimeAllLeap,
+ "julian": DatetimeJulian,
+ "360_day": Datetime360Day,
+ "gregorian": DatetimeGregorian
+ }
+
def to_calendar_specific_datetime(dt, calendar, use_python_datetime):
if use_python_datetime:
return real_datetime(
@@ -306,7 +318,8 @@ def to_calendar_specific_datetime(dt, calendar, use_python_datetime):
dt.second,
dt.microsecond)
else:
- return datetime(
+ date_type = DATE_TYPES[calendar]
+ return date_type(
dt.year,
dt.month,
dt.day,
@@ -1089,24 +1102,39 @@ The default format of the string produced by strftime is controlled by self.form
cdef _add_timedelta(self, other):
return NotImplemented
+ @staticmethod
+ def fromordinal(jday,calendar='standard'):
+ """Create a datetime instance from a julian day ordinal and calendar
+ (inverseeof toordinal)."""
+ if calendar in ['standard','julian','gregorian']:
+ units = 'days since -4713-1-1-12'
+ elif calendar == 'proleptic_gregorian':
+ units = 'days since -4714-11-24-12'
+ else:
+ units = 'days since 0-1-1-12'
+ return num2date(jday,units=units,calendar=calendar)
+
def toordinal(self,fractional=False):
- """Return julian day ordinal.
+ """Return (integer) julian day ordinal.
- January 1 of the year -4713 is day 0 for the julian,gregorian and standard
- calendars.
+ Day 0 starts at noon January 1 of the year -4713 for the
+ julian, gregorian and standard calendars.
- November 11 of the year -4714 is day 0 for the proleptic gregorian calendar.
+ Day 0 starts at noon on November 24 of the year -4714 for the
+ proleptic gregorian calendar.
- January 1 of the year zero is day 0 for the 360_day, 365_day, 366_day and
- no_leap calendars.
+ Day 0 starts at noon on January 1 of the year zero is for the
+ 360_day, 365_day, 366_day, all_leap and noleap calendars.
If fractional=True, fractional part of day is included (default
False)."""
ijd = _IntJulianDayFromDate(self.year, self.month, self.day, self.calendar,
skip_transition=False,has_year_zero=self.has_year_zero)
if fractional:
- fracday = self.hour / 24.0 + self.minute / 1440.0 + (self.second +
- self.microsecond/1.e6) / 86400.0
+ fracday = self.hour / np.array(24.,np.longdouble) + \
+ self.minute / np.array(1440.0,np.longdouble) + \
+ (self.second + self.microsecond/(np.array(1.e6,np.longdouble)))/\
+ np.array(86400.0,np.longdouble)
# at this point jd is an integer representing noon UTC on the given
# year,month,day.
# compute fractional day from hour,minute,second,microsecond
@@ -1129,17 +1157,23 @@ The default format of the string produced by strftime is controlled by self.form
# return calendar-specific subclasses for backward compatbility,
# even though after 1.3.0 this is no longer necessary.
if calendar == '360_day':
- return dt.__class__(*add_timedelta_360_day(dt, delta),calendar=calendar)
+ #return dt.__class__(*add_timedelta_360_day(dt, delta),calendar=calendar)
+ return Datetime360Day(*add_timedelta_360_day(dt, delta))
elif calendar == 'noleap':
- return dt.__class__(*add_timedelta(dt, delta, no_leap, False, True),calendar=calendar)
+ #return dt.__class__(*add_timedelta(dt, delta, no_leap, False, True),calendar=calendar)
+ return DatetimeNoLeap(*add_timedelta(dt, delta, no_leap, False, True))
elif calendar == 'all_leap':
- return dt.__class__(*add_timedelta(dt, delta, all_leap, False, True),calendar=calendar)
+ #return dt.__class__(*add_timedelta(dt, delta, all_leap, False, True),calendar=calendar)
+ return DatetimeAllLeap(*add_timedelta(dt, delta, all_leap, False, True))
elif calendar == 'julian':
- return dt.__class__(*add_timedelta(dt, delta, is_leap_julian, False, False),calendar=calendar)
+ #return dt.__class__(*add_timedelta(dt, delta, is_leap_julian, False, False),calendar=calendar)
+ return DatetimeJulian(*add_timedelta(dt, delta, is_leap_julian, False, False))
elif calendar == 'gregorian':
- return dt.__class__(*add_timedelta(dt, delta, is_leap_gregorian, True, False),calendar=calendar)
+ #return dt.__class__(*add_timedelta(dt, delta, is_leap_gregorian, True, False),calendar=calendar)
+ return DatetimeGregorian(*add_timedelta(dt, delta, is_leap_gregorian, True, False))
elif calendar == 'proleptic_gregorian':
- return dt.__class__(*add_timedelta(dt, delta, is_leap_proleptic_gregorian, False, False),calendar=calendar)
+ #return dt.__class__(*add_timedelta(dt, delta, is_leap_proleptic_gregorian, False, False),calendar=calendar)
+ return DatetimeProlepticGregorian(*add_timedelta(dt, delta, is_leap_proleptic_gregorian, False, False))
else:
return NotImplemented
@@ -1176,18 +1210,24 @@ datetime object."""
# return calendar-specific subclasses for backward compatbility,
# even though after 1.3.0 this is no longer necessary.
if self.calendar == '360_day':
- return self.__class__(*add_timedelta_360_day(self, -other),calendar=self.calendar)
+ #return self.__class__(*add_timedelta_360_day(self, -other),calendar=self.calendar)
+ return Datetime360Day(*add_timedelta_360_day(self, -other))
elif self.calendar == 'noleap':
- return self.__class__(*add_timedelta(self, -other, no_leap, False, True),calendar=self.calendar)
+ #return self.__class__(*add_timedelta(self, -other, no_leap, False, True),calendar=self.calendar)
+ return DatetimeNoLeap(*add_timedelta(self, -other, no_leap, False, True))
elif self.calendar == 'all_leap':
- return self.__class__(*add_timedelta(self, -other, all_leap, False, True),calendar=self.calendar)
+ #return self.__class__(*add_timedelta(self, -other, all_leap, False, True),calendar=self.calendar)
+ return DatetimeAllLeap(*add_timedelta(self, -other, all_leap, False, True))
elif self.calendar == 'julian':
- return self.__class__(*add_timedelta(self, -other, is_leap_julian, False, False),calendar=self.calendar)
+ #return self.__class__(*add_timedelta(self, -other, is_leap_julian, False, False),calendar=self.calendar)
+ return DatetimeJulian(*add_timedelta(self, -other, is_leap_julian, False, False))
elif self.calendar == 'gregorian':
- return self.__class__(*add_timedelta(self, -other, is_leap_gregorian, True, False),calendar=self.calendar)
+ #return self.__class__(*add_timedelta(self, -other, is_leap_gregorian, True, False),calendar=self.calendar)
+ return DatetimeGregorian(*add_timedelta(self, -other, is_leap_gregorian, True, False))
elif self.calendar == 'proleptic_gregorian':
- return self.__class__(*add_timedelta(self, -other,
- is_leap_proleptic_gregorian, False, False),calendar=self.calendar)
+ #return self.__class__(*add_timedelta(self, -other,
+ # is_leap_proleptic_gregorian, False, False),calendar=self.calendar)
+ return DatetimeProlepticGregorian(*add_timedelta(self, -other, is_leap_proleptic_gregorian, False, False))
else:
return NotImplemented
else:
@@ -1594,5 +1634,134 @@ cdef _IntJulianDayFromDate(int year,int month,int day,calendar,skip_transition=F
else:
return jday_greg
+ at cython.embedsignature(True)
+cdef class DatetimeNoLeap(datetime):
+ """
+Phony datetime object which mimics the python datetime object,
+but uses the "noleap" ("365_day") calendar.
+ """
+ def __init__(self, *args, **kwargs):
+ kwargs['calendar']='noleap'
+ super().__init__(*args, **kwargs)
+ def __repr__(self):
+ return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
+ self.__class__.__name__,
+ self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
+ cdef _getstate(self):
+ return (self.year, self.month, self.day, self.hour,
+ self.minute, self.second, self.microsecond,
+ self._dayofwk, self._dayofyr)
+
+ at cython.embedsignature(True)
+cdef class DatetimeAllLeap(datetime):
+ """
+Phony datetime object which mimics the python datetime object,
+but uses the "all_leap" ("366_day") calendar.
+ """
+ def __init__(self, *args, **kwargs):
+ kwargs['calendar']='all_leap'
+ super().__init__(*args, **kwargs)
+ def __repr__(self):
+ return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
+ self.__class__.__name__,
+ self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
+ cdef _getstate(self):
+ return (self.year, self.month, self.day, self.hour,
+ self.minute, self.second, self.microsecond,
+ self._dayofwk, self._dayofyr)
+
+ at cython.embedsignature(True)
+cdef class Datetime360Day(datetime):
+ """
+Phony datetime object which mimics the python datetime object,
+but uses the "360_day" calendar.
+ """
+ def __init__(self, *args, **kwargs):
+ kwargs['calendar']='360_day'
+ super().__init__(*args, **kwargs)
+ def __repr__(self):
+ return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
+ self.__class__.__name__,
+ self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
+ cdef _getstate(self):
+ return (self.year, self.month, self.day, self.hour,
+ self.minute, self.second, self.microsecond,
+ self._dayofwk, self._dayofyr)
+
+ at cython.embedsignature(True)
+cdef class DatetimeJulian(datetime):
+ """
+Phony datetime object which mimics the python datetime object,
+but uses the "julian" calendar.
+ """
+ def __init__(self, *args, **kwargs):
+ kwargs['calendar']='julian'
+ super().__init__(*args, **kwargs)
+ def __repr__(self):
+ return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
+ self.__class__.__name__,
+ self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
+ cdef _getstate(self):
+ return (self.year, self.month, self.day, self.hour,
+ self.minute, self.second, self.microsecond,
+ self._dayofwk, self._dayofyr)
+
+
+ at cython.embedsignature(True)
+cdef class DatetimeGregorian(datetime):
+ """
+Phony datetime object which mimics the python datetime object,
+but uses the mixed Julian-Gregorian ("standard", "gregorian") calendar.
+
+The last date of the Julian calendar is 1582-10-4, which is followed
+by 1582-10-15, using the Gregorian calendar.
+
+Instances using the date after 1582-10-15 can be compared to
+datetime.datetime instances and used to compute time differences
+(datetime.timedelta) by subtracting a DatetimeGregorian instance from
+a datetime.datetime instance or vice versa.
+ """
+ def __init__(self, *args, **kwargs):
+ kwargs['calendar']='gregorian'
+ super().__init__(*args, **kwargs)
+ def __repr__(self):
+ return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
+ self.__class__.__name__,
+ self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
+ cdef _getstate(self):
+ return (self.year, self.month, self.day, self.hour,
+ self.minute, self.second, self.microsecond,
+ self._dayofwk, self._dayofyr)
+
+ at cython.embedsignature(True)
+cdef class DatetimeProlepticGregorian(datetime):
+ """
+Phony datetime object which mimics the python datetime object,
+but allows for dates that don't exist in the proleptic gregorian calendar.
+
+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
+datetime instances using the same calendar; comparison with
+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,
+format, and calendar.
+ """
+ def __init__(self, *args, **kwargs):
+ kwargs['calendar']='proleptic_gregorian'
+ super().__init__( *args, **kwargs)
+ def __repr__(self):
+ return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
+ self.__class__.__name__,
+ self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
+ cdef _getstate(self):
+ return (self.year, self.month, self.day, self.hour,
+ self.minute, self.second, self.microsecond,
+ self._dayofwk, self._dayofyr)
+
# include legacy stuff no longer used by cftime.datetime
include "_cftime_legacy.pyx"
=====================================
src/cftime/_cftime_legacy.pyx
=====================================
@@ -1,134 +1,5 @@
# stuff below no longer used by cftime.datetime, kept here for backwards compatibility.
- at cython.embedsignature(True)
-cdef class DatetimeNoLeap(datetime):
- """
-Phony datetime object which mimics the python datetime object,
-but uses the "noleap" ("365_day") calendar.
- """
- def __init__(self, *args, **kwargs):
- kwargs['calendar']='noleap'
- super().__init__(*args, **kwargs)
- def __repr__(self):
- return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
- self.__class__.__name__,
- self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
- cdef _getstate(self):
- return (self.year, self.month, self.day, self.hour,
- self.minute, self.second, self.microsecond,
- self._dayofwk, self._dayofyr)
-
- at cython.embedsignature(True)
-cdef class DatetimeAllLeap(datetime):
- """
-Phony datetime object which mimics the python datetime object,
-but uses the "all_leap" ("366_day") calendar.
- """
- def __init__(self, *args, **kwargs):
- kwargs['calendar']='all_leap'
- super().__init__(*args, **kwargs)
- def __repr__(self):
- return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
- self.__class__.__name__,
- self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
- cdef _getstate(self):
- return (self.year, self.month, self.day, self.hour,
- self.minute, self.second, self.microsecond,
- self._dayofwk, self._dayofyr)
-
- at cython.embedsignature(True)
-cdef class Datetime360Day(datetime):
- """
-Phony datetime object which mimics the python datetime object,
-but uses the "360_day" calendar.
- """
- def __init__(self, *args, **kwargs):
- kwargs['calendar']='360_day'
- super().__init__(*args, **kwargs)
- def __repr__(self):
- return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
- self.__class__.__name__,
- self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
- cdef _getstate(self):
- return (self.year, self.month, self.day, self.hour,
- self.minute, self.second, self.microsecond,
- self._dayofwk, self._dayofyr)
-
- at cython.embedsignature(True)
-cdef class DatetimeJulian(datetime):
- """
-Phony datetime object which mimics the python datetime object,
-but uses the "julian" calendar.
- """
- def __init__(self, *args, **kwargs):
- kwargs['calendar']='julian'
- super().__init__(*args, **kwargs)
- def __repr__(self):
- return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
- self.__class__.__name__,
- self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
- cdef _getstate(self):
- return (self.year, self.month, self.day, self.hour,
- self.minute, self.second, self.microsecond,
- self._dayofwk, self._dayofyr)
-
- at cython.embedsignature(True)
-cdef class DatetimeGregorian(datetime):
- """
-Phony datetime object which mimics the python datetime object,
-but uses the mixed Julian-Gregorian ("standard", "gregorian") calendar.
-
-The last date of the Julian calendar is 1582-10-4, which is followed
-by 1582-10-15, using the Gregorian calendar.
-
-Instances using the date after 1582-10-15 can be compared to
-datetime.datetime instances and used to compute time differences
-(datetime.timedelta) by subtracting a DatetimeGregorian instance from
-a datetime.datetime instance or vice versa.
- """
- def __init__(self, *args, **kwargs):
- kwargs['calendar']='gregorian'
- super().__init__(*args, **kwargs)
- def __repr__(self):
- return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
- self.__class__.__name__,
- self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
- cdef _getstate(self):
- return (self.year, self.month, self.day, self.hour,
- self.minute, self.second, self.microsecond,
- self._dayofwk, self._dayofyr)
-
- at cython.embedsignature(True)
-cdef class DatetimeProlepticGregorian(datetime):
- """
-Phony datetime object which mimics the python datetime object,
-but allows for dates that don't exist in the proleptic gregorian calendar.
-
-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
-datetime instances using the same calendar; comparison with
-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,
-format, and calendar.
- """
- def __init__(self, *args, **kwargs):
- kwargs['calendar']='proleptic_gregorian'
- super().__init__( *args, **kwargs)
- def __repr__(self):
- return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8})".format('cftime',
- self.__class__.__name__,
- self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond)
- cdef _getstate(self):
- return (self.year, self.month, self.day, self.hour,
- self.minute, self.second, self.microsecond,
- self._dayofwk, self._dayofyr)
-
-
# The following function (_IntJulianDayToDate) is based on
# algorithms described in the book
# "Calendrical Calculations" by Dershowitz and Rheingold, 3rd edition, Cambridge University Press, 2007
=====================================
test/test_cftime.py
=====================================
@@ -291,13 +291,16 @@ class cftimeTestCase(unittest.TestCase):
self.assertTrue(str(d) == str(date))
# test julian day from date, date from julian day
d = cftime.datetime(1858, 11, 17, calendar='standard')
- # toordinal should produce same result as JulidaDayFromDate
+ # toordinal should produce same result as JulianDayFromDate
mjd1 = d.toordinal(fractional=True)
mjd2 = JulianDayFromDate(d)
assert_almost_equal(mjd1, 2400000.5)
assert_almost_equal(mjd1,mjd2)
- date = DateFromJulianDay(mjd1)
- self.assertTrue(str(date) == str(d))
+ # fromordinal should produce the same result as DateFromJulianDay
+ date1 = DateFromJulianDay(mjd1)
+ date2 = cftime.datetime.fromordinal(mjd1)
+ self.assertTrue(str(date1) == str(d))
+ self.assertTrue(str(date1) == str(date2))
# test iso 8601 units string
d = datetime(1970, 1, 1, 1)
t = self.cdftime_iso.date2num(d)
@@ -740,8 +743,7 @@ class cftimeTestCase(unittest.TestCase):
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.datetime(1848, 1, 17, 6, 0, 0, 40, calendar='gregorian') --]")
+ 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
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/commit/f763518945bd5d770cf445c052264965f68449a1
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/commit/f763518945bd5d770cf445c052264965f68449a1
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/20210202/aded6799/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list