diff --git a/arrow/factory.py b/arrow/factory.py index 0913bfe1..cbe6305a 100644 --- a/arrow/factory.py +++ b/arrow/factory.py @@ -38,6 +38,7 @@ def get( locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, + fold: Optional[int] = None, ) -> Arrow: ... # pragma: no cover @overload @@ -58,6 +59,7 @@ def get( locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, + fold: Optional[int] = None, ) -> Arrow: ... # pragma: no cover @overload @@ -69,6 +71,7 @@ def get( locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, + fold: Optional[int] = None, ) -> Arrow: ... # pragma: no cover @overload @@ -80,6 +83,7 @@ def get( locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, + fold: Optional[int] = None, ) -> Arrow: ... # pragma: no cover def get(self, *args: Any, **kwargs: Any) -> Arrow: @@ -92,6 +96,8 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: :param normalize_whitespace: (optional) a ``bool`` specifying whether or not to normalize redundant whitespace (spaces, tabs, and newlines) in a datetime string before parsing. Defaults to false. + :param fold: (optional) an ``int`` (0 or 1) to disambiguate repeated wall times during + DST transitions. Defaults to None (uses the parsed value or 0). Usage:: @@ -190,6 +196,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: locale = kwargs.pop("locale", DEFAULT_LOCALE) tz = kwargs.get("tzinfo", None) normalize_whitespace = kwargs.pop("normalize_whitespace", False) + fold = kwargs.pop("fold", None) # if kwargs given, send to constructor unless only tzinfo provided if len(kwargs) > 1: @@ -245,6 +252,8 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: # (str) -> parse @ tzinfo elif isinstance(arg, str): dt = parser.DateTimeParser(locale).parse_iso(arg, normalize_whitespace) + if fold is not None: + dt = dt.replace(fold=fold) return self.type.fromdatetime(dt, tzinfo=tz) # (struct_time) -> from struct_time @@ -285,6 +294,8 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: dt = parser.DateTimeParser(locale).parse( args[0], args[1], normalize_whitespace ) + if fold is not None: + dt = dt.replace(fold=fold) return self.type.fromdatetime(dt, tzinfo=tz) else: diff --git a/tests/test_factory.py b/tests/test_factory.py index 056cee41..e0abee1e 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -371,6 +371,52 @@ def test_locale_with_tzinfo(self): res = self.factory.get(locale="ja", tzinfo=ZoneInfo("Asia/Tokyo")) assert res.tzinfo == ZoneInfo("Asia/Tokyo") + def test_fold_kwarg_iso_string(self): + # Test fold=0 (first occurrence during DST transition) + result = self.factory.get( + "2001-10-28T01:30:00", + tzinfo=ZoneInfo("US/Pacific"), + fold=0, + ) + assert result._datetime.fold == 0 + assert result._datetime == datetime( + 2001, 10, 28, 1, 30, 0, tzinfo=ZoneInfo("US/Pacific") + ) + + # Test fold=1 (second occurrence during DST transition) + result = self.factory.get( + "2001-10-28T01:30:00", + tzinfo=ZoneInfo("US/Pacific"), + fold=1, + ) + assert result._datetime.fold == 1 + + def test_fold_kwarg_format_string(self): + # Test fold with explicit format string + result = self.factory.get( + "2001-10-28 01:30:00", + "YYYY-MM-DD HH:mm:ss", + tzinfo=ZoneInfo("US/Pacific"), + fold=0, + ) + assert result._datetime.fold == 0 + + result = self.factory.get( + "2001-10-28 01:30:00", + "YYYY-MM-DD HH:mm:ss", + tzinfo=ZoneInfo("US/Pacific"), + fold=1, + ) + assert result._datetime.fold == 1 + + def test_fold_kwarg_default(self): + # When fold is not specified, it should default to 0 (parser default) + result = self.factory.get( + "2001-10-28T01:30:00", + tzinfo=ZoneInfo("US/Pacific"), + ) + assert result._datetime.fold == 0 + @pytest.mark.usefixtures("arrow_factory") class TestUtcNow: