""" Implements the :class:`ArrowFactory ` class, providing factory methods for common :class:`Arrow ` construction scenarios. """ import calendar from datetime import date, datetime from datetime import tzinfo as dt_tzinfo from decimal import Decimal from time import struct_time from typing import Any, List, Optional, Tuple, Type, Union, overload from dateutil import tz as dateutil_tz from arrow import parser from arrow.arrow import TZ_EXPR, Arrow from arrow.constants import DEFAULT_LOCALE from arrow.util import is_timestamp, iso_to_gregorian class ArrowFactory: """A factory for generating :class:`Arrow ` objects. :param type: (optional) the :class:`Arrow `-based class to construct from. Defaults to :class:`Arrow `. """ type: Type[Arrow] def __init__(self, type: Type[Arrow] = Arrow) -> None: self.type = type @overload def get( self, *, locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: ... # pragma: no cover @overload def get( self, __obj: Union[ Arrow, datetime, date, struct_time, dt_tzinfo, int, float, str, Tuple[int, int, int], ], *, locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: ... # pragma: no cover @overload def get( self, __arg1: Union[datetime, date], __arg2: TZ_EXPR, *, locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: ... # pragma: no cover @overload def get( self, __arg1: str, __arg2: Union[str, List[str]], *, locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: ... # pragma: no cover def get(self, *args: Any, **kwargs: Any) -> Arrow: """Returns an :class:`Arrow ` object based on flexible inputs. :param locale: (optional) a ``str`` specifying a locale for the parser. Defaults to 'en-us'. :param tzinfo: (optional) a :ref:`timezone expression ` or tzinfo object. Replaces the timezone unless using an input form that is explicitly UTC or specifies the timezone in a positional argument. Defaults to UTC. :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. Usage:: >>> import arrow **No inputs** to get current UTC time:: >>> arrow.get() **One** :class:`Arrow ` object, to get a copy. >>> arw = arrow.utcnow() >>> arrow.get(arw) **One** ``float`` or ``int``, convertible to a floating-point timestamp, to get that timestamp in UTC:: >>> arrow.get(1367992474.293378) >>> arrow.get(1367992474) **One** ISO 8601-formatted ``str``, to parse it:: >>> arrow.get('2013-09-29T01:26:43.830580') **One** ISO 8601-formatted ``str``, in basic format, to parse it:: >>> arrow.get('20160413T133656.456289') **One** ``tzinfo``, to get the current time **converted** to that timezone:: >>> arrow.get(tz.tzlocal()) **One** naive ``datetime``, to get that datetime in UTC:: >>> arrow.get(datetime(2013, 5, 5)) **One** aware ``datetime``, to get that datetime:: >>> arrow.get(datetime(2013, 5, 5, tzinfo=tz.tzlocal())) **One** naive ``date``, to get that date in UTC:: >>> arrow.get(date(2013, 5, 5)) **One** time.struct time:: >>> arrow.get(gmtime(0)) **One** iso calendar ``tuple``, to get that week date in UTC:: >>> arrow.get((2013, 18, 7)) **Two** arguments, a naive or aware ``datetime``, and a replacement :ref:`timezone expression `:: >>> arrow.get(datetime(2013, 5, 5), 'US/Pacific') **Two** arguments, a naive ``date``, and a replacement :ref:`timezone expression `:: >>> arrow.get(date(2013, 5, 5), 'US/Pacific') **Two** arguments, both ``str``, to parse the first according to the format of the second:: >>> arrow.get('2013-05-05 12:30:45 America/Chicago', 'YYYY-MM-DD HH:mm:ss ZZZ') **Two** arguments, first a ``str`` to parse and second a ``list`` of formats to try:: >>> arrow.get('2013-05-05 12:30:45', ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss']) **Three or more** arguments, as for the direct constructor of an ``Arrow`` object:: >>> arrow.get(2013, 5, 5, 12, 30, 45) """ arg_count = len(args) locale = kwargs.pop("locale", DEFAULT_LOCALE) tz = kwargs.get("tzinfo", None) normalize_whitespace = kwargs.pop("normalize_whitespace", False) # if kwargs given, send to constructor unless only tzinfo provided if len(kwargs) > 1: arg_count = 3 # tzinfo kwarg is not provided if len(kwargs) == 1 and tz is None: arg_count = 3 # () -> now, @ tzinfo or utc if arg_count == 0: if isinstance(tz, str): tz = parser.TzinfoParser.parse(tz) return self.type.now(tzinfo=tz) if isinstance(tz, dt_tzinfo): return self.type.now(tzinfo=tz) return self.type.utcnow() if arg_count == 1: arg = args[0] if isinstance(arg, Decimal): arg = float(arg) # (None) -> raises an exception if arg is None: raise TypeError("Cannot parse argument of type None.") # try (int, float) -> from timestamp @ tzinfo elif not isinstance(arg, str) and is_timestamp(arg): if tz is None: # set to UTC by default tz = dateutil_tz.tzutc() return self.type.fromtimestamp(arg, tzinfo=tz) # (Arrow) -> from the object's datetime @ tzinfo elif isinstance(arg, Arrow): return self.type.fromdatetime(arg.datetime, tzinfo=tz) # (datetime) -> from datetime @ tzinfo elif isinstance(arg, datetime): return self.type.fromdatetime(arg, tzinfo=tz) # (date) -> from date @ tzinfo elif isinstance(arg, date): return self.type.fromdate(arg, tzinfo=tz) # (tzinfo) -> now @ tzinfo elif isinstance(arg, dt_tzinfo): return self.type.now(tzinfo=arg) # (str) -> parse @ tzinfo elif isinstance(arg, str): dt = parser.DateTimeParser(locale).parse_iso(arg, normalize_whitespace) return self.type.fromdatetime(dt, tzinfo=tz) # (struct_time) -> from struct_time elif isinstance(arg, struct_time): return self.type.utcfromtimestamp(calendar.timegm(arg)) # (iso calendar) -> convert then from date @ tzinfo elif isinstance(arg, tuple) and len(arg) == 3: d = iso_to_gregorian(*arg) return self.type.fromdate(d, tzinfo=tz) else: raise TypeError(f"Cannot parse single argument of type {type(arg)!r}.") elif arg_count == 2: arg_1, arg_2 = args[0], args[1] if isinstance(arg_1, datetime): # (datetime, tzinfo/str) -> fromdatetime @ tzinfo if isinstance(arg_2, (dt_tzinfo, str)): return self.type.fromdatetime(arg_1, tzinfo=arg_2) else: raise TypeError( f"Cannot parse two arguments of types 'datetime', {type(arg_2)!r}." ) elif isinstance(arg_1, date): # (date, tzinfo/str) -> fromdate @ tzinfo if isinstance(arg_2, (dt_tzinfo, str)): return self.type.fromdate(arg_1, tzinfo=arg_2) else: raise TypeError( f"Cannot parse two arguments of types 'date', {type(arg_2)!r}." ) # (str, format) -> parse @ tzinfo elif isinstance(arg_1, str) and isinstance(arg_2, (str, list)): dt = parser.DateTimeParser(locale).parse( args[0], args[1], normalize_whitespace ) return self.type.fromdatetime(dt, tzinfo=tz) else: raise TypeError( f"Cannot parse two arguments of types {type(arg_1)!r} and {type(arg_2)!r}." ) # 3+ args -> datetime-like via constructor else: return self.type(*args, **kwargs) def utcnow(self) -> Arrow: """Returns an :class:`Arrow ` object, representing "now" in UTC time. Usage:: >>> import arrow >>> arrow.utcnow() """ return self.type.utcnow() def now(self, tz: Optional[TZ_EXPR] = None) -> Arrow: """Returns an :class:`Arrow ` object, representing "now" in the given timezone. :param tz: (optional) A :ref:`timezone expression `. Defaults to local time. Usage:: >>> import arrow >>> arrow.now() >>> arrow.now('US/Pacific') >>> arrow.now('+02:00') >>> arrow.now('local') """ if tz is None: tz = dateutil_tz.tzlocal() elif not isinstance(tz, dt_tzinfo): tz = parser.TzinfoParser.parse(tz) return self.type.now(tz)