factory.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. """
  2. Implements the :class:`ArrowFactory <arrow.factory.ArrowFactory>` class,
  3. providing factory methods for common :class:`Arrow <arrow.arrow.Arrow>`
  4. construction scenarios.
  5. """
  6. import calendar
  7. from datetime import date, datetime, timezone
  8. from datetime import tzinfo as dt_tzinfo
  9. from decimal import Decimal
  10. from time import struct_time
  11. from typing import Any, List, Optional, Tuple, Type, Union, overload
  12. from arrow import parser
  13. from arrow.arrow import TZ_EXPR, Arrow
  14. from arrow.constants import DEFAULT_LOCALE
  15. from arrow.util import is_timestamp, iso_to_gregorian
  16. class ArrowFactory:
  17. """A factory for generating :class:`Arrow <arrow.arrow.Arrow>` objects.
  18. :param type: (optional) the :class:`Arrow <arrow.arrow.Arrow>`-based class to construct from.
  19. Defaults to :class:`Arrow <arrow.arrow.Arrow>`.
  20. """
  21. type: Type[Arrow]
  22. def __init__(self, type: Type[Arrow] = Arrow) -> None:
  23. self.type = type
  24. @overload
  25. def get(
  26. self,
  27. *,
  28. locale: str = DEFAULT_LOCALE,
  29. tzinfo: Optional[TZ_EXPR] = None,
  30. normalize_whitespace: bool = False,
  31. ) -> Arrow: ... # pragma: no cover
  32. @overload
  33. def get(
  34. self,
  35. __obj: Union[
  36. Arrow,
  37. datetime,
  38. date,
  39. struct_time,
  40. dt_tzinfo,
  41. int,
  42. float,
  43. str,
  44. Tuple[int, int, int],
  45. ],
  46. *,
  47. locale: str = DEFAULT_LOCALE,
  48. tzinfo: Optional[TZ_EXPR] = None,
  49. normalize_whitespace: bool = False,
  50. ) -> Arrow: ... # pragma: no cover
  51. @overload
  52. def get(
  53. self,
  54. __arg1: Union[datetime, date],
  55. __arg2: TZ_EXPR,
  56. *,
  57. locale: str = DEFAULT_LOCALE,
  58. tzinfo: Optional[TZ_EXPR] = None,
  59. normalize_whitespace: bool = False,
  60. ) -> Arrow: ... # pragma: no cover
  61. @overload
  62. def get(
  63. self,
  64. __arg1: str,
  65. __arg2: Union[str, List[str]],
  66. *,
  67. locale: str = DEFAULT_LOCALE,
  68. tzinfo: Optional[TZ_EXPR] = None,
  69. normalize_whitespace: bool = False,
  70. ) -> Arrow: ... # pragma: no cover
  71. def get(self, *args: Any, **kwargs: Any) -> Arrow:
  72. """Returns an :class:`Arrow <arrow.arrow.Arrow>` object based on flexible inputs.
  73. :param locale: (optional) a ``str`` specifying a locale for the parser. Defaults to 'en-us'.
  74. :param tzinfo: (optional) a :ref:`timezone expression <tz-expr>` or tzinfo object.
  75. Replaces the timezone unless using an input form that is explicitly UTC or specifies
  76. the timezone in a positional argument. Defaults to UTC.
  77. :param normalize_whitespace: (optional) a ``bool`` specifying whether or not to normalize
  78. redundant whitespace (spaces, tabs, and newlines) in a datetime string before parsing.
  79. Defaults to false.
  80. Usage::
  81. >>> import arrow
  82. **No inputs** to get current UTC time::
  83. >>> arrow.get()
  84. <Arrow [2013-05-08T05:51:43.316458+00:00]>
  85. **One** :class:`Arrow <arrow.arrow.Arrow>` object, to get a copy.
  86. >>> arw = arrow.utcnow()
  87. >>> arrow.get(arw)
  88. <Arrow [2013-10-23T15:21:54.354846+00:00]>
  89. **One** ``float`` or ``int``, convertible to a floating-point timestamp, to get
  90. that timestamp in UTC::
  91. >>> arrow.get(1367992474.293378)
  92. <Arrow [2013-05-08T05:54:34.293378+00:00]>
  93. >>> arrow.get(1367992474)
  94. <Arrow [2013-05-08T05:54:34+00:00]>
  95. **One** ISO 8601-formatted ``str``, to parse it::
  96. >>> arrow.get('2013-09-29T01:26:43.830580')
  97. <Arrow [2013-09-29T01:26:43.830580+00:00]>
  98. **One** ISO 8601-formatted ``str``, in basic format, to parse it::
  99. >>> arrow.get('20160413T133656.456289')
  100. <Arrow [2016-04-13T13:36:56.456289+00:00]>
  101. **One** ``tzinfo``, to get the current time **converted** to that timezone::
  102. >>> arrow.get(tz.tzlocal())
  103. <Arrow [2013-05-07T22:57:28.484717-07:00]>
  104. **One** naive ``datetime``, to get that datetime in UTC::
  105. >>> arrow.get(datetime(2013, 5, 5))
  106. <Arrow [2013-05-05T00:00:00+00:00]>
  107. **One** aware ``datetime``, to get that datetime::
  108. >>> arrow.get(datetime(2013, 5, 5, tzinfo=tz.tzlocal()))
  109. <Arrow [2013-05-05T00:00:00-07:00]>
  110. **One** naive ``date``, to get that date in UTC::
  111. >>> arrow.get(date(2013, 5, 5))
  112. <Arrow [2013-05-05T00:00:00+00:00]>
  113. **One** time.struct time::
  114. >>> arrow.get(gmtime(0))
  115. <Arrow [1970-01-01T00:00:00+00:00]>
  116. **One** iso calendar ``tuple``, to get that week date in UTC::
  117. >>> arrow.get((2013, 18, 7))
  118. <Arrow [2013-05-05T00:00:00+00:00]>
  119. **Two** arguments, a naive or aware ``datetime``, and a replacement
  120. :ref:`timezone expression <tz-expr>`::
  121. >>> arrow.get(datetime(2013, 5, 5), 'US/Pacific')
  122. <Arrow [2013-05-05T00:00:00-07:00]>
  123. **Two** arguments, a naive ``date``, and a replacement
  124. :ref:`timezone expression <tz-expr>`::
  125. >>> arrow.get(date(2013, 5, 5), 'US/Pacific')
  126. <Arrow [2013-05-05T00:00:00-07:00]>
  127. **Two** arguments, both ``str``, to parse the first according to the format of the second::
  128. >>> arrow.get('2013-05-05 12:30:45 America/Chicago', 'YYYY-MM-DD HH:mm:ss ZZZ')
  129. <Arrow [2013-05-05T12:30:45-05:00]>
  130. **Two** arguments, first a ``str`` to parse and second a ``list`` of formats to try::
  131. >>> arrow.get('2013-05-05 12:30:45', ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss'])
  132. <Arrow [2013-05-05T12:30:45+00:00]>
  133. **Three or more** arguments, as for the direct constructor of an ``Arrow`` object::
  134. >>> arrow.get(2013, 5, 5, 12, 30, 45)
  135. <Arrow [2013-05-05T12:30:45+00:00]>
  136. """
  137. arg_count = len(args)
  138. locale = kwargs.pop("locale", DEFAULT_LOCALE)
  139. tz = kwargs.get("tzinfo", None)
  140. normalize_whitespace = kwargs.pop("normalize_whitespace", False)
  141. # if kwargs given, send to constructor unless only tzinfo provided
  142. if len(kwargs) > 1:
  143. arg_count = 3
  144. # tzinfo kwarg is not provided
  145. if len(kwargs) == 1 and tz is None:
  146. arg_count = 3
  147. # () -> now, @ tzinfo or utc
  148. if arg_count == 0:
  149. if isinstance(tz, str):
  150. tz = parser.TzinfoParser.parse(tz)
  151. return self.type.now(tzinfo=tz)
  152. if isinstance(tz, dt_tzinfo):
  153. return self.type.now(tzinfo=tz)
  154. return self.type.utcnow()
  155. if arg_count == 1:
  156. arg = args[0]
  157. if isinstance(arg, Decimal):
  158. arg = float(arg)
  159. # (None) -> raises an exception
  160. if arg is None:
  161. raise TypeError("Cannot parse argument of type None.")
  162. # try (int, float) -> from timestamp @ tzinfo
  163. elif not isinstance(arg, str) and is_timestamp(arg):
  164. if tz is None:
  165. # set to UTC by default
  166. tz = timezone.utc
  167. return self.type.fromtimestamp(arg, tzinfo=tz)
  168. # (Arrow) -> from the object's datetime @ tzinfo
  169. elif isinstance(arg, Arrow):
  170. return self.type.fromdatetime(arg.datetime, tzinfo=tz)
  171. # (datetime) -> from datetime @ tzinfo
  172. elif isinstance(arg, datetime):
  173. return self.type.fromdatetime(arg, tzinfo=tz)
  174. # (date) -> from date @ tzinfo
  175. elif isinstance(arg, date):
  176. return self.type.fromdate(arg, tzinfo=tz)
  177. # (tzinfo) -> now @ tzinfo
  178. elif isinstance(arg, dt_tzinfo):
  179. return self.type.now(tzinfo=arg)
  180. # (str) -> parse @ tzinfo
  181. elif isinstance(arg, str):
  182. dt = parser.DateTimeParser(locale).parse_iso(arg, normalize_whitespace)
  183. return self.type.fromdatetime(dt, tzinfo=tz)
  184. # (struct_time) -> from struct_time
  185. elif isinstance(arg, struct_time):
  186. return self.type.utcfromtimestamp(calendar.timegm(arg))
  187. # (iso calendar) -> convert then from date @ tzinfo
  188. elif isinstance(arg, tuple) and len(arg) == 3:
  189. d = iso_to_gregorian(*arg)
  190. return self.type.fromdate(d, tzinfo=tz)
  191. else:
  192. raise TypeError(f"Cannot parse single argument of type {type(arg)!r}.")
  193. elif arg_count == 2:
  194. arg_1, arg_2 = args[0], args[1]
  195. if isinstance(arg_1, datetime):
  196. # (datetime, tzinfo/str) -> fromdatetime @ tzinfo
  197. if isinstance(arg_2, (dt_tzinfo, str)):
  198. return self.type.fromdatetime(arg_1, tzinfo=arg_2)
  199. else:
  200. raise TypeError(
  201. f"Cannot parse two arguments of types 'datetime', {type(arg_2)!r}."
  202. )
  203. elif isinstance(arg_1, date):
  204. # (date, tzinfo/str) -> fromdate @ tzinfo
  205. if isinstance(arg_2, (dt_tzinfo, str)):
  206. return self.type.fromdate(arg_1, tzinfo=arg_2)
  207. else:
  208. raise TypeError(
  209. f"Cannot parse two arguments of types 'date', {type(arg_2)!r}."
  210. )
  211. # (str, format) -> parse @ tzinfo
  212. elif isinstance(arg_1, str) and isinstance(arg_2, (str, list)):
  213. dt = parser.DateTimeParser(locale).parse(
  214. args[0], args[1], normalize_whitespace
  215. )
  216. return self.type.fromdatetime(dt, tzinfo=tz)
  217. else:
  218. raise TypeError(
  219. f"Cannot parse two arguments of types {type(arg_1)!r} and {type(arg_2)!r}."
  220. )
  221. # 3+ args -> datetime-like via constructor
  222. else:
  223. return self.type(*args, **kwargs)
  224. def utcnow(self) -> Arrow:
  225. """Returns an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in UTC time.
  226. Usage::
  227. >>> import arrow
  228. >>> arrow.utcnow()
  229. <Arrow [2013-05-08T05:19:07.018993+00:00]>
  230. """
  231. return self.type.utcnow()
  232. def now(self, tz: Optional[TZ_EXPR] = None) -> Arrow:
  233. """Returns an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in the given
  234. timezone.
  235. :param tz: (optional) A :ref:`timezone expression <tz-expr>`. Defaults to local time.
  236. Usage::
  237. >>> import arrow
  238. >>> arrow.now()
  239. <Arrow [2013-05-07T22:19:11.363410-07:00]>
  240. >>> arrow.now('US/Pacific')
  241. <Arrow [2013-05-07T22:19:15.251821-07:00]>
  242. >>> arrow.now('+02:00')
  243. <Arrow [2013-05-08T07:19:25.618646+02:00]>
  244. >>> arrow.now('local')
  245. <Arrow [2013-05-07T22:19:39.130059-07:00]>
  246. """
  247. if tz is None:
  248. tz = datetime.now().astimezone().tzinfo
  249. elif not isinstance(tz, dt_tzinfo):
  250. tz = parser.TzinfoParser.parse(tz)
  251. return self.type.now(tz)