Skip to content

Commit 4e052f5

Browse files
committed
Don't convert time data to timedelta by default
Closes #843
1 parent 9d8bb13 commit 4e052f5

File tree

3 files changed

+42
-24
lines changed

3 files changed

+42
-24
lines changed

xarray/backends/api.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ def check_name(name):
8080

8181

8282
def open_dataset(filename_or_obj, group=None, decode_cf=True,
83-
mask_and_scale=True, decode_times=True,
84-
concat_characters=True, decode_coords=True, engine=None,
83+
mask_and_scale=True, decode_datetimes=True,
84+
decode_timedeltas=False, concat_characters=True,
85+
decode_coords=True, engine=None,
8586
chunks=None, lock=None, drop_variables=None):
8687
"""Load and decode a dataset from a file or file-like object.
8788
@@ -107,9 +108,12 @@ def open_dataset(filename_or_obj, group=None, decode_cf=True,
107108
`missing_value` attribute contains multiple values a warning will be
108109
issued and all array values matching one of the multiple values will
109110
be replaced by NA.
110-
decode_times : bool, optional
111+
decode_datetimes : bool, optional
111112
If True, decode times encoded in the standard NetCDF datetime format
112113
into datetime objects. Otherwise, leave them encoded as numbers.
114+
decode_timedeltas : bool, optional
115+
If True, decode time data encoded in the standard NetCDF datetime format
116+
into timedelta objects. Otherwise, leave them encoded as numbers.
113117
concat_characters : bool, optional
114118
If True, concatenate along the last dimension of character arrays to
115119
form string arrays. Dimensions will only be concatenated over (and
@@ -148,15 +152,16 @@ def open_dataset(filename_or_obj, group=None, decode_cf=True,
148152
"""
149153
if not decode_cf:
150154
mask_and_scale = False
151-
decode_times = False
155+
decode_datetimes = False
156+
decode_timedeltas = False
152157
concat_characters = False
153158
decode_coords = False
154159

155160
def maybe_decode_store(store, lock=False):
156161
ds = conventions.decode_cf(
157-
store, mask_and_scale=mask_and_scale, decode_times=decode_times,
158-
concat_characters=concat_characters, decode_coords=decode_coords,
159-
drop_variables=drop_variables)
162+
store, mask_and_scale=mask_and_scale, decode_datetimes=decode_datetimes,
163+
decode_timedeltas=decode_timedeltas, concat_characters=concat_characters,
164+
decode_coords=decode_coords, drop_variables=drop_variables)
160165

161166
if chunks is not None:
162167
try:
@@ -174,7 +179,8 @@ def maybe_decode_store(store, lock=False):
174179
else:
175180
file_arg = filename_or_obj
176181
token = tokenize(file_arg, group, decode_cf, mask_and_scale,
177-
decode_times, concat_characters, decode_coords,
182+
decode_datetimes, decode_timedeltas,
183+
concat_characters, decode_coords,
178184
engine, chunks, drop_variables)
179185
name_prefix = '%s:%s/' % (filename_or_obj, group or '')
180186
ds2 = ds.chunk(chunks, name_prefix=name_prefix, token=token,

xarray/conventions.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ def __init__(self, array, units, calendar=None):
379379
calendar_msg = ('the default calendar' if calendar is None
380380
else 'calendar %r' % calendar)
381381
msg = ('unable to decode time units %r with %s. Try '
382-
'opening your dataset with decode_times=False.'
382+
'opening your dataset with decode_datestimes=False.'
383383
% (units, calendar_msg))
384384
if not PY3:
385385
msg += ' Full traceback:\n' + traceback.format_exc()
@@ -718,7 +718,8 @@ def encode_cf_variable(var, needs_copy=True, name=None):
718718

719719

720720
def decode_cf_variable(var, concat_characters=True, mask_and_scale=True,
721-
decode_times=True, decode_endianness=True):
721+
decode_datetimes=True, decode_timedeltas=False,
722+
decode_endianness=True):
722723
"""
723724
Decodes a variable which may hold CF encoded information.
724725
@@ -737,8 +738,10 @@ def decode_cf_variable(var, concat_characters=True, mask_and_scale=True,
737738
mask_and_scale: bool
738739
Lazily scale (using scale_factor and add_offset) and mask
739740
(using _FillValue).
740-
decode_times : bool
741+
decode_datetimes : bool
741742
Decode cf times ('hours since 2000-01-01') to np.datetime64.
743+
decode_timedeltas : bool
744+
Decode cf time data ('seconds') to np.timedelta64.
742745
decode_endianness : bool
743746
Decode arrays from non-native to native endianness.
744747
@@ -792,13 +795,13 @@ def decode_cf_variable(var, concat_characters=True, mask_and_scale=True,
792795
data = MaskedAndScaledArray(data, fill_value, scale_factor,
793796
add_offset, dtype)
794797

795-
if decode_times and 'units' in attributes:
798+
if decode_datetimes and 'units' in attributes:
796799
if 'since' in attributes['units']:
797800
# datetime
798801
units = pop_to(attributes, encoding, 'units')
799802
calendar = pop_to(attributes, encoding, 'calendar')
800803
data = DecodedCFDatetimeArray(data, units, calendar)
801-
elif attributes['units'] in TIME_UNITS:
804+
elif decode_timedeltas and attributes['units'] in TIME_UNITS:
802805
# timedelta
803806
units = pop_to(attributes, encoding, 'units')
804807
data = DecodedCFTimedeltaArray(data, units)
@@ -823,8 +826,9 @@ def decode_cf_variable(var, concat_characters=True, mask_and_scale=True,
823826

824827

825828
def decode_cf_variables(variables, attributes, concat_characters=True,
826-
mask_and_scale=True, decode_times=True,
827-
decode_coords=True, drop_variables=None):
829+
mask_and_scale=True, decode_datetimes=True,
830+
decode_timedeltas=False, decode_coords=True,
831+
drop_variables=None):
828832
"""
829833
Decode a several CF encoded variables.
830834
@@ -860,7 +864,8 @@ def stackable(dim):
860864
stackable(v.dims[-1]))
861865
new_vars[k] = decode_cf_variable(
862866
v, concat_characters=concat, mask_and_scale=mask_and_scale,
863-
decode_times=decode_times)
867+
decode_datetimes=decode_datetimes,
868+
decode_timedeltas=decode_timedeltas)
864869
if decode_coords:
865870
var_attrs = new_vars[k].attrs
866871
if 'coordinates' in var_attrs:
@@ -879,7 +884,8 @@ def stackable(dim):
879884

880885

881886
def decode_cf(obj, concat_characters=True, mask_and_scale=True,
882-
decode_times=True, decode_coords=True, drop_variables=None):
887+
decode_datetimes=True, decode_timedeltas=False,
888+
decode_coords=True, drop_variables=None):
883889
"""Decode the given Dataset or Datastore according to CF conventions into
884890
a new Dataset.
885891
@@ -893,9 +899,11 @@ def decode_cf(obj, concat_characters=True, mask_and_scale=True,
893899
mask_and_scale: bool, optional
894900
Lazily scale (using scale_factor and add_offset) and mask
895901
(using _FillValue).
896-
decode_times : bool, optional
902+
decode_datetimes : bool, optional
897903
Decode cf times (e.g., integers since 'hours since 2000-01-01') to
898904
np.datetime64.
905+
decode_timedeltas : bool, optional
906+
Decode cf time data (e.g., 'seconds') to np.timedelta64.
899907
decode_coords : bool, optional
900908
Use the 'coordinates' attribute on variable (or the dataset itself) to
901909
identify coordinates.
@@ -924,8 +932,8 @@ def decode_cf(obj, concat_characters=True, mask_and_scale=True,
924932
raise TypeError('can only decode Dataset or DataStore objects')
925933

926934
vars, attrs, coord_names = decode_cf_variables(
927-
vars, attrs, concat_characters, mask_and_scale, decode_times,
928-
decode_coords, drop_variables=drop_variables)
935+
vars, attrs, concat_characters, mask_and_scale, decode_datetimes,
936+
decode_timedeltas, decode_coords, drop_variables=drop_variables)
929937
ds = Dataset(vars, attrs=attrs)
930938
ds = ds.set_coords(coord_names.union(extra_coords))
931939
ds._file_obj = file_obj
@@ -934,7 +942,7 @@ def decode_cf(obj, concat_characters=True, mask_and_scale=True,
934942

935943
def cf_decoder(variables, attributes,
936944
concat_characters=True, mask_and_scale=True,
937-
decode_times=True):
945+
decode_datetimes=True, decode_timedeltas=False):
938946
"""
939947
Decode a set of CF encoded variables and attributes.
940948
@@ -952,8 +960,10 @@ def cf_decoder(variables, attributes,
952960
mask_and_scale: bool
953961
Lazily scale (using scale_factor and add_offset) and mask
954962
(using _FillValue).
955-
decode_times : bool
963+
decode_datetimes : bool
956964
Decode cf times ('hours since 2000-01-01') to np.datetime64.
965+
decode_timedeltas : bool
966+
Decode cf time data ('seconds') to np.timedelta64.
957967
958968
Returns
959969
-------
@@ -963,7 +973,8 @@ def cf_decoder(variables, attributes,
963973
A dictionary mapping from attribute name to values.
964974
"""
965975
variables, attributes, _ = decode_cf_variables(
966-
variables, attributes, concat_characters, mask_and_scale, decode_times)
976+
variables, attributes, concat_characters, mask_and_scale,
977+
decode_datetimes, decode_timedeltas)
967978
return variables, attributes
968979

969980

xarray/test/test_backends.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ def test_roundtrip_datetime_data(self):
201201
def test_roundtrip_timedelta_data(self):
202202
time_deltas = pd.to_timedelta(['1h', '2h', 'NaT'])
203203
expected = Dataset({'td': ('td', time_deltas), 'td0': time_deltas[0]})
204-
with self.roundtrip(expected) as actual:
204+
with self.roundtrip(
205+
expected, open_kwargs={'decode_timedeltas': True}) as actual:
205206
self.assertDatasetIdentical(expected, actual)
206207

207208
def test_roundtrip_float64_data(self):

0 commit comments

Comments
 (0)