|
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