Skip to content

Fix non-standard calendars #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions test/test_conventions.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,30 @@ def test_cf_datetime(self):
pd.Index(actual), units, calendar)
self.assertArrayEqual(num_dates, np.around(encoded))

@requires_netCDF4
def test_decoded_cf_datetime_array(self):
import netCDF4 as nc4

actual = conventions.DecodedCFDatetimeArray(
[0, 1, 2], 'days since 1900-01-01', 'standard')
expected = pd.date_range('1900-01-01', periods=3).values
self.assertEqual(actual.dtype, np.dtype('datetime64[ns]'))
self.assertArrayEqual(actual, expected)

# default calendar
actual = conventions.DecodedCFDatetimeArray(
[0, 1, 2], 'days since 1900-01-01')
self.assertEqual(actual.dtype, np.dtype('datetime64[ns]'))
self.assertArrayEqual(actual, expected)

num_dates = [722000, 720000.5]
units = 'days since 0001-01-01 0:0:0'
calendar = 'noleap'
actual = conventions.DecodedCFDatetimeArray(num_dates, units, calendar)
expected = nc4.num2date(num_dates, units, calendar)
self.assertEqual(actual.dtype, np.dtype('O'))
self.assertArrayEqual(actual, expected)

@requires_netCDF4
def test_cf_datetime_nan(self):
for num_dates, units, expected_list in [
Expand Down
13 changes: 11 additions & 2 deletions xray/conventions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
# coerced instead as indicated by the "coerce_nc3_dtype" function
_nc3_dtype_coercions = {'int64': 'int32', 'float64': 'float32', 'bool': 'int8'}

# standard calendars recognized by netcdftime
_STANDARD_CALENDARS = {'standard', 'gregorian', 'proleptic_gregorian'}


def coerce_nc3_dtype(arr):
"""Coerce an array to a data type that can be stored in a netCDF-3 file
Expand Down Expand Up @@ -163,7 +166,7 @@ def nan_safe_num2date(num):
else:
max_date = min_date

if (calendar not in ['standard', 'gregorian', 'proleptic_gregorian']
if (calendar not in _STANDARD_CALENDARS
or min_date < datetime(1678, 1, 1)
or max_date > datetime(2262, 4, 11)):
dates = nc4.num2date(num_dates, units, calendar)
Expand Down Expand Up @@ -324,7 +327,13 @@ def __init__(self, array, units, calendar=None):

@property
def dtype(self):
return np.dtype('datetime64[ns]')
if self.calendar is None or self.calendar in _STANDARD_CALENDARS:
# TODO: return the proper dtype (object) for a standard calendar
# that can't be expressed in ns precision. Perhaps we could guess
# this from the units?
return np.dtype('datetime64[ns]')
else:
return np.dtype('O')

def __getitem__(self, key):
return decode_cf_datetime(self.array, units=self.units,
Expand Down