Skip to content
Commits on Source (9)
environment:
CONDA_INSTALL_LOCN: C:\\Miniconda36-x64
PYTHON: "C:\\myminiconda3"
matrix:
- TARGET_ARCH: x64
NPY: 1.16
PY: 3.6
- TARGET_ARCH: x64
NPY: 1.16
NPY: 1.17
PY: 3.7
platform:
- x64
- TARGET_ARCH: x64
NPY: 1.17
PY: 3.8
init:
- "ECHO %PYTHON_VERSION% %MINICONDA%"
install:
# If there is a newer build queued for the same PR, cancel this one.
......@@ -24,18 +25,19 @@ install:
throw "There are newer queued builds for this pull request, failing early." }
# Add path, activate `conda` and update conda.
- cmd: call %CONDA_INSTALL_LOCN%\Scripts\activate.bat
- cmd: conda config --set always_yes yes --set changeps1 no --set show_channel_urls true
- cmd: conda update conda
- cmd: conda config --add channels conda-forge --force
- cmd: conda config --set channel_priority strict
- cmd: set PYTHONUNBUFFERED=1
- cmd: conda install conda-build vs2008_express_vc_python_patch
- cmd: call setup_x64
- cmd: conda.exe create --name TEST python=%PY% numpy=%NPY% cython pip pytest pytest-cov
- cmd: conda info --all
- cmd: conda activate TEST
- set URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe"
- curl -fsS -o miniconda3.exe %URL%
- start /wait "" miniconda3.exe /InstallationType=JustMe /RegisterPython=0 /S /D=%PYTHON%
- "set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- call %PYTHON%\Scripts\activate
- conda config --set always_yes yes --set changeps1 no --set show_channel_urls true
- conda config --add channels conda-forge --force
- set PYTHONUNBUFFERED=1
- conda install conda-build vs2008_express_vc_python_patch
- call setup_x64
- conda create --name TEST python=%PY% numpy=%NPY% cython pip pytest pytest-cov
- conda activate TEST
# Skip .NET project specific build phase.
build: off
......
......@@ -4,6 +4,5 @@
build/
*.egg?
*.egg-info
_build/
__pycache__
.pytest_cache/
......@@ -8,8 +8,8 @@ notifications:
env:
- PYTHON_VERSION=2.7
- PYTHON_VERSION=3.6
- PYTHON_VERSION=3.7
- PYTHON_VERSION=3.8
matrix:
include:
......
version 1.1.0 (not yet released)
================================
* improved exceptions for time differences (issue #128, PR #131).
* fix intersphinx entries (issue #133, PR #133)
* make only_use_cftime_datetimes=True by default, so cftime datetime
instances are returned by default by num2date (instead of returning python
datetime instances where possible). Issue #136, PR #135.
* Add daysinmonth attribute (issue #137, PR #138).
* If only_use_python_datetimes=True and only_use_cftime_datetimes=False,
num2date only returns python datetime instances and raises an exception
if this is not possible. num2pydate convenience function added which just calls
num2date with only_use_python_datetimes=True and
only_use_cftime_datetimes=False.
Remove positive times check, raise ValueError if python datetime
tries to compute a date before MINYEAR (issue #134, PR #139)
* Fix for fractional seconds in reference date in units string (issue #140,
PR # 141).
version 1.0.4.2 release
=======================
* fix for issue #126 (date2num error when converting a DatetimeProlepticGregorian
object). PR #127.
......@@ -10,6 +10,14 @@ Time-handling functionality from netcdf4-python
[![Commits Status](https://img.shields.io/github/commits-since/UniData/cftime/latest.svg)](https://github.com/UniData/cftime/commits/master)
## News
For details on the latest updates, see the [Changelog](https://github.com/Unidata/cftime/blob/master/Changelog).
2/12/2019: version 1.1.0 released. `cftime.datetime` instances are returned by default from `num2date`
(instead of returning python datetime instances where possible ([issue #136](https://github.com/Unidata/cftime/issues/136)). `num2pydate`
convenience function added (always returns python datetime instances, [issue #134](https://github.com/Unidata/cftime/issues/134)). Fix for
fraction seconds in reference date string ([issue #140](https://github.com/Unidata/cftime/issues/140)). Added `daysinmonth` attribute
([issue #137](https://github.com/Unidata/cftime/issues/137)).
10/25/2019: version 1.0.4.2 released (fix for [issue #126](https://github.com/Unidata/cftime/issues/126)).
10/21/2019: version 1.0.4 released.
......
......@@ -5,5 +5,5 @@ from ._cftime import DatetimeNoLeap, DatetimeAllLeap, Datetime360Day, DatetimeJu
DatetimeGregorian, DatetimeProlepticGregorian
from ._cftime import microsec_units, millisec_units, \
sec_units, hr_units, day_units, min_units
from ._cftime import num2date, date2num, date2index
from ._cftime import num2date, date2num, date2index, num2pydate
from ._cftime import __version__
......@@ -4,6 +4,7 @@ Performs conversions of netCDF time coordinate data to/from datetime objects.
from cpython.object cimport (PyObject_RichCompare, Py_LT, Py_LE, Py_EQ,
Py_NE, Py_GT, Py_GE)
from numpy cimport int64_t, int32_t
import cython
import numpy as np
import re
......@@ -39,11 +40,19 @@ cdef int[12] _dpm_360 = [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30]
# Same as above, but SUM of previous months (no leap years).
cdef int[13] _spm_365day = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
cdef int[13] _spm_366day = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
# Slightly more performant cython lookups than a 2D table
# The first 12 entries correspond to month lengths for non-leap years.
# The remaining 12 entries give month lengths for leap years
cdef int32_t* days_per_month_array = [
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
# Reverse operator lookup for datetime.__richcmp__
_rop_lookup = {Py_LT: '__gt__', Py_LE: '__ge__', Py_EQ: '__eq__',
Py_GT: '__lt__', Py_GE: '__le__', Py_NE: '__ne__'}
__version__ = '1.0.4.2'
__version__ = '1.1.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.
......@@ -57,8 +66,31 @@ ISO8601_REGEX = re.compile(r"(?P<year>[+-]?[0-9]{1,4})(-(?P<month>[0-9]{1,2})(-(
TIMEZONE_REGEX = re.compile(
"(?P<prefix>[+-])(?P<hours>[0-9]{2})(?:(?::(?P<minutes1>[0-9]{2}))|(?P<minutes2>[0-9]{2}))?")
# Taken from pandas ccalendar.pyx
@cython.wraparound(False)
@cython.boundscheck(False)
cpdef int32_t get_days_in_month(bint isleap, int month) nogil:
"""
Return the number of days in the given month of the given year.
Parameters
----------
leap : int [0,1]
month : int
Returns
-------
days_in_month : int
Notes
-----
Assumes that the arguments are valid. Passing a month not between 1 and 12
risks a segfault.
"""
return days_per_month_array[12 * isleap + month - 1]
class real_datetime(datetime_python):
"""add dayofwk and dayofyr attributes to python datetime instance"""
"""add dayofwk, dayofyr attributes to python datetime instance"""
@property
def dayofwk(self):
# 0=Monday, 6=Sunday
......@@ -97,12 +129,14 @@ def _dateparse(timestr):
year, month, day, hour, minute, second, microsecond, utc_offset =\
_parse_date( isostring.strip() )
if year >= MINYEAR:
basedate = real_datetime(year, month, day, hour, minute, second)
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.)
else:
if not utc_offset:
basedate = datetime(year, month, day, hour, minute, second)
basedate = datetime(year, month, day, hour, minute, second,
microsecond)
else:
raise ValueError('cannot use utc_offset for reference years <= 0')
return basedate
......@@ -215,8 +249,19 @@ def date2num(dates,units,calendar='standard'):
return cdftime.date2num(dates)
def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
"""num2date(times,units,calendar='standard')
def num2pydate(times,units,calendar='standard'):
"""num2pydate(times,units,calendar='standard')
Always returns python datetime.datetime
objects and raise an error if this is not possible.
Same as
num2date(times,units,calendar,only_use_cftime_datetimes=False,only_use_python_datetimes=True)
"""
return num2date(times,units,calendar,only_use_cftime_datetimes=False,only_use_python_datetimes=True)
def num2date(times,units,calendar='standard',\
only_use_cftime_datetimes=True,only_use_python_datetimes=False):
"""num2date(times,units,calendar='standard',only_use_cftime_datetimes=True,only_use_python_datetimes=False)
Return datetime objects given numeric time values. The units
of the numeric time values are described by the `units` argument
......@@ -238,40 +283,54 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'`.
Default is `'standard'`, which is a mixed Julian/Gregorian calendar.
**`only_use_cftime_datetimes`**: if False (default), datetime.datetime
**`only_use_cftime_datetimes`**: if False, python datetime.datetime
objects are returned from num2date where possible; if True dates which
subclass cftime.datetime are returned for all calendars.
subclass cftime.datetime are returned for all calendars. Default `True`.
**`only_use_python_datetimes`**: always return python datetime.datetime
objects and raise an error if this is not possible. Ignored unless
`only_use_cftime_datetimes=False`. Default `False`.
returns a datetime instance, or an array of datetime instances with
approximately 100 microsecond accuracy.
***Note***: The datetime instances returned are 'real' python datetime
***Note***: If only_use_cftime_datetimes=False and
use_only_python_datetimes=False, the datetime instances
returned are 'real' python datetime
objects if `calendar='proleptic_gregorian'`, or
`calendar='standard'` or `'gregorian'`
and the date is after the breakpoint between the Julian and
Gregorian calendars (1582-10-15). Otherwise, they are 'phony' datetime
objects which support some but not all the methods of 'real' python
Gregorian calendars (1582-10-15). Otherwise, they are ctime.datetime
objects which support some but not all the methods of native python
datetime objects. The datetime instances
do not contain a time-zone offset, even if the specified `units`
contains one.
"""
calendar = calendar.lower()
basedate = _dateparse(units)
can_use_python_datetime=\
((calendar == 'proleptic_gregorian' and basedate.year >= MINYEAR) or \
(calendar in ['gregorian','standard'] and basedate > gregorian))
if not only_use_cftime_datetimes and only_use_python_datetimes:
if not can_use_python_datetime:
msg='illegal calendar or reference date for python datetime'
raise ValueError(msg)
(unit, ignore) = _datesplit(units)
# real-world calendars limited to positive reference years.
if calendar in ['julian', 'standard', 'gregorian', 'proleptic_gregorian']:
if basedate.year == 0:
msg='zero not allowed as a reference year, does not exist in Julian or Gregorian calendars'
raise ValueError(msg)
postimes = (np.asarray(times) > 0).all() # netcdf4-python issue #659
if only_use_cftime_datetimes:
if only_use_cftime_datetimes or not \
(only_use_python_datetimes and can_use_python_datetime):
cdftime = utime(units, calendar=calendar,
only_use_cftime_datetimes=only_use_cftime_datetimes)
return cdftime.num2date(times)
elif postimes and ((calendar == 'proleptic_gregorian' and basedate.year >= MINYEAR) or \
(calendar in ['gregorian','standard'] and basedate > gregorian)):
# use python datetime module,
else: # use python datetime module
isscalar = False
try:
times[0]
......@@ -313,15 +372,17 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
msecs = np.round(msecsd - secs*1.e6)
td = timedelta(days=days,seconds=secs,microseconds=msecs)
# add time delta to base date.
try:
date = basedate + td
except OverflowError:
msg="""
OverflowError in python datetime, probably because year < datetime.MINYEAR"""
raise ValueError(msg)
dates.append(date)
if isscalar:
return dates[0]
else:
return np.reshape(np.array(dates), shape)
else: # use cftime for other calendars
cdftime = utime(units,calendar=calendar)
return cdftime.num2date(times)
def date2index(dates, nctime, calendar=None, select='exact'):
......@@ -435,7 +496,7 @@ def JulianDayFromDate(date, calendar='standard'):
else:
return jd
def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=False,
def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=True,
return_tuple=False):
"""
......@@ -451,10 +512,9 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=False,
If only_use_cftime_datetimes is set to True, then cftime.datetime
objects are returned for all calendars. Otherwise the datetime object is a
'real' datetime object if the date falls in the Gregorian calendar
native python datetime object if the date falls in the Gregorian calendar
(i.e. calendar='proleptic_gregorian', or calendar = 'standard'/'gregorian'
and the date is after 1582-10-15). In all other cases a 'phony' datetime
objects are used, which are actually instances of cftime.datetime.
and the date is after 1582-10-15).
"""
julian = np.atleast_1d(np.array(JD, dtype=np.longdouble))
......@@ -640,16 +700,15 @@ leap year if it is divisible by 4.
The C{L{num2date}} and C{L{date2num}} class methods can used to convert datetime
instances to/from the specified time units using the specified calendar.
The datetime instances returned by C{num2date} are 'real' python datetime
The datetime instances returned by C{num2date} are native python datetime
objects if the date falls in the Gregorian calendar (i.e.
C{calendar='proleptic_gregorian', 'standard'} or C{'gregorian'} and
the date is after 1582-10-15). Otherwise, they are 'phony' datetime
the date is after 1582-10-15). Otherwise, they are native datetime
objects which are actually instances of C{L{cftime.datetime}}. This is
because the python datetime module cannot handle the weird dates in some
calendars (such as C{'360_day'} and C{'all_leap'}) which don't exist in any real
world calendar.
Example usage:
>>> from cftime import utime
......@@ -690,7 +749,7 @@ it should be noted that udunits treats 0 AD as identical to 1 AD."
"""
def __init__(self, unit_string, calendar='standard',
only_use_cftime_datetimes=False):
only_use_cftime_datetimes=True):
"""
@param unit_string: a string of the form
C{'time-units since <time-origin>'} defining the time units.
......@@ -723,9 +782,9 @@ are:
Proleptic Julian calendar, extended to dates after 1582-10-5. A year is a
leap year if it is divisible by 4.
@keyword only_use_cftime_datetimes: if False (default), datetime.datetime
@keyword only_use_cftime_datetimes: if False, datetime.datetime
objects are returned from num2date where possible; if True dates which subclass
cftime.datetime are returned for all calendars.
cftime.datetime are returned for all calendars. Default True.
@returns: A class instance which may be used for converting times from netCDF
units to datetime objects.
......@@ -823,10 +882,10 @@ units to datetime objects.
Works for scalars, sequences and numpy arrays.
Returns a scalar if input is a scalar, else returns a numpy array.
The datetime instances returned by C{num2date} are 'real' python datetime
The datetime instances returned by C{num2date} are native python datetime
objects if the date falls in the Gregorian calendar (i.e.
C{calendar='proleptic_gregorian'}, or C{calendar = 'standard'/'gregorian'} and
the date is after 1582-10-15). Otherwise, they are 'phony' datetime
the date is after 1582-10-15). Otherwise, they are cftime.datetime
objects which are actually instances of cftime.datetime. This is
because the python datetime module cannot handle the weird dates in some
calendars (such as C{'360_day'} and C{'all_leap'}) which
......@@ -1184,7 +1243,7 @@ The base class implementing most methods of datetime classes that
mimic datetime.datetime but support calendars other than the proleptic
Gregorial calendar.
"""
cdef readonly int year, month, day, hour, minute, dayofwk, dayofyr
cdef readonly int year, month, day, hour, minute, dayofwk, dayofyr, daysinmonth
cdef readonly int second, microsecond
cdef readonly str calendar
......@@ -1208,7 +1267,7 @@ Gregorial calendar.
self.second = second
self.microsecond = microsecond
self.calendar = ""
self.daysinmonth = -1
self.datetime_compatible = True
@property
......@@ -1368,7 +1427,12 @@ Gregorial calendar.
elif isinstance(other, datetime_python):
# datetime - real_datetime
if not dt.datetime_compatible:
raise ValueError("cannot compute the time difference between dates with different calendars")
msg="""
Cannot compute the time difference between dates with different calendars.
One of the datetime objects may have been converted to a native python
datetime instance. Try using only_use_cftime_datetimes=True when creating the
datetime object."""
raise ValueError(msg)
return dt._to_real_datetime() - other
elif isinstance(other, timedelta):
# datetime - timedelta
......@@ -1379,7 +1443,12 @@ Gregorial calendar.
if isinstance(self, datetime_python):
# real_datetime - datetime
if not other.datetime_compatible:
raise ValueError("cannot compute the time difference between dates with different calendars")
msg="""
Cannot compute the time difference between dates with different calendars.
One of the datetime objects may have been converted to a native python
datetime instance. Try using only_use_cftime_datetimes=True when creating the
datetime object."""
raise ValueError(msg)
return self - other._to_real_datetime()
else:
return NotImplemented
......@@ -1402,6 +1471,7 @@ but uses the "noleap" ("365_day") calendar.
DateFromJulianDay(jd,return_tuple=True,calendar='365_day')
self.dayofwk = dayofwk
self.dayofyr = dayofyr
self.daysinmonth = _dpm[self.month-1]
cdef _add_timedelta(self, delta):
return DatetimeNoLeap(*add_timedelta(self, delta, no_leap, False))
......@@ -1424,6 +1494,7 @@ but uses the "all_leap" ("366_day") calendar.
DateFromJulianDay(jd,return_tuple=True,calendar='366_day')
self.dayofwk = dayofwk
self.dayofyr = dayofyr
self.daysinmonth = _dpm_leap[self.month-1]
cdef _add_timedelta(self, delta):
return DatetimeAllLeap(*add_timedelta(self, delta, all_leap, False))
......@@ -1446,6 +1517,7 @@ but uses the "360_day" calendar.
DateFromJulianDay(jd,return_tuple=True,calendar='360_day')
self.dayofwk = dayofwk
self.dayofyr = dayofyr
self.daysinmonth = 30
cdef _add_timedelta(self, delta):
return Datetime360Day(*add_timedelta_360_day(self, delta))
......@@ -1468,6 +1540,7 @@ but uses the "julian" calendar.
DateFromJulianDay(jd,return_tuple=True,calendar='julian')
self.dayofwk = dayofwk
self.dayofyr = dayofyr
self.daysinmonth = get_days_in_month(_is_leap(self.year, self.calendar), self.month)
cdef _add_timedelta(self, delta):
return DatetimeJulian(*add_timedelta(self, delta, is_leap_julian, False))
......@@ -1504,6 +1577,7 @@ a datetime.datetime instance or vice versa.
DateFromJulianDay(jd,return_tuple=True,calendar='gregorian')
self.dayofwk = dayofwk
self.dayofyr = dayofyr
self.daysinmonth = get_days_in_month(_is_leap(self.year, self.calendar), self.month)
cdef _add_timedelta(self, delta):
return DatetimeGregorian(*add_timedelta(self, delta, is_leap_gregorian, True))
......@@ -1518,9 +1592,9 @@ 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 phony
(default %Y-%m-%d %H:%M:%S). Supports comparisons with other
datetime instances using the same calendar; comparison with
datetime.datetime instances is possible for cftime.datetime
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,
......@@ -1538,6 +1612,7 @@ format, and calendar.
DateFromJulianDay(jd,return_tuple=True,calendar='proleptic_gregorian')
self.dayofwk = dayofwk
self.dayofyr = dayofyr
self.daysinmonth = get_days_in_month(_is_leap(self.year, self.calendar), self.month)
cdef _add_timedelta(self, delta):
return DatetimeProlepticGregorian(*add_timedelta(self, delta,
......
cftime (1.0.4.2-2) UNRELEASED; urgency=medium
cftime (1.1.0+ds-1) unstable; urgency=medium
* New upstream release.
* Drop Name field from upstream metadata.
* Bump Standards-Version to 4.5.0, no changes.
* Repack upstream tarball to exclude pre-built documentation.
* Reinstate coverage support.
-- Bas Couwenberg <sebastic@debian.org> Mon, 09 Dec 2019 09:05:25 +0100
-- Bas Couwenberg <sebastic@debian.org> Fri, 14 Feb 2020 06:57:51 +0100
cftime (1.0.4.2-1) unstable; urgency=medium
......
......@@ -6,9 +6,11 @@ Priority: optional
Build-Depends: debhelper (>= 9),
dh-python,
python3-all-dev,
python3-coverage,
python3-setuptools,
python3-numpy,
python3-pytest,
python3-pytest-cov,
cython3
Standards-Version: 4.5.0
Vcs-Browser: https://salsa.debian.org/debian-gis-team/cftime/
......
......@@ -2,6 +2,9 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cftime
Upstream-Contact: Jeff Whitaker <jeffrey.s.whitaker@noaa.gov>
Source: https://github.com/Unidata/cftime
Comment: Pre-built documentation is excluded from the upstream tarball.
Files-Excluded:
docs/_build/*
Files: *
Copyright: 2008, Jeffrey Whitaker
......
Description: Disable coverage support.
pytest-cov not compatible with pytest 4 yet.
Author: Bas Couwenberg <sebastic@debian.org>
Forwarded: not-needed
--- a/setup.cfg
+++ b/setup.cfg
@@ -4,7 +4,4 @@ addopts =
-ra
-v
--doctest-modules
- --cov-config .coveragerc
- --cov=cftime
- --cov-report term-missing
doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS
......@@ -2,6 +2,7 @@ version=3
opts=\
dversionmangle=s/\+(debian|dfsg|ds|deb)\d*$//,\
uversionmangle=s/(\d)rel$/$1/g;s/_/./g;s/(\d)[_\.\-\+]?((RC|rc|pre|dev|gamma|beta|alpha|b|a)[\-\.]?(\d*))$/$1~$3$4/;s/RC/rc/;s/\.cd$//,\
filenamemangle=s/(?:.*?)?[vr]?(\d[\d\.\-\w]*)\.(tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))/cftime-$1.$2/ \
filenamemangle=s/(?:.*?)?[vr]?(\d[\d\.\-\w]*)\.(tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))/cftime-$1.$2/,\
repacksuffix=+ds \
https://github.com/Unidata/cftime/releases \
(?:.*/archive/)*(?:rel|v|r|cftime|)[\-\_]?(\d[\d\-\.\w]+)\.(?:tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
......@@ -2,6 +2,5 @@ API
===
.. automodule:: cftime
:members: datetime, date2num, date2index, num2date, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian
:members: datetime, date2num, num2date, num2pydate, date2index, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian
:show-inheritance:
:noindex:
\ No newline at end of file
<meta http-equiv="refresh" content="0; url=./_build/html/index.html" />
......@@ -2,6 +2,7 @@ from __future__ import print_function
import os
import sys
import numpy
from setuptools import Command, Extension, setup
......@@ -88,7 +89,8 @@ if any([arg in CMDS_NOCYTHONIZE for arg in sys.argv]):
else:
extension = Extension('{}._{}'.format(NAME, NAME),
sources=[CYTHON_FNAME],
define_macros=DEFINE_MACROS)
define_macros=DEFINE_MACROS,
include_dirs=[numpy.get_include(),])
ext_modules = [extension]
if cythonize:
ext_modules = cythonize(extension,
......
......@@ -440,7 +440,8 @@ class cftimeTestCase(unittest.TestCase):
err = np.abs(mins1 - mins2)
maxerr = max(err,maxerr)
assert(err < eps)
assert(date1.strftime(dateformat) == date2.strftime(dateformat))
diff = abs(date1-date2)
assert(diff.microseconds < 100)
if verbose:
print('calendar = %s max abs err (mins) = %s eps = %s' % \
(calendar,maxerr,eps))
......@@ -456,7 +457,8 @@ class cftimeTestCase(unittest.TestCase):
err = np.abs(hrs1 - hrs2)
maxerr = max(err,maxerr)
assert(err < eps)
assert(date1.strftime(dateformat) == date2.strftime(dateformat))
diff = abs(date1-date2)
assert(diff.microseconds < 100)
if verbose:
print('calendar = %s max abs err (hours) = %s eps = %s' % \
(calendar,maxerr,eps))
......@@ -472,7 +474,8 @@ class cftimeTestCase(unittest.TestCase):
err = np.abs(days1 - days2)
maxerr = max(err,maxerr)
assert(err < eps)
assert(date1.strftime(dateformat) == date2.strftime(dateformat))
diff = abs(date1-date2)
assert(diff.microseconds < 100)
if verbose:
print('calendar = %s max abs err (days) = %s eps = %s' % \
(calendar,maxerr,eps))
......@@ -616,6 +619,13 @@ class cftimeTestCase(unittest.TestCase):
assert (d.month == 1)
assert (d.day == 1)
assert (d.hour == 0)
# cftime issue #134
d = num2date(-657071, units, calendar='proleptic_gregorian',
only_use_cftime_datetimes=False,only_use_python_datetimes=True)
assert(d==datetime(1,1,1,0))
self.assertRaises(ValueError, num2date, \
-657072, units, calendar='proleptic_gregorian',
only_use_cftime_datetimes=False,only_use_python_datetimes=True)
# issue 685: wrong time zone conversion
# 'The following times all refer to the same moment: "18:30Z", "22:30+04", "1130-0700", and "15:00-03:30'
# (https://en.wikipedia.org/w/index.php?title=ISO_8601&oldid=787811367#Time_offsets_from_UTC)
......@@ -715,6 +725,10 @@ class cftimeTestCase(unittest.TestCase):
d = cftime.DatetimeProlepticGregorian(1, 1, 1)
assert(cftime.date2num(d, 'days since 0001-01-01',\
'proleptic_gregorian') == 0.0)
# issue #140 (fractional seconds in reference date)
d = datetime.strptime('2018-01-23 09:27:10.950000',"%Y-%m-%d %H:%M:%S.%f")
units = 'seconds since 2018-01-23 09:31:42.94'
assert(cftime.date2num(d, units) == -271.99)
class TestDate2index(unittest.TestCase):
......@@ -895,8 +909,9 @@ class TestDate2index(unittest.TestCase):
datetime(1995, 11, 25, 18, 7, 59, 999999)]
times2 = date2num(dates, units)
dates2 = num2date(times2, units)
for date, date2 in zip(dates, dates2):
assert_equal(date, date2)
datediff = abs(dates-dates2)
for diff in datediff:
assert(diff.microseconds < 100) # tolerance of 100 ms
def test_issue444(self):
# make sure integer overflow not causing error in
......@@ -1488,6 +1503,16 @@ def test_dayofwk_after_replace(date_type):
assert result == expected
def test_daysinmonth_non_leap(date_type, month, days_per_month_non_leap_year):
date = date_type(1, month, 1)
assert date.daysinmonth == days_per_month_non_leap_year
def test_daysinmonth_leap(date_type, month, days_per_month_leap_year):
date = date_type(2000, month, 1)
assert date.daysinmonth == days_per_month_leap_year
@pytest.mark.parametrize('argument', ['dayofyr', 'dayofwk'])
def test_replace_dayofyr_or_dayofwk_error(date_type, argument):
with pytest.raises(ValueError):
......