14
14
Dataset ,
15
15
Variable ,
16
16
cftime_range ,
17
- coding ,
18
17
conventions ,
19
18
date_range ,
20
19
decode_cf ,
21
20
)
21
+ from xarray .coding .times import _STANDARD_CALENDARS as _STANDARD_CALENDARS_UNSORTED
22
22
from xarray .coding .times import (
23
+ CFDatetimeCoder ,
23
24
_encode_datetime_with_cftime ,
25
+ _netcdf_to_numpy_timeunit ,
24
26
_numpy_to_netcdf_timeunit ,
25
27
_should_cftime_be_used ,
26
28
cftime_to_nptime ,
27
29
decode_cf_datetime ,
28
30
decode_cf_timedelta ,
29
31
encode_cf_datetime ,
30
32
encode_cf_timedelta ,
33
+ format_cftime_datetime ,
34
+ infer_datetime_units ,
35
+ infer_timedelta_units ,
31
36
to_timedelta_unboxed ,
32
37
)
33
38
from xarray .coding .variables import SerializationWarning
53
58
"all_leap" ,
54
59
"366_day" ,
55
60
}
56
- _ALL_CALENDARS = sorted (
57
- _NON_STANDARD_CALENDARS_SET .union (coding .times ._STANDARD_CALENDARS )
58
- )
61
+ _STANDARD_CALENDARS = sorted (_STANDARD_CALENDARS_UNSORTED )
62
+ _ALL_CALENDARS = sorted (_NON_STANDARD_CALENDARS_SET .union (_STANDARD_CALENDARS ))
59
63
_NON_STANDARD_CALENDARS = sorted (_NON_STANDARD_CALENDARS_SET )
60
- _STANDARD_CALENDARS = sorted (coding .times ._STANDARD_CALENDARS )
61
64
_CF_DATETIME_NUM_DATES_UNITS = [
62
65
(np .arange (10 ), "days since 2000-01-01" ),
63
66
(np .arange (10 ).astype ("float64" ), "days since 2000-01-01" ),
@@ -130,7 +133,7 @@ def test_cf_datetime(num_dates, units, calendar) -> None:
130
133
131
134
with warnings .catch_warnings ():
132
135
warnings .filterwarnings ("ignore" , "Unable to decode time axis" )
133
- actual = coding . times . decode_cf_datetime (num_dates , units , calendar )
136
+ actual = decode_cf_datetime (num_dates , units , calendar )
134
137
135
138
abs_diff = np .asarray (abs (actual - expected )).ravel ()
136
139
abs_diff = pd .to_timedelta (abs_diff .tolist ()).to_numpy ()
@@ -139,17 +142,15 @@ def test_cf_datetime(num_dates, units, calendar) -> None:
139
142
# we could do this check with near microsecond accuracy:
140
143
# https://github.com/Unidata/netcdf4-python/issues/355
141
144
assert (abs_diff <= np .timedelta64 (1 , "s" )).all ()
142
- encoded , _ , _ = coding . times . encode_cf_datetime (actual , units , calendar )
145
+ encoded , _ , _ = encode_cf_datetime (actual , units , calendar )
143
146
144
- assert_array_equal (num_dates , np .around (encoded , 1 ))
147
+ assert_array_equal (num_dates , np .round (encoded , 1 ))
145
148
if hasattr (num_dates , "ndim" ) and num_dates .ndim == 1 and "1000" not in units :
146
149
# verify that wrapping with a pandas.Index works
147
150
# note that it *does not* currently work to put
148
151
# non-datetime64 compatible dates into a pandas.Index
149
- encoded , _ , _ = coding .times .encode_cf_datetime (
150
- pd .Index (actual ), units , calendar
151
- )
152
- assert_array_equal (num_dates , np .around (encoded , 1 ))
152
+ encoded , _ , _ = encode_cf_datetime (pd .Index (actual ), units , calendar )
153
+ assert_array_equal (num_dates , np .round (encoded , 1 ))
153
154
154
155
155
156
@requires_cftime
@@ -169,7 +170,7 @@ def test_decode_cf_datetime_overflow() -> None:
169
170
for i , day in enumerate (days ):
170
171
with warnings .catch_warnings ():
171
172
warnings .filterwarnings ("ignore" , "Unable to decode time axis" )
172
- result = coding . times . decode_cf_datetime (day , units )
173
+ result = decode_cf_datetime (day , units )
173
174
assert result == expected [i ]
174
175
175
176
@@ -178,7 +179,7 @@ def test_decode_cf_datetime_non_standard_units() -> None:
178
179
# netCDFs from madis.noaa.gov use this format for their time units
179
180
# they cannot be parsed by cftime, but pd.Timestamp works
180
181
units = "hours since 1-1-1970"
181
- actual = coding . times . decode_cf_datetime (np .arange (100 ), units )
182
+ actual = decode_cf_datetime (np .arange (100 ), units )
182
183
assert_array_equal (actual , expected )
183
184
184
185
@@ -193,7 +194,7 @@ def test_decode_cf_datetime_non_iso_strings() -> None:
193
194
(np .arange (100 ), "hours since 2000-01-01 0:00" ),
194
195
]
195
196
for num_dates , units in cases :
196
- actual = coding . times . decode_cf_datetime (num_dates , units )
197
+ actual = decode_cf_datetime (num_dates , units )
197
198
abs_diff = abs (actual - expected .values )
198
199
# once we no longer support versions of netCDF4 older than 1.1.5,
199
200
# we could do this check with near microsecond accuracy:
@@ -212,7 +213,7 @@ def test_decode_standard_calendar_inside_timestamp_range(calendar) -> None:
212
213
expected = times .values
213
214
expected_dtype = np .dtype ("M8[ns]" )
214
215
215
- actual = coding . times . decode_cf_datetime (time , units , calendar = calendar )
216
+ actual = decode_cf_datetime (time , units , calendar = calendar )
216
217
assert actual .dtype == expected_dtype
217
218
abs_diff = abs (actual - expected )
218
219
# once we no longer support versions of netCDF4 older than 1.1.5,
@@ -235,9 +236,7 @@ def test_decode_non_standard_calendar_inside_timestamp_range(calendar) -> None:
235
236
)
236
237
expected_dtype = np .dtype ("O" )
237
238
238
- actual = coding .times .decode_cf_datetime (
239
- non_standard_time , units , calendar = calendar
240
- )
239
+ actual = decode_cf_datetime (non_standard_time , units , calendar = calendar )
241
240
assert actual .dtype == expected_dtype
242
241
abs_diff = abs (actual - expected )
243
242
# once we no longer support versions of netCDF4 older than 1.1.5,
@@ -264,7 +263,7 @@ def test_decode_dates_outside_timestamp_range(calendar) -> None:
264
263
265
264
with warnings .catch_warnings ():
266
265
warnings .filterwarnings ("ignore" , "Unable to decode time axis" )
267
- actual = coding . times . decode_cf_datetime (time , units , calendar = calendar )
266
+ actual = decode_cf_datetime (time , units , calendar = calendar )
268
267
assert all (isinstance (value , expected_date_type ) for value in actual )
269
268
abs_diff = abs (actual - expected )
270
269
# once we no longer support versions of netCDF4 older than 1.1.5,
@@ -282,7 +281,7 @@ def test_decode_standard_calendar_single_element_inside_timestamp_range(
282
281
for num_time in [735368 , [735368 ], [[735368 ]]]:
283
282
with warnings .catch_warnings ():
284
283
warnings .filterwarnings ("ignore" , "Unable to decode time axis" )
285
- actual = coding . times . decode_cf_datetime (num_time , units , calendar = calendar )
284
+ actual = decode_cf_datetime (num_time , units , calendar = calendar )
286
285
assert actual .dtype == np .dtype ("M8[ns]" )
287
286
288
287
@@ -295,7 +294,7 @@ def test_decode_non_standard_calendar_single_element_inside_timestamp_range(
295
294
for num_time in [735368 , [735368 ], [[735368 ]]]:
296
295
with warnings .catch_warnings ():
297
296
warnings .filterwarnings ("ignore" , "Unable to decode time axis" )
298
- actual = coding . times . decode_cf_datetime (num_time , units , calendar = calendar )
297
+ actual = decode_cf_datetime (num_time , units , calendar = calendar )
299
298
assert actual .dtype == np .dtype ("O" )
300
299
301
300
@@ -309,9 +308,7 @@ def test_decode_single_element_outside_timestamp_range(calendar) -> None:
309
308
for num_time in [days , [days ], [[days ]]]:
310
309
with warnings .catch_warnings ():
311
310
warnings .filterwarnings ("ignore" , "Unable to decode time axis" )
312
- actual = coding .times .decode_cf_datetime (
313
- num_time , units , calendar = calendar
314
- )
311
+ actual = decode_cf_datetime (num_time , units , calendar = calendar )
315
312
316
313
expected = cftime .num2date (
317
314
days , units , calendar , only_use_cftime_datetimes = True
@@ -338,7 +335,7 @@ def test_decode_standard_calendar_multidim_time_inside_timestamp_range(
338
335
expected1 = times1 .values
339
336
expected2 = times2 .values
340
337
341
- actual = coding . times . decode_cf_datetime (mdim_time , units , calendar = calendar )
338
+ actual = decode_cf_datetime (mdim_time , units , calendar = calendar )
342
339
assert actual .dtype == np .dtype ("M8[ns]" )
343
340
344
341
abs_diff1 = abs (actual [:, 0 ] - expected1 )
@@ -379,7 +376,7 @@ def test_decode_nonstandard_calendar_multidim_time_inside_timestamp_range(
379
376
380
377
expected_dtype = np .dtype ("O" )
381
378
382
- actual = coding . times . decode_cf_datetime (mdim_time , units , calendar = calendar )
379
+ actual = decode_cf_datetime (mdim_time , units , calendar = calendar )
383
380
384
381
assert actual .dtype == expected_dtype
385
382
abs_diff1 = abs (actual [:, 0 ] - expected1 )
@@ -412,7 +409,7 @@ def test_decode_multidim_time_outside_timestamp_range(calendar) -> None:
412
409
413
410
with warnings .catch_warnings ():
414
411
warnings .filterwarnings ("ignore" , "Unable to decode time axis" )
415
- actual = coding . times . decode_cf_datetime (mdim_time , units , calendar = calendar )
412
+ actual = decode_cf_datetime (mdim_time , units , calendar = calendar )
416
413
417
414
assert actual .dtype == np .dtype ("O" )
418
415
@@ -435,7 +432,7 @@ def test_decode_non_standard_calendar_single_element(calendar, num_time) -> None
435
432
436
433
units = "days since 0001-01-01"
437
434
438
- actual = coding . times . decode_cf_datetime (num_time , units , calendar = calendar )
435
+ actual = decode_cf_datetime (num_time , units , calendar = calendar )
439
436
440
437
expected = np .asarray (
441
438
cftime .num2date (num_time , units , calendar , only_use_cftime_datetimes = True )
@@ -460,9 +457,7 @@ def test_decode_360_day_calendar() -> None:
460
457
461
458
with warnings .catch_warnings (record = True ) as w :
462
459
warnings .simplefilter ("always" )
463
- actual = coding .times .decode_cf_datetime (
464
- num_times , units , calendar = calendar
465
- )
460
+ actual = decode_cf_datetime (num_times , units , calendar = calendar )
466
461
assert len (w ) == 0
467
462
468
463
assert actual .dtype == np .dtype ("O" )
@@ -476,8 +471,8 @@ def test_decode_abbreviation() -> None:
476
471
477
472
val = np .array ([1586628000000.0 ])
478
473
units = "msecs since 1970-01-01T00:00:00Z"
479
- actual = coding . times . decode_cf_datetime (val , units )
480
- expected = coding . times . cftime_to_nptime (cftime .num2date (val , units ))
474
+ actual = decode_cf_datetime (val , units )
475
+ expected = cftime_to_nptime (cftime .num2date (val , units ))
481
476
assert_array_equal (actual , expected )
482
477
483
478
@@ -498,7 +493,7 @@ def test_decode_abbreviation() -> None:
498
493
def test_cf_datetime_nan (num_dates , units , expected_list ) -> None :
499
494
with warnings .catch_warnings ():
500
495
warnings .filterwarnings ("ignore" , "All-NaN" )
501
- actual = coding . times . decode_cf_datetime (num_dates , units )
496
+ actual = decode_cf_datetime (num_dates , units )
502
497
# use pandas because numpy will deprecate timezone-aware conversions
503
498
expected = pd .to_datetime (expected_list ).to_numpy (dtype = "datetime64[ns]" )
504
499
assert_array_equal (expected , actual )
@@ -510,7 +505,7 @@ def test_decoded_cf_datetime_array_2d() -> None:
510
505
variable = Variable (
511
506
("x" , "y" ), np .array ([[0 , 1 ], [2 , 3 ]]), {"units" : "days since 2000-01-01" }
512
507
)
513
- result = coding . times . CFDatetimeCoder ().decode (variable )
508
+ result = CFDatetimeCoder ().decode (variable )
514
509
assert result .dtype == "datetime64[ns]"
515
510
expected = pd .date_range ("2000-01-01" , periods = 4 ).values .reshape (2 , 2 )
516
511
assert_array_equal (np .asarray (result ), expected )
@@ -531,7 +526,7 @@ def test_decoded_cf_datetime_array_2d() -> None:
531
526
def test_infer_datetime_units (freq , units ) -> None :
532
527
dates = pd .date_range ("2000" , periods = 2 , freq = freq )
533
528
expected = f"{ units } since 2000-01-01 00:00:00"
534
- assert expected == coding . times . infer_datetime_units (dates )
529
+ assert expected == infer_datetime_units (dates )
535
530
536
531
537
532
@pytest .mark .parametrize (
@@ -549,7 +544,7 @@ def test_infer_datetime_units(freq, units) -> None:
549
544
],
550
545
)
551
546
def test_infer_datetime_units_with_NaT (dates , expected ) -> None :
552
- assert expected == coding . times . infer_datetime_units (dates )
547
+ assert expected == infer_datetime_units (dates )
553
548
554
549
555
550
_CFTIME_DATETIME_UNITS_TESTS = [
@@ -573,7 +568,7 @@ def test_infer_datetime_units_with_NaT(dates, expected) -> None:
573
568
def test_infer_cftime_datetime_units (calendar , date_args , expected ) -> None :
574
569
date_type = _all_cftime_date_types ()[calendar ]
575
570
dates = [date_type (* args ) for args in date_args ]
576
- assert expected == coding . times . infer_datetime_units (dates )
571
+ assert expected == infer_datetime_units (dates )
577
572
578
573
579
574
@pytest .mark .filterwarnings ("ignore:Timedeltas can't be serialized faithfully" )
@@ -600,18 +595,18 @@ def test_cf_timedelta(timedeltas, units, numbers) -> None:
600
595
numbers = np .array (numbers )
601
596
602
597
expected = numbers
603
- actual , _ = coding . times . encode_cf_timedelta (timedeltas , units )
598
+ actual , _ = encode_cf_timedelta (timedeltas , units )
604
599
assert_array_equal (expected , actual )
605
600
assert expected .dtype == actual .dtype
606
601
607
602
if units is not None :
608
603
expected = timedeltas
609
- actual = coding . times . decode_cf_timedelta (numbers , units )
604
+ actual = decode_cf_timedelta (numbers , units )
610
605
assert_array_equal (expected , actual )
611
606
assert expected .dtype == actual .dtype
612
607
613
608
expected = np .timedelta64 ("NaT" , "ns" )
614
- actual = coding . times . decode_cf_timedelta (np .array (np .nan ), "days" )
609
+ actual = decode_cf_timedelta (np .array (np .nan ), "days" )
615
610
assert_array_equal (expected , actual )
616
611
617
612
@@ -622,7 +617,7 @@ def test_cf_timedelta_2d() -> None:
622
617
timedeltas = np .atleast_2d (to_timedelta_unboxed (["1D" , "2D" , "3D" ]))
623
618
expected = timedeltas
624
619
625
- actual = coding . times . decode_cf_timedelta (numbers , units )
620
+ actual = decode_cf_timedelta (numbers , units )
626
621
assert_array_equal (expected , actual )
627
622
assert expected .dtype == actual .dtype
628
623
@@ -637,7 +632,7 @@ def test_cf_timedelta_2d() -> None:
637
632
],
638
633
)
639
634
def test_infer_timedelta_units (deltas , expected ) -> None :
640
- assert expected == coding . times . infer_timedelta_units (deltas )
635
+ assert expected == infer_timedelta_units (deltas )
641
636
642
637
643
638
@requires_cftime
@@ -653,7 +648,7 @@ def test_infer_timedelta_units(deltas, expected) -> None:
653
648
def test_format_cftime_datetime (date_args , expected ) -> None :
654
649
date_types = _all_cftime_date_types ()
655
650
for date_type in date_types .values ():
656
- result = coding . times . format_cftime_datetime (date_type (* date_args ))
651
+ result = format_cftime_datetime (date_type (* date_args ))
657
652
assert result == expected
658
653
659
654
@@ -1008,7 +1003,7 @@ def test_decode_ambiguous_time_warns(calendar) -> None:
1008
1003
1009
1004
# we don't decode non-standard calendards with
1010
1005
# pandas so expect no warning will be emitted
1011
- is_standard_calendar = calendar in coding . times . _STANDARD_CALENDARS
1006
+ is_standard_calendar = calendar in _STANDARD_CALENDARS
1012
1007
1013
1008
dates = [1 , 2 , 3 ]
1014
1009
units = "days since 1-1-1"
@@ -1043,9 +1038,9 @@ def test_encode_cf_datetime_defaults_to_correct_dtype(
1043
1038
pytest .skip ("Nanosecond frequency is not valid for cftime dates." )
1044
1039
times = date_range ("2000" , periods = 3 , freq = freq )
1045
1040
units = f"{ encoding_units } since 2000-01-01"
1046
- encoded , _units , _ = coding . times . encode_cf_datetime (times , units )
1041
+ encoded , _units , _ = encode_cf_datetime (times , units )
1047
1042
1048
- numpy_timeunit = coding . times . _netcdf_to_numpy_timeunit (encoding_units )
1043
+ numpy_timeunit = _netcdf_to_numpy_timeunit (encoding_units )
1049
1044
encoding_units_as_timedelta = np .timedelta64 (1 , numpy_timeunit )
1050
1045
if pd .to_timedelta (1 , freq ) >= encoding_units_as_timedelta :
1051
1046
assert encoded .dtype == np .int64
@@ -1202,7 +1197,7 @@ def test_decode_float_datetime():
1202
1197
def test_scalar_unit () -> None :
1203
1198
# test that a scalar units (often NaN when using to_netcdf) does not raise an error
1204
1199
variable = Variable (("x" , "y" ), np .array ([[0 , 1 ], [2 , 3 ]]), {"units" : np .nan })
1205
- result = coding . times . CFDatetimeCoder ().decode (variable )
1200
+ result = CFDatetimeCoder ().decode (variable )
1206
1201
assert np .isnan (result .attrs ["units" ])
1207
1202
1208
1203
0 commit comments