diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index aa7e6801ba431..fcdb89dd8a334 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -685,7 +685,7 @@ def __init__(self, unit="ns", tz=None): tz = timezones.tz_standardize(tz) elif tz is not None: raise pytz.UnknownTimeZoneError(tz) - elif tz is None: + if tz is None: raise TypeError("A 'tz' is required.") self._unit = unit @@ -737,14 +737,17 @@ def construct_from_string(cls, string): """ if isinstance(string, str): msg = "Could not construct DatetimeTZDtype from '{}'" - try: - match = cls._match.match(string) - if match: - d = match.groupdict() + match = cls._match.match(string) + if match: + d = match.groupdict() + try: return cls(unit=d["unit"], tz=d["tz"]) - except Exception: - # TODO(py3): Change this pass to `raise TypeError(msg) from e` - pass + except (KeyError, TypeError, ValueError) as err: + # KeyError if maybe_get_tz tries and fails to get a + # pytz timezone (actually pytz.UnknownTimeZoneError). + # TypeError if we pass a nonsense tz; + # ValueError if we pass a unit other than "ns" + raise TypeError(msg.format(string)) from err raise TypeError(msg.format(string)) raise TypeError("Could not construct DatetimeTZDtype") diff --git a/pandas/tests/dtypes/test_dtypes.py b/pandas/tests/dtypes/test_dtypes.py index 3288c9c584565..6013e58bc6929 100644 --- a/pandas/tests/dtypes/test_dtypes.py +++ b/pandas/tests/dtypes/test_dtypes.py @@ -248,9 +248,19 @@ def test_construct_from_string_raises(self): with pytest.raises(TypeError, match="notatz"): DatetimeTZDtype.construct_from_string("datetime64[ns, notatz]") - with pytest.raises(TypeError, match="^Could not construct DatetimeTZDtype$"): + msg = "^Could not construct DatetimeTZDtype" + with pytest.raises(TypeError, match=msg): + # list instead of string DatetimeTZDtype.construct_from_string(["datetime64[ns, notatz]"]) + with pytest.raises(TypeError, match=msg): + # non-nano unit + DatetimeTZDtype.construct_from_string("datetime64[ps, UTC]") + + with pytest.raises(TypeError, match=msg): + # dateutil str that returns None from gettz + DatetimeTZDtype.construct_from_string("datetime64[ns, dateutil/invalid]") + def test_is_dtype(self): assert not DatetimeTZDtype.is_dtype(None) assert DatetimeTZDtype.is_dtype(self.dtype)