Skip to content

Commit

Permalink
Merge 60fc69f into c970ec9
Browse files Browse the repository at this point in the history
  • Loading branch information
jswhit committed Jul 18, 2020
2 parents c970ec9 + 60fc69f commit e044fce
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 5 deletions.
4 changes: 4 additions & 0 deletions Changelog
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ version 1.2.1 (not yet released)
* num2date uses 'proleptic_gregorian' scheme when basedate is post-Gregorian but date is pre-Gregorian
(issue #182).
* fix 1.2.0 regression (date2num no longer works with numpy scalar array inputs, issue #185).
* Fix for issue #187 (have date2num round to the nearest second when within 1
microsecond).
* Fix for issue #188 (leap years calculated incorrectly for negative years in
proleptic_gregorian calendar).

version 1.2.0 (release tag v1.2.0rel)
=====================================
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Time-handling functionality from netcdf4-python
## News
For details on the latest updates, see the [Changelog](https://github.com/Unidata/cftime/blob/master/Changelog).

??/??/2020: Version 1.2.1 released. Fixes a couple of regressions introduced in 1.2.0. See Changelog for details.

7/06/2020: version 1.2.0 released. New microsecond accurate algorithm for date2num/num2date contributed by [spencerkclark](https://github.com/spencerkclark). Bugs fixed in masked array handling.

5/12/2020: version 1.1.3 released. Add isoformat method for compatibility with python datetime (issue #152).
Expand Down
22 changes: 17 additions & 5 deletions cftime/_cftime.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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.0'
__version__ = '1.2.1'

# 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.
Expand Down Expand Up @@ -353,16 +353,28 @@ _MAX_INT64 = np.iinfo("int64").max
_MIN_INT64 = np.iinfo("int64").min


def cast_to_int(num):
def cast_to_int(num, units=None):
if num.dtype.kind in "iu":
return num
else:
if np.any(num < _MIN_INT64) or np.any(num > _MAX_INT64):
raise OverflowError('time values outside range of 64 bit signed integers')
if isinstance(num, np.ma.core.MaskedArray):
int_num = np.ma.masked_array(np.rint(num), dtype=np.int64)
# use ceil instead of rint if 1 microsec less than a second
# or floor if 1 microsec greater than a second (issue #187)
if units not in microsec_units and units not in millisec_units:
int_num = np.ma.where(int_num%1000000 == 1, \
np.ma.masked_array(np.floor(num),dtype=np.int64), int_num)
int_num = np.ma.where(int_num%1000000 == 999999, \
np.ma.masked_array(np.ceil(num),dtype=np.int64), int_num)
else:
int_num = np.array(np.rint(num), dtype=np.int64)
if units not in microsec_units and units not in millisec_units:
int_num = np.where(int_num%1000000 == 1, \
np.array(np.floor(num),dtype=np.int64), int_num)
int_num = np.where(int_num%1000000 == 999999, \
np.array(np.ceil(num),dtype=np.int64), int_num)
return int_num


Expand Down Expand Up @@ -483,7 +495,7 @@ def num2date(
times = np.asanyarray(times) # Allow list as input
times = upcast_times(times)
scaled_times = scale_times(times, factor)
scaled_times = cast_to_int(scaled_times)
scaled_times = cast_to_int(scaled_times,units=unit)

# Through np.timedelta64, convert integers scaled to have units of
# microseconds to datetime.timedelta objects, the timedelta type compatible
Expand Down Expand Up @@ -1494,9 +1506,9 @@ cdef _is_leap(int year, calendar):
if calendar == 'proleptic_gregorian' or (calendar == 'standard' and year > 1581):
if tyear % 4: # not divisible by 4
leap = False
elif year % 100: # not divisible by 100
elif tyear % 100: # not divisible by 100
leap = True
elif year % 400: # not divisible by 400
elif tyear % 400: # not divisible by 400
leap = False
else:
leap = True
Expand Down
12 changes: 12 additions & 0 deletions test/test_cftime.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,18 @@ def test_tz_naive(self):
# issue #185: date2num should work the numpy scalar array of dates (1.2.0 regression)
dates = np.array(datetime(2010, 2, 2, 0, 0))
assert (date2num(dates, units="hours since 2010-02-01 00:00:00") == 24.)
# issue #187 - roundtrip near second boundary
dt1 = datetime(1810, 4, 24, 16, 15, 10)
units = 'days since -4713-01-01 12:00'
dt2 = num2date(date2num(dt1, units), units)
assert(dt1 == dt2)
# issue #188 - leap years calculated incorrectly for negative years in proleptic_gregorian calendar
dt1 = datetime(2020, 4, 24, 16, 15, 10)
units = 'days since -4713-01-01 12:00'
cal = 'proleptic_gregorian'
dt2 = num2date(date2num(dt1, units, cal), units, cal)
assert(dt1 == dt2)


class TestDate2index(unittest.TestCase):

Expand Down

0 comments on commit e044fce

Please sign in to comment.