Skip to content

Commit 23c565c

Browse files
committed
reset order of encoding/decoding but special case times in CFMaskCoder
1 parent f5db449 commit 23c565c

File tree

2 files changed

+29
-32
lines changed

2 files changed

+29
-32
lines changed

xarray/coding/variables.py

+22-23
Original file line numberDiff line numberDiff line change
@@ -236,20 +236,19 @@ def encode(self, variable: Variable, name: T_Name = None):
236236
f"Variable {name!r} has conflicting _FillValue ({fv}) and missing_value ({mv}). Cannot encode data."
237237
)
238238

239+
# cast to correct dtype in case of times
240+
# see GH 7817
241+
units = attrs.get("units", None)
242+
if isinstance(units, str) and "since" in units:
243+
encoded_dtype = encoding.pop("dtype")
244+
if encoded_dtype is not None and encoded_dtype != data.dtype:
245+
data = np.asarray(data, dtype=encoded_dtype)
246+
239247
if fv_exists:
240248
# Ensure _FillValue is cast to same dtype as data's
241249
encoding["_FillValue"] = dtype.type(fv)
242250
fill_value = pop_to(encoding, attrs, "_FillValue", name=name)
243-
# retrieve _FillValue in case of np.datetime64
244-
# see GH 7817
245-
if np.issubdtype(data.dtype, np.datetime64):
246-
units = encoding.get("units", None)
247-
if isinstance(units, str) and "since" in units:
248-
delta, _ = times._unpack_netcdf_time_units(units)
249-
delta = times._netcdf_to_numpy_timeunit(delta)
250-
fill_value = np.datetime64(fill_value.item(), delta).astype(
251-
"datetime64[ns]"
252-
)
251+
253252
if not pd.isnull(fill_value):
254253
data = duck_array_ops.fillna(data, fill_value)
255254

@@ -284,19 +283,19 @@ def decode(self, variable: Variable, name: T_Name = None):
284283
SerializationWarning,
285284
stacklevel=3,
286285
)
287-
288-
dtype, decoded_fill_value = dtypes.maybe_promote(data.dtype)
289-
# retrieve _FillValue in case of np.datetime64
290-
# see GH 7817
291-
if np.issubdtype(data.dtype, np.datetime64) and decoded_fill_value.astype(
292-
np.int64
293-
) == np.datetime64("NaT").astype(np.int64):
294-
delta, _ = times._unpack_netcdf_time_units(encoding["units"])
295-
delta = times._netcdf_to_numpy_timeunit(delta)
296-
encoded_fill_values = {
297-
np.datetime64(encfill.item(), delta).astype("datetime64[ns]")
298-
for encfill in encoded_fill_values
299-
}
286+
units = attrs.get("units", None)
287+
# try to cast to correct dtypes for data and fill_value
288+
# GH 7817
289+
if ((
290+
isinstance(units, str)
291+
and "since" in units
292+
and np.issubdtype(data.dtype, np.integer)) or np.issubdtype(data.dtype, np.datetime64)
293+
):
294+
dtype, decoded_fill_value = data.dtype, np.datetime64("NaT").astype(
295+
data.dtype
296+
)
297+
else:
298+
dtype, decoded_fill_value = dtypes.maybe_promote(data.dtype)
300299
if encoded_fill_values:
301300
transform = partial(
302301
_apply_mask,

xarray/conventions.py

+7-9
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ def encode_cf_variable(
174174
ensure_not_multiindex(var, name=name)
175175

176176
for coder in [
177-
variables.CFScaleOffsetCoder(),
178-
variables.CFMaskCoder(),
179177
times.CFDatetimeCoder(),
180178
times.CFTimedeltaCoder(),
179+
variables.CFScaleOffsetCoder(),
180+
variables.CFMaskCoder(),
181181
variables.UnsignedIntegerCoder(),
182182
variables.NonStringCoder(),
183183
variables.DefaultFillvalueCoder(),
@@ -263,13 +263,6 @@ def decode_cf_variable(
263263
var = strings.CharacterArrayCoder().decode(var, name=name)
264264
var = strings.EncodedStringCoder().decode(var)
265265

266-
# time decoding before masking
267-
# GH 7817
268-
if decode_timedelta:
269-
var = times.CFTimedeltaCoder().decode(var, name=name)
270-
if decode_times:
271-
var = times.CFDatetimeCoder(use_cftime=use_cftime).decode(var, name=name)
272-
273266
if mask_and_scale:
274267
for coder in [
275268
variables.UnsignedIntegerCoder(),
@@ -278,6 +271,11 @@ def decode_cf_variable(
278271
]:
279272
var = coder.decode(var, name=name)
280273

274+
if decode_timedelta:
275+
var = times.CFTimedeltaCoder().decode(var, name=name)
276+
if decode_times:
277+
var = times.CFDatetimeCoder(use_cftime=use_cftime).decode(var, name=name)
278+
281279
if decode_endianness and not var.dtype.isnative:
282280
var = variables.EndianCoder().decode(var)
283281
original_dtype = var.dtype

0 commit comments

Comments
 (0)