|
32 | 32 | from pandas.core.ops import get_op_result_name |
33 | 33 | import pandas.core.tools.datetimes as tools |
34 | 34 |
|
35 | | -from pandas.tseries import offsets |
36 | 35 | from pandas.tseries.frequencies import Resolution, to_offset |
37 | | -from pandas.tseries.offsets import CDay, prefix_mapping |
| 36 | +from pandas.tseries.offsets import CDay, Nano, prefix_mapping |
38 | 37 |
|
39 | 38 |
|
40 | 39 | def _new_DatetimeIndex(cls, d): |
@@ -852,54 +851,57 @@ def _parsed_string_to_bounds(self, reso, parsed): |
852 | 851 | lower, upper: pd.Timestamp |
853 | 852 |
|
854 | 853 | """ |
| 854 | + valid_resos = {'year', 'month', 'quarter', 'day', 'hour', 'minute', |
| 855 | + 'second', 'minute', 'second', 'microsecond'} |
| 856 | + if reso not in valid_resos: |
| 857 | + raise KeyError |
855 | 858 | if reso == 'year': |
856 | | - return (Timestamp(datetime(parsed.year, 1, 1), tz=self.tz), |
857 | | - Timestamp(datetime(parsed.year, 12, 31, 23, |
858 | | - 59, 59, 999999), tz=self.tz)) |
| 859 | + start = Timestamp(parsed.year, 1, 1) |
| 860 | + end = Timestamp(parsed.year, 12, 31, 23, 59, 59, 999999) |
859 | 861 | elif reso == 'month': |
860 | 862 | d = ccalendar.get_days_in_month(parsed.year, parsed.month) |
861 | | - return (Timestamp(datetime(parsed.year, parsed.month, 1), |
862 | | - tz=self.tz), |
863 | | - Timestamp(datetime(parsed.year, parsed.month, d, 23, |
864 | | - 59, 59, 999999), tz=self.tz)) |
| 863 | + start = Timestamp(parsed.year, parsed.month, 1) |
| 864 | + end = Timestamp(parsed.year, parsed.month, d, 23, 59, 59, 999999) |
865 | 865 | elif reso == 'quarter': |
866 | 866 | qe = (((parsed.month - 1) + 2) % 12) + 1 # two months ahead |
867 | 867 | d = ccalendar.get_days_in_month(parsed.year, qe) # at end of month |
868 | | - return (Timestamp(datetime(parsed.year, parsed.month, 1), |
869 | | - tz=self.tz), |
870 | | - Timestamp(datetime(parsed.year, qe, d, 23, 59, |
871 | | - 59, 999999), tz=self.tz)) |
| 868 | + start = Timestamp(parsed.year, parsed.month, 1) |
| 869 | + end = Timestamp(parsed.year, qe, d, 23, 59, 59, 999999) |
872 | 870 | elif reso == 'day': |
873 | | - st = datetime(parsed.year, parsed.month, parsed.day) |
874 | | - return (Timestamp(st, tz=self.tz), |
875 | | - Timestamp(Timestamp(st + offsets.Day(), |
876 | | - tz=self.tz).value - 1)) |
| 871 | + start = Timestamp(parsed.year, parsed.month, parsed.day) |
| 872 | + end = start + timedelta(days=1) - Nano(1) |
877 | 873 | elif reso == 'hour': |
878 | | - st = datetime(parsed.year, parsed.month, parsed.day, |
879 | | - hour=parsed.hour) |
880 | | - return (Timestamp(st, tz=self.tz), |
881 | | - Timestamp(Timestamp(st + offsets.Hour(), |
882 | | - tz=self.tz).value - 1)) |
| 874 | + start = Timestamp(parsed.year, parsed.month, parsed.day, |
| 875 | + parsed.hour) |
| 876 | + end = start + timedelta(hours=1) - Nano(1) |
883 | 877 | elif reso == 'minute': |
884 | | - st = datetime(parsed.year, parsed.month, parsed.day, |
885 | | - hour=parsed.hour, minute=parsed.minute) |
886 | | - return (Timestamp(st, tz=self.tz), |
887 | | - Timestamp(Timestamp(st + offsets.Minute(), |
888 | | - tz=self.tz).value - 1)) |
| 878 | + start = Timestamp(parsed.year, parsed.month, parsed.day, |
| 879 | + parsed.hour, parsed.minute) |
| 880 | + end = start + timedelta(minutes=1) - Nano(1) |
889 | 881 | elif reso == 'second': |
890 | | - st = datetime(parsed.year, parsed.month, parsed.day, |
891 | | - hour=parsed.hour, minute=parsed.minute, |
892 | | - second=parsed.second) |
893 | | - return (Timestamp(st, tz=self.tz), |
894 | | - Timestamp(Timestamp(st + offsets.Second(), |
895 | | - tz=self.tz).value - 1)) |
| 882 | + start = Timestamp(parsed.year, parsed.month, parsed.day, |
| 883 | + parsed.hour, parsed.minute, parsed.second) |
| 884 | + end = start + timedelta(seconds=1) - Nano(1) |
896 | 885 | elif reso == 'microsecond': |
897 | | - st = datetime(parsed.year, parsed.month, parsed.day, |
898 | | - parsed.hour, parsed.minute, parsed.second, |
899 | | - parsed.microsecond) |
900 | | - return (Timestamp(st, tz=self.tz), Timestamp(st, tz=self.tz)) |
901 | | - else: |
902 | | - raise KeyError |
| 886 | + start = Timestamp(parsed.year, parsed.month, parsed.day, |
| 887 | + parsed.hour, parsed.minute, parsed.second, |
| 888 | + parsed.microsecond) |
| 889 | + end = start + timedelta(microseconds=1) - Nano(1) |
| 890 | + # GH 24076 |
| 891 | + # If an incoming date string contained a UTC offset, need to localize |
| 892 | + # the parsed date to this offset first before aligning with the index's |
| 893 | + # timezone |
| 894 | + if parsed.tzinfo is not None: |
| 895 | + if self.tz is None: |
| 896 | + raise ValueError("The index must be timezone aware " |
| 897 | + "when indexing with a date string with a " |
| 898 | + "UTC offset") |
| 899 | + start = start.tz_localize(parsed.tzinfo).tz_convert(self.tz) |
| 900 | + end = end.tz_localize(parsed.tzinfo).tz_convert(self.tz) |
| 901 | + elif self.tz is not None: |
| 902 | + start = start.tz_localize(self.tz) |
| 903 | + end = end.tz_localize(self.tz) |
| 904 | + return start, end |
903 | 905 |
|
904 | 906 | def _partial_date_slice(self, reso, parsed, use_lhs=True, use_rhs=True): |
905 | 907 | is_monotonic = self.is_monotonic |
|
0 commit comments