Skip to content

Commit bd9f82d

Browse files
committed
Ensure decoding as datetime64[ns]
Pandas seems to have trouble constructing multi-indices when it's given datetime64 arrays which don't have ns precision. The current version of decode_cf_datetime will give datetime arrays with the default precision, which is us. Hence, when coupled with the dtype restoring wrapper from PR #54, the `to_series()` and `to_dataframe()` methods were broken when using decoded datetimes.
1 parent fdbfb7c commit bd9f82d

File tree

2 files changed

+15
-4
lines changed

2 files changed

+15
-4
lines changed

src/xray/utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ def decode_cf_datetime(num_dates, units, calendar=None):
135135
or max_date > datetime(2262, 4, 11)):
136136
dates = nc4.num2date(num_dates, units, calendar)
137137
else:
138-
# we can safely use np.datetime64
138+
# we can safely use np.datetime64 with nanosecond precision (pandas
139+
# likes ns precision so it can directly make DatetimeIndex objects)
139140
if min_num == max_num:
140141
# we can't safely divide by max_num - min_num
141142
dates = np.repeat(np.datetime64(min_date), num_dates.size)
@@ -156,8 +157,8 @@ def decode_cf_datetime(num_dates, units, calendar=None):
156157
denominator = max_num - min_num
157158
dates = (time_delta * numerator / denominator
158159
+ np.datetime64(min_date))
159-
# restore original shape
160-
dates = dates.reshape(num_dates.shape)
160+
# restore original shape and ensure dates are given in ns
161+
dates = dates.reshape(num_dates.shape).astype('M8[ns]')
161162
return dates
162163

163164

test/test_utils.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,17 @@ def test_cf_datetime(self):
8989
for calendar in ['standard', 'gregorian', 'proleptic_gregorian']:
9090
expected = nc4.num2date(num_dates, units, calendar)
9191
actual = utils.decode_cf_datetime(num_dates, units, calendar)
92-
self.assertArrayEqual(expected, actual)
92+
if (isinstance(actual, np.ndarray)
93+
and np.issubdtype(actual.dtype, np.datetime64)):
94+
self.assertEqual(actual.dtype, np.dtype('M8[ns]'))
95+
# For some reason, numpy 1.8 does not compare ns precision
96+
# datetime64 arrays as equal to arrays of datetime objects,
97+
# but it works for us precision. Thus, convert to us
98+
# precision for the actual array equal comparison...
99+
actual_cmp = actual.astype('M8[us]')
100+
else:
101+
actual_cmp = actual
102+
self.assertArrayEqual(expected, actual_cmp)
93103
encoded, _, _ = utils.encode_cf_datetime(actual, units, calendar)
94104
self.assertArrayEqual(num_dates, np.around(encoded))
95105
if (hasattr(num_dates, 'ndim') and num_dates.ndim == 1

0 commit comments

Comments
 (0)