arrow.py 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492
  1. # -*- coding: utf-8 -*-
  2. """
  3. Provides the :class:`Arrow <arrow.arrow.Arrow>` class, an enhanced ``datetime``
  4. replacement.
  5. """
  6. from __future__ import absolute_import
  7. import calendar
  8. import sys
  9. from datetime import datetime, timedelta
  10. from datetime import tzinfo as dt_tzinfo
  11. from math import trunc
  12. from dateutil import tz as dateutil_tz
  13. from dateutil.relativedelta import relativedelta
  14. from arrow import formatter, locales, parser, util
  15. class Arrow(object):
  16. """An :class:`Arrow <arrow.arrow.Arrow>` object.
  17. Implements the ``datetime`` interface, behaving as an aware ``datetime`` while implementing
  18. additional functionality.
  19. :param year: the calendar year.
  20. :param month: the calendar month.
  21. :param day: the calendar day.
  22. :param hour: (optional) the hour. Defaults to 0.
  23. :param minute: (optional) the minute, Defaults to 0.
  24. :param second: (optional) the second, Defaults to 0.
  25. :param microsecond: (optional) the microsecond. Defaults 0.
  26. :param tzinfo: (optional) A timezone expression. Defaults to UTC.
  27. .. _tz-expr:
  28. Recognized timezone expressions:
  29. - A ``tzinfo`` object.
  30. - A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
  31. - A ``str`` in ISO 8601 style, as in '+07:00'.
  32. - A ``str``, one of the following: 'local', 'utc', 'UTC'.
  33. Usage::
  34. >>> import arrow
  35. >>> arrow.Arrow(2013, 5, 5, 12, 30, 45)
  36. <Arrow [2013-05-05T12:30:45+00:00]>
  37. """
  38. resolution = datetime.resolution
  39. _ATTRS = ["year", "month", "day", "hour", "minute", "second", "microsecond"]
  40. _ATTRS_PLURAL = ["{}s".format(a) for a in _ATTRS]
  41. _MONTHS_PER_QUARTER = 3
  42. _SECS_PER_MINUTE = float(60)
  43. _SECS_PER_HOUR = float(60 * 60)
  44. _SECS_PER_DAY = float(60 * 60 * 24)
  45. _SECS_PER_WEEK = float(60 * 60 * 24 * 7)
  46. _SECS_PER_MONTH = float(60 * 60 * 24 * 30.5)
  47. _SECS_PER_YEAR = float(60 * 60 * 24 * 365.25)
  48. def __init__(
  49. self, year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None
  50. ):
  51. if tzinfo is None:
  52. tzinfo = dateutil_tz.tzutc()
  53. # detect that tzinfo is a pytz object (issue #626)
  54. elif (
  55. isinstance(tzinfo, dt_tzinfo)
  56. and hasattr(tzinfo, "localize")
  57. and hasattr(tzinfo, "zone")
  58. and tzinfo.zone
  59. ):
  60. tzinfo = parser.TzinfoParser.parse(tzinfo.zone)
  61. elif util.isstr(tzinfo):
  62. tzinfo = parser.TzinfoParser.parse(tzinfo)
  63. self._datetime = datetime(
  64. year, month, day, hour, minute, second, microsecond, tzinfo
  65. )
  66. # factories: single object, both original and from datetime.
  67. @classmethod
  68. def now(cls, tzinfo=None):
  69. """Constructs an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in the given
  70. timezone.
  71. :param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time.
  72. Usage::
  73. >>> arrow.now('Asia/Baku')
  74. <Arrow [2019-01-24T20:26:31.146412+04:00]>
  75. """
  76. if tzinfo is None:
  77. tzinfo = dateutil_tz.tzlocal()
  78. dt = datetime.now(tzinfo)
  79. return cls(
  80. dt.year,
  81. dt.month,
  82. dt.day,
  83. dt.hour,
  84. dt.minute,
  85. dt.second,
  86. dt.microsecond,
  87. dt.tzinfo,
  88. )
  89. @classmethod
  90. def utcnow(cls):
  91. """ Constructs an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in UTC
  92. time.
  93. Usage::
  94. >>> arrow.utcnow()
  95. <Arrow [2019-01-24T16:31:40.651108+00:00]>
  96. """
  97. dt = datetime.now(dateutil_tz.tzutc())
  98. return cls(
  99. dt.year,
  100. dt.month,
  101. dt.day,
  102. dt.hour,
  103. dt.minute,
  104. dt.second,
  105. dt.microsecond,
  106. dt.tzinfo,
  107. )
  108. @classmethod
  109. def fromtimestamp(cls, timestamp, tzinfo=None):
  110. """ Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a timestamp, converted to
  111. the given timezone.
  112. :param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either.
  113. :param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time.
  114. """
  115. if tzinfo is None:
  116. tzinfo = dateutil_tz.tzlocal()
  117. elif util.isstr(tzinfo):
  118. tzinfo = parser.TzinfoParser.parse(tzinfo)
  119. if not util.is_timestamp(timestamp):
  120. raise ValueError(
  121. "The provided timestamp '{}' is invalid.".format(timestamp)
  122. )
  123. dt = datetime.fromtimestamp(float(timestamp), tzinfo)
  124. return cls(
  125. dt.year,
  126. dt.month,
  127. dt.day,
  128. dt.hour,
  129. dt.minute,
  130. dt.second,
  131. dt.microsecond,
  132. dt.tzinfo,
  133. )
  134. @classmethod
  135. def utcfromtimestamp(cls, timestamp):
  136. """Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a timestamp, in UTC time.
  137. :param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either.
  138. """
  139. if not util.is_timestamp(timestamp):
  140. raise ValueError(
  141. "The provided timestamp '{}' is invalid.".format(timestamp)
  142. )
  143. dt = datetime.utcfromtimestamp(float(timestamp))
  144. return cls(
  145. dt.year,
  146. dt.month,
  147. dt.day,
  148. dt.hour,
  149. dt.minute,
  150. dt.second,
  151. dt.microsecond,
  152. dateutil_tz.tzutc(),
  153. )
  154. @classmethod
  155. def fromdatetime(cls, dt, tzinfo=None):
  156. """ Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a ``datetime`` and
  157. optional replacement timezone.
  158. :param dt: the ``datetime``
  159. :param tzinfo: (optional) A :ref:`timezone expression <tz-expr>`. Defaults to ``dt``'s
  160. timezone, or UTC if naive.
  161. If you only want to replace the timezone of naive datetimes::
  162. >>> dt
  163. datetime.datetime(2013, 5, 5, 0, 0, tzinfo=tzutc())
  164. >>> arrow.Arrow.fromdatetime(dt, dt.tzinfo or 'US/Pacific')
  165. <Arrow [2013-05-05T00:00:00+00:00]>
  166. """
  167. if tzinfo is None:
  168. if dt.tzinfo is None:
  169. tzinfo = dateutil_tz.tzutc()
  170. else:
  171. tzinfo = dt.tzinfo
  172. return cls(
  173. dt.year,
  174. dt.month,
  175. dt.day,
  176. dt.hour,
  177. dt.minute,
  178. dt.second,
  179. dt.microsecond,
  180. tzinfo,
  181. )
  182. @classmethod
  183. def fromdate(cls, date, tzinfo=None):
  184. """ Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a ``date`` and optional
  185. replacement timezone. Time values are set to 0.
  186. :param date: the ``date``
  187. :param tzinfo: (optional) A :ref:`timezone expression <tz-expr>`. Defaults to UTC.
  188. """
  189. if tzinfo is None:
  190. tzinfo = dateutil_tz.tzutc()
  191. return cls(date.year, date.month, date.day, tzinfo=tzinfo)
  192. @classmethod
  193. def strptime(cls, date_str, fmt, tzinfo=None):
  194. """ Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a date string and format,
  195. in the style of ``datetime.strptime``. Optionally replaces the parsed timezone.
  196. :param date_str: the date string.
  197. :param fmt: the format string.
  198. :param tzinfo: (optional) A :ref:`timezone expression <tz-expr>`. Defaults to the parsed
  199. timezone if ``fmt`` contains a timezone directive, otherwise UTC.
  200. Usage::
  201. >>> arrow.Arrow.strptime('20-01-2019 15:49:10', '%d-%m-%Y %H:%M:%S')
  202. <Arrow [2019-01-20T15:49:10+00:00]>
  203. """
  204. dt = datetime.strptime(date_str, fmt)
  205. if tzinfo is None:
  206. tzinfo = dt.tzinfo
  207. return cls(
  208. dt.year,
  209. dt.month,
  210. dt.day,
  211. dt.hour,
  212. dt.minute,
  213. dt.second,
  214. dt.microsecond,
  215. tzinfo,
  216. )
  217. # factories: ranges and spans
  218. @classmethod
  219. def range(cls, frame, start, end=None, tz=None, limit=None):
  220. """ Returns an iterator of :class:`Arrow <arrow.arrow.Arrow>` objects, representing
  221. points in time between two inputs.
  222. :param frame: The timeframe. Can be any ``datetime`` property (day, hour, minute...).
  223. :param start: A datetime expression, the start of the range.
  224. :param end: (optional) A datetime expression, the end of the range.
  225. :param tz: (optional) A :ref:`timezone expression <tz-expr>`. Defaults to
  226. ``start``'s timezone, or UTC if ``start`` is naive.
  227. :param limit: (optional) A maximum number of tuples to return.
  228. **NOTE**: The ``end`` or ``limit`` must be provided. Call with ``end`` alone to
  229. return the entire range. Call with ``limit`` alone to return a maximum # of results from
  230. the start. Call with both to cap a range at a maximum # of results.
  231. **NOTE**: ``tz`` internally **replaces** the timezones of both ``start`` and ``end`` before
  232. iterating. As such, either call with naive objects and ``tz``, or aware objects from the
  233. same timezone and no ``tz``.
  234. Supported frame values: year, quarter, month, week, day, hour, minute, second.
  235. Recognized datetime expressions:
  236. - An :class:`Arrow <arrow.arrow.Arrow>` object.
  237. - A ``datetime`` object.
  238. Usage::
  239. >>> start = datetime(2013, 5, 5, 12, 30)
  240. >>> end = datetime(2013, 5, 5, 17, 15)
  241. >>> for r in arrow.Arrow.range('hour', start, end):
  242. ... print(repr(r))
  243. ...
  244. <Arrow [2013-05-05T12:30:00+00:00]>
  245. <Arrow [2013-05-05T13:30:00+00:00]>
  246. <Arrow [2013-05-05T14:30:00+00:00]>
  247. <Arrow [2013-05-05T15:30:00+00:00]>
  248. <Arrow [2013-05-05T16:30:00+00:00]>
  249. **NOTE**: Unlike Python's ``range``, ``end`` *may* be included in the returned iterator::
  250. >>> start = datetime(2013, 5, 5, 12, 30)
  251. >>> end = datetime(2013, 5, 5, 13, 30)
  252. >>> for r in arrow.Arrow.range('hour', start, end):
  253. ... print(repr(r))
  254. ...
  255. <Arrow [2013-05-05T12:30:00+00:00]>
  256. <Arrow [2013-05-05T13:30:00+00:00]>
  257. """
  258. _, frame_relative, relative_steps = cls._get_frames(frame)
  259. tzinfo = cls._get_tzinfo(start.tzinfo if tz is None else tz)
  260. start = cls._get_datetime(start).replace(tzinfo=tzinfo)
  261. end, limit = cls._get_iteration_params(end, limit)
  262. end = cls._get_datetime(end).replace(tzinfo=tzinfo)
  263. current = cls.fromdatetime(start)
  264. i = 0
  265. while current <= end and i < limit:
  266. i += 1
  267. yield current
  268. values = [getattr(current, f) for f in cls._ATTRS]
  269. current = cls(*values, tzinfo=tzinfo) + relativedelta(
  270. **{frame_relative: relative_steps}
  271. )
  272. @classmethod
  273. def span_range(cls, frame, start, end, tz=None, limit=None, bounds="[)"):
  274. """ Returns an iterator of tuples, each :class:`Arrow <arrow.arrow.Arrow>` objects,
  275. representing a series of timespans between two inputs.
  276. :param frame: The timeframe. Can be any ``datetime`` property (day, hour, minute...).
  277. :param start: A datetime expression, the start of the range.
  278. :param end: (optional) A datetime expression, the end of the range.
  279. :param tz: (optional) A :ref:`timezone expression <tz-expr>`. Defaults to
  280. ``start``'s timezone, or UTC if ``start`` is naive.
  281. :param limit: (optional) A maximum number of tuples to return.
  282. :param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
  283. whether to include or exclude the start and end values in each span in the range. '(' excludes
  284. the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
  285. If the bounds are not specified, the default bound '[)' is used.
  286. **NOTE**: The ``end`` or ``limit`` must be provided. Call with ``end`` alone to
  287. return the entire range. Call with ``limit`` alone to return a maximum # of results from
  288. the start. Call with both to cap a range at a maximum # of results.
  289. **NOTE**: ``tz`` internally **replaces** the timezones of both ``start`` and ``end`` before
  290. iterating. As such, either call with naive objects and ``tz``, or aware objects from the
  291. same timezone and no ``tz``.
  292. Supported frame values: year, quarter, month, week, day, hour, minute, second.
  293. Recognized datetime expressions:
  294. - An :class:`Arrow <arrow.arrow.Arrow>` object.
  295. - A ``datetime`` object.
  296. **NOTE**: Unlike Python's ``range``, ``end`` will *always* be included in the returned
  297. iterator of timespans.
  298. Usage:
  299. >>> start = datetime(2013, 5, 5, 12, 30)
  300. >>> end = datetime(2013, 5, 5, 17, 15)
  301. >>> for r in arrow.Arrow.span_range('hour', start, end):
  302. ... print(r)
  303. ...
  304. (<Arrow [2013-05-05T12:00:00+00:00]>, <Arrow [2013-05-05T12:59:59.999999+00:00]>)
  305. (<Arrow [2013-05-05T13:00:00+00:00]>, <Arrow [2013-05-05T13:59:59.999999+00:00]>)
  306. (<Arrow [2013-05-05T14:00:00+00:00]>, <Arrow [2013-05-05T14:59:59.999999+00:00]>)
  307. (<Arrow [2013-05-05T15:00:00+00:00]>, <Arrow [2013-05-05T15:59:59.999999+00:00]>)
  308. (<Arrow [2013-05-05T16:00:00+00:00]>, <Arrow [2013-05-05T16:59:59.999999+00:00]>)
  309. (<Arrow [2013-05-05T17:00:00+00:00]>, <Arrow [2013-05-05T17:59:59.999999+00:00]>)
  310. """
  311. tzinfo = cls._get_tzinfo(start.tzinfo if tz is None else tz)
  312. start = cls.fromdatetime(start, tzinfo).span(frame)[0]
  313. _range = cls.range(frame, start, end, tz, limit)
  314. return (r.span(frame, bounds=bounds) for r in _range)
  315. @classmethod
  316. def interval(cls, frame, start, end, interval=1, tz=None, bounds="[)"):
  317. """ Returns an iterator of tuples, each :class:`Arrow <arrow.arrow.Arrow>` objects,
  318. representing a series of intervals between two inputs.
  319. :param frame: The timeframe. Can be any ``datetime`` property (day, hour, minute...).
  320. :param start: A datetime expression, the start of the range.
  321. :param end: (optional) A datetime expression, the end of the range.
  322. :param interval: (optional) Time interval for the given time frame.
  323. :param tz: (optional) A timezone expression. Defaults to UTC.
  324. :param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
  325. whether to include or exclude the start and end values in the intervals. '(' excludes
  326. the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
  327. If the bounds are not specified, the default bound '[)' is used.
  328. Supported frame values: year, quarter, month, week, day, hour, minute, second
  329. Recognized datetime expressions:
  330. - An :class:`Arrow <arrow.arrow.Arrow>` object.
  331. - A ``datetime`` object.
  332. Recognized timezone expressions:
  333. - A ``tzinfo`` object.
  334. - A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
  335. - A ``str`` in ISO 8601 style, as in '+07:00'.
  336. - A ``str``, one of the following: 'local', 'utc', 'UTC'.
  337. Usage:
  338. >>> start = datetime(2013, 5, 5, 12, 30)
  339. >>> end = datetime(2013, 5, 5, 17, 15)
  340. >>> for r in arrow.Arrow.interval('hour', start, end, 2):
  341. ... print r
  342. ...
  343. (<Arrow [2013-05-05T12:00:00+00:00]>, <Arrow [2013-05-05T13:59:59.999999+00:00]>)
  344. (<Arrow [2013-05-05T14:00:00+00:00]>, <Arrow [2013-05-05T15:59:59.999999+00:00]>)
  345. (<Arrow [2013-05-05T16:00:00+00:00]>, <Arrow [2013-05-05T17:59:59.999999+00:0]>)
  346. """
  347. if interval < 1:
  348. raise ValueError("interval has to be a positive integer")
  349. spanRange = iter(cls.span_range(frame, start, end, tz, bounds=bounds))
  350. while True:
  351. try:
  352. intvlStart, intvlEnd = next(spanRange)
  353. for _ in range(interval - 1):
  354. _, intvlEnd = next(spanRange)
  355. yield intvlStart, intvlEnd
  356. except StopIteration:
  357. return
  358. # representations
  359. def __repr__(self):
  360. return "<{} [{}]>".format(self.__class__.__name__, self.__str__())
  361. def __str__(self):
  362. return self._datetime.isoformat()
  363. def __format__(self, formatstr):
  364. if len(formatstr) > 0:
  365. return self.format(formatstr)
  366. return str(self)
  367. def __hash__(self):
  368. return self._datetime.__hash__()
  369. # attributes & properties
  370. def __getattr__(self, name):
  371. if name == "week":
  372. return self.isocalendar()[1]
  373. if name == "quarter":
  374. return int((self.month - 1) / self._MONTHS_PER_QUARTER) + 1
  375. if not name.startswith("_"):
  376. value = getattr(self._datetime, name, None)
  377. if value is not None:
  378. return value
  379. return object.__getattribute__(self, name)
  380. @property
  381. def tzinfo(self):
  382. """ Gets the ``tzinfo`` of the :class:`Arrow <arrow.arrow.Arrow>` object.
  383. Usage::
  384. >>> arw=arrow.utcnow()
  385. >>> arw.tzinfo
  386. tzutc()
  387. """
  388. return self._datetime.tzinfo
  389. @tzinfo.setter
  390. def tzinfo(self, tzinfo):
  391. """ Sets the ``tzinfo`` of the :class:`Arrow <arrow.arrow.Arrow>` object. """
  392. self._datetime = self._datetime.replace(tzinfo=tzinfo)
  393. @property
  394. def datetime(self):
  395. """ Returns a datetime representation of the :class:`Arrow <arrow.arrow.Arrow>` object.
  396. Usage::
  397. >>> arw=arrow.utcnow()
  398. >>> arw.datetime
  399. datetime.datetime(2019, 1, 24, 16, 35, 27, 276649, tzinfo=tzutc())
  400. """
  401. return self._datetime
  402. @property
  403. def naive(self):
  404. """ Returns a naive datetime representation of the :class:`Arrow <arrow.arrow.Arrow>`
  405. object.
  406. Usage::
  407. >>> nairobi = arrow.now('Africa/Nairobi')
  408. >>> nairobi
  409. <Arrow [2019-01-23T19:27:12.297999+03:00]>
  410. >>> nairobi.naive
  411. datetime.datetime(2019, 1, 23, 19, 27, 12, 297999)
  412. """
  413. return self._datetime.replace(tzinfo=None)
  414. @property
  415. def timestamp(self):
  416. """ Returns a timestamp representation of the :class:`Arrow <arrow.arrow.Arrow>` object, in
  417. UTC time.
  418. Usage::
  419. >>> arrow.utcnow().timestamp
  420. 1548260567
  421. """
  422. return calendar.timegm(self._datetime.utctimetuple())
  423. @property
  424. def float_timestamp(self):
  425. """ Returns a floating-point representation of the :class:`Arrow <arrow.arrow.Arrow>`
  426. object, in UTC time.
  427. Usage::
  428. >>> arrow.utcnow().float_timestamp
  429. 1548260516.830896
  430. """
  431. return self.timestamp + float(self.microsecond) / 1000000
  432. # mutation and duplication.
  433. def clone(self):
  434. """ Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, cloned from the current one.
  435. Usage:
  436. >>> arw = arrow.utcnow()
  437. >>> cloned = arw.clone()
  438. """
  439. return self.fromdatetime(self._datetime)
  440. def replace(self, **kwargs):
  441. """ Returns a new :class:`Arrow <arrow.arrow.Arrow>` object with attributes updated
  442. according to inputs.
  443. Use property names to set their value absolutely::
  444. >>> import arrow
  445. >>> arw = arrow.utcnow()
  446. >>> arw
  447. <Arrow [2013-05-11T22:27:34.787885+00:00]>
  448. >>> arw.replace(year=2014, month=6)
  449. <Arrow [2014-06-11T22:27:34.787885+00:00]>
  450. You can also replace the timezone without conversion, using a
  451. :ref:`timezone expression <tz-expr>`::
  452. >>> arw.replace(tzinfo=tz.tzlocal())
  453. <Arrow [2013-05-11T22:27:34.787885-07:00]>
  454. """
  455. absolute_kwargs = {}
  456. for key, value in kwargs.items():
  457. if key in self._ATTRS:
  458. absolute_kwargs[key] = value
  459. elif key in ["week", "quarter"]:
  460. raise AttributeError("setting absolute {} is not supported".format(key))
  461. elif key != "tzinfo":
  462. raise AttributeError('unknown attribute: "{}"'.format(key))
  463. current = self._datetime.replace(**absolute_kwargs)
  464. tzinfo = kwargs.get("tzinfo")
  465. if tzinfo is not None:
  466. tzinfo = self._get_tzinfo(tzinfo)
  467. current = current.replace(tzinfo=tzinfo)
  468. return self.fromdatetime(current)
  469. def shift(self, **kwargs):
  470. """ Returns a new :class:`Arrow <arrow.arrow.Arrow>` object with attributes updated
  471. according to inputs.
  472. Use pluralized property names to relatively shift their current value:
  473. >>> import arrow
  474. >>> arw = arrow.utcnow()
  475. >>> arw
  476. <Arrow [2013-05-11T22:27:34.787885+00:00]>
  477. >>> arw.shift(years=1, months=-1)
  478. <Arrow [2014-04-11T22:27:34.787885+00:00]>
  479. Day-of-the-week relative shifting can use either Python's weekday numbers
  480. (Monday = 0, Tuesday = 1 .. Sunday = 6) or using dateutil.relativedelta's
  481. day instances (MO, TU .. SU). When using weekday numbers, the returned
  482. date will always be greater than or equal to the starting date.
  483. Using the above code (which is a Saturday) and asking it to shift to Saturday:
  484. >>> arw.shift(weekday=5)
  485. <Arrow [2013-05-11T22:27:34.787885+00:00]>
  486. While asking for a Monday:
  487. >>> arw.shift(weekday=0)
  488. <Arrow [2013-05-13T22:27:34.787885+00:00]>
  489. """
  490. relative_kwargs = {}
  491. additional_attrs = ["weeks", "quarters", "weekday"]
  492. for key, value in kwargs.items():
  493. if key in self._ATTRS_PLURAL or key in additional_attrs:
  494. relative_kwargs[key] = value
  495. else:
  496. raise AttributeError(
  497. "Invalid shift time frame. Please select one of the following: {}.".format(
  498. ", ".join(self._ATTRS_PLURAL + additional_attrs)
  499. )
  500. )
  501. # core datetime does not support quarters, translate to months.
  502. relative_kwargs.setdefault("months", 0)
  503. relative_kwargs["months"] += (
  504. relative_kwargs.pop("quarters", 0) * self._MONTHS_PER_QUARTER
  505. )
  506. current = self._datetime + relativedelta(**relative_kwargs)
  507. return self.fromdatetime(current)
  508. def to(self, tz):
  509. """ Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, converted
  510. to the target timezone.
  511. :param tz: A :ref:`timezone expression <tz-expr>`.
  512. Usage::
  513. >>> utc = arrow.utcnow()
  514. >>> utc
  515. <Arrow [2013-05-09T03:49:12.311072+00:00]>
  516. >>> utc.to('US/Pacific')
  517. <Arrow [2013-05-08T20:49:12.311072-07:00]>
  518. >>> utc.to(tz.tzlocal())
  519. <Arrow [2013-05-08T20:49:12.311072-07:00]>
  520. >>> utc.to('-07:00')
  521. <Arrow [2013-05-08T20:49:12.311072-07:00]>
  522. >>> utc.to('local')
  523. <Arrow [2013-05-08T20:49:12.311072-07:00]>
  524. >>> utc.to('local').to('utc')
  525. <Arrow [2013-05-09T03:49:12.311072+00:00]>
  526. """
  527. if not isinstance(tz, dt_tzinfo):
  528. tz = parser.TzinfoParser.parse(tz)
  529. dt = self._datetime.astimezone(tz)
  530. return self.__class__(
  531. dt.year,
  532. dt.month,
  533. dt.day,
  534. dt.hour,
  535. dt.minute,
  536. dt.second,
  537. dt.microsecond,
  538. dt.tzinfo,
  539. )
  540. @classmethod
  541. def _validate_bounds(cls, bounds):
  542. if bounds != "()" and bounds != "(]" and bounds != "[)" and bounds != "[]":
  543. raise AttributeError(
  544. 'Invalid bounds. Please select between "()", "(]", "[)", or "[]".'
  545. )
  546. def span(self, frame, count=1, bounds="[)"):
  547. """ Returns two new :class:`Arrow <arrow.arrow.Arrow>` objects, representing the timespan
  548. of the :class:`Arrow <arrow.arrow.Arrow>` object in a given timeframe.
  549. :param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
  550. :param count: (optional) the number of frames to span.
  551. :param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
  552. whether to include or exclude the start and end values in the span. '(' excludes
  553. the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
  554. If the bounds are not specified, the default bound '[)' is used.
  555. Supported frame values: year, quarter, month, week, day, hour, minute, second.
  556. Usage::
  557. >>> arrow.utcnow()
  558. <Arrow [2013-05-09T03:32:36.186203+00:00]>
  559. >>> arrow.utcnow().span('hour')
  560. (<Arrow [2013-05-09T03:00:00+00:00]>, <Arrow [2013-05-09T03:59:59.999999+00:00]>)
  561. >>> arrow.utcnow().span('day')
  562. (<Arrow [2013-05-09T00:00:00+00:00]>, <Arrow [2013-05-09T23:59:59.999999+00:00]>)
  563. >>> arrow.utcnow().span('day', count=2)
  564. (<Arrow [2013-05-09T00:00:00+00:00]>, <Arrow [2013-05-10T23:59:59.999999+00:00]>)
  565. >>> arrow.utcnow().span('day', bounds='[]')
  566. (<Arrow [2013-05-09T00:00:00+00:00]>, <Arrow [2013-05-10T00:00:00+00:00]>)
  567. """
  568. self._validate_bounds(bounds)
  569. frame_absolute, frame_relative, relative_steps = self._get_frames(frame)
  570. if frame_absolute == "week":
  571. attr = "day"
  572. elif frame_absolute == "quarter":
  573. attr = "month"
  574. else:
  575. attr = frame_absolute
  576. index = self._ATTRS.index(attr)
  577. frames = self._ATTRS[: index + 1]
  578. values = [getattr(self, f) for f in frames]
  579. for _ in range(3 - len(values)):
  580. values.append(1)
  581. floor = self.__class__(*values, tzinfo=self.tzinfo)
  582. if frame_absolute == "week":
  583. floor = floor + relativedelta(days=-(self.isoweekday() - 1))
  584. elif frame_absolute == "quarter":
  585. floor = floor + relativedelta(months=-((self.month - 1) % 3))
  586. ceil = floor + relativedelta(**{frame_relative: count * relative_steps})
  587. if bounds[0] == "(":
  588. floor += relativedelta(microseconds=1)
  589. if bounds[1] == ")":
  590. ceil += relativedelta(microseconds=-1)
  591. return floor, ceil
  592. def floor(self, frame):
  593. """ Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, representing the "floor"
  594. of the timespan of the :class:`Arrow <arrow.arrow.Arrow>` object in a given timeframe.
  595. Equivalent to the first element in the 2-tuple returned by
  596. :func:`span <arrow.arrow.Arrow.span>`.
  597. :param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
  598. Usage::
  599. >>> arrow.utcnow().floor('hour')
  600. <Arrow [2013-05-09T03:00:00+00:00]>
  601. """
  602. return self.span(frame)[0]
  603. def ceil(self, frame):
  604. """ Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, representing the "ceiling"
  605. of the timespan of the :class:`Arrow <arrow.arrow.Arrow>` object in a given timeframe.
  606. Equivalent to the second element in the 2-tuple returned by
  607. :func:`span <arrow.arrow.Arrow.span>`.
  608. :param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
  609. Usage::
  610. >>> arrow.utcnow().ceil('hour')
  611. <Arrow [2013-05-09T03:59:59.999999+00:00]>
  612. """
  613. return self.span(frame)[1]
  614. # string output and formatting.
  615. def format(self, fmt="YYYY-MM-DD HH:mm:ssZZ", locale="en_us"):
  616. """ Returns a string representation of the :class:`Arrow <arrow.arrow.Arrow>` object,
  617. formatted according to a format string.
  618. :param fmt: the format string.
  619. Usage::
  620. >>> arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ')
  621. '2013-05-09 03:56:47 -00:00'
  622. >>> arrow.utcnow().format('X')
  623. '1368071882'
  624. >>> arrow.utcnow().format('MMMM DD, YYYY')
  625. 'May 09, 2013'
  626. >>> arrow.utcnow().format()
  627. '2013-05-09 03:56:47 -00:00'
  628. """
  629. return formatter.DateTimeFormatter(locale).format(self._datetime, fmt)
  630. def humanize(
  631. self, other=None, locale="en_us", only_distance=False, granularity="auto"
  632. ):
  633. """ Returns a localized, humanized representation of a relative difference in time.
  634. :param other: (optional) an :class:`Arrow <arrow.arrow.Arrow>` or ``datetime`` object.
  635. Defaults to now in the current :class:`Arrow <arrow.arrow.Arrow>` object's timezone.
  636. :param locale: (optional) a ``str`` specifying a locale. Defaults to 'en_us'.
  637. :param only_distance: (optional) returns only time difference eg: "11 seconds" without "in" or "ago" part.
  638. :param granularity: (optional) defines the precision of the output. Set it to strings 'second', 'minute',
  639. 'hour', 'day', 'week', 'month' or 'year' or a list of any combination of these strings
  640. Usage::
  641. >>> earlier = arrow.utcnow().shift(hours=-2)
  642. >>> earlier.humanize()
  643. '2 hours ago'
  644. >>> later = earlier.shift(hours=4)
  645. >>> later.humanize(earlier)
  646. 'in 4 hours'
  647. """
  648. locale_name = locale
  649. locale = locales.get_locale(locale)
  650. if other is None:
  651. utc = datetime.utcnow().replace(tzinfo=dateutil_tz.tzutc())
  652. dt = utc.astimezone(self._datetime.tzinfo)
  653. elif isinstance(other, Arrow):
  654. dt = other._datetime
  655. elif isinstance(other, datetime):
  656. if other.tzinfo is None:
  657. dt = other.replace(tzinfo=self._datetime.tzinfo)
  658. else:
  659. dt = other.astimezone(self._datetime.tzinfo)
  660. else:
  661. raise TypeError(
  662. "Invalid 'other' argument of type '{}'. "
  663. "Argument must be of type None, Arrow, or datetime.".format(
  664. type(other).__name__
  665. )
  666. )
  667. if isinstance(granularity, list) and len(granularity) == 1:
  668. granularity = granularity[0]
  669. delta = int(round(util.total_seconds(self._datetime - dt)))
  670. sign = -1 if delta < 0 else 1
  671. diff = abs(delta)
  672. delta = diff
  673. try:
  674. if granularity == "auto":
  675. if diff < 10:
  676. return locale.describe("now", only_distance=only_distance)
  677. if diff < 45:
  678. seconds = sign * delta
  679. return locale.describe(
  680. "seconds", seconds, only_distance=only_distance
  681. )
  682. elif diff < 90:
  683. return locale.describe("minute", sign, only_distance=only_distance)
  684. elif diff < 2700:
  685. minutes = sign * int(max(delta / 60, 2))
  686. return locale.describe(
  687. "minutes", minutes, only_distance=only_distance
  688. )
  689. elif diff < 5400:
  690. return locale.describe("hour", sign, only_distance=only_distance)
  691. elif diff < 79200:
  692. hours = sign * int(max(delta / 3600, 2))
  693. return locale.describe("hours", hours, only_distance=only_distance)
  694. # anything less than 48 hours should be 1 day
  695. elif diff < 172800:
  696. return locale.describe("day", sign, only_distance=only_distance)
  697. elif diff < 554400:
  698. days = sign * int(max(delta / 86400, 2))
  699. return locale.describe("days", days, only_distance=only_distance)
  700. elif diff < 907200:
  701. return locale.describe("week", sign, only_distance=only_distance)
  702. elif diff < 2419200:
  703. weeks = sign * int(max(delta / 604800, 2))
  704. return locale.describe("weeks", weeks, only_distance=only_distance)
  705. elif diff < 3888000:
  706. return locale.describe("month", sign, only_distance=only_distance)
  707. elif diff < 29808000:
  708. self_months = self._datetime.year * 12 + self._datetime.month
  709. other_months = dt.year * 12 + dt.month
  710. months = sign * int(max(abs(other_months - self_months), 2))
  711. return locale.describe(
  712. "months", months, only_distance=only_distance
  713. )
  714. elif diff < 47260800:
  715. return locale.describe("year", sign, only_distance=only_distance)
  716. else:
  717. years = sign * int(max(delta / 31536000, 2))
  718. return locale.describe("years", years, only_distance=only_distance)
  719. elif util.isstr(granularity):
  720. if granularity == "second":
  721. delta = sign * delta
  722. if abs(delta) < 2:
  723. return locale.describe("now", only_distance=only_distance)
  724. elif granularity == "minute":
  725. delta = sign * delta / self._SECS_PER_MINUTE
  726. elif granularity == "hour":
  727. delta = sign * delta / self._SECS_PER_HOUR
  728. elif granularity == "day":
  729. delta = sign * delta / self._SECS_PER_DAY
  730. elif granularity == "week":
  731. delta = sign * delta / self._SECS_PER_WEEK
  732. elif granularity == "month":
  733. delta = sign * delta / self._SECS_PER_MONTH
  734. elif granularity == "year":
  735. delta = sign * delta / self._SECS_PER_YEAR
  736. else:
  737. raise AttributeError(
  738. "Invalid level of granularity. Please select between 'second', 'minute', 'hour', 'day', 'week', 'month' or 'year'"
  739. )
  740. if trunc(abs(delta)) != 1:
  741. granularity += "s"
  742. return locale.describe(granularity, delta, only_distance=only_distance)
  743. else:
  744. timeframes = []
  745. if "year" in granularity:
  746. years = sign * delta / self._SECS_PER_YEAR
  747. delta %= self._SECS_PER_YEAR
  748. timeframes.append(["year", years])
  749. if "month" in granularity:
  750. months = sign * delta / self._SECS_PER_MONTH
  751. delta %= self._SECS_PER_MONTH
  752. timeframes.append(["month", months])
  753. if "week" in granularity:
  754. weeks = sign * delta / self._SECS_PER_WEEK
  755. delta %= self._SECS_PER_WEEK
  756. timeframes.append(["week", weeks])
  757. if "day" in granularity:
  758. days = sign * delta / self._SECS_PER_DAY
  759. delta %= self._SECS_PER_DAY
  760. timeframes.append(["day", days])
  761. if "hour" in granularity:
  762. hours = sign * delta / self._SECS_PER_HOUR
  763. delta %= self._SECS_PER_HOUR
  764. timeframes.append(["hour", hours])
  765. if "minute" in granularity:
  766. minutes = sign * delta / self._SECS_PER_MINUTE
  767. delta %= self._SECS_PER_MINUTE
  768. timeframes.append(["minute", minutes])
  769. if "second" in granularity:
  770. seconds = sign * delta
  771. timeframes.append(["second", seconds])
  772. if len(timeframes) < len(granularity):
  773. raise AttributeError(
  774. "Invalid level of granularity. "
  775. "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month' or 'year'."
  776. )
  777. for tf in timeframes:
  778. # Make granularity plural if the delta is not equal to 1
  779. if trunc(abs(tf[1])) != 1:
  780. tf[0] += "s"
  781. return locale.describe_multi(timeframes, only_distance=only_distance)
  782. except KeyError as e:
  783. raise ValueError(
  784. "Humanization of the {} granularity is not currently translated in the '{}' locale. "
  785. "Please consider making a contribution to this locale.".format(
  786. e, locale_name
  787. )
  788. )
  789. # query functions
  790. def is_between(self, start, end, bounds="()"):
  791. """ Returns a boolean denoting whether the specified date and time is between
  792. the start and end dates and times.
  793. :param start: an :class:`Arrow <arrow.arrow.Arrow>` object.
  794. :param end: an :class:`Arrow <arrow.arrow.Arrow>` object.
  795. :param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
  796. whether to include or exclude the start and end values in the range. '(' excludes
  797. the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
  798. If the bounds are not specified, the default bound '()' is used.
  799. Usage::
  800. >>> start = arrow.get(datetime(2013, 5, 5, 12, 30, 10))
  801. >>> end = arrow.get(datetime(2013, 5, 5, 12, 30, 36))
  802. >>> arrow.get(datetime(2013, 5, 5, 12, 30, 27)).is_between(start, end)
  803. True
  804. >>> start = arrow.get(datetime(2013, 5, 5))
  805. >>> end = arrow.get(datetime(2013, 5, 8))
  806. >>> arrow.get(datetime(2013, 5, 8)).is_between(start, end, '[]')
  807. True
  808. >>> start = arrow.get(datetime(2013, 5, 5))
  809. >>> end = arrow.get(datetime(2013, 5, 8))
  810. >>> arrow.get(datetime(2013, 5, 8)).is_between(start, end, '[)')
  811. False
  812. """
  813. self._validate_bounds(bounds)
  814. if not isinstance(start, Arrow):
  815. raise TypeError(
  816. "Can't parse start date argument type of '{}'".format(type(start))
  817. )
  818. if not isinstance(end, Arrow):
  819. raise TypeError(
  820. "Can't parse end date argument type of '{}'".format(type(end))
  821. )
  822. include_start = bounds[0] == "["
  823. include_end = bounds[1] == "]"
  824. target_timestamp = self.float_timestamp
  825. start_timestamp = start.float_timestamp
  826. end_timestamp = end.float_timestamp
  827. if include_start and include_end:
  828. return (
  829. target_timestamp >= start_timestamp
  830. and target_timestamp <= end_timestamp
  831. )
  832. elif include_start and not include_end:
  833. return (
  834. target_timestamp >= start_timestamp and target_timestamp < end_timestamp
  835. )
  836. elif not include_start and include_end:
  837. return (
  838. target_timestamp > start_timestamp and target_timestamp <= end_timestamp
  839. )
  840. else:
  841. return (
  842. target_timestamp > start_timestamp and target_timestamp < end_timestamp
  843. )
  844. # math
  845. def __add__(self, other):
  846. if isinstance(other, (timedelta, relativedelta)):
  847. return self.fromdatetime(self._datetime + other, self._datetime.tzinfo)
  848. return NotImplemented
  849. def __radd__(self, other):
  850. return self.__add__(other)
  851. def __sub__(self, other):
  852. if isinstance(other, (timedelta, relativedelta)):
  853. return self.fromdatetime(self._datetime - other, self._datetime.tzinfo)
  854. elif isinstance(other, datetime):
  855. return self._datetime - other
  856. elif isinstance(other, Arrow):
  857. return self._datetime - other._datetime
  858. return NotImplemented
  859. def __rsub__(self, other):
  860. if isinstance(other, datetime):
  861. return other - self._datetime
  862. return NotImplemented
  863. # comparisons
  864. def __eq__(self, other):
  865. if not isinstance(other, (Arrow, datetime)):
  866. return False
  867. return self._datetime == self._get_datetime(other)
  868. def __ne__(self, other):
  869. if not isinstance(other, (Arrow, datetime)):
  870. return True
  871. return not self.__eq__(other)
  872. def __gt__(self, other):
  873. if not isinstance(other, (Arrow, datetime)):
  874. return NotImplemented
  875. return self._datetime > self._get_datetime(other)
  876. def __ge__(self, other):
  877. if not isinstance(other, (Arrow, datetime)):
  878. return NotImplemented
  879. return self._datetime >= self._get_datetime(other)
  880. def __lt__(self, other):
  881. if not isinstance(other, (Arrow, datetime)):
  882. return NotImplemented
  883. return self._datetime < self._get_datetime(other)
  884. def __le__(self, other):
  885. if not isinstance(other, (Arrow, datetime)):
  886. return NotImplemented
  887. return self._datetime <= self._get_datetime(other)
  888. def __cmp__(self, other):
  889. if sys.version_info[0] < 3: # pragma: no cover
  890. if not isinstance(other, (Arrow, datetime)):
  891. raise TypeError(
  892. "can't compare '{}' to '{}'".format(type(self), type(other))
  893. )
  894. # datetime methods
  895. def date(self):
  896. """ Returns a ``date`` object with the same year, month and day.
  897. Usage::
  898. >>> arrow.utcnow().date()
  899. datetime.date(2019, 1, 23)
  900. """
  901. return self._datetime.date()
  902. def time(self):
  903. """ Returns a ``time`` object with the same hour, minute, second, microsecond.
  904. Usage::
  905. >>> arrow.utcnow().time()
  906. datetime.time(12, 15, 34, 68352)
  907. """
  908. return self._datetime.time()
  909. def timetz(self):
  910. """ Returns a ``time`` object with the same hour, minute, second, microsecond and
  911. tzinfo.
  912. Usage::
  913. >>> arrow.utcnow().timetz()
  914. datetime.time(12, 5, 18, 298893, tzinfo=tzutc())
  915. """
  916. return self._datetime.timetz()
  917. def astimezone(self, tz):
  918. """ Returns a ``datetime`` object, converted to the specified timezone.
  919. :param tz: a ``tzinfo`` object.
  920. Usage::
  921. >>> pacific=arrow.now('US/Pacific')
  922. >>> nyc=arrow.now('America/New_York').tzinfo
  923. >>> pacific.astimezone(nyc)
  924. datetime.datetime(2019, 1, 20, 10, 24, 22, 328172, tzinfo=tzfile('/usr/share/zoneinfo/America/New_York'))
  925. """
  926. return self._datetime.astimezone(tz)
  927. def utcoffset(self):
  928. """ Returns a ``timedelta`` object representing the whole number of minutes difference from
  929. UTC time.
  930. Usage::
  931. >>> arrow.now('US/Pacific').utcoffset()
  932. datetime.timedelta(-1, 57600)
  933. """
  934. return self._datetime.utcoffset()
  935. def dst(self):
  936. """ Returns the daylight savings time adjustment.
  937. Usage::
  938. >>> arrow.utcnow().dst()
  939. datetime.timedelta(0)
  940. """
  941. return self._datetime.dst()
  942. def timetuple(self):
  943. """ Returns a ``time.struct_time``, in the current timezone.
  944. Usage::
  945. >>> arrow.utcnow().timetuple()
  946. time.struct_time(tm_year=2019, tm_mon=1, tm_mday=20, tm_hour=15, tm_min=17, tm_sec=8, tm_wday=6, tm_yday=20, tm_isdst=0)
  947. """
  948. return self._datetime.timetuple()
  949. def utctimetuple(self):
  950. """ Returns a ``time.struct_time``, in UTC time.
  951. Usage::
  952. >>> arrow.utcnow().utctimetuple()
  953. time.struct_time(tm_year=2019, tm_mon=1, tm_mday=19, tm_hour=21, tm_min=41, tm_sec=7, tm_wday=5, tm_yday=19, tm_isdst=0)
  954. """
  955. return self._datetime.utctimetuple()
  956. def toordinal(self):
  957. """ Returns the proleptic Gregorian ordinal of the date.
  958. Usage::
  959. >>> arrow.utcnow().toordinal()
  960. 737078
  961. """
  962. return self._datetime.toordinal()
  963. def weekday(self):
  964. """ Returns the day of the week as an integer (0-6).
  965. Usage::
  966. >>> arrow.utcnow().weekday()
  967. 5
  968. """
  969. return self._datetime.weekday()
  970. def isoweekday(self):
  971. """ Returns the ISO day of the week as an integer (1-7).
  972. Usage::
  973. >>> arrow.utcnow().isoweekday()
  974. 6
  975. """
  976. return self._datetime.isoweekday()
  977. def isocalendar(self):
  978. """ Returns a 3-tuple, (ISO year, ISO week number, ISO weekday).
  979. Usage::
  980. >>> arrow.utcnow().isocalendar()
  981. (2019, 3, 6)
  982. """
  983. return self._datetime.isocalendar()
  984. def isoformat(self, sep="T"):
  985. """Returns an ISO 8601 formatted representation of the date and time.
  986. Usage::
  987. >>> arrow.utcnow().isoformat()
  988. '2019-01-19T18:30:52.442118+00:00'
  989. """
  990. return self._datetime.isoformat(sep)
  991. def ctime(self):
  992. """ Returns a ctime formatted representation of the date and time.
  993. Usage::
  994. >>> arrow.utcnow().ctime()
  995. 'Sat Jan 19 18:26:50 2019'
  996. """
  997. return self._datetime.ctime()
  998. def strftime(self, format):
  999. """ Formats in the style of ``datetime.strftime``.
  1000. :param format: the format string.
  1001. Usage::
  1002. >>> arrow.utcnow().strftime('%d-%m-%Y %H:%M:%S')
  1003. '23-01-2019 12:28:17'
  1004. """
  1005. return self._datetime.strftime(format)
  1006. def for_json(self):
  1007. """Serializes for the ``for_json`` protocol of simplejson.
  1008. Usage::
  1009. >>> arrow.utcnow().for_json()
  1010. '2019-01-19T18:25:36.760079+00:00'
  1011. """
  1012. return self.isoformat()
  1013. # internal tools.
  1014. @staticmethod
  1015. def _get_tzinfo(tz_expr):
  1016. if tz_expr is None:
  1017. return dateutil_tz.tzutc()
  1018. if isinstance(tz_expr, dt_tzinfo):
  1019. return tz_expr
  1020. else:
  1021. try:
  1022. return parser.TzinfoParser.parse(tz_expr)
  1023. except parser.ParserError:
  1024. raise ValueError("'{}' not recognized as a timezone".format(tz_expr))
  1025. @classmethod
  1026. def _get_datetime(cls, expr):
  1027. """Get datetime object for a specified expression."""
  1028. if isinstance(expr, Arrow):
  1029. return expr.datetime
  1030. elif isinstance(expr, datetime):
  1031. return expr
  1032. elif util.is_timestamp(expr):
  1033. timestamp = float(expr)
  1034. return cls.utcfromtimestamp(timestamp).datetime
  1035. else:
  1036. raise ValueError(
  1037. "'{}' not recognized as a datetime or timestamp.".format(expr)
  1038. )
  1039. @classmethod
  1040. def _get_frames(cls, name):
  1041. if name in cls._ATTRS:
  1042. return name, "{}s".format(name), 1
  1043. elif name[-1] == "s" and name[:-1] in cls._ATTRS:
  1044. return name[:-1], name, 1
  1045. elif name in ["week", "weeks"]:
  1046. return "week", "weeks", 1
  1047. elif name in ["quarter", "quarters"]:
  1048. return "quarter", "months", 3
  1049. supported = ", ".join(
  1050. [
  1051. "year(s)",
  1052. "month(s)",
  1053. "day(s)",
  1054. "hour(s)",
  1055. "minute(s)",
  1056. "second(s)",
  1057. "microsecond(s)",
  1058. "week(s)",
  1059. "quarter(s)",
  1060. ]
  1061. )
  1062. raise AttributeError(
  1063. "range/span over frame {} not supported. Supported frames: {}".format(
  1064. name, supported
  1065. )
  1066. )
  1067. @classmethod
  1068. def _get_iteration_params(cls, end, limit):
  1069. if end is None:
  1070. if limit is None:
  1071. raise ValueError("one of 'end' or 'limit' is required")
  1072. return cls.max, limit
  1073. else:
  1074. if limit is None:
  1075. return end, sys.maxsize
  1076. return end, limit
  1077. Arrow.min = Arrow.fromdatetime(datetime.min)
  1078. Arrow.max = Arrow.fromdatetime(datetime.max)