1515)
1616from pandas ._libs .tslibs import (
1717 BaseOffset ,
18+ NaT ,
1819 Period ,
1920 Resolution ,
2021 Tick ,
2122)
22- from pandas ._libs .tslibs .parsing import (
23- DateParseError ,
24- parse_time_string ,
25- )
2623from pandas ._typing import (
2724 Dtype ,
2825 DtypeObj ,
3532 pandas_dtype ,
3633)
3734from pandas .core .dtypes .dtypes import PeriodDtype
35+ from pandas .core .dtypes .missing import is_valid_na_for_dtype
3836
3937from pandas .core .arrays .period import (
4038 PeriodArray ,
@@ -409,43 +407,50 @@ def get_loc(self, key, method=None, tolerance=None):
409407 orig_key = key
410408
411409 self ._check_indexing_error (key )
412- if isinstance (key , str ):
413410
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 ):
419415
420416 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 :
423419 # A string with invalid format
424420 raise KeyError (f"Cannot interpret '{ key } ' as period" ) from err
425421
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
427428
428429 if reso == self .dtype .resolution :
429430 # 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 )
431432 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)
432435 return loc
433436 elif method is None :
434437 raise KeyError (key )
435438 else :
436- key = asdt
439+ key = Period ( parsed , freq = self . freq )
437440
438- elif is_integer (key ):
439- # Period constructor will cast to string, which we dont want
440- raise KeyError (key )
441441 elif isinstance (key , Period ) and key .freq != self .freq :
442442 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 )
449454
450455 try :
451456 return Index .get_loc (self , key , method , tolerance )
@@ -496,7 +501,7 @@ def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime):
496501 iv = Period (parsed , freq = grp .value )
497502 return (iv .asfreq (self .freq , how = "start" ), iv .asfreq (self .freq , how = "end" ))
498503
499- def _validate_partial_date_slice (self , reso : Resolution ):
504+ def _can_partial_date_slice (self , reso : Resolution ) -> bool :
500505 assert isinstance (reso , Resolution ), (type (reso ), reso )
501506 grp = reso .freq_group
502507 freqn = self .dtype .freq_group_code
@@ -505,7 +510,9 @@ def _validate_partial_date_slice(self, reso: Resolution):
505510 # TODO: we used to also check for
506511 # reso in ["day", "hour", "minute", "second"]
507512 # why is that check not needed?
508- raise ValueError
513+ return False
514+
515+ return True
509516
510517
511518def period_range (
0 commit comments