15
15
)
16
16
from pandas ._libs .tslibs import (
17
17
BaseOffset ,
18
+ NaT ,
18
19
Period ,
19
20
Resolution ,
20
21
Tick ,
21
22
)
22
- from pandas ._libs .tslibs .parsing import (
23
- DateParseError ,
24
- parse_time_string ,
25
- )
26
23
from pandas ._typing import (
27
24
Dtype ,
28
25
DtypeObj ,
35
32
pandas_dtype ,
36
33
)
37
34
from pandas .core .dtypes .dtypes import PeriodDtype
35
+ from pandas .core .dtypes .missing import is_valid_na_for_dtype
38
36
39
37
from pandas .core .arrays .period import (
40
38
PeriodArray ,
@@ -409,43 +407,50 @@ def get_loc(self, key, method=None, tolerance=None):
409
407
orig_key = key
410
408
411
409
self ._check_indexing_error (key )
412
- if isinstance (key , str ):
413
410
414
- try :
415
- loc = self ._get_string_slice (key )
416
- return loc
417
- except (TypeError , ValueError ):
418
- pass
411
+ if is_valid_na_for_dtype (key , self .dtype ):
412
+ key = NaT
413
+
414
+ elif isinstance (key , str ):
419
415
420
416
try :
421
- asdt , reso_str = parse_time_string ( key , self .freq )
422
- except ( ValueError , DateParseError ) as err :
417
+ parsed , reso = self ._parse_with_reso ( key )
418
+ except ValueError as err :
423
419
# A string with invalid format
424
420
raise KeyError (f"Cannot interpret '{ key } ' as period" ) from err
425
421
426
- reso = Resolution .from_attrname (reso_str )
422
+ if self ._can_partial_date_slice (reso ):
423
+ try :
424
+ return self ._partial_date_slice (reso , parsed )
425
+ except KeyError as err :
426
+ # TODO: pass if method is not None, like DTI does?
427
+ raise KeyError (key ) from err
427
428
428
429
if reso == self .dtype .resolution :
429
430
# the reso < self.dtype.resolution case goes through _get_string_slice
430
- key = Period (asdt , freq = self .freq )
431
+ key = Period (parsed , freq = self .freq )
431
432
loc = self .get_loc (key , method = method , tolerance = tolerance )
433
+ # Recursing instead of falling through matters for the exception
434
+ # message in test_get_loc3 (though not clear if that really matters)
432
435
return loc
433
436
elif method is None :
434
437
raise KeyError (key )
435
438
else :
436
- key = asdt
439
+ key = Period ( parsed , freq = self . freq )
437
440
438
- elif is_integer (key ):
439
- # Period constructor will cast to string, which we dont want
440
- raise KeyError (key )
441
441
elif isinstance (key , Period ) and key .freq != self .freq :
442
442
raise KeyError (key )
443
-
444
- try :
445
- key = Period (key , freq = self .freq )
446
- except ValueError as err :
447
- # we cannot construct the Period
448
- raise KeyError (orig_key ) from err
443
+ elif isinstance (key , Period ):
444
+ pass
445
+ elif isinstance (key , datetime ):
446
+ try :
447
+ key = Period (key , freq = self .freq )
448
+ except ValueError as err :
449
+ # we cannot construct the Period
450
+ raise KeyError (orig_key ) from err
451
+ else :
452
+ # in particular integer, which Period constructor would cast to string
453
+ raise KeyError (key )
449
454
450
455
try :
451
456
return Index .get_loc (self , key , method , tolerance )
@@ -496,7 +501,7 @@ def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime):
496
501
iv = Period (parsed , freq = grp .value )
497
502
return (iv .asfreq (self .freq , how = "start" ), iv .asfreq (self .freq , how = "end" ))
498
503
499
- def _validate_partial_date_slice (self , reso : Resolution ):
504
+ def _can_partial_date_slice (self , reso : Resolution ) -> bool :
500
505
assert isinstance (reso , Resolution ), (type (reso ), reso )
501
506
grp = reso .freq_group
502
507
freqn = self .dtype .freq_group_code
@@ -505,7 +510,9 @@ def _validate_partial_date_slice(self, reso: Resolution):
505
510
# TODO: we used to also check for
506
511
# reso in ["day", "hour", "minute", "second"]
507
512
# why is that check not needed?
508
- raise ValueError
513
+ return False
514
+
515
+ return True
509
516
510
517
511
518
def period_range (
0 commit comments