diff --git a/Changelog b/Changelog index 78ae9c1d..e4a48282 100644 --- a/Changelog +++ b/Changelog @@ -1,6 +1,11 @@ -since version 1.2.1 -=================== +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 + '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 + as stubs that just instantiate the base class and override __repr__. version 1.2.1 (release tag v1.2.1rel) ===================================== diff --git a/cftime/_cftime.pyx b/cftime/_cftime.pyx index 0caa6641..83ad25b8 100644 --- a/cftime/_cftime.pyx +++ b/cftime/_cftime.pyx @@ -53,7 +53,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.2.1' +__version__ = '1.3.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. @@ -104,9 +104,6 @@ class real_datetime(datetime_python): 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 -gregorian = real_datetime(1582,10,15) - def _datesplit(timestr): """split a time string into two components, units and the remainder after 'since' @@ -137,34 +134,25 @@ def _dateparse(timestr,calendar): # parse the date string. year, month, day, hour, minute, second, microsecond, utc_offset =\ _parse_date( isostring.strip() ) - basedate = None - if year >= MINYEAR and year <= MAXYEAR: - try: - 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.) - except ValueError: - pass - if not basedate: - if not utc_offset: - basedate = datetime(year, month, day, hour, minute, second, - microsecond) - else: - raise ValueError('cannot use utc_offset for this reference date/calendar') if calendar in ['julian', 'standard', 'gregorian', 'proleptic_gregorian']: - if basedate.year == 0: + if year == 0: msg='zero not allowed as a reference year, does not exist in Julian or Gregorian calendars' raise ValueError(msg) - if calendar in ['noleap', '365_day'] and basedate.month == 2 and basedate.day == 29: + 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') - if calendar == '360_day' and basedate.day > 30: + if calendar == '360_day' and day > 30: raise ValueError( 'there are only 30 days in every month with the 360_day calendar') + basedate = datetime(year, month, day, hour, minute, second, + microsecond,calendar=calendar) + # subtract utc_offset from basedate time instance (which is timezone naive) + if utc_offset: + basedate -= timedelta(days=utc_offset/1440.) return basedate def _can_use_python_datetime(date,calendar): + gregorian = datetime(1582,10,15,calendar=calendar) return ((calendar == 'proleptic_gregorian' and date.year >= MINYEAR and date.year <= MAXYEAR) or \ (calendar in ['gregorian','standard'] and date > gregorian and date.year <= MAXYEAR)) @@ -237,8 +225,7 @@ def date2num(dates,units,calendar='standard'): else: use_python_datetime = False # convert basedate to specified calendar - if not isinstance(basedate, DATE_TYPES[calendar]): - basedate = to_calendar_specific_datetime(basedate, calendar, False) + basedate = to_calendar_specific_datetime(basedate, calendar, False) times = []; n = 0 for date in dates.flat: # use python datetime if possible. @@ -247,8 +234,7 @@ def date2num(dates,units,calendar='standard'): if getattr(date, 'tzinfo',None) is not None: date = date.replace(tzinfo=None) - date.utcoffset() else: # convert date to same calendar specific cftime.datetime instance - if not isinstance(date, DATE_TYPES[calendar]): - date = to_calendar_specific_datetime(date, calendar, False) + date = to_calendar_specific_datetime(date, calendar, False) if ismasked and mask.flat[n]: times.append(None) else: @@ -332,20 +318,42 @@ DATE_TYPES = { } -def to_calendar_specific_datetime(datetime, calendar, use_python_datetime): +#def to_calendar_specific_datetime(dt, calendar, use_python_datetime): +# if use_python_datetime: +# return real_datetime( +# dt.year, +# dt.month, +# dt.day, +# dt.hour, +# dt.minute, +# dt.second, +# dt.microsecond) +# else: +# return datetime( +# dt.year, +# dt.month, +# dt.day, +# dt.hour, +# dt.minute, +# dt.second, +# dt.microsecond, +# calendar=calendar) +# return calendar-specific subclasses for backward compatbility, +# even though after 1.3.0 this is no longer necessary. +def to_calendar_specific_datetime(dt, calendar, use_python_datetime): if use_python_datetime: date_type = real_datetime else: date_type = DATE_TYPES[calendar] return date_type( - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second, - datetime.microsecond + dt.year, + dt.month, + dt.day, + dt.hour, + dt.minute, + dt.second, + dt.microsecond ) @@ -691,21 +699,21 @@ def _date2index(dates, nctime, calendar=None, select='exact'): The datetime objects should not include a time-zone offset. **nctime**: A netCDF time variable object. The nctime object must have a - C{units} attribute. The entries are assumed to be stored in increasing + `units` attribute. The entries are assumed to be stored in increasing order. **calendar**: Describes the calendar used in the time calculation. - Valid calendars C{'standard', 'gregorian', 'proleptic_gregorian' - 'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'}. - Default is C{'standard'}, which is a mixed Julian/Gregorian calendar - If C{calendar} is None, its value is given by C{nctime.calendar} or - C{standard} if no such attribute exists. - - **select**: C{'exact', 'before', 'after', 'nearest'} - The index selection method. C{exact} will return the indices perfectly - matching the dates given. C{before} and C{after} will return the indices + 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 + If `calendar` is None, its value is given by `nctime.calendar` or + `standard` if no such attribute exists. + + **select**: 'exact', 'before', 'after', 'nearest' + The index selection method. `exact` will return the indices perfectly + matching the dates given. `before` and `after` will return the indices corresponding to the dates just before or just after the given dates if - an exact match cannot be found. C{nearest} will return the indices that + an exact match cannot be found. `nearest` will return the indices that correspond to the closest dates. """ try: @@ -727,21 +735,21 @@ def time2index(times, nctime, calendar=None, select='exact'): **times**: A numeric time or a sequence of numeric times. **nctime**: A netCDF time variable object. The nctime object must have a - C{units} attribute. The entries are assumed to be stored in increasing + `units` attribute. The entries are assumed to be stored in increasing order. **calendar**: Describes the calendar used in the time calculation. - Valid calendars C{'standard', 'gregorian', 'proleptic_gregorian' - 'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'}. - Default is C{'standard'}, which is a mixed Julian/Gregorian calendar - If C{calendar} is None, its value is given by C{nctime.calendar} or - C{standard} if no such attribute exists. + 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 + If `calendar` is None, its value is given by `nctime.calendar` or + `standard` if no such attribute exists. **select**: **'exact', 'before', 'after', 'nearest'** - The index selection method. C{exact} will return the indices perfectly - matching the times given. C{before} and C{after} will return the indices + The index selection method. `exact` will return the indices perfectly + matching the times given. `before` and `after` will return the indices corresponding to the times just before or just after the given times if - an exact match cannot be found. C{nearest} will return the indices that + an exact match cannot be found. `nearest` will return the indices that correspond to the closest times. """ try: @@ -843,14 +851,41 @@ cdef to_tuple(dt): @cython.embedsignature(True) cdef class datetime(object): """ -The base class implementing most methods of datetime classes that -mimic datetime.datetime but support calendars other than the proleptic -Gregorial calendar. +This class mimics datetime.datetime but support calendars other than the proleptic +Gregorian calendar. + +Supports timedelta operations by overloading +/-, and +comparisons with other instances using the same calendar. + +Comparison with native python datetime instances is possible +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. +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. +'standard' and 'gregorian' are synonyms, as are 'all_leap'/'366_day' +and 'noleap'/'365_day'. + +If the calendar kwarg is set to a blank string ('') or None (the default is 'standard') the +instance will not be calendar-aware and some methods will not work. + +Has isoformat, strftime, timetuple, replace, dayofwk, dayofyr, daysinmonth, +__repr__, __add__, __sub__, __str__ and comparison methods. + +dayofwk, dayofyr, daysinmonth, __add__ and __sub__ only work for calendar-aware +instances. + +The default format of the string produced by strftime is controlled by self.format +(default %Y-%m-%d %H:%M:%S). """ cdef readonly int year, month, day, hour, minute cdef readonly int second, microsecond cdef readonly str calendar cdef readonly int _dayofwk, _dayofyr + cdef readonly bint has_year_zero # Python's datetime.datetime uses the proleptic Gregorian # calendar. This boolean is used to decide whether a @@ -869,10 +904,53 @@ Gregorial calendar. self.minute = minute self.second = second self.microsecond = microsecond - self.calendar = calendar - self.datetime_compatible = True self._dayofwk = dayofwk self._dayofyr = dayofyr + if calendar: + calendar = calendar.lower() + if calendar == 'gregorian' or calendar == 'standard': + # dates after 1582-10-15 can be converted to and compared to + # proleptic Gregorian dates + self.calendar = 'gregorian' + if self.to_tuple() >= (1582, 10, 15, 0, 0, 0, 0): + self.datetime_compatible = True + else: + self.datetime_compatible = False + assert_valid_date(self, is_leap_gregorian, True) + self.has_year_zero = False + elif calendar == 'noleap' or calendar == '365_day': + self.calendar = 'noleap' + self.datetime_compatible = False + assert_valid_date(self, no_leap, False, has_year_zero=True) + self.has_year_zero = True + elif calendar == 'all_leap' or calendar == '366_day': + self.calendar = 'all_leap' + self.datetime_compatible = False + assert_valid_date(self, all_leap, False, has_year_zero=True) + self.has_year_zero = True + elif calendar == '360_day': + self.calendar = calendar + self.datetime_compatible = False + assert_valid_date(self, no_leap, False, has_year_zero=True, is_360_day=True) + self.has_year_zero = True + elif calendar == 'julian': + self.calendar = calendar + self.datetime_compatible = False + assert_valid_date(self, is_leap_julian, False) + self.has_year_zero = False + elif calendar == 'proleptic_gregorian': + self.calendar = calendar + self.datetime_compatible = True + assert_valid_date(self, is_leap_proleptic_gregorian, False) + self.has_year_zero = False + elif calendar == '' or calendar is None: + # instance not calendar-aware, some method will not work + self.calendar = '' + self.datetime_compatible = False + self.has_year_zero = False + else: + raise ValueError( + "calendar must be one of %s, got '%s'" % (str(_calendars), calendar)) @property def format(self): @@ -880,9 +958,11 @@ Gregorial calendar. @property def dayofwk(self): - if self._dayofwk < 0 and self.calendar != '': - jd = _IntJulianDayFromDate(self.year,self.month,self.day,self.calendar) - year,month,day,dayofwk,dayofyr = _IntJulianDayToDate(jd,self.calendar) + if self._dayofwk < 0 and self.calendar: + jd = _IntJulianDayFromDate(self.year,self.month,self.day,self.calendar, + skip_transition=False,has_year_zero=self.has_year_zero) + year,month,day,dayofwk,dayofyr = _IntJulianDayToDate(jd,self.calendar, + skip_transition=False,has_year_zero=self.has_year_zero) # cache results for dayofwk, dayofyr self._dayofwk = dayofwk self._dayofyr = dayofyr @@ -892,9 +972,11 @@ Gregorial calendar. @property def dayofyr(self): - if self._dayofyr < 0 and self.calendar != '': - jd = _IntJulianDayFromDate(self.year,self.month,self.day,self.calendar) - year,month,day,dayofwk,dayofyr = _IntJulianDayToDate(jd,self.calendar) + if self._dayofyr < 0 and self.calendar: + jd = _IntJulianDayFromDate(self.year,self.month,self.day,self.calendar, + skip_transition=False,has_year_zero=self.has_year_zero) + year,month,day,dayofwk,dayofyr = _IntJulianDayToDate(jd,self.calendar, + skip_transition=False,has_year_zero=self.has_year_zero) # cache results for dayofwk, dayofyr self._dayofwk = dayofwk self._dayofyr = dayofyr @@ -904,7 +986,15 @@ Gregorial calendar. @property def daysinmonth(self): - return get_days_in_month(_is_leap(self.year,self.calendar), self.month) + if self.calendar == 'noleap': + return _dpm[self.month-1] + elif self.calendar == 'all_leap': + return _dpm_leap[self.month-1] + elif self.calendar == '360_day': + return _dpm_360[self.month-1] + else: + return get_days_in_month(_is_leap(self.year,self.calendar, + has_year_zero=self.has_year_zero), self.month) def strftime(self, format=None): """ @@ -957,9 +1047,15 @@ Gregorial calendar. self.microsecond) 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) + if self.calendar == None: + return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8}, calendar={9})".format('cftime', + self.__class__.__name__, + self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond,self.calendar) + else: + return "{0}.{1}({2}, {3}, {4}, {5}, {6}, {7}, {8}, calendar='{9}')".format('cftime', + self.__class__.__name__, + self.year,self.month,self.day,self.hour,self.minute,self.second,self.microsecond,self.calendar) + def __str__(self): return self.isoformat(' ') @@ -1057,13 +1153,36 @@ Gregorial calendar. cdef datetime dt if isinstance(self, datetime) and isinstance(other, timedelta): dt = self + calendar = self.calendar delta = other elif isinstance(self, timedelta) and isinstance(other, datetime): dt = other + calendar = other.calendar delta = self else: return NotImplemented - return dt._add_timedelta(delta) + # 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 Datetime360Day(*add_timedelta_360_day(dt, delta)) + elif calendar == 'noleap': + #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 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 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 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 DatetimeProlepticGregorian(*add_timedelta(dt, delta, is_leap_proleptic_gregorian, False, False)) + else: + return NotImplemented def __sub__(self, other): cdef datetime dt @@ -1075,8 +1194,10 @@ Gregorial calendar. raise ValueError("cannot compute the time difference between dates with different calendars") if dt.calendar == "": raise ValueError("cannot compute the time difference between dates that are not calendar-aware") - ordinal_self = _IntJulianDayFromDate(dt.year, dt.month, dt.day, dt.calendar) - ordinal_other = _IntJulianDayFromDate(other.year, other.month, other.day, other.calendar) + ordinal_self = _IntJulianDayFromDate(dt.year, dt.month, dt.day, dt.calendar, + skip_transition=False,has_year_zero=self.has_year_zero) + ordinal_other = _IntJulianDayFromDate(other.year, other.month, other.day, other.calendar, + skip_transition=False,has_year_zero=self.has_year_zero) days = ordinal_self - ordinal_other seconds_self = dt.second + 60 * dt.minute + 3600 * dt.hour seconds_other = other.second + 60 * other.minute + 3600 * other.hour @@ -1095,7 +1216,29 @@ datetime object.""" return dt._to_real_datetime() - other elif isinstance(other, timedelta): # datetime - timedelta - return dt._add_timedelta(-other) + # 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 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 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 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 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 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 DatetimeProlepticGregorian(*add_timedelta(self, -other, is_leap_proleptic_gregorian, False, False)) + else: + return NotImplemented else: return NotImplemented else: @@ -1112,6 +1255,9 @@ datetime object.""" else: return NotImplemented +# these calendar-specific sub-classes are no longer used, but stubs +# remain for backward compatibility. + @cython.embedsignature(True) cdef class DatetimeNoLeap(datetime): """ @@ -1119,17 +1265,12 @@ Phony datetime object which mimics the python datetime object, but uses the "noleap" ("365_day") calendar. """ def __init__(self, *args, **kwargs): - datetime.__init__(self, *args, **kwargs) - self.calendar = "noleap" - self.datetime_compatible = False - assert_valid_date(self, no_leap, False, has_year_zero=True) - - cdef _add_timedelta(self, delta): - return DatetimeNoLeap(*add_timedelta(self, delta, no_leap, False, True)) - - @property - def daysinmonth(self): - return _dpm[self.month-1] + 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) @cython.embedsignature(True) cdef class DatetimeAllLeap(datetime): @@ -1138,17 +1279,12 @@ Phony datetime object which mimics the python datetime object, but uses the "all_leap" ("366_day") calendar. """ def __init__(self, *args, **kwargs): - datetime.__init__(self, *args, **kwargs) - self.calendar = "all_leap" - self.datetime_compatible = False - assert_valid_date(self, all_leap, False, has_year_zero=True) - - cdef _add_timedelta(self, delta): - return DatetimeAllLeap(*add_timedelta(self, delta, all_leap, False, True)) - - @property - def daysinmonth(self): - return _dpm_leap[self.month-1] + 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) @cython.embedsignature(True) cdef class Datetime360Day(datetime): @@ -1157,17 +1293,12 @@ Phony datetime object which mimics the python datetime object, but uses the "360_day" calendar. """ def __init__(self, *args, **kwargs): - datetime.__init__(self, *args, **kwargs) - self.calendar = "360_day" - self.datetime_compatible = False - assert_valid_date(self, no_leap, False, has_year_zero=True, is_360_day=True) - - cdef _add_timedelta(self, delta): - return Datetime360Day(*add_timedelta_360_day(self, delta)) - - @property - def daysinmonth(self): - return _dpm_360[self.month-1] + 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) @cython.embedsignature(True) cdef class DatetimeJulian(datetime): @@ -1176,13 +1307,12 @@ Phony datetime object which mimics the python datetime object, but uses the "julian" calendar. """ def __init__(self, *args, **kwargs): - datetime.__init__(self, *args, **kwargs) - self.calendar = "julian" - self.datetime_compatible = False - assert_valid_date(self, is_leap_julian, False) - - cdef _add_timedelta(self, delta): - return DatetimeJulian(*add_timedelta(self, delta, is_leap_julian, False, False)) + 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) @cython.embedsignature(True) cdef class DatetimeGregorian(datetime): @@ -1199,19 +1329,12 @@ datetime.datetime instances and used to compute time differences a datetime.datetime instance or vice versa. """ def __init__(self, *args, **kwargs): - datetime.__init__(self, *args, **kwargs) - self.calendar = "gregorian" - - # dates after 1582-10-15 can be converted to and compared to - # proleptic Gregorian dates - if self.to_tuple() >= (1582, 10, 15, 0, 0, 0, 0): - self.datetime_compatible = True - else: - self.datetime_compatible = False - assert_valid_date(self, is_leap_gregorian, True) - - cdef _add_timedelta(self, delta): - return DatetimeGregorian(*add_timedelta(self, delta, is_leap_gregorian, True, False)) + 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) @cython.embedsignature(True) cdef class DatetimeProlepticGregorian(datetime): @@ -1232,14 +1355,12 @@ Instance variables are year,month,day,hour,minute,second,microsecond,dayofwk,day format, and calendar. """ def __init__(self, *args, **kwargs): - datetime.__init__(self, *args, **kwargs) - self.calendar = "proleptic_gregorian" - self.datetime_compatible = True - assert_valid_date(self, is_leap_proleptic_gregorian, False) - - cdef _add_timedelta(self, delta): - return DatetimeProlepticGregorian(*add_timedelta(self, delta, - is_leap_proleptic_gregorian, False, False)) + 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) _illegal_s = re.compile(r"((^|[^%])(%%)*%s)") @@ -1489,16 +1610,16 @@ cdef tuple add_timedelta_360_day(datetime dt, delta): # Calendar calculations base on calcals.c by David W. Pierce # http://meteora.ucsd.edu/~pierce/calcalcs -cdef _is_leap(int year, calendar): +cdef _is_leap(int year, calendar, has_year_zero=False): cdef int tyear cdef bint leap calendar = _check_calendar(calendar) - if year == 0: + if year == 0 and not has_year_zero: raise ValueError('year zero does not exist in the %s calendar' %\ calendar) # Because there is no year 0 in the Julian calendar, years -1, -5, -9, etc # are leap years. - if year < 0: + if year < 0 and not has_year_zero: tyear = year + 1 else: tyear = year @@ -1519,7 +1640,7 @@ cdef _is_leap(int year, calendar): leap = False return leap -cdef _IntJulianDayFromDate(int year,int month,int day,calendar,skip_transition=False): +cdef _IntJulianDayFromDate(int year,int month,int day,calendar,skip_transition=False,has_year_zero=False): """Compute integer Julian Day from year,month,day and calendar. Allowed calendars are 'standard', 'gregorian', 'julian', @@ -1541,7 +1662,7 @@ cdef _IntJulianDayFromDate(int year,int month,int day,calendar,skip_transition=F date is noon UTC 0-1-1 for other calendars. There is no year zero in standard (mixed), julian, or proleptic_gregorian - calendars. + calendars by default. If has_year_zero=True, then year zero is included. Subtract 0.5 to get 00 UTC on that day. @@ -1569,18 +1690,18 @@ cdef _IntJulianDayFromDate(int year,int month,int day,calendar,skip_transition=F return _IntJulianDayFromDate_366day(year,month,day) # handle standard, julian, proleptic_gregorian calendars. - if year == 0: + if year == 0 and not has_year_zero: raise ValueError('year zero does not exist in the %s calendar' %\ calendar) if (calendar == 'proleptic_gregorian' and year < -4714) or\ (calendar in ['julian','standard'] and year < -4713): raise ValueError('year out of range for %s calendar' % calendar) - leap = _is_leap(year,calendar) + leap = _is_leap(year,calendar,has_year_zero=has_year_zero) if not leap and month == 2 and day == 29: raise ValueError('%s is not a leap year' % year) # add year offset - if year < 0: + if year < 0 and not has_year_zero: year += 4801 else: year += 4800 @@ -1616,7 +1737,7 @@ cdef _IntJulianDayFromDate(int year,int month,int day,calendar,skip_transition=F return jday -cdef _IntJulianDayToDate(int jday,calendar,skip_transition=False): +cdef _IntJulianDayToDate(int jday,calendar,skip_transition=False,has_year_zero=False): """Compute the year,month,day,dow,doy given the integer Julian day. and calendar. (dow = day of week with 0=Mon,6=Sun and doy is day of year). @@ -1665,18 +1786,18 @@ cdef _IntJulianDayToDate(int jday,calendar,skip_transition=False): # Advance years until we find the right one yp1 = year + 1 - if yp1 == 0: + if yp1 == 0 and not has_year_zero: yp1 = 1 # no year 0 - tjday = _IntJulianDayFromDate(yp1,1,1,calendar,skip_transition=True) + tjday = _IntJulianDayFromDate(yp1,1,1,calendar,skip_transition=True,has_year_zero=has_year_zero) while jday >= tjday: year += 1 - if year == 0: + if year == 0 and not has_year_zero: year = 1 yp1 = year + 1 - if yp1 == 0: + if yp1 == 0 and not has_year_zero: yp1 = 1 - tjday = _IntJulianDayFromDate(yp1,1,1,calendar,skip_transition=True) - if _is_leap(year, calendar): + tjday = _IntJulianDayFromDate(yp1,1,1,calendar,skip_transition=True,has_year_zero=has_year_zero) + if _is_leap(year, calendar,has_year_zero=has_year_zero): dpm2use = _dpm_leap spm2use = _spm_366day else: @@ -1684,12 +1805,12 @@ cdef _IntJulianDayToDate(int jday,calendar,skip_transition=False): spm2use = _spm_365day month = 1 tjday =\ - _IntJulianDayFromDate(year,month,dpm2use[month-1],calendar,skip_transition=True) + _IntJulianDayFromDate(year,month,dpm2use[month-1],calendar,skip_transition=True,has_year_zero=has_year_zero) while jday > tjday: month += 1 tjday =\ - _IntJulianDayFromDate(year,month,dpm2use[month-1],calendar,skip_transition=True) - tjday = _IntJulianDayFromDate(year,month,1,calendar,skip_transition=True) + _IntJulianDayFromDate(year,month,dpm2use[month-1],calendar,skip_transition=True,has_year_zero=has_year_zero) + tjday = _IntJulianDayFromDate(year,month,1,calendar,skip_transition=True,has_year_zero=has_year_zero) day = jday - tjday + 1 if month == 1: doy = day @@ -1999,51 +2120,51 @@ class utime: Performs conversions of netCDF time coordinate data to/from datetime objects. -To initialize: C{t = utime(unit_string,calendar='standard')} +To initialize: `t = utime(unit_string,calendar='standard'` where -B{C{unit_string}} is a string of the form -C{'time-units since '} defining the time units. +`unit_string` is a string of the form +`time-units since ` defining the time units. Valid time-units are days, hours, minutes and seconds (the singular forms -are also accepted). An example unit_string would be C{'hours -since 0001-01-01 00:00:00'}. months is allowed as a time unit +are also accepted). An example unit_string would be `hours +since 0001-01-01 00:00:00`. months is allowed as a time unit *only* for the 360_day calendar. -The B{C{calendar}} keyword describes the calendar used in the time calculations. +The calendar keyword describes the calendar used in the time calculations. All the values currently defined in the U{CF metadata convention } -are accepted. The default is C{'standard'}, which corresponds to the mixed -Gregorian/Julian calendar used by the C{udunits library}. Valid calendars +are accepted. The default is 'standard', which corresponds to the mixed +Gregorian/Julian calendar used by the udunits library. Valid calendars are: -C{'gregorian'} or C{'standard'} (default): +'gregorian' or 'standard' (default): Mixed Gregorian/Julian calendar as defined by udunits. -C{'proleptic_gregorian'}: +'proleptic_gregorian': A Gregorian calendar extended to dates before 1582-10-15. That is, a year is a leap year if either (i) it is divisible by 4 but not by 100 or (ii) it is divisible by 400. -C{'noleap'} or C{'365_day'}: +'noleap' or '365_day': Gregorian calendar without leap years, i.e., all years are 365 days long. all_leap or 366_day Gregorian calendar with every year being a leap year, i.e., all years are 366 days long. -C{'360_day'}: +'360_day': All years are 360 days divided into 30 day months. -C{'julian'}: +'julian': Proleptic Julian calendar, extended to dates after 1582-10-5. A year is a leap year if it is divisible by 4. -The C{L{num2date}} and C{L{date2num}} class methods can used to convert datetime +The num2date and date2num class methods can used to convert datetime instances to/from the specified time units using the specified calendar. Example usage: @@ -2067,8 +2188,8 @@ Example usage: The resolution of the transformation operation is approximately a microsecond. Warning: Dates between 1582-10-5 and 1582-10-15 do not exist in the -C{'standard'} or C{'gregorian'} calendars. An exception will be raised if you pass -a 'datetime-like' object in that range to the C{L{date2num}} class method. +'standard' or 'gregorian' calendars. An exception will be raised if you pass +a 'datetime-like' object in that range to the date2num class method. Words of Wisdom from the British MetOffice concerning reference dates: @@ -2080,42 +2201,42 @@ that the reference date be later than 1582. If earlier dates must be used, it should be noted that udunits treats 0 AD as identical to 1 AD." @ivar origin: datetime instance defining the origin of the netCDF time variable. -@ivar calendar: the calendar used (as specified by the C{calendar} keyword). +@ivar calendar: the calendar used (as specified by the `calendar` keyword). @ivar unit_string: a string defining the the netCDF time variable. -@ivar units: the units part of C{unit_string} (i.e. 'days', 'hours', 'seconds'). +@ivar units: the units part of `unit_string` (i.e. 'days', 'hours', 'seconds'). """ def __init__(self, unit_string, calendar='standard', only_use_cftime_datetimes=True,only_use_python_datetimes=False): """ @param unit_string: a string of the form -C{'time-units since '} defining the time units. +`time-units since ` defining the time units. Valid time-units are days, hours, minutes and seconds (the singular forms -are also accepted). An example unit_string would be C{'hours -since 0001-01-01 00:00:00'}. months is allowed as a time unit +are also accepted). An example unit_string would be `hours +since 0001-01-01 00:00:00`. months is allowed as a time unit *only* for the 360_day calendar. @keyword calendar: describes the calendar used in the time calculations. All the values currently defined in the U{CF metadata convention } -are accepted. The default is C{'standard'}, which corresponds to the mixed -Gregorian/Julian calendar used by the C{udunits library}. Valid calendars +are accepted. The default is `standard`, which corresponds to the mixed +Gregorian/Julian calendar used by the udunits library. Valid calendars are: - - C{'gregorian'} or C{'standard'} (default): + - `gregorian` or `standard` (default): Mixed Gregorian/Julian calendar as defined by udunits. - - C{'proleptic_gregorian'}: + - `proleptic_gregorian`: A Gregorian calendar extended to dates before 1582-10-15. That is, a year is a leap year if either (i) it is divisible by 4 but not by 100 or (ii) it is divisible by 400. - - C{'noleap'} or C{'365_day'}: + - `noleap` or `365_day`: Gregorian calendar without leap years, i.e., all years are 365 days long. - - C{'all_leap'} or C{'366_day'}: + - `all_leap` or `366_day`: Gregorian calendar with every year being a leap year, i.e., all years are 366 days long. - -C{'360_day'}: + -`360_day`: All years are 360 days divided into 30 day months. - -C{'julian'}: + -`julian`: Proleptic Julian calendar, extended to dates after 1582-10-5. A year is a leap year if it is divisible by 4. @@ -2145,8 +2266,8 @@ units to datetime objects. def date2num(self, date): """ - Returns C{time_value} in units described by L{unit_string}, using - the specified L{calendar}, given a 'datetime-like' object. + Returns `time_value` in units described by `unit_string`, using + the specified `calendar`, given a 'datetime-like' object. The datetime object must represent UTC with no time-zone offset. If there is a time-zone offset implied by L{unit_string}, it will @@ -2154,7 +2275,7 @@ units to datetime objects. Resolution is approximately a microsecond. - If C{calendar = 'standard'} or C{'gregorian'} (indicating + If calendar = 'standard' or 'gregorian' (indicating that the mixed Julian/Gregorian calendar is to be used), an exception will be raised if the 'datetime-like' object describes a date between 1582-10-5 and 1582-10-15. @@ -2166,8 +2287,8 @@ units to datetime objects. def num2date(self, time_value): """ - Return a 'datetime-like' object given a C{time_value} in units - described by L{unit_string}, using L{calendar}. + Return a 'datetime-like' object given a `time_value` in units + described by `unit_string`, using `calendar`. dates are in UTC with no offset, even if L{unit_string} contains a time zone offset from UTC. diff --git a/docs/_build/doctrees/api.doctree b/docs/_build/doctrees/api.doctree index 1eaeabb2..2cee1568 100644 Binary files a/docs/_build/doctrees/api.doctree and b/docs/_build/doctrees/api.doctree differ diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle index e3b8194d..2e44ca50 100644 Binary files a/docs/_build/doctrees/environment.pickle and b/docs/_build/doctrees/environment.pickle differ diff --git a/docs/_build/html/api.html b/docs/_build/html/api.html index cab276cc..8f9677ae 100644 --- a/docs/_build/html/api.html +++ b/docs/_build/html/api.html @@ -37,9 +37,28 @@
class cftime.datetime(int year, int month, int day, int hour=0, int minute=0, int second=0, int microsecond=0, int dayofwk=-1, int dayofyr=-1, calendar='standard')

Bases: object

-

The base class implementing most methods of datetime classes that -mimic datetime.datetime but support calendars other than the proleptic -Gregorial calendar.

+

This class mimics datetime.datetime but support calendars other than the proleptic +Gregorian calendar.

+

Supports timedelta operations by overloading +/-, and +comparisons with other instances using the same calendar.

+

Comparison with native python datetime instances is possible +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. +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. +‘standard’ and ‘gregorian’ are synonyms, as are ‘all_leap’/’366_day’ +and ‘noleap’/’365_day’.

+

If the calendar kwarg is set to a blank string (‘’) the +instance will not be calendar-aware and some methods will not work.

+

Has isoformat, strftime, timetuple, replace, dayofwk, dayofyr, daysinmonth, +__repr__, __add__, __sub__, __str__ and comparison methods.

+

dayofwk, dayofyr, daysinmonth, __add__ and __sub__ only work for calendar-aware +instances.

+

The default format of the string produced by strftime is controlled by self.format +(default %Y-%m-%d %H:%M:%S).

isoformat(self, sep='T', timespec='auto')
@@ -260,19 +279,19 @@

Return indices of a netCDF time variable corresponding to the given times.

times: A numeric time or a sequence of numeric times.

nctime: A netCDF time variable object. The nctime object must have a -C{units} attribute. The entries are assumed to be stored in increasing +units attribute. The entries are assumed to be stored in increasing order.

calendar: Describes the calendar used in the time calculation. -Valid calendars C{‘standard’, ‘gregorian’, ‘proleptic_gregorian’ -‘noleap’, ‘365_day’, ‘360_day’, ‘julian’, ‘all_leap’, ‘366_day’}. -Default is C{‘standard’}, which is a mixed Julian/Gregorian calendar -If C{calendar} is None, its value is given by C{nctime.calendar} or -C{standard} if no such attribute exists.

+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 +If calendar is None, its value is given by nctime.calendar or +standard if no such attribute exists.

select: ‘exact’, ‘before’, ‘after’, ‘nearest’ -The index selection method. C{exact} will return the indices perfectly -matching the times given. C{before} and C{after} will return the indices +The index selection method. exact will return the indices perfectly +matching the times given. before and after will return the indices corresponding to the times just before or just after the given times if -an exact match cannot be found. C{nearest} will return the indices that +an exact match cannot be found. nearest will return the indices that correspond to the closest times.

diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index e1c53bc3..393825bf 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["api","index","installing"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["api.rst","index.rst","installing.rst"],objects:{"":{cftime:[0,0,0,"-"]},"cftime.datetime":{isoformat:[0,3,1,""],replace:[0,3,1,""],strftime:[0,3,1,""],timetuple:[0,3,1,""]},cftime:{DateFromJulianDay:[0,1,1,""],DatetimeAllLeap:[0,2,1,""],DatetimeGregorian:[0,2,1,""],DatetimeJulian:[0,2,1,""],DatetimeNoLeap:[0,2,1,""],DatetimeProlepticGregorian:[0,2,1,""],JulianDayFromDate:[0,1,1,""],date2index:[0,1,1,""],date2num:[0,1,1,""],datetime:[0,2,1,""],num2date:[0,1,1,""],num2pydate:[0,1,1,""],time2index:[0,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","class","Python class"],"3":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:function","2":"py:class","3":"py:method"},terms:{"1st":0,"360_dai":0,"365_dai":0,"366_dai":0,"class":0,"default":[0,2],"int":0,"new":[0,2],"return":0,"true":0,For:0,Has:0,The:[0,2],Then:2,__repr__:0,__str__:0,_cftime:0,accuraci:0,after:0,all:0,all_leap:0,allow:0,also:2,alwai:0,api:1,appear:2,appli:0,approxim:0,arg:0,argument:0,arrai:0,assum:0,attribut:0,auto:0,base:0,befor:[0,2],behavior:0,being:2,between:0,breakpoint:0,build:2,build_ext:2,calcul:0,calendar:0,can:0,cannot:0,cartopi:2,cfconvent:0,cftime:[0,2],chang:2,channel:2,check:2,climat:1,clone:2,closest:0,command:2,commun:2,compar:0,comparison:0,complet:0,comput:0,conda:2,conform:1,contain:0,control:0,convent:[0,1],correspond:0,creat:0,ctime:0,current:0,cython:2,dai:0,date2index:0,date2num:0,date:0,datefromjuliandai:0,datetim:0,datetimeallleap:0,datetimegregorian:0,datetimejulian:0,datetimenoleap:0,datetimeprolepticgregorian:0,dayofwk:0,dayofyr:0,decod:1,defin:0,depend:1,describ:0,develop:1,differ:0,difficult:2,direct:0,document:0,don:[0,2],dst:0,easiest:2,entri:0,equival:0,error:0,even:0,everyth:2,exact:0,exist:0,explicit:0,extens:2,fall:0,fals:0,field:0,file:1,first:2,flag:0,follow:0,forecast:1,forg:2,form:0,format:0,found:0,fraction:0,from:0,get:2,github:2,given:0,gregori:0,gregorian:0,have:[0,2],hour:0,http:0,ignor:0,implement:0,includ:0,increas:0,index:[0,1],indic:0,inplac:2,instal:1,instanc:0,instruct:1,isoformat:0,its:0,januari:0,julian:0,juliandayfromd:0,just:0,keyword:0,kwarg:0,last:0,later:2,librari:1,like:0,line:2,list:0,localtim:0,mai:2,maintain:2,match:0,metadata:0,method:0,microsecond:0,millisecond:0,mimic:0,minut:0,mix:0,modul:1,month:0,months_sinc:0,most:0,must:0,nativ:0,nctime:0,nearest:0,need:2,netcdf:[0,1],noleap:0,none:0,note:0,num2dat:0,num2pyd:0,number:0,numer:0,numpi:2,object:0,offset:0,one:0,onli:0,only_use_cftime_datetim:0,only_use_python_datetim:0,oper:0,order:0,org:0,origin:0,other:0,otherwis:0,overload:0,page:1,pass:2,perfectli:0,phoni:0,pip:2,place:2,possibl:0,produc:0,prolept:0,proleptic_gregorian:0,pyarg:2,pynio:2,pytest:2,python:[0,1,2],rais:0,real:0,recommend:2,refer:0,releas:2,replac:0,repositori:2,repres:0,requir:1,return_tupl:0,run:2,same:0,search:1,second:0,section:0,see:0,select:0,self:0,sep:0,sequenc:0,set:0,setup:2,should:0,sinc:0,some:0,specifi:0,standard:0,start:0,store:0,strftime:0,string:0,strptime:0,struct_tim:0,subclass:0,subtract:0,suit:2,support:0,sure:2,test:2,than:0,thei:0,thi:0,time2index:0,time:[0,1],timedelta:0,timespec:0,timetupl:0,tool:2,unit:[0,1],unless:0,updat:2,use:2,use_only_python_datetim:0,used:0,uses:0,using:[0,2],utc:0,valid:0,valu:[0,1],variabl:[0,1],versa:0,vice:0,wai:2,weekdai:0,when:2,where:0,which:0,within:0,ydai:0,year:0,you:2,zone:0},titles:["API","cftime","Installation"],titleterms:{api:0,cftime:1,content:1,depend:2,develop:2,indic:1,instal:2,instruct:2,requir:2,tabl:1}}) \ No newline at end of file +Search.setIndex({docnames:["api","index","installing"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["api.rst","index.rst","installing.rst"],objects:{"":{cftime:[0,0,0,"-"]},"cftime.datetime":{isoformat:[0,3,1,""],replace:[0,3,1,""],strftime:[0,3,1,""],timetuple:[0,3,1,""]},cftime:{DateFromJulianDay:[0,1,1,""],DatetimeAllLeap:[0,2,1,""],DatetimeGregorian:[0,2,1,""],DatetimeJulian:[0,2,1,""],DatetimeNoLeap:[0,2,1,""],DatetimeProlepticGregorian:[0,2,1,""],JulianDayFromDate:[0,1,1,""],date2index:[0,1,1,""],date2num:[0,1,1,""],datetime:[0,2,1,""],num2date:[0,1,1,""],num2pydate:[0,1,1,""],time2index:[0,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","class","Python class"],"3":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:function","2":"py:class","3":"py:method"},terms:{"1st":0,"360_dai":0,"365_dai":0,"366_dai":0,"class":0,"default":[0,2],"int":0,"new":[0,2],"return":0,"true":0,For:0,Has:0,The:[0,2],Then:2,__add__:0,__repr__:0,__str__:0,__sub__:0,_cftime:0,accuraci:0,after:0,all:0,all_leap:0,allow:0,also:2,alwai:0,api:1,appear:2,appli:0,approxim:0,arg:0,argument:0,arrai:0,assum:0,attribut:0,auto:0,awar:0,base:0,befor:[0,2],behavior:0,being:2,between:0,blank:0,breakpoint:0,build:2,build_ext:2,calcul:0,calendar:0,can:0,cannot:0,cartopi:2,cfconvent:0,cftime:[0,2],chang:2,channel:2,check:2,climat:1,clone:2,closest:0,command:2,commun:2,compar:0,comparison:0,complet:0,comput:0,conda:2,conform:1,contain:0,control:0,convent:[0,1],correspond:0,creat:0,ctime:0,current:0,cython:2,dai:0,date2index:0,date2num:0,date:0,datefromjuliandai:0,datetim:0,datetimeallleap:0,datetimegregorian:0,datetimejulian:0,datetimenoleap:0,datetimeprolepticgregorian:0,dayofwk:0,dayofyr:0,daysinmonth:0,decod:1,defin:0,depend:1,describ:0,develop:1,differ:0,difficult:2,direct:0,document:0,don:[0,2],dst:0,easiest:2,entri:0,equival:0,error:0,even:0,everyth:2,exact:0,exist:0,explicit:0,extens:2,fall:0,fals:0,field:0,file:1,first:2,flag:0,follow:0,forecast:1,forg:2,form:0,format:0,found:0,fraction:0,from:0,get:2,github:2,given:0,gregori:[],gregorian:0,have:[0,2],hour:0,http:0,ignor:0,implement:[],includ:0,increas:0,index:[0,1],indic:0,inplac:2,instal:1,instanc:0,instruct:1,isoformat:0,its:0,januari:0,julian:0,juliandayfromd:0,just:0,keyword:0,kwarg:0,last:0,later:2,librari:1,like:0,line:2,list:0,localtim:0,mai:2,maintain:2,match:0,metadata:0,method:0,microsecond:0,millisecond:0,mimic:0,minut:0,mix:0,modul:1,month:0,months_sinc:0,most:[],must:0,nativ:0,nctime:0,nearest:0,need:2,netcdf:[0,1],noleap:0,none:0,note:0,num2dat:0,num2pyd:0,number:0,numer:0,numpi:2,object:0,offset:0,one:0,onli:0,only_use_cftime_datetim:0,only_use_python_datetim:0,oper:0,order:0,org:0,origin:0,other:0,otherwis:0,overload:0,page:1,pass:2,perfectli:0,phoni:0,pip:2,place:2,possibl:0,produc:0,prolept:0,proleptic_gregorian:0,pyarg:2,pynio:2,pytest:2,python:[0,1,2],rais:0,real:0,recommend:2,refer:0,releas:2,replac:0,repositori:2,repres:0,requir:1,return_tupl:0,run:2,same:0,search:1,second:0,section:0,see:0,select:0,self:0,sep:0,sequenc:0,set:0,setup:2,should:0,sinc:0,some:0,specifi:0,standard:0,start:0,store:0,strftime:0,string:0,strptime:0,struct_tim:0,subclass:0,subtract:0,suit:2,support:0,sure:2,synonym:0,test:2,than:0,thei:0,thi:0,time2index:0,time:[0,1],timedelta:0,timespec:0,timetupl:0,tool:2,unit:[0,1],unless:0,updat:2,use:2,use_only_python_datetim:0,used:0,uses:0,using:[0,2],utc:0,valid:0,valu:[0,1],variabl:[0,1],versa:0,vice:0,wai:2,weekdai:0,when:2,where:0,which:0,within:0,work:0,ydai:0,year:0,you:2,zone:0},titles:["API","cftime","Installation"],titleterms:{api:0,cftime:1,content:1,depend:2,develop:2,indic:1,instal:2,instruct:2,requir:2,tabl:1}}) \ No newline at end of file diff --git a/test/test_cftime.py b/test/test_cftime.py index 3e68b5ee..b82a5ad3 100644 --- a/test/test_cftime.py +++ b/test/test_cftime.py @@ -51,6 +51,19 @@ def dst(self, dt): dtime = namedtuple('dtime', ('values', 'units', 'calendar')) dateformat = '%Y-%m-%d %H:%M:%S' +calendars=['standard', 'gregorian', 'proleptic_gregorian', 'noleap', 'julian',\ + 'all_leap', '365_day', '366_day', '360_day'] +def adjust_calendar(calendar): + # check for and remove calendar synonyms. + calendar = calendar.lower() + if calendar == 'gregorian' or calendar == 'standard': + return 'gregorian' + elif calendar == 'noleap' or calendar == '365_day': + return 'noleap' + elif calendar == 'all_leap' or calendar == '366_day': + return 'all_leap' + else: + return calendar class CFTimeVariable(object): '''dummy "netCDF" variable to hold time info''' @@ -248,7 +261,7 @@ def test_tz_naive(self): # check date2num,num2date methods. # use datetime from cftime, since this date doesn't # exist in "normal" calendars. - d = datetimex(2000, 2, 30) + d = datetimex(2000, 2, 30, calendar='') t1 = self.cdftime_360day.date2num(d) assert_almost_equal(t1, 360 * 400.) d2 = self.cdftime_360day.num2date(t1) @@ -297,7 +310,7 @@ def test_tz_naive(self): t2 = date2num(d, units='days since 0001-01-01 00:00:00') assert(abs(t2 - t) < 1e-5) # values should be less than second # Check equality testing - d1 = datetimex(1979, 6, 21, 9, 23, 12) + d1 = datetimex(1979, 6, 21, 9, 23, 12, calendar='standard') d2 = datetime(1979, 6, 21, 9, 23, 12) assert(d1 == d2) # check timezone offset @@ -371,108 +384,55 @@ def test_tz_naive(self): # test rountrip accuracy # also tests error found in issue #349 - calendars=['standard', 'gregorian', 'proleptic_gregorian', 'noleap', 'julian',\ - 'all_leap', '365_day', '366_day', '360_day'] dateref = datetime(2015,2,28,12) - ntimes = 1001 verbose = True # print out max error diagnostics + ntimes = 101 + def roundtrip(delta,eps,units): + times1 = date2num(dateref,units,calendar=calendar) + times1 += delta*np.arange(0,ntimes) + dates1 = num2date(times1,units,calendar=calendar) + times2 = date2num(dates1,units,calendar=calendar) + dates2 = num2date(times2,units,calendar=calendar) + err = np.abs(times1 - times2) + assert(np.all(err