Skip to content
GitLab
Explore
Sign in
Register
Commits on Source (2)
New upstream version 1.1.0
· aec29138
Bas Couwenberg
authored
Feb 14, 2020
aec29138
New upstream version 1.1.0+ds
· 3b9efa37
Bas Couwenberg
authored
Feb 14, 2020
3b9efa37
Show whitespace changes
Inline
Side-by-side
.appveyor.yml
View file @
3b9efa37
environment
:
CONDA_INSTALL_LOC
N
:
C:\\
M
iniconda3
6-x64
PYTHO
N
:
"
C:
\\
mym
iniconda3
"
matrix
:
-
TARGET_ARCH
:
x64
NPY
:
1.16
PY
:
3.6
-
TARGET_ARCH
:
x64
NPY
:
1.1
6
NPY
:
1.1
7
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
...
...
.gitignore
View file @
3b9efa37
...
...
@@ -4,6 +4,5 @@
build/
*.egg?
*.egg-info
_build/
__pycache__
.pytest_cache/
.travis.yml
View file @
3b9efa37
...
...
@@ -8,8 +8,8 @@ notifications:
env
:
-
PYTHON_VERSION=2.7
-
PYTHON_VERSION=3.6
-
PYTHON_VERSION=3.7
-
PYTHON_VERSION=3.8
matrix
:
include
:
...
...
Changelog
0 → 100644
View file @
3b9efa37
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.
README.md
View file @
3b9efa37
...
...
@@ -10,6 +10,14 @@ Time-handling functionality from netcdf4-python
[

](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.
...
...
cftime/__init__.py
View file @
3b9efa37
...
...
@@ -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__
cftime/_cftime.pyx
View file @
3b9efa37
...
...
@@ -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
_datetime
s
:
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
=
Fals
e
,
def
DateFromJulianDay
(
JD
,
calendar
=
'
standard
'
,
only_use_cftime_datetimes
=
Tru
e
,
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
=
Fals
e
):
only_use_cftime_datetimes
=
Tru
e
):
"""
@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
d
at
etime.
datetime instances is possible for cftime.datetime
n
at
ive 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
,
...
...
docs/api.rst
View file @
3b9efa37
...
...
@@ -2,6 +2,5 @@ API
===
.. automodule:: cftime
:members: datetime, date2num, date
2index
, num2date, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian
:members: datetime, date2num,
num2
date, num2
py
date,
date2index,
JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian
:show-inheritance:
:noindex:
\ No newline at end of file
docs/index.html
0 → 100644
View file @
3b9efa37
<meta
http-equiv=
"refresh"
content=
"0; url=./_build/html/index.html"
/>
setup.py
View file @
3b9efa37
...
...
@@ -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
,
...
...
test/test_cftime.py
View file @
3b9efa37
...
...
@@ -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
):
...
...