[Git][debian-gis-team/cftime][upstream] New upstream version 1.0.4
Bas Couwenberg
gitlab at salsa.debian.org
Tue Oct 22 05:32:54 BST 2019
Bas Couwenberg pushed to branch upstream at Debian GIS Project / cftime
Commits:
973f9a06 by Bas Couwenberg at 2019-10-22T04:13:31Z
New upstream version 1.0.4
- - - - -
4 changed files:
- .appveyor.yml
- README.md
- cftime/_cftime.pyx
- test/test_cftime.py
Changes:
=====================================
.appveyor.yml
=====================================
@@ -2,46 +2,44 @@ environment:
CONDA_INSTALL_LOCN: C:\\Miniconda36-x64
matrix:
- TARGET_ARCH: x64
- NPY: 1.15
+ NPY: 1.16
PY: 3.6
- TARGET_ARCH: x64
- NPY: 1.15
+ NPY: 1.16
PY: 3.7
platform:
- x64
install:
- # If there is a newer build queued for the same PR, cancel this one.
- # The AppVeyor 'rollout builds' option is supposed to serve the same
- # purpose but it is problematic because it tends to cancel builds pushed
- # directly to master instead of just PR builds (or the converse).
- # credits: JuliaLang developers.
- - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
- https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
- Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
- 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.exe config --set always_yes yes --set changeps1 no --set show_channel_urls true
- - cmd: conda.exe update conda
- - cmd: conda.exe config --remove channels defaults --force
- - cmd: conda.exe config --add channels conda-forge --force
-
- - cmd: set PYTHONUNBUFFERED=1
-
- - cmd: conda.exe install conda-build vs2008_express_vc_python_patch
- - cmd: call setup_x64
- - cmd: conda.exe info --all
- - cmd: conda.exe list
+ # If there is a newer build queued for the same PR, cancel this one.
+ # The AppVeyor 'rollout builds' option is supposed to serve the same
+ # purpose but it is problematic because it tends to cancel builds pushed
+ # directly to master instead of just PR builds (or the converse).
+ # credits: JuliaLang developers.
+ - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
+ https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
+ Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
+ 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
# Skip .NET project specific build phase.
build: off
test_script:
- - conda.exe create --name TEST python=%PY% numpy=%NPY% cython pip pytest pytest-cov
- - conda activate TEST
- python -m pip install . --no-deps --ignore-installed --no-cache-dir -vvv
- py.test -vv test
=====================================
README.md
=====================================
@@ -10,6 +10,8 @@ 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
+10/21/2019: version 1.0.4 released.
+
12/05/2018: version 1.0.3.4 released (just to fix a problem with the source
tarball on pypi).
=====================================
cftime/_cftime.pyx
=====================================
@@ -17,8 +17,9 @@ try:
except ImportError: # python 3.x
pass
+
microsec_units = ['microseconds','microsecond', 'microsec', 'microsecs']
-millisec_units = ['milliseconds', 'millisecond', 'millisec', 'millisecs']
+millisec_units = ['milliseconds', 'millisecond', 'millisec', 'millisecs', 'msec', 'msecs', 'ms']
sec_units = ['second', 'seconds', 'sec', 'secs', 's']
min_units = ['minute', 'minutes', 'min', 'mins']
hr_units = ['hour', 'hours', 'hr', 'hrs', 'h']
@@ -42,7 +43,7 @@ cdef int[13] _spm_366day = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 33
_rop_lookup = {Py_LT: '__gt__', Py_LE: '__ge__', Py_EQ: '__eq__',
Py_GT: '__lt__', Py_GE: '__le__', Py_NE: '__ne__'}
-__version__ = '1.0.3.4'
+__version__ = '1.0.4'
# 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.
@@ -93,7 +94,7 @@ def _dateparse(timestr):
(units, isostring) = _datesplit(timestr)
# parse the date string.
- year, month, day, hour, minute, second, utc_offset =\
+ 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)
@@ -121,7 +122,7 @@ cdef _parse_date_and_units(timestr,calendar='standard'):
raise ValueError(
"units must be one of 'seconds', 'minutes', 'hours' or 'days' (or singular version of these), got '%s'" % units)
# parse the date string.
- year, month, day, hour, minute, second, utc_offset = _parse_date(
+ year, month, day, hour, minute, second, microsecond, utc_offset = _parse_date(
isostring.strip())
return units, utc_offset, datetime(year, month, day, hour, minute, second)
@@ -182,6 +183,9 @@ def date2num(dates,units,calendar='standard'):
ismasked = True
times = []
for date in dates.flat:
+ if getattr(date, 'tzinfo') is not None:
+ date = date.replace(tzinfo=None) - date.utcoffset()
+
if ismasked and not date:
times.append(None)
else:
@@ -408,6 +412,9 @@ def JulianDayFromDate(date, calendar='standard'):
cdef Py_ssize_t i
for i in range(i_max):
d = date[i]
+ if getattr(d, 'tzinfo', None) is not None:
+ d = d.replace(tzinfo=None) - d.utcoffset()
+
year[i] = d.year
month[i] = d.month
day[i] = d.day
@@ -909,7 +916,12 @@ cpdef _parse_date(datestring):
The timezone is parsed from the date string, assuming UTC
by default.
+ Note that a seconds element with a fractional component
+ (e.g. 12.5) is converted into integer seconds and integer
+ microseconds.
+
Adapted from pyiso8601 (http://code.google.com/p/pyiso8601/)
+
"""
if not isinstance(datestring, str) and not isinstance(datestring, unicode):
raise ValueError("Expecting a string %r" % datestring)
@@ -924,13 +936,14 @@ cpdef _parse_date(datestring):
groups["minute"] = 0
if groups["second"] is None:
groups["second"] = 0
- # if groups["fraction"] is None:
- # groups["fraction"] = 0
- # else:
- # groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6)
+ if groups["fraction"] is None:
+ groups["fraction"] = 0
+ else:
+ groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6)
iyear = int(groups["year"])
return iyear, int(groups["month"]), int(groups["day"]),\
int(groups["hour"]), int(groups["minute"]), int(groups["second"]),\
+ int(groups["fraction"]),\
tzoffset_mins
cdef _check_index(indices, times, nctime, calendar, select):
@@ -1220,9 +1233,11 @@ Gregorial calendar.
"hour": self.hour,
"minute": self.minute,
"second": self.second,
- "microsecond": self.microsecond,
- "dayofwk": self.dayofwk,
- "dayofyr": self.dayofyr}
+ "microsecond": self.microsecond}
+
+ if 'dayofyr' in kwargs or 'dayofwk' in kwargs:
+ raise ValueError('Replacing the dayofyr or dayofwk of a datetime is '
+ 'not supported.')
for name, value in kwargs.items():
args[name] = value
@@ -1246,13 +1261,17 @@ Gregorial calendar.
self.microsecond)
def __repr__(self):
- return "{0}.{1}{2}".format('cftime',
- self.__class__.__name__,
- self._getstate())
+ return "{0}.{1}({2})".format('cftime',
+ self.__class__.__name__,
+ str(self))
def __str__(self):
- return "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(
- self.year, self.month, self.day, self.hour, self.minute, self.second)
+ second = '{:02d}'.format(self.second)
+ if self.microsecond:
+ second += '.{}'.format(self.microsecond)
+
+ return "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{}".format(
+ self.year, self.month, self.day, self.hour, self.minute, second)
def __hash__(self):
try:
=====================================
test/test_cftime.py
=====================================
@@ -19,6 +19,33 @@ from cftime import (DateFromJulianDay, Datetime360Day, DatetimeAllLeap,
DatetimeProlepticGregorian, JulianDayFromDate, _parse_date,
date2index, date2num, num2date, utime)
+try:
+ from datetime import timezone
+except ImportError: # python2.7
+ from datetime import tzinfo
+ class timezone(tzinfo):
+ """
+ Fixed offset in minutes east from UTC. adapted from
+ python 2.7 docs FixedOffset
+ """
+
+ def __init__(self, offset, name):
+ self.__offset = offset
+ self.__name = name
+
+ def utcoffset(self, dt):
+ return self.__offset
+
+ def tzname(self, dt):
+ return self.__name
+
+ def dst(self, dt):
+ return timedelta(hours=0)
+
+
+utc = timezone(timedelta(hours=0), 'UTC')
+est = timezone(timedelta(hours=-5), 'UTC')
+
# test cftime module for netCDF time <--> python datetime conversions.
dtime = namedtuple('dtime', ('values', 'units', 'calendar'))
@@ -81,7 +108,20 @@ class cftimeTestCase(unittest.TestCase):
self.cdftime_noleap_capcal = utime(
'days since 1600-02-28 00:00:00', calendar='NOLEAP')
- def runTest(self):
+ def test_tz_aware(self):
+ """testing with timezone"""
+ self.assertTrue(self.cdftime_mixed.units == 'hours')
+ d1 = datetime(1582, 10, 4, 23, tzinfo=utc)
+ t1 = self.cdftime_mixed.date2num(d1)
+ d2 = datetime(1582, 10, 4, 18, tzinfo=est)
+ t2 = self.cdftime_mixed.date2num(d2)
+ d3 = d2.replace(tzinfo=None)
+ t3 = self.cdftime_mixed.date2num(d3)
+ assert_almost_equal(t1, 13865687.0)
+ assert_almost_equal(t2, 13865687.0)
+ assert_almost_equal(t3, 13865682.0)
+
+ def test_tz_naive(self):
"""testing cftime"""
# test mixed julian/gregorian calendar
# check attributes.
@@ -734,6 +774,15 @@ class TestDate2index(unittest.TestCase):
values=date2num(dates, units),
units=units)
+ def test_tz_aware(self):
+ """implicit test of date2num"""
+ dutc = datetime(1950, 2, 1, 0, tzinfo=utc)
+ t1 = date2index(dutc, self.standardtime)
+ assert_equal(t1, 31)
+ dest = datetime(1950, 1, 31, 19, tzinfo=est)
+ t2 = date2index(dest, self.standardtime)
+ assert_equal(t2, 31)
+
def test_simple(self):
t = date2index(datetime(1950, 2, 1), self.standardtime)
assert_equal(t, 31)
@@ -1043,8 +1092,6 @@ class DateTime(unittest.TestCase):
self.assertEqual(self.date1_365_day.replace(minute=3).minute, 3)
self.assertEqual(self.date1_365_day.replace(second=3).second, 3)
self.assertEqual(self.date1_365_day.replace(microsecond=3).microsecond, 3)
- self.assertEqual(self.date1_365_day.replace(dayofwk=3).dayofwk, 3)
- self.assertEqual(self.date1_365_day.replace(dayofyr=3).dayofyr, 3)
def test_pickling(self):
"Test reversibility of pickling."
@@ -1186,25 +1233,25 @@ class issue17TestCase(unittest.TestCase):
"Test timezone parsing in _parse_date"
# these should succeed and are ISO8601 compliant
- expected_parsed_date = (2017, 5, 1, 0, 0, 0, 60.0)
+ expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0, 60.0)
for datestr in ("2017-05-01 00:00+01:00", "2017-05-01 00:00+0100", "2017-05-01 00:00+01"):
d = _parse_date(datestr)
assert_equal(d, expected_parsed_date)
# some more tests with non-zero minutes, should all be ISO compliant and work
- expected_parsed_date = (2017, 5, 1, 0, 0, 0, 85.0)
+ expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0, 85.0)
for datestr in ("2017-05-01 00:00+01:25", "2017-05-01 00:00+0125"):
d = _parse_date(datestr)
assert_equal(d, expected_parsed_date)
# these are NOT ISO8601 compliant and should not even be parseable but will be parsed with timezone anyway
# because, due to support of other legacy time formats, they are difficult to reject
# ATTENTION: only the hours part of this will be parsed, single-digit minutes will be ignored!
- expected_parsed_date = (2017, 5, 1, 0, 0, 0, 60.0)
+ expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0, 60.0)
for datestr in ("2017-05-01 00:00+01:0", "2017-05-01 00:00+01:", "2017-05-01 00:00+01:5"):
d = _parse_date(datestr)
assert_equal(d, expected_parsed_date)
# these should not even be parseable as datestrings but are parseable anyway with ignored timezone
# this is because the module also supports some legacy, non-standard time strings
- expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0.0)
+ expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0, 0.0)
for datestr in ("2017-05-01 00:00+1",):
d = _parse_date(datestr)
assert_equal(d, expected_parsed_date)
@@ -1368,8 +1415,9 @@ def test_valid_julian_gregorian_mixed_dates(date_type, date_args):
@pytest.mark.parametrize(
'date_args',
[(1, 2, 3, 4, 5, 6), (10, 2, 3, 4, 5, 6), (100, 2, 3, 4, 5, 6),
- (1000, 2, 3, 4, 5, 6)],
- ids=['1', '10', '100', '1000'])
+ (1000, 2, 3, 4, 5, 6),
+ (2000, 1, 1, 12, 34, 56, 123456)],
+ ids=['1', '10', '100', '1000', '2000'])
def test_str_matches_datetime_str(date_type, date_args):
assert str(date_type(*date_args)) == str(datetime(*date_args))
@@ -1419,13 +1467,28 @@ def test_num2date_only_use_cftime_datetimes_post_gregorian(
def test_repr():
- expected = 'cftime.datetime(2000, 1, 1, 0, 0, 0, 0, -1, 1)'
- # dayofwk, dayofyr not set
+ expected = 'cftime.datetime(2000-01-01 00:00:00)'
assert repr(datetimex(2000, 1, 1)) == expected
- expected = 'cftime.DatetimeGregorian(2000, 1, 1, 0, 0, 0, 0, 5, 1)'
- # dayofwk, dayofyr are set
- assert repr(DatetimeGregorian(2000, 1, 1)) == expected
+
+def test_dayofyr_after_replace(date_type):
+ date = date_type(1, 1, 1)
+ assert date.dayofyr == 1
+ assert date.replace(day=2).dayofyr == 2
+
+
+def test_dayofwk_after_replace(date_type):
+ date = date_type(1, 1, 1)
+ original_dayofwk = date.dayofwk
+ expected = (original_dayofwk + 1) % 7
+ result = date.replace(day=2).dayofwk
+ assert result == expected
+
+
+ at pytest.mark.parametrize('argument', ['dayofyr', 'dayofwk'])
+def test_replace_dayofyr_or_dayofwk_error(date_type, argument):
+ with pytest.raises(ValueError):
+ date_type(1, 1, 1).replace(**{argument: 3})
if __name__ == '__main__':
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/commit/973f9a0695131fa7a39d7b6cf4d63bb31af66394
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/commit/973f9a0695131fa7a39d7b6cf4d63bb31af66394
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20191022/f7ad1854/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list