[Git][debian-gis-team/cftime][master] 5 commits: New upstream version 1.5.1+ds
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Sat Sep 25 05:34:02 BST 2021
Bas Couwenberg pushed to branch master at Debian GIS Project / cftime
Commits:
238c33a5 by Bas Couwenberg at 2021-09-25T06:24:53+02:00
New upstream version 1.5.1+ds
- - - - -
47db80ba by Bas Couwenberg at 2021-09-25T06:24:54+02:00
Update upstream source from tag 'upstream/1.5.1+ds'
Update to upstream version '1.5.1+ds'
with Debian dir 58a6f244cb72136eefb3dcca65a73cafcad9e787
- - - - -
a978e5ad by Bas Couwenberg at 2021-09-25T06:25:21+02:00
New upstream release.
- - - - -
dd5e4c8f by Bas Couwenberg at 2021-09-25T06:26:28+02:00
Drop spelling-errors.patch, applied upstream.
- - - - -
88f0e1b7 by Bas Couwenberg at 2021-09-25T06:26:42+02:00
Set distribution to unstable.
- - - - -
8 changed files:
- Changelog
- README.md
- debian/changelog
- − debian/patches/series
- − debian/patches/spelling-errors.patch
- docs/index.rst
- src/cftime/_cftime.pyx
- test/test_cftime.py
Changes:
=====================================
Changelog
=====================================
@@ -1,3 +1,21 @@
+version 1.5.1 (not yet released)
+======================================
+ * added support for "common_year" and "common_years" units for "noleap"
+ and "365_day" calendars (issue #5, PR #246)
+ * check consistency of year arg and has_year_zero kwarg in cftime.datetime
+ (issue #248). Also assume if has_year_zero not specified it should be True
+ if year=0. Allow replace method to change has_year_zero. Issue UserWarning
+ if year set to zero and calendar default is changed from False to True
+ (so that user is aware the resulting instance will not be CF compliant).
+ * '360_day' was missing from list of 'idealized' calendars.
+ * fixed a bug that led to subclasses losing their type identity upon
+ pickling (issue #251, PR #252).
+ * Change default behavior of proleptic_gregorian to has_year_zero=T
+ (to be consistent with ISO-8601 since CF does not specify the year zero convention
+ for this calendar). Issue warning when trying to
+ to create a cftime.datetime instance that is not allowed in CF (PR #238).
+
+
version 1.5.0 (release tag v1.5.0.rel)
======================================
* clean-up deprecated calendar specific subclasses (PR #231).
@@ -52,7 +70,7 @@ version 1.3.1 (release tag v1.3.1rel)
version 1.3.0 (release tag v1.3.0rel)
=====================================
* zero pad years in strtime (issue #194)
- * have cftime.datetime constuctor create 'calendar-aware' instances (default is
+ * have cftime.datetime constructor create 'calendar-aware' instances (default is
'standard' calendar, if calendar='' or None the instance is not calendar aware and some
methods, like dayofwk, dayofyr, __add__ and __sub__, will not work). Fixes issue #198.
The calendar specific sub-classes are now deprecated, but remain for now
=====================================
README.md
=====================================
@@ -12,6 +12,14 @@ Time-handling functionality from netcdf4-python
## News
For details on the latest updates, see the [Changelog](https://github.com/Unidata/cftime/blob/master/Changelog).
+10/1/2021: Version 1.5.1 released. Changed default behavior of ``proleptic_gregorian``
+to has_year_zero=T (since it is allowed in ISO-8601 and CF does not specify the
+year zero convention for this calendar). Raise warning message when trying
+to create a calendar that is not supported by CF version 1.9 (no years < 1
+allowed for 'standard'/'gregorian' or 'julian' calendars).
+Added support for "common_year" and "common_years" units for "noleap"
+and "365_day" calendars.
+
5/20/2021: Version 1.5.0 released. Includes support for astronomical year numbering
(including the year zero) for real-world calendars ('julian', 'gregorian'/'standard',
and 'proleptic_gregorian') using 'has_year_zero' `cftime.datetime` kwarg..
=====================================
debian/changelog
=====================================
@@ -1,10 +1,12 @@
-cftime (1.5.0+ds-2) UNRELEASED; urgency=medium
+cftime (1.5.1+ds-1) unstable; urgency=medium
+ * New upstream release.
* Bump Standards-Version to 4.6.0, no changes.
* Bump debhelper compat to 12, changes:
- Drop --list-missing from dh_install
+ * Drop spelling-errors.patch, applied upstream.
- -- Bas Couwenberg <sebastic at debian.org> Wed, 08 Sep 2021 16:18:54 +0200
+ -- Bas Couwenberg <sebastic at debian.org> Sat, 25 Sep 2021 06:26:30 +0200
cftime (1.5.0+ds-1) unstable; urgency=medium
=====================================
debian/patches/series deleted
=====================================
@@ -1 +0,0 @@
-spelling-errors.patch
=====================================
debian/patches/spelling-errors.patch deleted
=====================================
@@ -1,16 +0,0 @@
-Description: Fix spelling errors.
- * allowd -> allowed
-Author: Bas Couwenberg <sebastic at debian.org>
-Forwarded: https://github.com/Unidata/cftime/pull/240
-
---- a/src/cftime/_cftime.pyx
-+++ b/src/cftime/_cftime.pyx
-@@ -1287,7 +1287,7 @@ The default format of the string produce
-
- Day 0 starts at noon January 1 of the year -4713 for the
- julian, gregorian and standard calendars (year -4712 if year
-- zero allowd).
-+ zero allowed).
-
- Day 0 starts at noon on November 24 of the year -4714 for the
- proleptic gregorian calendar (year -4713 if year zero allowed).
=====================================
docs/index.rst
=====================================
@@ -2,7 +2,7 @@ cftime
======
Python library for decoding time units and variable values in a netCDF file
-conforming to the Climate and Forecasting (CF) netCDF conventions.
+conforming to the `Climate and Forecasting (CF) netCDF conventions <http://cfconventions.org/cf-conventions/cf-conventions#time-coordinate>`__.
Contents
--------
=====================================
src/cftime/_cftime.pyx
=====================================
@@ -22,14 +22,15 @@ min_units = ['minute', 'minutes', 'min', 'mins']
hr_units = ['hour', 'hours', 'hr', 'hrs', 'h']
day_units = ['day', 'days', 'd']
month_units = ['month', 'months'] # only allowed for 360_day calendar
+year_units = ['common_year', 'common_years'] # only allowed for 365_day and noleap calendars
_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#calendar
# for definitions.
_calendars = ['standard', 'gregorian', 'proleptic_gregorian',
'noleap', 'julian', 'all_leap', '365_day', '366_day', '360_day']
-_idealized_calendars= ['all_leap','noleap','366_day','365_day']
+_idealized_calendars= ['all_leap','noleap','366_day','365_day','360_day']
# Following are number of days per month
cdef int[12] _dayspermonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
cdef int[12] _dayspermonth_leap = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
@@ -37,7 +38,7 @@ cdef int[12] _dayspermonth_leap = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 3
cdef int[13] _cumdayspermonth = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
cdef int[13] _cumdayspermonth_leap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
-__version__ = '1.5.0'
+__version__ = '1.5.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.
@@ -46,7 +47,7 @@ ISO8601_REGEX = re.compile(r"(?P<year>[+-]?[0-9]+)(-(?P<month>[0-9]{1,2})(-(?P<d
r"(((?P<separator1>.)(?P<hour>[0-9]{1,2}):(?P<minute>[0-9]{1,2})(:(?P<second>[0-9]{1,2})(\.(?P<fraction>[0-9]+))?)?)?"
r"((?P<separator2>.?)(?P<timezone>Z|(([-+])([0-9]{2})((:([0-9]{2}))|([0-9]{2}))?)))?)?)?)?"
)
-# Note: The re module apparently does not support branch reset groups that allow redifinition of the same group name in alternative branches as PCRE does.
+# Note: The re module apparently does not support branch reset groups that allow redefinition of the same group name in alternative branches as PCRE does.
# Using two different group names is also somewhat ugly, but other solutions might hugely inflate the expression. feel free to contribute a better solution.
TIMEZONE_REGEX = re.compile(
"(?P<prefix>[+-])(?P<hours>[0-9]{2})(?:(?::(?P<minutes1>[0-9]{2}))|(?P<minutes2>[0-9]{2}))?")
@@ -93,19 +94,20 @@ def _dateparse(timestr,calendar,has_year_zero=None):
if has_year_zero is None:
has_year_zero = _year_zero_defaults(calendar)
(units, isostring) = _datesplit(timestr)
- if not ((units in month_units and calendar=='360_day') or units in _units):
+ if not ((units in month_units and calendar=='360_day') or (units in year_units and calendar in {'365_day', 'noleap'}) or units in _units):
if units in month_units and calendar != '360_day':
raise ValueError("'months since' units only allowed for '360_day' calendar")
+ if units in year_units and calendar not in {'365_day', 'noleap'}:
+ raise ValueError("'%s' units only allowed for '365_day' and 'noleap' calendars" % units)
else:
raise ValueError(
"units must be one of 'seconds', 'minutes', 'hours' or 'days' (or singular version of these), got '%s'" % units)
# parse the date string.
year, month, day, hour, minute, second, microsecond, utc_offset =\
_parse_date( isostring.strip() )
- if calendar in ['julian', 'standard', 'gregorian', 'proleptic_gregorian']:
- if year == 0:
- msg='zero not allowed as a reference year, does not exist in Julian or Gregorian calendars'
- raise ValueError(msg)
+ if year == 0 and not has_year_zero and calendar in ['julian', 'standard', 'gregorian', 'proleptic_gregorian']:
+ msg='zero not allowed as a reference year when has_year_zero=False'
+ raise ValueError(msg)
if calendar in ['noleap', '365_day'] and month == 2 and day == 29:
raise ValueError(
'cannot specify a leap day as the reference time with the noleap calendar')
@@ -142,11 +144,12 @@ def date2num(dates,units,calendar=None,has_year_zero=None):
**units**: a string of the form **<time units> since <reference time>**
describing the time units. **<time units>** can be days, hours, minutes,
seconds, milliseconds or microseconds. **<reference time>** is the time
- origin. **months_since** is allowed *only* for the **360_day** calendar.
+ origin. **months since** is allowed *only* for the **360_day** calendar
+ and **common_years since** is allowed *only* for the **365_day** calendar.
**calendar**: describes the calendar to be used in the time calculations.
All the values currently defined in the
- `CF metadata convention <http://cfconventions.org>`__ are supported.
+ `CF metadata convention <http://cfconventions.org/cf-conventions/cf-conventions#calendar>`__ are supported.
Valid calendars **'standard', 'gregorian', 'proleptic_gregorian'
'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'**.
Default is `None` which means the calendar associated with the first
@@ -156,8 +159,13 @@ def date2num(dates,units,calendar=None,has_year_zero=None):
is used and the year zero exists. If set to False for real-world
calendars, then historical year numbering is used and the year 1 is
preceded by year -1 and no year zero exists.
- The defaults are False for real-world calendars
- and True for idealized calendars.
+ The defaults are set to conform with
+ CF version 1.9 conventions (False for 'julian', 'gregorian'/'standard', True
+ for 'proleptic_gregorian' (ISO 8601) and True for the idealized
+ calendars 'noleap'/'365_day', '360_day', 366_day'/'all_leap')
+ Note that CF v1.9 does not specifically mention whether year zero
+ is allowed in the proleptic_gregorian calendar, but ISO-8601 has
+ a year zero so we have adopted this as the default.
The defaults can only be over-ridden for the real-world calendars,
for the the idealized calendars the year zero
always exists and the has_year_zero kwarg is ignored.
@@ -320,7 +328,10 @@ UNIT_CONVERSION_FACTORS = {
"days": 86400 * 1000000,
"d": 86400 * 1000000,
"month": 30 * 86400 * 1000000, # Only allowed for 360_day calendar
- "months": 30 * 86400 * 1000000
+ "months": 30 * 86400 * 1000000,
+ "common_year": 365 * 86400 * 1000000, # Only allowed for 365_day and no_leap calendars
+ "common_years": 365 * 86400 * 1000000 # Only allowed for 365_day and no_leap calendars
+
}
DATE_TYPES = {
@@ -433,11 +444,12 @@ def num2date(
**units**: a string of the form **<time units> since <reference time>**
describing the time units. **<time units>** can be days, hours, minutes,
seconds, milliseconds or microseconds. **<reference time>** is the time
- origin. **months_since** is allowed *only* for the **360_day** calendar.
+ origin. **months since** is allowed *only* for the **360_day** calendar
+ and **common_years since** is allowed *only* for the **365_day** calendar.
**calendar**: describes the calendar used in the time calculations.
All the values currently defined in the
- `CF metadata convention <http://cfconventions.org>`__ are supported.
+ `CF metadata convention <http://cfconventions.org/cf-conventions/cf-conventions#calendar>`__ are supported.
Valid calendars **'standard', 'gregorian', 'proleptic_gregorian'
'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'**.
Default is **'standard'**, which is a mixed Julian/Gregorian calendar.
@@ -454,8 +466,10 @@ def num2date(
is used and the year zero exists. If set to False for real-world
calendars, then historical year numbering is used and the year 1 is
preceded by year -1 and no year zero exists.
- The defaults are False for real-world calendars
- and True for idealized calendars.
+ The defaults are set to conform with
+ CF version 1.9 conventions (False for 'julian', 'gregorian'/'standard', True
+ for 'proleptic_gregorian' (ISO 8601) and True for the idealized
+ calendars 'noleap'/'365_day', '360_day', 366_day'/'all_leap')
The defaults can only be over-ridden for the real-world calendars,
for the the idealized calendars the year zero
always exists and the has_year_zero kwarg is ignored.
@@ -499,7 +513,7 @@ def num2date(
use_python_datetime = False
if only_use_python_datetimes and not only_use_cftime_datetimes:
- # only_use_cftime_datetimes takes precendence
+ # only_use_cftime_datetimes takes precedence
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
@@ -543,7 +557,7 @@ def date2index(dates, nctime, calendar=None, select='exact', has_year_zero=None)
**calendar**: describes the calendar to be used in the time calculations.
All the values currently defined in the
- `CF metadata convention <http://cfconventions.org>`__ are supported.
+ `CF metadata convention <http://cfconventions.org/cf-conventions/cf-conventions#calendar>`__ are supported.
Valid calendars **'standard', 'gregorian', 'proleptic_gregorian'
'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'**.
Default is `None` which means the calendar associated with the first
@@ -560,8 +574,10 @@ def date2index(dates, nctime, calendar=None, select='exact', has_year_zero=None)
is used and the year zero exists. If set to False for real-world
calendars, then historical year numbering is used and the year 1 is
preceded by year -1 and no year zero exists.
- The defaults are False for real-world calendars
- and True for idealized calendars.
+ The defaults are set to conform with
+ CF version 1.9 conventions (False for 'julian', 'gregorian'/'standard', True
+ for 'proleptic_gregorian' (ISO 8601) and True for the idealized
+ calendars 'noleap'/'365_day', '360_day', 366_day'/'all_leap')
The defaults can only be over-ridden for the real-world calendars,
for the the idealized calendars the year zero
always exists and the has_year_zero kwarg is ignored.
@@ -771,8 +787,10 @@ def _date2index(dates, nctime, calendar=None, select='exact', has_year_zero=None
is used and the year zero exists. If set to False for real-world
calendars, then historical year numbering is used and the year 1 is
preceded by year -1 and no year zero exists.
- The defaults are False for real-world calendars
- and True for idealized calendars.
+ The defaults are set to conform with
+ CF version 1.9 conventions (False for 'julian', 'gregorian'/'standard', True
+ for 'proleptic_gregorian' (ISO 8601) and True for the idealized
+ calendars 'noleap'/'365_day', '360_day', 366_day'/'all_leap')
The defaults can only be over-ridden for the real-world calendars,
for the the idealized calendars the year zero
always exists and the has_year_zero kwarg is ignored.
@@ -913,16 +931,16 @@ cdef _year_zero_defaults(calendar):
if calendar in ['standard','gregorian','julian']:
return False
elif calendar in ['proleptic_gregorian']:
- #return True # ISO 8601 year zero=1 BC
- return False
+ return True # ISO 8601 year zero=1 BC
elif calendar in _idealized_calendars:
return True
else:
return False
# factory function without optional kwargs that can be used in datetime.__reduce__
-def _create_datetime(args, kwargs): return datetime(*args, **kwargs)
+def _create_datetime(date_type, args, kwargs): return date_type(*args, **kwargs)
# custorm warning for invalid CF dates.
+cfwarnmsg="this date/calendar/year zero convention is not supported by CF"
class CFWarning(UserWarning):
pass
@@ -940,7 +958,7 @@ for cftime.datetime instances using
'gregorian' and 'proleptic_gregorian' calendars.
All the calendars currently defined in the
-`CF metadata convention <http://cfconventions.org>`__ are supported.
+`CF metadata convention <http://cfconventions.org/cf-conventions/cf-conventions#calendar>`__ are supported.
Valid calendars are 'standard', 'gregorian', 'proleptic_gregorian'
'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'.
Default is 'standard', which is a mixed Julian/Gregorian calendar.
@@ -954,8 +972,10 @@ If the has_year_zero kwarg is set to True, astronomical year numbering
is used and the year zero exists. If set to False for real-world
calendars, then historical year numbering is used and the year 1 is
preceded by year -1 and no year zero exists.
-The defaults are False for real-world calendars
-and True for idealized calendars.
+The defaults are set to conform with
+CF version 1.9 conventions (False for 'julian', 'gregorian'/'standard', True
+for 'proleptic_gregorian' (ISO 8601) and True for the idealized
+calendars 'noleap'/'365_day', '360_day', 366_day'/'all_leap')
The defaults can only be over-ridden for the real-world calendars,
for the the idealized calendars the year zero
always exists and the has_year_zero kwarg is ignored.
@@ -1002,13 +1022,30 @@ The default format of the string produced by strftime is controlled by self.form
calendar = calendar.lower()
# set calendar-specific defaults for has_year_zero
if has_year_zero is None:
- has_year_zero = _year_zero_defaults(calendar)
+ if year == 0:
+ # assume if user sets year to zero, the calendar should
+ # include the year zero (issue #248)
+ # warn if calendar is being set to non-CF calendar
+ msg="year=0 was specified - this date/calendar/year zero convention is not supported by CF"
+ if calendar is not None and not _year_zero_defaults(calendar):
+ warnings.warn(msg,category=CFWarning)
+ has_year_zero=True
+ else:
+ has_year_zero = _year_zero_defaults(calendar)
+ # warn if requested date not allowed by CF
+ # (no years < 1 in mixed Julian/Gregorian calendar).
+ # CF version 1.9 does not specify whether a year zero should exist
+ # for the proleptic_gregorian calendar. IS0 8601 uses proleptic_gregorian
+ # and has a year zero, so for now this is the default in cftime.
+ if calendar in ['julian','gregorian','standard'] and year <= 0:
+ warnings.warn(cfwarnmsg,category=CFWarning)
+ # raise exception if year zero requested but has_year_zero set
+ # to False (issue #248).
+ if year == 0 and has_year_zero==False:
+ msg='year zero requested, but has_year_zero=False'
+ raise ValueError(msg)
if not has_year_zero and calendar in _idealized_calendars:
warnings.warn('has_year_zero kwarg ignored for idealized calendars (always True)')
- #if (calendar in ['julian','gregorian','standard'] and year <= 0) or\
- # (calendar == 'proleptic_gregorian' and not has_year_zero and year < 1):
- # msg="this date/calendar/year zero convention is not supported by CF"
- # warnings.warn(msg,category=CFWarning)
self.has_year_zero = has_year_zero
if calendar == 'gregorian' or calendar == 'standard':
# dates after 1582-10-15 can be converted to and compared to
@@ -1119,6 +1156,7 @@ The default format of the string produced by strftime is controlled by self.form
"minute": self.minute,
"second": self.second,
"microsecond": self.microsecond,
+ "has_year_zero": self.has_year_zero,
"calendar": self.calendar}
if 'dayofyr' in kwargs or 'dayofwk' in kwargs:
@@ -1129,6 +1167,12 @@ The default format of the string produced by strftime is controlled by self.form
raise ValueError('Replacing the calendar of a datetime is '
'not supported.')
+ # if attempting to set year to zero, also set has_year_zero=True
+ # (issue #248)
+ if 'year' in kwargs:
+ if kwargs['year']==0 and 'has_year_zero' not in kwargs:
+ kwargs['has_year_zero']=True
+
for name, value in kwargs.items():
args[name] = value
@@ -1247,7 +1291,8 @@ The default format of the string produced by strftime is controlled by self.form
def __reduce__(self):
"""special method that allows instance to be pickled"""
args, kwargs = self._getstate()
- return (_create_datetime, (args, kwargs))
+ date_type = type(self)
+ return (_create_datetime, (date_type, args, kwargs))
cdef _add_timedelta(self, other):
return NotImplemented
@@ -1277,9 +1322,9 @@ The default format of the string produced by strftime is controlled by self.form
else:
units = 'days since 0-1-1-12'
# suppress warning about invalid CF date (year <= 0)
- #with warnings.catch_warnings():
- # warnings.simplefilter("ignore",category=CFWarning)
- jd = num2date(jday,units=units,calendar=calendar,has_year_zero=has_year_zero)
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore",category=CFWarning)
+ jd = num2date(jday,units=units,calendar=calendar,has_year_zero=has_year_zero)
return jd
def toordinal(self,fractional=False):
@@ -1287,7 +1332,7 @@ The default format of the string produced by strftime is controlled by self.form
Day 0 starts at noon January 1 of the year -4713 for the
julian, gregorian and standard calendars (year -4712 if year
- zero allowd).
+ zero allowed).
Day 0 starts at noon on November 24 of the year -4714 for the
proleptic gregorian calendar (year -4713 if year zero allowed).
@@ -1335,7 +1380,7 @@ The default format of the string produced by strftime is controlled by self.form
delta = self
else:
return NotImplemented
- # return calendar-specific subclasses for backward compatbility,
+ # return calendar-specific subclasses for backward compatibility,
# 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)
@@ -1391,7 +1436,7 @@ datetime object."""
return dt._to_real_datetime() - other
elif isinstance(other, timedelta):
# datetime - timedelta
- # return calendar-specific subclasses for backward compatbility,
+ # return calendar-specific subclasses for backward compatibility,
# even though after 1.3.0 this is no longer necessary.
has_year_zero=self.has_year_zero
if self.calendar == '360_day':
@@ -1754,8 +1799,10 @@ cdef _IntJulianDayFromDate(int year,int month,int day,calendar,skip_transition=F
is used and the year zero exists. If set to False for real-world
calendars, then historical year numbering is used and the year 1 is
preceded by year -1 and no year zero exists.
- The defaults are False for real-world calendars
- and True for idealized calendars.
+ The defaults are set to conform with
+ CF version 1.9 conventions (False for 'julian', 'gregorian'/'standard', True
+ for 'proleptic_gregorian' (ISO 8601) and True for the idealized
+ calendars 'noleap'/'365_day', '360_day', 366_day'/'all_leap')
The defaults can only be over-ridden for the real-world calendars,
for the the idealized calendars the year zero
always exists and the has_year_zero kwarg is ignored.
=====================================
test/test_cftime.py
=====================================
@@ -167,7 +167,7 @@ class cftimeTestCase(unittest.TestCase):
# check num2date method.
d2 = self.cdftime_mixed.num2date(t1)
self.assertTrue(str(d) == str(d2))
- # this is a non-existant date, should raise ValueError.
+ # this is a non-existent date, should raise ValueError.
d = datetime(1582, 10, 5, 0)
self.assertRaises(ValueError, self.cdftime_mixed.date2num, d)
# check date2num/num2date with date after switch.
@@ -210,7 +210,7 @@ class cftimeTestCase(unittest.TestCase):
ndayr = d.timetuple()[7]
self.assertTrue(ndayr == 125)
# check noleap calendar.
- # this is a non-existant date, should raise ValueError.
+ # this is a non-existent date, should raise ValueError.
self.assertRaises(
ValueError, utime, 'days since 1600-02-29 00:00:00', calendar='noleap')
self.assertTrue(self.cdftime_noleap.units == 'days')
@@ -236,7 +236,7 @@ class cftimeTestCase(unittest.TestCase):
# check day of year.
ndayr = d2.timetuple()[7]
self.assertTrue(ndayr == 59)
- # non-existant date, should raise ValueError.
+ # non-existent date, should raise ValueError.
date = datetime(2000, 2, 29)
self.assertRaises(ValueError, self.cdftime_noleap.date2num, date)
# check all_leap calendar.
@@ -407,7 +407,7 @@ class cftimeTestCase(unittest.TestCase):
units = 'hours since 2013-12-12T12:00:00'
assert(1.0 == date2num(num2date(1.0, units), units))
- # test rountrip accuracy
+ # test roundtrip accuracy
# also tests error found in issue #349
dateref = datetime(2015,2,28,12)
verbose = True # print out max error diagnostics
@@ -575,7 +575,7 @@ class cftimeTestCase(unittest.TestCase):
except ValueError:
pass
# test fix for issue #596 - julian day calculations wrong for negative years,
- # caused incorrect rountrip num2date(date2num(date)) roundtrip for dates with year
+ # caused incorrect roundtrip num2date(date2num(date)) roundtrip for dates with year
# < 0.
u = utime("seconds since 1-1-1",calendar='julian')
with warnings.catch_warnings():
@@ -900,6 +900,25 @@ class cftimeTestCase(unittest.TestCase):
assert(d.toordinal() == jdref)
d2 = cftime.datetime.fromordinal(jd,calendar=calendar,has_year_zero=has_year_zero)
assert(d2 == d)
+ # issue #248. Set has_year_zero=True if year zero requested
+ # on instance creation, or by using replace method.
+ d=cftime.datetime(0, 0, 0, calendar=None)
+ assert(d.has_year_zero==True)
+ d=cftime.datetime(1, 0, 0, calendar=None)
+ assert(d.has_year_zero==False)
+ d = d.replace(year=0)
+ assert(d.has_year_zero==True)
+ # this should raise a warning, since the default has_year_zero
+ # is changed if year specified as zero. (issue #248, PR #249)
+ self.assertWarns(UserWarning, cftime.datetime, 0, 1, 1,\
+ calendar='standard')
+ # check that for idealized calendars has_year_zero is always True
+ d=cftime.datetime(0, 1, 1, calendar='360_day')
+ assert(d.has_year_zero==True)
+ d=cftime.datetime(1, 1, 1, calendar='360_day')
+ assert(d.has_year_zero==True)
+ d = d.replace(year=0)
+ assert(d.has_year_zero==True)
class TestDate2index(unittest.TestCase):
@@ -921,7 +940,7 @@ class TestDate2index(unittest.TestCase):
self.calendar = calendar
t0 = date2num(start, units, calendar)
self._data = (t0 + np.arange(n) * step).astype('float')
- self.dtype = np.float
+ self.dtype = float
def __getitem__(self, item):
return self._data[item]
@@ -1305,8 +1324,15 @@ class DateTime(unittest.TestCase):
import pickle
date = Datetime360Day(year=1, month=2, day=3, hour=4, minute=5, second=6, microsecond=7)
- self.assertEqual(date, pickle.loads(pickle.dumps(date)))
-
+ deserialized = pickle.loads(pickle.dumps(date))
+ self.assertEqual(date, deserialized)
+ self.assertEqual(type(date), type(deserialized))
+
+ date = datetimex(1, 2, 3, 4, 5, 6, 7, calendar="360_day")
+ deserialized = pickle.loads(pickle.dumps(date))
+ self.assertEqual(date, deserialized)
+ self.assertEqual(type(date), type(deserialized))
+
def test_misc(self):
"Miscellaneous tests."
# make sure repr succeeds
@@ -1319,7 +1345,7 @@ class DateTime(unittest.TestCase):
def invalid_year():
with warnings.catch_warnings():
warnings.simplefilter("ignore",category=cftime.CFWarning)
- DatetimeGregorian(0, 1, 1) + self.delta
+ DatetimeGregorian(0, 1, 1, has_year_zero=False) + self.delta
def invalid_month():
DatetimeGregorian(1, 13, 1) + self.delta
@@ -1372,67 +1398,6 @@ class DateTime(unittest.TestCase):
for func in [not_comparable_1, not_comparable_2, not_comparable_3, not_comparable_4, not_comparable_5]:
self.assertRaises(TypeError, func)
- @pytest.mark.skipif(sys.version_info.major != 2,
- reason='python2 specific, non-comparable test')
- def test_richcmp_py2(self):
- class Rich(object):
- """Dummy class with traditional rich comparison support."""
- def __lt__(self, other):
- raise NotImplementedError('__lt__')
- def __le__(self, other):
- raise NotImplementedError('__le__')
- def __eq__(self, other):
- raise NotImplementedError('__eq__')
- def __ne__(self, other):
- raise NotImplementedError('__ne__')
- def __gt__(self, other):
- raise NotImplementedError('__gt__')
- def __ge__(self, other):
- raise NotImplementedError('__ge__')
-
- class CythonRich(object):
- """Dummy class with spoof cython rich comparison support."""
- def __richcmp__(self, other):
- """
- This method is never called. However it is introspected
- by the cftime.datetime.__richcmp__ method, which will then
- return NotImplemented, causing Python to call this classes
- __cmp__ method as a back-stop, and hence spoofing the
- cython specific rich comparison behaviour.
- """
- pass
- def __cmp__(self, other):
- raise NotImplementedError('__richcmp__')
-
- class Pass(object):
- """Dummy class with no rich comparison support whatsoever."""
- pass
-
- class Pass___cmp__(object):
- """Dummy class that delegates all comparisons."""
- def __cmp__(self, other):
- return NotImplemented
-
- # Test LHS operand comparison operator processing.
- for op, expected in [(operator.gt, '__lt__'), (operator.ge, '__le__'),
- (operator.eq, '__eq__'), (operator.ne, '__ne__'),
- (operator.lt, '__gt__'), (operator.le, '__ge__')]:
- with self.assertRaisesRegex(NotImplementedError, expected):
- op(self.date1_365_day, Rich())
-
- with self.assertRaisesRegex(NotImplementedError, '__richcmp__'):
- op(self.date1_365_day, CythonRich())
-
- # Test RHS operand comparison operator processing.
- for op in [operator.gt, operator.ge, operator.eq, operator.ne,
- operator.lt, operator.le]:
- with self.assertRaisesRegex(TypeError, 'cannot compare'):
- op(Pass(), self.date1_365_day)
-
- with self.assertRaisesRegex(TypeError, 'cannot compare'):
- op(Pass___cmp__(), self.date1_365_day)
-
-
class issue17TestCase(unittest.TestCase):
"""Regression tests for issue #17/#669."""
# issue 17 / 699: timezone formats not supported correctly
@@ -1529,13 +1494,14 @@ def test_zero_year(date_type):
# Proleptic Gregorian calendars by default.
with warnings.catch_warnings():
warnings.simplefilter("ignore",category=cftime.CFWarning)
- #if date_type in [DatetimeNoLeap, DatetimeAllLeap, Datetime360Day,
- # DatetimeProlepticGregorian]:
- if date_type in [DatetimeNoLeap, DatetimeAllLeap, Datetime360Day]:
+ if date_type in [DatetimeNoLeap, DatetimeAllLeap, Datetime360Day,
+ DatetimeProlepticGregorian]:
date_type(0, 1, 1)
else:
+ d=date_type(0,1,1) # has_year_zero=True set if year 0 specified
+ assert(d.has_year_zero) # (issue #248)
with pytest.raises(ValueError):
- date_type(0, 1, 1)
+ date_type(0, 1, 1, has_year_zero=False)
def test_invalid_month(date_type):
@@ -1755,6 +1721,7 @@ _MINUTE_UNITS = ["minutes", "minute", "min", "mins"]
_HOUR_UNITS = ["hours", "hour", "hr", "hrs", "h"]
_DAY_UNITS = ["day", "days", "d"]
_MONTH_UNITS = ["month", "months"]
+_YEAR_UNITS = ["common_years", "common_year"]
_DTYPES = [np.dtype("int64"), np.dtype("float64")]
_STANDARD_CALENDARS = [
"standard",
@@ -1767,6 +1734,11 @@ _REAL_WORLD_CALENDARS = [
"gregorian",
"proleptic_gregorian"
]
+_NO_YEAR_ZERO_CALENDARS = [
+ "julian",
+ "standard",
+ "gregorian",
+]
_ARTIFICIAL_CALENDARS = ["noleap", "all_leap", "360_day"]
@@ -1880,6 +1852,23 @@ def test_num2date_month_units(calendar, unit, shape, dtype):
result = num2date(numeric_times, units=units, calendar=calendar)
np.testing.assert_equal(result, expected)
+ at pytest.mark.parametrize("unit", _YEAR_UNITS)
+def test_num2date_year_units(calendar, unit, shape, dtype):
+ date_type = _EXPECTED_DATE_TYPES[calendar]
+ expected = np.array([date_type(2001, 1, 1, 0, 0, 0, 0),
+ date_type(2002, 1, 1, 0, 0, 0, 0),
+ date_type(2003, 1, 1, 0, 0, 0, 0),
+ date_type(2004, 1, 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 not in {"365_day", "noleap"}:
+ 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
@@ -1941,7 +1930,7 @@ def test_num2date_only_use_python_datetimes_invalid_basedate(
)
- at pytest.mark.parametrize("real_world_calendar", _REAL_WORLD_CALENDARS)
+ at pytest.mark.parametrize("real_world_calendar", _NO_YEAR_ZERO_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])
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/compare/e5e7ebf968ba697ee4aad3ea01728b4a32675425...88f0e1b7dd18d20839a5ca292144808d45a9de7b
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/compare/e5e7ebf968ba697ee4aad3ea01728b4a32675425...88f0e1b7dd18d20839a5ca292144808d45a9de7b
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/20210925/6d9c93ad/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list