Skip to content

Commit

Permalink
Revert "fix for issue #49"
Browse files Browse the repository at this point in the history
This reverts commit 4cce4de.
  • Loading branch information
jswhit committed Aug 16, 2018
1 parent 4cce4de commit b4886e5
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 72 deletions.
125 changes: 63 additions & 62 deletions cftime/_cftime.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Performs conversions of netCDF time coordinate data to/from datetime objects.
from cpython.object cimport PyObject_RichCompare
import cython
import numpy as np
import math
import numpy
import re
import time
from datetime import datetime as real_datetime
Expand Down Expand Up @@ -142,12 +144,12 @@ def date2num(dates,units,calendar='standard'):
except:
isscalar = True
if isscalar:
dates = np.array([dates])
dates = numpy.array([dates])
else:
dates = np.array(dates)
dates = numpy.array(dates)
shape = dates.shape
ismasked = False
if np.ma.isMA(dates) and np.ma.is_masked(dates):
if numpy.ma.isMA(dates) and numpy.ma.is_masked(dates):
mask = dates.mask
ismasked = True
times = []
Expand Down Expand Up @@ -175,7 +177,7 @@ def date2num(dates,units,calendar='standard'):
if isscalar:
return times[0]
else:
return np.reshape(np.array(times), shape)
return numpy.reshape(numpy.array(times), shape)
else: # use cftime module for other calendars
cdftime = utime(units,calendar=calendar)
return cdftime.date2num(dates)
Expand Down Expand Up @@ -233,7 +235,7 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
msg='negative reference year in time units, must be >= 1'
raise ValueError(msg)

postimes = (np.asarray(times) > 0).all() # netcdf4-python issue #659
postimes = (numpy.asarray(times) > 0).all() # netcdf4-python issue #659
if only_use_cftime_datetimes:
cdftime = utime(units, calendar=calendar,
only_use_cftime_datetimes=only_use_cftime_datetimes)
Expand All @@ -247,12 +249,12 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
except:
isscalar = True
if isscalar:
times = np.array([times],dtype='d')
times = numpy.array([times],dtype='d')
else:
times = np.array(times, dtype='d')
times = numpy.array(times, dtype='d')
shape = times.shape
ismasked = False
if np.ma.isMA(times) and np.ma.is_masked(times):
if numpy.ma.isMA(times) and numpy.ma.is_masked(times):
mask = times.mask
ismasked = True
dates = []
Expand All @@ -279,15 +281,15 @@ def num2date(times,units,calendar='standard',only_use_cftime_datetimes=False):
days = tsecs // 86400.
msecsd = tsecs*1.e6 - days*86400.*1.e6
secs = msecsd // 1.e6
msecs = np.round(msecsd - secs*1.e6)
msecs = numpy.round(msecsd - secs*1.e6)
td = timedelta(days=days,seconds=secs,microseconds=msecs)
# add time delta to base date.
date = basedate + td
dates.append(date)
if isscalar:
return dates[0]
else:
return np.reshape(np.array(dates), shape)
return numpy.reshape(numpy.array(dates), shape)
else: # use cftime for other calendars
cdftime = utime(units,calendar=calendar)
return cdftime.num2date(times)
Expand Down Expand Up @@ -572,22 +574,21 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=False):

# based on redate.py by David Finlayson.

julian = np.array(JD, dtype=np.float64)
julian = np.array(JD, dtype=float)

if np.min(julian) < 0:
raise ValueError('Julian Day must be positive')

dayofwk = np.atleast_1d(np.int32(np.fmod(np.int32(julian + 1.5), 7)))
# get the day (Z) and the fraction of the day (F)
# add 0.000005 which is 452 ms in case of jd being after
# second 23:59:59 of a day we want to round to the next day
# (see netcdf4-python issue #75, also cftime issue #49)
Z = np.atleast_1d(np.int32(np.round(julian+0.000005)))
# second 23:59:59 of a day we want to round to the next day see issue #75
Z = np.atleast_1d(np.int32(np.round(julian)))
F = np.atleast_1d(julian + 0.5 - Z).astype(np.float64)
if calendar in ['standard', 'gregorian']:
# MC
#alpha = np.int32((Z - 1867216.25)/36524.25)
#A = Z + 1 + alpha - np.int32(alpha/4)
# alpha = int((Z - 1867216.25)/36524.25)
# A = Z + 1 + alpha - int(alpha/4)
alpha = np.int32(((Z - 1867216.) - 0.25) / 36524.25)
A = Z + 1 + alpha - np.int32(0.25 * alpha)
# check if dates before oct 5th 1582 are in the array
Expand All @@ -609,8 +610,8 @@ def DateFromJulianDay(JD, calendar='standard', only_use_cftime_datetimes=False):

B = A + 1524
# MC
#C = np.atleast_1d(np.int32((B - 122.1)/365.25))
#D = np.atleast_1d(np.int32(365.25 * C))
# C = int((B - 122.1)/365.25)
# D = int(365.25 * C)
C = np.atleast_1d(np.int32(6680. + ((B - 2439870.) - 122.1) / 365.25))
D = np.atleast_1d(np.int32(365 * C + np.int32(0.25 * C)))
E = np.atleast_1d(np.int32((B - D) / 30.6001))
Expand Down Expand Up @@ -731,8 +732,8 @@ days. Julian Day is a fractional day with approximately millisecond accuracy.
else:
year_offset = 0

dayofwk = int(np.fmod(int(JD + 1.5), 7))
(F, Z) = np.modf(JD + 0.5)
dayofwk = int(math.fmod(int(JD + 1.5), 7))
(F, Z) = math.modf(JD + 0.5)
Z = int(Z)
A = Z
B = A + 1524
Expand All @@ -758,18 +759,18 @@ days. Julian Day is a fractional day with approximately millisecond accuracy.
year = C - 4715

# Convert fractions of a day to time
(dfrac, days) = np.modf(day / 1.0)
(hfrac, hours) = np.modf(dfrac * 24.0)
(mfrac, minutes) = np.modf(hfrac * 60.0)
(sfrac, seconds) = np.modf(mfrac * 60.0)
(dfrac, days) = math.modf(day / 1.0)
(hfrac, hours) = math.modf(dfrac * 24.0)
(mfrac, minutes) = math.modf(hfrac * 60.0)
(sfrac, seconds) = math.modf(mfrac * 60.0)
microseconds = sfrac*1.e6

if year_offset > 0:
# correct dayofwk

# 365 mod 7 = 1, so the day of the week changes by one day for
# every year in year_offset
dayofwk -= int(np.fmod(year_offset, 7))
dayofwk -= int(math.fmod(year_offset, 7))

if dayofwk < 0:
dayofwk += 7
Expand All @@ -792,8 +793,8 @@ Julian Day is a fractional day with approximately millisecond accuracy.
if JD < 0:
raise ValueError('Julian Day must be positive')

dayofwk = int(np.fmod(int(JD + 1.5), 7))
(F, Z) = np.modf(JD + 0.5)
dayofwk = int(math.fmod(int(JD + 1.5), 7))
(F, Z) = math.modf(JD + 0.5)
Z = int(Z)
A = Z
B = A + 1524
Expand Down Expand Up @@ -821,10 +822,10 @@ Julian Day is a fractional day with approximately millisecond accuracy.
year = C - 4715

# Convert fractions of a day to time
(dfrac, days) = np.modf(day / 1.0)
(hfrac, hours) = np.modf(dfrac * 24.0)
(mfrac, minutes) = np.modf(hfrac * 60.0)
(sfrac, seconds) = np.modf(mfrac * 60.0)
(dfrac, days) = math.modf(day / 1.0)
(hfrac, hours) = math.modf(dfrac * 24.0)
(mfrac, minutes) = math.modf(hfrac * 60.0)
(sfrac, seconds) = math.modf(mfrac * 60.0)
microseconds = sfrac*1.e6

return DatetimeAllLeap(year, month, int(days), int(hours), int(minutes),
Expand All @@ -847,17 +848,17 @@ Julian Day is a fractional day with approximately millisecond accuracy.
year_offset = 0

#jd = int(360. * (year + 4716)) + int(30. * (month - 1)) + day
(F, Z) = np.modf(JD)
(F, Z) = math.modf(JD)
year = int((Z - 0.5) / 360.) - 4716
dayofyr = Z - (year + 4716) * 360
month = int((dayofyr - 0.5) / 30) + 1
day = dayofyr - (month - 1) * 30 + F

# Convert fractions of a day to time
(dfrac, days) = np.modf(day / 1.0)
(hfrac, hours) = np.modf(dfrac * 24.0)
(mfrac, minutes) = np.modf(hfrac * 60.0)
(sfrac, seconds) = np.modf(mfrac * 60.0)
(dfrac, days) = math.modf(day / 1.0)
(hfrac, hours) = math.modf(dfrac * 24.0)
(mfrac, minutes) = math.modf(hfrac * 60.0)
(sfrac, seconds) = math.modf(mfrac * 60.0)
microseconds = sfrac*1.e6

return Datetime360Day(year - year_offset, month, int(days), int(hours), int(minutes),
Expand Down Expand Up @@ -1064,7 +1065,7 @@ units to datetime objects.
except:
isscalar = True
if not isscalar:
date = np.array(date)
date = numpy.array(date)
shape = date.shape
if self.calendar in ['julian', 'standard', 'gregorian', 'proleptic_gregorian']:
if isscalar:
Expand Down Expand Up @@ -1104,7 +1105,7 @@ units to datetime objects.
'there are only 30 days in every month with the 360_day calendar')
jdelta.append(_360DayFromDate(d) - self._jd0)
if not isscalar:
jdelta = np.array(jdelta)
jdelta = numpy.array(jdelta)
# convert to desired units, add time zone offset.
if self.units in microsec_units:
jdelta = jdelta * 86400. * 1.e6 + self.tzoffset * 60. * 1.e6
Expand All @@ -1123,7 +1124,7 @@ units to datetime objects.
if isscalar:
return jdelta
else:
return np.reshape(jdelta, shape)
return numpy.reshape(jdelta, shape)

def num2date(self, time_value):
"""
Expand Down Expand Up @@ -1153,11 +1154,11 @@ units to datetime objects.
except:
isscalar = True
ismasked = False
if np.ma.isMA(time_value) and np.ma.is_masked(time_value):
if numpy.ma.isMA(time_value) and numpy.ma.is_masked(time_value):
mask = time_value.mask
ismasked = True
if not isscalar:
time_value = np.array(time_value, dtype='d')
time_value = numpy.array(time_value, dtype='d')
shape = time_value.shape
# convert to desired units, subtract time zone offset.
if self.units in microsec_units:
Expand Down Expand Up @@ -1212,7 +1213,7 @@ units to datetime objects.
if isscalar:
return date
else:
return np.reshape(np.array(date), shape)
return numpy.reshape(numpy.array(date), shape)


cdef _parse_timezone(tzstring):
Expand Down Expand Up @@ -1313,23 +1314,23 @@ cdef _check_index(indices, times, nctime, calendar, select):
# t.append(nctime[ind])

if select == 'exact':
return np.all(t == times)
return numpy.all(t == times)

elif select == 'before':
ta = nctime[np.clip(indices + 1, 0, N - 1)]
return np.all(t <= times) and np.all(ta > times)
ta = nctime[numpy.clip(indices + 1, 0, N - 1)]
return numpy.all(t <= times) and numpy.all(ta > times)

elif select == 'after':
tb = nctime[np.clip(indices - 1, 0, N - 1)]
return np.all(t >= times) and np.all(tb < times)
tb = nctime[numpy.clip(indices - 1, 0, N - 1)]
return numpy.all(t >= times) and numpy.all(tb < times)

elif select == 'nearest':
ta = nctime[np.clip(indices + 1, 0, N - 1)]
tb = nctime[np.clip(indices - 1, 0, N - 1)]
ta = nctime[numpy.clip(indices + 1, 0, N - 1)]
tb = nctime[numpy.clip(indices - 1, 0, N - 1)]
delta_after = ta - t
delta_before = t - tb
delta_check = np.abs(times - t)
return np.all(delta_check <= delta_after) and np.all(delta_check <= delta_before)
delta_check = numpy.abs(times - t)
return numpy.all(delta_check <= delta_after) and numpy.all(delta_check <= delta_before)


def _date2index(dates, nctime, calendar=None, select='exact'):
Expand Down Expand Up @@ -1405,7 +1406,7 @@ def time2index(times, nctime, calendar=None, select='exact'):
if calendar == None:
calendar = getattr(nctime, 'calendar', 'standard')

num = np.atleast_1d(times)
num = numpy.atleast_1d(times)
N = len(nctime)

# Trying to infer the correct index from the starting time and the stride.
Expand All @@ -1417,11 +1418,11 @@ def time2index(times, nctime, calendar=None, select='exact'):
t0 = nctime[0]
dt = 1.
if select in ['exact', 'before']:
index = np.array((num - t0) / dt, int)
index = numpy.array((num - t0) / dt, int)
elif select == 'after':
index = np.array(np.ceil((num - t0) / dt), int)
index = numpy.array(numpy.ceil((num - t0) / dt), int)
else:
index = np.array(np.around((num - t0) / dt), int)
index = numpy.array(numpy.around((num - t0) / dt), int)

# Checking that the index really corresponds to the given time.
# If the times do not correspond, then it means that the times
Expand All @@ -1430,26 +1431,26 @@ def time2index(times, nctime, calendar=None, select='exact'):

# Use the bisection method. Assumes nctime is ordered.
import bisect
index = np.array([bisect.bisect_right(nctime, n) for n in num], int)
index = numpy.array([bisect.bisect_right(nctime, n) for n in num], int)
before = index == 0

index = np.array([bisect.bisect_left(nctime, n) for n in num], int)
index = numpy.array([bisect.bisect_left(nctime, n) for n in num], int)
after = index == N

if select in ['before', 'exact'] and np.any(before):
if select in ['before', 'exact'] and numpy.any(before):
raise ValueError(
'Some of the times given are before the first time in `nctime`.')

if select in ['after', 'exact'] and np.any(after):
if select in ['after', 'exact'] and numpy.any(after):
raise ValueError(
'Some of the times given are after the last time in `nctime`.')

# Find the times for which the match is not perfect.
# Use list comprehension instead of the simpler `nctime[index]` since
# not all time objects support numpy integer indexing (eg dap).
index[after] = N - 1
ncnum = np.squeeze([nctime[i] for i in index])
mismatch = np.nonzero(ncnum != num)[0]
ncnum = numpy.squeeze([nctime[i] for i in index])
mismatch = numpy.nonzero(ncnum != num)[0]

if select == 'exact':
if len(mismatch) > 0:
Expand All @@ -1464,7 +1465,7 @@ def time2index(times, nctime, calendar=None, select='exact'):
pass

elif select == 'nearest':
nearest_to_left = num[mismatch] < np.array(
nearest_to_left = num[mismatch] < numpy.array(
[float(nctime[i - 1]) + float(nctime[i]) for i in index[mismatch]]) / 2.
index[mismatch] = index[mismatch] - 1 * nearest_to_left

Expand Down
13 changes: 3 additions & 10 deletions test/test_cftime.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,14 +561,6 @@ def runTest(self):
n = date2num(utc_date, units, calendar="julian")
# n should always be 0 as all units refer to the same point in time
assert_almost_equal(n, 0)
# cftime issue #49
d = DateFromJulianDay(2450022.5, "standard")
assert (d.year == 1995)
assert (d.month == 11)
assert (d.day == 1)
assert (d.hour == 0)
assert (d.minute == 0)
assert (d.second == 0)


class TestDate2index(unittest.TestCase):
Expand Down Expand Up @@ -753,6 +745,7 @@ def test_issue444(self):
# calculation of nearest index when sum of adjacent
# time values won't fit in 32 bits.
query_time = datetime(2037, 1, 3, 21, 12)
print(self.time_vars['time3'])
index = date2index(query_time, self.time_vars['time3'],
select='nearest')
assert(index == 11)
Expand Down Expand Up @@ -1050,13 +1043,13 @@ def test_parse_date_tz(self):

class issue57TestCase(unittest.TestCase):
"""Regression tests for issue #57."""
# issue 57: cftime._cftime._dateparse returns quite opaque error messages that make it difficult to
# issue 57: cftime._cftime._dateparse returns quite opaque error messages that make it difficult to
# track down the source of problem
def setUp(self):
pass

def test_parse_incorrect_unitstring(self):
for datestr in ("days since2017-05-01 ", "dayssince 2017-05-01 00:00", "days snce 2017-05-01 00:00", "days_since_2017-05-01 00:00",
for datestr in ("days since2017-05-01 ", "dayssince 2017-05-01 00:00", "days snce 2017-05-01 00:00", "days_since_2017-05-01 00:00",
"days_since_2017-05-01_00:00"):
self.assertRaises(
ValueError, cftime._cftime._dateparse, datestr)
Expand Down

0 comments on commit b4886e5

Please sign in to comment.