Skip to content

Commit

Permalink
Merge 77699b7 into 1aa6f55
Browse files Browse the repository at this point in the history
  • Loading branch information
jswhit committed Feb 1, 2020
2 parents 1aa6f55 + 77699b7 commit d1c99ac
Show file tree
Hide file tree
Showing 48 changed files with 15,751 additions and 51 deletions.
40 changes: 21 additions & 19 deletions .appveyor.yml
@@ -1,16 +1,17 @@
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.
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -4,6 +4,5 @@
build/
*.egg?
*.egg-info
_build/
__pycache__
.pytest_cache/
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -8,8 +8,8 @@ notifications:

env:
- PYTHON_VERSION=2.7
- PYTHON_VERSION=3.6
- PYTHON_VERSION=3.7
- PYTHON_VERSION=3.8

matrix:
include:
Expand Down
10 changes: 9 additions & 1 deletion Changelog
Expand Up @@ -5,12 +5,20 @@ version 1.1.0 (not yet released)

* fix intersphinx entries (issue #133, PR #133)

* make use_only_cftime_datetimes=True by default, so cftime datetime
* 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)

version 1.0.4.2 release
=======================

Expand Down
2 changes: 1 addition & 1 deletion cftime/__init__.py
Expand Up @@ -5,5 +5,5 @@
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__
79 changes: 52 additions & 27 deletions cftime/_cftime.pyx
Expand Up @@ -90,7 +90,7 @@ cpdef int32_t get_days_in_month(bint isleap, int month) nogil:


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
Expand Down Expand Up @@ -247,8 +247,19 @@ def date2num(dates,units,calendar='standard'):
return cdftime.date2num(dates)


def num2date(times,units,calendar='standard',only_use_cftime_datetimes=True):
"""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
Expand All @@ -270,40 +281,54 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=True):
'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, 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. Default is True.
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]
Expand Down Expand Up @@ -345,15 +370,17 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=True):
msecs = np.round(msecsd - secs*1.e6)
td = timedelta(days=days,seconds=secs,microseconds=msecs)
# add time delta to base date.
date = basedate + td
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'):
Expand Down Expand Up @@ -483,10 +510,9 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=True,
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))
Expand Down Expand Up @@ -672,16 +698,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
Expand Down Expand Up @@ -855,10 +880,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
Expand Down Expand Up @@ -1565,9 +1590,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,
Expand Down
Binary file added docs/_build/doctrees/api.doctree
Binary file not shown.
Binary file added docs/_build/doctrees/environment.pickle
Binary file not shown.
Binary file added docs/_build/doctrees/index.doctree
Binary file not shown.
Binary file added docs/_build/doctrees/installing.doctree
Binary file not shown.
4 changes: 4 additions & 0 deletions docs/_build/html/.buildinfo
@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 2629b59f89353a6a3eea208d9d747f2a
tags: 645f666f9bcd5a90fca523b33c5a78b7
6 changes: 6 additions & 0 deletions docs/_build/html/_sources/api.rst.txt
@@ -0,0 +1,6 @@
API
===

.. automodule:: cftime
:members: datetime, date2num, num2date, num2pydate, date2index, JulianDayFromDate, DatetimeJulian, DatetimeProlepticGregorian, DatetimeNoLeap, DatetimeAllLeap, DatetimeGregorian
:show-inheritance:
21 changes: 21 additions & 0 deletions docs/_build/html/_sources/index.rst.txt
@@ -0,0 +1,21 @@
cftime
======

Python library for decoding time units and variable values in a netCDF file
conforming to the Climate and Forecasting (CF) netCDF conventions.

Contents
--------

.. toctree::
:maxdepth: 2

installing
api

Indices and tables
------------------

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
42 changes: 42 additions & 0 deletions docs/_build/html/_sources/installing.rst.txt
@@ -0,0 +1,42 @@
Installation
============

Required dependencies
---------------------

- Python 2.7, 3.4, 3.5, or 3.6
- `numpy <http://www.numpy.org/>`__ (1.7 or later)


Instructions
------------

The easiest way to get everything installed is to use conda_ command line tool::

$ conda install cftime

.. _conda: http://conda.io/

We recommend using the community maintained `conda-forge <https://conda-forge.github.io/>`__ channel if you need difficult\-to\-build dependencies such as cartopy or pynio::

$ conda install -c conda-forge cftime

New releases may also appear in conda-forge before being updated in the default
channel.

If you don't use conda, be sure you have the required dependencies (numpy and
cython) installed first. Then, install cftime with pip::

$ pip install cftime


Developing
----------


When developing we recommend cloning the GitHub repository,
building the extension in-place with `cython <http://cython.org/>`__ 0.19 or later
``python setup.py build_ext --inplace``

and running the test suite to check if the changes are passing the tests
``pytest --pyargs test``
Binary file added docs/_build/html/_static/ajax-loader.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d1c99ac

Please sign in to comment.