factory.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. # -*- coding: utf-8 -*-
  2. """
  3. Implements the :class:`ArrowFactory <arrow.factory.ArrowFactory>` class,
  4. providing factory methods for common :class:`Arrow <arrow.arrow.Arrow>`
  5. construction scenarios.
  6. """
  7. from __future__ import absolute_import
  8. import calendar
  9. from datetime import date, datetime
  10. from datetime import tzinfo as dt_tzinfo
  11. from time import struct_time
  12. from dateutil import tz as dateutil_tz
  13. from arrow import parser
  14. from arrow.arrow import Arrow
  15. from arrow.util import is_timestamp, iso_to_gregorian, isstr
  16. class ArrowFactory(object):
  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. def __init__(self, type=Arrow):
  22. self.type = type
  23. def get(self, *args, **kwargs):
  24. """ Returns an :class:`Arrow <arrow.arrow.Arrow>` object based on flexible inputs.
  25. :param locale: (optional) a ``str`` specifying a locale for the parser. Defaults to
  26. 'en_us'.
  27. :param tzinfo: (optional) a :ref:`timezone expression <tz-expr>` or tzinfo object.
  28. Replaces the timezone unless using an input form that is explicitly UTC or specifies
  29. the timezone in a positional argument. Defaults to UTC.
  30. Usage::
  31. >>> import arrow
  32. **No inputs** to get current UTC time::
  33. >>> arrow.get()
  34. <Arrow [2013-05-08T05:51:43.316458+00:00]>
  35. **None** to also get current UTC time::
  36. >>> arrow.get(None)
  37. <Arrow [2013-05-08T05:51:49.016458+00:00]>
  38. **One** :class:`Arrow <arrow.arrow.Arrow>` object, to get a copy.
  39. >>> arw = arrow.utcnow()
  40. >>> arrow.get(arw)
  41. <Arrow [2013-10-23T15:21:54.354846+00:00]>
  42. **One** ``float`` or ``int``, convertible to a floating-point timestamp, to get
  43. that timestamp in UTC::
  44. >>> arrow.get(1367992474.293378)
  45. <Arrow [2013-05-08T05:54:34.293378+00:00]>
  46. >>> arrow.get(1367992474)
  47. <Arrow [2013-05-08T05:54:34+00:00]>
  48. **One** ISO 8601-formatted ``str``, to parse it::
  49. >>> arrow.get('2013-09-29T01:26:43.830580')
  50. <Arrow [2013-09-29T01:26:43.830580+00:00]>
  51. **One** ISO 8601-formatted ``str``, in basic format, to parse it::
  52. >>> arrow.get('20160413T133656.456289')
  53. <Arrow [2016-04-13T13:36:56.456289+00:00]>
  54. **One** ``tzinfo``, to get the current time **converted** to that timezone::
  55. >>> arrow.get(tz.tzlocal())
  56. <Arrow [2013-05-07T22:57:28.484717-07:00]>
  57. **One** naive ``datetime``, to get that datetime in UTC::
  58. >>> arrow.get(datetime(2013, 5, 5))
  59. <Arrow [2013-05-05T00:00:00+00:00]>
  60. **One** aware ``datetime``, to get that datetime::
  61. >>> arrow.get(datetime(2013, 5, 5, tzinfo=tz.tzlocal()))
  62. <Arrow [2013-05-05T00:00:00-07:00]>
  63. **One** naive ``date``, to get that date in UTC::
  64. >>> arrow.get(date(2013, 5, 5))
  65. <Arrow [2013-05-05T00:00:00+00:00]>
  66. **One** time.struct time::
  67. >>> arrow.get(gmtime(0))
  68. <Arrow [1970-01-01T00:00:00+00:00]>
  69. **One** iso calendar ``tuple``, to get that week date in UTC::
  70. >>> arrow.get((2013, 18, 7))
  71. <Arrow [2013-05-05T00:00:00+00:00]>
  72. **Two** arguments, a naive or aware ``datetime``, and a replacement
  73. :ref:`timezone expression <tz-expr>`::
  74. >>> arrow.get(datetime(2013, 5, 5), 'US/Pacific')
  75. <Arrow [2013-05-05T00:00:00-07:00]>
  76. **Two** arguments, a naive ``date``, and a replacement
  77. :ref:`timezone expression <tz-expr>`::
  78. >>> arrow.get(date(2013, 5, 5), 'US/Pacific')
  79. <Arrow [2013-05-05T00:00:00-07:00]>
  80. **Two** arguments, both ``str``, to parse the first according to the format of the second::
  81. >>> arrow.get('2013-05-05 12:30:45 America/Chicago', 'YYYY-MM-DD HH:mm:ss ZZZ')
  82. <Arrow [2013-05-05T12:30:45-05:00]>
  83. **Two** arguments, first a ``str`` to parse and second a ``list`` of formats to try::
  84. >>> arrow.get('2013-05-05 12:30:45', ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss'])
  85. <Arrow [2013-05-05T12:30:45+00:00]>
  86. **Three or more** arguments, as for the constructor of a ``datetime``::
  87. >>> arrow.get(2013, 5, 5, 12, 30, 45)
  88. <Arrow [2013-05-05T12:30:45+00:00]>
  89. """
  90. arg_count = len(args)
  91. locale = kwargs.pop("locale", "en_us")
  92. tz = kwargs.get("tzinfo", None)
  93. # if kwargs given, send to constructor unless only tzinfo provided
  94. if len(kwargs) > 1:
  95. arg_count = 3
  96. # tzinfo kwarg is not provided
  97. if len(kwargs) == 1 and tz is None:
  98. arg_count = 3
  99. # () -> now, @ utc.
  100. if arg_count == 0:
  101. if isstr(tz):
  102. tz = parser.TzinfoParser.parse(tz)
  103. return self.type.now(tz)
  104. if isinstance(tz, dt_tzinfo):
  105. return self.type.now(tz)
  106. return self.type.utcnow()
  107. if arg_count == 1:
  108. arg = args[0]
  109. # (None) -> now, @ utc.
  110. if arg is None:
  111. return self.type.utcnow()
  112. # try (int, float) -> from timestamp with tz
  113. elif not isstr(arg) and is_timestamp(arg):
  114. if tz is None:
  115. # set to UTC by default
  116. tz = dateutil_tz.tzutc()
  117. return self.type.fromtimestamp(arg, tzinfo=tz)
  118. # (Arrow) -> from the object's datetime.
  119. elif isinstance(arg, Arrow):
  120. return self.type.fromdatetime(arg.datetime)
  121. # (datetime) -> from datetime.
  122. elif isinstance(arg, datetime):
  123. return self.type.fromdatetime(arg)
  124. # (date) -> from date.
  125. elif isinstance(arg, date):
  126. return self.type.fromdate(arg)
  127. # (tzinfo) -> now, @ tzinfo.
  128. elif isinstance(arg, dt_tzinfo):
  129. return self.type.now(arg)
  130. # (str) -> parse.
  131. elif isstr(arg):
  132. dt = parser.DateTimeParser(locale).parse_iso(arg)
  133. return self.type.fromdatetime(dt, tz)
  134. # (struct_time) -> from struct_time
  135. elif isinstance(arg, struct_time):
  136. return self.type.utcfromtimestamp(calendar.timegm(arg))
  137. # (iso calendar) -> convert then from date
  138. elif isinstance(arg, tuple) and len(arg) == 3:
  139. dt = iso_to_gregorian(*arg)
  140. return self.type.fromdate(dt)
  141. else:
  142. raise TypeError(
  143. "Can't parse single argument of type '{}'".format(type(arg))
  144. )
  145. elif arg_count == 2:
  146. arg_1, arg_2 = args[0], args[1]
  147. if isinstance(arg_1, datetime):
  148. # (datetime, tzinfo/str) -> fromdatetime replace tzinfo.
  149. if isinstance(arg_2, dt_tzinfo) or isstr(arg_2):
  150. return self.type.fromdatetime(arg_1, arg_2)
  151. else:
  152. raise TypeError(
  153. "Can't parse two arguments of types 'datetime', '{}'".format(
  154. type(arg_2)
  155. )
  156. )
  157. elif isinstance(arg_1, date):
  158. # (date, tzinfo/str) -> fromdate replace tzinfo.
  159. if isinstance(arg_2, dt_tzinfo) or isstr(arg_2):
  160. return self.type.fromdate(arg_1, tzinfo=arg_2)
  161. else:
  162. raise TypeError(
  163. "Can't parse two arguments of types 'date', '{}'".format(
  164. type(arg_2)
  165. )
  166. )
  167. # (str, format) -> parse.
  168. elif isstr(arg_1) and (isstr(arg_2) or isinstance(arg_2, list)):
  169. dt = parser.DateTimeParser(locale).parse(args[0], args[1])
  170. return self.type.fromdatetime(dt, tzinfo=tz)
  171. else:
  172. raise TypeError(
  173. "Can't parse two arguments of types '{}' and '{}'".format(
  174. type(arg_1), type(arg_2)
  175. )
  176. )
  177. # 3+ args -> datetime-like via constructor.
  178. else:
  179. return self.type(*args, **kwargs)
  180. def utcnow(self):
  181. """Returns an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in UTC time.
  182. Usage::
  183. >>> import arrow
  184. >>> arrow.utcnow()
  185. <Arrow [2013-05-08T05:19:07.018993+00:00]>
  186. """
  187. return self.type.utcnow()
  188. def now(self, tz=None):
  189. """Returns an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in the given
  190. timezone.
  191. :param tz: (optional) A :ref:`timezone expression <tz-expr>`. Defaults to local time.
  192. Usage::
  193. >>> import arrow
  194. >>> arrow.now()
  195. <Arrow [2013-05-07T22:19:11.363410-07:00]>
  196. >>> arrow.now('US/Pacific')
  197. <Arrow [2013-05-07T22:19:15.251821-07:00]>
  198. >>> arrow.now('+02:00')
  199. <Arrow [2013-05-08T07:19:25.618646+02:00]>
  200. >>> arrow.now('local')
  201. <Arrow [2013-05-07T22:19:39.130059-07:00]>
  202. """
  203. if tz is None:
  204. tz = dateutil_tz.tzlocal()
  205. elif not isinstance(tz, dt_tzinfo):
  206. tz = parser.TzinfoParser.parse(tz)
  207. return self.type.now(tz)