42
42
from __future__ import annotations
43
43
44
44
import math
45
- import re
46
45
import warnings
47
46
from datetime import timedelta
48
47
from typing import TYPE_CHECKING , Any
53
52
54
53
from xarray .coding .times import (
55
54
_STANDARD_CALENDARS ,
55
+ _parse_iso8601 ,
56
56
cftime_to_nptime ,
57
57
infer_calendar_name ,
58
58
)
78
78
OUT_OF_BOUNDS_TIMEDELTA_ERRORS = (OverflowError ,)
79
79
80
80
81
- def named (name , pattern ):
82
- return "(?P<" + name + ">" + pattern + ")"
83
-
84
-
85
- def optional (x ):
86
- return "(?:" + x + ")?"
87
-
88
-
89
- def trailing_optional (xs ):
90
- if not xs :
91
- return ""
92
- return xs [0 ] + optional (trailing_optional (xs [1 :]))
93
-
94
-
95
- def build_pattern (date_sep = r"\-" , datetime_sep = r"T" , time_sep = r"\:" , micro_sep = r"." ):
96
- pieces = [
97
- (None , "year" , r"\d{4}" ),
98
- (date_sep , "month" , r"\d{2}" ),
99
- (date_sep , "day" , r"\d{2}" ),
100
- (datetime_sep , "hour" , r"\d{2}" ),
101
- (time_sep , "minute" , r"\d{2}" ),
102
- (time_sep , "second" , r"\d{2}" ),
103
- (micro_sep , "microsecond" , r"\d{1,6}" ),
104
- ]
105
- pattern_list = []
106
- for sep , name , sub_pattern in pieces :
107
- pattern_list .append ((sep if sep else "" ) + named (name , sub_pattern ))
108
- # TODO: allow timezone offsets?
109
- return "^" + trailing_optional (pattern_list ) + "$"
110
-
111
-
112
- _BASIC_PATTERN = build_pattern (date_sep = "" , time_sep = "" )
113
- _EXTENDED_PATTERN = build_pattern ()
114
- _CFTIME_PATTERN = build_pattern (datetime_sep = " " )
115
- _PATTERNS = [_BASIC_PATTERN , _EXTENDED_PATTERN , _CFTIME_PATTERN ]
116
-
117
-
118
- def parse_iso8601_like (datetime_string ):
119
- for pattern in _PATTERNS :
120
- match = re .match (pattern , datetime_string )
121
- if match :
122
- return match .groupdict ()
123
- raise ValueError (
124
- f"no ISO-8601 or cftime-string-like match for string: { datetime_string } "
125
- )
126
-
127
-
128
- def _parse_iso8601_with_reso (date_type , timestr ):
129
- _ = attempt_import ("cftime" )
130
-
131
- default = date_type (1 , 1 , 1 )
132
- result = parse_iso8601_like (timestr )
133
- replace = {}
134
-
135
- for attr in ["year" , "month" , "day" , "hour" , "minute" , "second" , "microsecond" ]:
136
- value = result .get (attr , None )
137
- if value is not None :
138
- if attr == "microsecond" :
139
- # convert match string into valid microsecond value
140
- value = 10 ** (6 - len (value )) * int (value )
141
- replace [attr ] = int (value )
142
- resolution = attr
143
- return default .replace (** replace ), resolution
144
-
145
-
146
81
def _parsed_string_to_bounds (date_type , resolution , parsed ):
147
82
"""Generalization of
148
83
pandas.tseries.index.DatetimeIndex._parsed_string_to_bounds
@@ -436,7 +371,7 @@ def _partial_date_slice(self, resolution, parsed):
436
371
437
372
def _get_string_slice (self , key ):
438
373
"""Adapted from pandas.tseries.index.DatetimeIndex._get_string_slice"""
439
- parsed , resolution = _parse_iso8601_with_reso (self .date_type , key )
374
+ parsed , resolution = _parse_iso8601 (self .date_type , key )
440
375
try :
441
376
loc = self ._partial_date_slice (resolution , parsed )
442
377
except KeyError as err :
@@ -483,7 +418,7 @@ def _maybe_cast_slice_bound(self, label, side):
483
418
if not isinstance (label , str ):
484
419
return label
485
420
486
- parsed , resolution = _parse_iso8601_with_reso (self .date_type , label )
421
+ parsed , resolution = _parse_iso8601 (self .date_type , label )
487
422
start , end = _parsed_string_to_bounds (self .date_type , resolution , parsed )
488
423
if self .is_monotonic_decreasing and len (self ) > 1 :
489
424
return end if side == "left" else start
@@ -811,11 +746,6 @@ def is_leap_year(self):
811
746
return func (self .year , calendar = self .calendar )
812
747
813
748
814
- def _parse_iso8601_without_reso (date_type , datetime_str ):
815
- date , _ = _parse_iso8601_with_reso (date_type , datetime_str )
816
- return date
817
-
818
-
819
749
def _parse_array_of_cftime_strings (strings , date_type ):
820
750
"""Create a numpy array from an array of strings.
821
751
@@ -833,9 +763,9 @@ def _parse_array_of_cftime_strings(strings, date_type):
833
763
-------
834
764
np.array
835
765
"""
836
- return np .array (
837
- [ _parse_iso8601_without_reso ( date_type , s ) for s in strings .ravel ()]
838
- ). reshape ( strings . shape )
766
+ return np .array ([ _parse_iso8601 ( date_type , s )[ 0 ] for s in strings . ravel ()]). reshape (
767
+ strings .shape
768
+ )
839
769
840
770
841
771
def _contains_datetime_timedeltas (array ):
0 commit comments