mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-08-20 13:23:24 -07:00
Fix datestamp and timestamp notification options
This commit is contained in:
parent
4a120e7a54
commit
7484d65dbb
18 changed files with 7771 additions and 186 deletions
8
lib/arrow/__init__.py
Normal file
8
lib/arrow/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .arrow import Arrow
|
||||
from .factory import ArrowFactory
|
||||
from .api import get, now, utcnow
|
||||
|
||||
__version__ = '0.7.0'
|
||||
VERSION = __version__
|
55
lib/arrow/api.py
Normal file
55
lib/arrow/api.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Provides the default implementation of :class:`ArrowFactory <arrow.factory.ArrowFactory>`
|
||||
methods for use as a module API.
|
||||
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from arrow.factory import ArrowFactory
|
||||
|
||||
|
||||
# internal default factory.
|
||||
_factory = ArrowFactory()
|
||||
|
||||
|
||||
def get(*args, **kwargs):
|
||||
''' Implements the default :class:`ArrowFactory <arrow.factory.ArrowFactory>`
|
||||
``get`` method.
|
||||
|
||||
'''
|
||||
|
||||
return _factory.get(*args, **kwargs)
|
||||
|
||||
def utcnow():
|
||||
''' Implements the default :class:`ArrowFactory <arrow.factory.ArrowFactory>`
|
||||
``utcnow`` method.
|
||||
|
||||
'''
|
||||
|
||||
return _factory.utcnow()
|
||||
|
||||
|
||||
def now(tz=None):
|
||||
''' Implements the default :class:`ArrowFactory <arrow.factory.ArrowFactory>`
|
||||
``now`` method.
|
||||
|
||||
'''
|
||||
|
||||
return _factory.now(tz)
|
||||
|
||||
|
||||
def factory(type):
|
||||
''' Returns an :class:`.ArrowFactory` for the specified :class:`Arrow <arrow.arrow.Arrow>`
|
||||
or derived type.
|
||||
|
||||
:param type: the type, :class:`Arrow <arrow.arrow.Arrow>` or derived.
|
||||
|
||||
'''
|
||||
|
||||
return ArrowFactory(type)
|
||||
|
||||
|
||||
__all__ = ['get', 'utcnow', 'now', 'factory', 'iso']
|
||||
|
896
lib/arrow/arrow.py
Normal file
896
lib/arrow/arrow.py
Normal file
|
@ -0,0 +1,896 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Provides the :class:`Arrow <arrow.arrow.Arrow>` class, an enhanced ``datetime``
|
||||
replacement.
|
||||
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from datetime import datetime, timedelta, tzinfo
|
||||
from dateutil import tz as dateutil_tz
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import calendar
|
||||
import sys
|
||||
|
||||
from arrow import util, locales, parser, formatter
|
||||
|
||||
|
||||
class Arrow(object):
|
||||
'''An :class:`Arrow <arrow.arrow.Arrow>` object.
|
||||
|
||||
Implements the ``datetime`` interface, behaving as an aware ``datetime`` while implementing
|
||||
additional functionality.
|
||||
|
||||
:param year: the calendar year.
|
||||
:param month: the calendar month.
|
||||
:param day: the calendar day.
|
||||
:param hour: (optional) the hour. Defaults to 0.
|
||||
:param minute: (optional) the minute, Defaults to 0.
|
||||
:param second: (optional) the second, Defaults to 0.
|
||||
:param microsecond: (optional) the microsecond. Defaults 0.
|
||||
:param tzinfo: (optional) the ``tzinfo`` object. Defaults to ``None``.
|
||||
|
||||
If tzinfo is None, it is assumed to be UTC on creation.
|
||||
|
||||
Usage::
|
||||
|
||||
>>> import arrow
|
||||
>>> arrow.Arrow(2013, 5, 5, 12, 30, 45)
|
||||
<Arrow [2013-05-05T12:30:45+00:00]>
|
||||
|
||||
'''
|
||||
|
||||
resolution = datetime.resolution
|
||||
|
||||
_ATTRS = ['year', 'month', 'day', 'hour', 'minute', 'second', 'microsecond']
|
||||
_ATTRS_PLURAL = ['{0}s'.format(a) for a in _ATTRS]
|
||||
|
||||
def __init__(self, year, month, day, hour=0, minute=0, second=0, microsecond=0,
|
||||
tzinfo=None):
|
||||
|
||||
if util.isstr(tzinfo):
|
||||
tzinfo = parser.TzinfoParser.parse(tzinfo)
|
||||
tzinfo = tzinfo or dateutil_tz.tzutc()
|
||||
|
||||
self._datetime = datetime(year, month, day, hour, minute, second,
|
||||
microsecond, tzinfo)
|
||||
|
||||
|
||||
# factories: single object, both original and from datetime.
|
||||
|
||||
@classmethod
|
||||
def now(cls, tzinfo=None):
|
||||
'''Constructs an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now".
|
||||
|
||||
:param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time.
|
||||
|
||||
'''
|
||||
|
||||
utc = datetime.utcnow().replace(tzinfo=dateutil_tz.tzutc())
|
||||
dt = utc.astimezone(dateutil_tz.tzlocal() if tzinfo is None else tzinfo)
|
||||
|
||||
return cls(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
|
||||
dt.microsecond, dt.tzinfo)
|
||||
|
||||
@classmethod
|
||||
def utcnow(cls):
|
||||
''' Constructs an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in UTC
|
||||
time.
|
||||
|
||||
'''
|
||||
|
||||
dt = datetime.utcnow()
|
||||
|
||||
return cls(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
|
||||
dt.microsecond, dateutil_tz.tzutc())
|
||||
|
||||
@classmethod
|
||||
def fromtimestamp(cls, timestamp, tzinfo=None):
|
||||
''' Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a timestamp.
|
||||
|
||||
:param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either.
|
||||
:param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time.
|
||||
|
||||
'''
|
||||
|
||||
tzinfo = tzinfo or dateutil_tz.tzlocal()
|
||||
timestamp = cls._get_timestamp_from_input(timestamp)
|
||||
dt = datetime.fromtimestamp(timestamp, tzinfo)
|
||||
|
||||
return cls(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
|
||||
dt.microsecond, tzinfo)
|
||||
|
||||
@classmethod
|
||||
def utcfromtimestamp(cls, timestamp):
|
||||
'''Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a timestamp, in UTC time.
|
||||
|
||||
:param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either.
|
||||
|
||||
'''
|
||||
|
||||
timestamp = cls._get_timestamp_from_input(timestamp)
|
||||
dt = datetime.utcfromtimestamp(timestamp)
|
||||
|
||||
return cls(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
|
||||
dt.microsecond, dateutil_tz.tzutc())
|
||||
|
||||
@classmethod
|
||||
def fromdatetime(cls, dt, tzinfo=None):
|
||||
''' Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a ``datetime`` and optional
|
||||
``tzinfo`` object.
|
||||
|
||||
:param dt: the ``datetime``
|
||||
:param tzinfo: (optional) a ``tzinfo`` object. Defaults to UTC.
|
||||
|
||||
'''
|
||||
|
||||
tzinfo = tzinfo or dt.tzinfo or dateutil_tz.tzutc()
|
||||
|
||||
return cls(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
|
||||
dt.microsecond, tzinfo)
|
||||
|
||||
@classmethod
|
||||
def fromdate(cls, date, tzinfo=None):
|
||||
''' Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a ``date`` and optional
|
||||
``tzinfo`` object. Time values are set to 0.
|
||||
|
||||
:param date: the ``date``
|
||||
:param tzinfo: (optional) a ``tzinfo`` object. Defaults to UTC.
|
||||
'''
|
||||
|
||||
tzinfo = tzinfo or dateutil_tz.tzutc()
|
||||
|
||||
return cls(date.year, date.month, date.day, tzinfo=tzinfo)
|
||||
|
||||
@classmethod
|
||||
def strptime(cls, date_str, fmt, tzinfo=None):
|
||||
''' Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a date string and format,
|
||||
in the style of ``datetime.strptime``.
|
||||
|
||||
:param date_str: the date string.
|
||||
:param fmt: the format string.
|
||||
:param tzinfo: (optional) an optional ``tzinfo``
|
||||
'''
|
||||
|
||||
dt = datetime.strptime(date_str, fmt)
|
||||
tzinfo = tzinfo or dt.tzinfo
|
||||
|
||||
return cls(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
|
||||
dt.microsecond, tzinfo)
|
||||
|
||||
|
||||
# factories: ranges and spans
|
||||
|
||||
@classmethod
|
||||
def range(cls, frame, start, end=None, tz=None, limit=None):
|
||||
''' Returns an array of :class:`Arrow <arrow.arrow.Arrow>` objects, representing
|
||||
an iteration of time between two inputs.
|
||||
|
||||
:param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
|
||||
:param start: A datetime expression, the start of the range.
|
||||
:param end: (optional) A datetime expression, the end of the range.
|
||||
:param tz: (optional) A timezone expression. Defaults to UTC.
|
||||
:param limit: (optional) A maximum number of tuples to return.
|
||||
|
||||
**NOTE**: the **end** or **limit** must be provided. Call with **end** alone to
|
||||
return the entire range, with **limit** alone to return a maximum # of results from the
|
||||
start, and with both to cap a range at a maximum # of results.
|
||||
|
||||
Supported frame values: year, quarter, month, week, day, hour, minute, second
|
||||
|
||||
Recognized datetime expressions:
|
||||
|
||||
- An :class:`Arrow <arrow.arrow.Arrow>` object.
|
||||
- A ``datetime`` object.
|
||||
|
||||
Recognized timezone expressions:
|
||||
|
||||
- A ``tzinfo`` object.
|
||||
- A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
|
||||
- A ``str`` in ISO-8601 style, as in '+07:00'.
|
||||
- A ``str``, one of the following: 'local', 'utc', 'UTC'.
|
||||
|
||||
Usage:
|
||||
|
||||
>>> start = datetime(2013, 5, 5, 12, 30)
|
||||
>>> end = datetime(2013, 5, 5, 17, 15)
|
||||
>>> for r in arrow.Arrow.range('hour', start, end):
|
||||
... print repr(r)
|
||||
...
|
||||
<Arrow [2013-05-05T12:30:00+00:00]>
|
||||
<Arrow [2013-05-05T13:30:00+00:00]>
|
||||
<Arrow [2013-05-05T14:30:00+00:00]>
|
||||
<Arrow [2013-05-05T15:30:00+00:00]>
|
||||
<Arrow [2013-05-05T16:30:00+00:00]>
|
||||
|
||||
'''
|
||||
|
||||
_, frame_relative, relative_steps = cls._get_frames(frame)
|
||||
|
||||
tzinfo = cls._get_tzinfo(start.tzinfo if tz is None else tz)
|
||||
|
||||
start = cls._get_datetime(start).replace(tzinfo=tzinfo)
|
||||
end, limit = cls._get_iteration_params(end, limit)
|
||||
end = cls._get_datetime(end).replace(tzinfo=tzinfo)
|
||||
|
||||
current = cls.fromdatetime(start)
|
||||
results = []
|
||||
|
||||
while current <= end and len(results) < limit:
|
||||
results.append(current)
|
||||
|
||||
values = [getattr(current, f) for f in cls._ATTRS]
|
||||
current = cls(*values, tzinfo=tzinfo) + relativedelta(**{frame_relative: relative_steps})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@classmethod
|
||||
def span_range(cls, frame, start, end, tz=None, limit=None):
|
||||
''' Returns an array of tuples, each :class:`Arrow <arrow.arrow.Arrow>` objects,
|
||||
representing a series of timespans between two inputs.
|
||||
|
||||
:param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
|
||||
:param start: A datetime expression, the start of the range.
|
||||
:param end: (optional) A datetime expression, the end of the range.
|
||||
:param tz: (optional) A timezone expression. Defaults to UTC.
|
||||
:param limit: (optional) A maximum number of tuples to return.
|
||||
|
||||
**NOTE**: the **end** or **limit** must be provided. Call with **end** alone to
|
||||
return the entire range, with **limit** alone to return a maximum # of results from the
|
||||
start, and with both to cap a range at a maximum # of results.
|
||||
|
||||
Supported frame values: year, quarter, month, week, day, hour, minute, second
|
||||
|
||||
Recognized datetime expressions:
|
||||
|
||||
- An :class:`Arrow <arrow.arrow.Arrow>` object.
|
||||
- A ``datetime`` object.
|
||||
|
||||
Recognized timezone expressions:
|
||||
|
||||
- A ``tzinfo`` object.
|
||||
- A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
|
||||
- A ``str`` in ISO-8601 style, as in '+07:00'.
|
||||
- A ``str``, one of the following: 'local', 'utc', 'UTC'.
|
||||
|
||||
Usage:
|
||||
|
||||
>>> start = datetime(2013, 5, 5, 12, 30)
|
||||
>>> end = datetime(2013, 5, 5, 17, 15)
|
||||
>>> for r in arrow.Arrow.span_range('hour', start, end):
|
||||
... print r
|
||||
...
|
||||
(<Arrow [2013-05-05T12:00:00+00:00]>, <Arrow [2013-05-05T12:59:59.999999+00:00]>)
|
||||
(<Arrow [2013-05-05T13:00:00+00:00]>, <Arrow [2013-05-05T13:59:59.999999+00:00]>)
|
||||
(<Arrow [2013-05-05T14:00:00+00:00]>, <Arrow [2013-05-05T14:59:59.999999+00:00]>)
|
||||
(<Arrow [2013-05-05T15:00:00+00:00]>, <Arrow [2013-05-05T15:59:59.999999+00:00]>)
|
||||
(<Arrow [2013-05-05T16:00:00+00:00]>, <Arrow [2013-05-05T16:59:59.999999+00:00]>)
|
||||
|
||||
'''
|
||||
tzinfo = cls._get_tzinfo(start.tzinfo if tz is None else tz)
|
||||
start = cls.fromdatetime(start, tzinfo).span(frame)[0]
|
||||
_range = cls.range(frame, start, end, tz, limit)
|
||||
return [r.span(frame) for r in _range]
|
||||
|
||||
|
||||
# representations
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
dt = self._datetime
|
||||
attrs = ', '.join([str(i) for i in [dt.year, dt.month, dt.day, dt.hour, dt.minute,
|
||||
dt.second, dt.microsecond]])
|
||||
|
||||
return '<{0} [{1}]>'.format(self.__class__.__name__, self.__str__())
|
||||
|
||||
def __str__(self):
|
||||
return self._datetime.isoformat()
|
||||
|
||||
def __format__(self, formatstr):
|
||||
|
||||
if len(formatstr) > 0:
|
||||
return self.format(formatstr)
|
||||
|
||||
return str(self)
|
||||
|
||||
def __hash__(self):
|
||||
return self._datetime.__hash__()
|
||||
|
||||
|
||||
# attributes & properties
|
||||
|
||||
def __getattr__(self, name):
|
||||
|
||||
if name == 'week':
|
||||
return self.isocalendar()[1]
|
||||
|
||||
if not name.startswith('_'):
|
||||
value = getattr(self._datetime, name, None)
|
||||
|
||||
if value is not None:
|
||||
return value
|
||||
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
@property
|
||||
def tzinfo(self):
|
||||
''' Gets the ``tzinfo`` of the :class:`Arrow <arrow.arrow.Arrow>` object. '''
|
||||
|
||||
return self._datetime.tzinfo
|
||||
|
||||
@tzinfo.setter
|
||||
def tzinfo(self, tzinfo):
|
||||
''' Sets the ``tzinfo`` of the :class:`Arrow <arrow.arrow.Arrow>` object. '''
|
||||
|
||||
self._datetime = self._datetime.replace(tzinfo=tzinfo)
|
||||
|
||||
@property
|
||||
def datetime(self):
|
||||
''' Returns a datetime representation of the :class:`Arrow <arrow.arrow.Arrow>` object. '''
|
||||
|
||||
return self._datetime
|
||||
|
||||
@property
|
||||
def naive(self):
|
||||
''' Returns a naive datetime representation of the :class:`Arrow <arrow.arrow.Arrow>` object. '''
|
||||
|
||||
return self._datetime.replace(tzinfo=None)
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
''' Returns a timestamp representation of the :class:`Arrow <arrow.arrow.Arrow>` object. '''
|
||||
|
||||
return calendar.timegm(self._datetime.utctimetuple())
|
||||
|
||||
@property
|
||||
def float_timestamp(self):
|
||||
''' Returns a floating-point representation of the :class:`Arrow <arrow.arrow.Arrow>` object. '''
|
||||
|
||||
return self.timestamp + float(self.microsecond) / 1000000
|
||||
|
||||
|
||||
# mutation and duplication.
|
||||
|
||||
def clone(self):
|
||||
''' Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, cloned from the current one.
|
||||
|
||||
Usage:
|
||||
|
||||
>>> arw = arrow.utcnow()
|
||||
>>> cloned = arw.clone()
|
||||
|
||||
'''
|
||||
|
||||
return self.fromdatetime(self._datetime)
|
||||
|
||||
def replace(self, **kwargs):
|
||||
''' Returns a new :class:`Arrow <arrow.arrow.Arrow>` object with attributes updated
|
||||
according to inputs.
|
||||
|
||||
Use single property names to set their value absolutely:
|
||||
|
||||
>>> import arrow
|
||||
>>> arw = arrow.utcnow()
|
||||
>>> arw
|
||||
<Arrow [2013-05-11T22:27:34.787885+00:00]>
|
||||
>>> arw.replace(year=2014, month=6)
|
||||
<Arrow [2014-06-11T22:27:34.787885+00:00]>
|
||||
|
||||
Use plural property names to shift their current value relatively:
|
||||
|
||||
>>> arw.replace(years=1, months=-1)
|
||||
<Arrow [2014-04-11T22:27:34.787885+00:00]>
|
||||
|
||||
You can also provide a timezone expression can also be replaced:
|
||||
|
||||
>>> arw.replace(tzinfo=tz.tzlocal())
|
||||
<Arrow [2013-05-11T22:27:34.787885-07:00]>
|
||||
|
||||
Recognized timezone expressions:
|
||||
|
||||
- A ``tzinfo`` object.
|
||||
- A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
|
||||
- A ``str`` in ISO-8601 style, as in '+07:00'.
|
||||
- A ``str``, one of the following: 'local', 'utc', 'UTC'.
|
||||
|
||||
'''
|
||||
|
||||
absolute_kwargs = {}
|
||||
relative_kwargs = {}
|
||||
|
||||
for key, value in kwargs.items():
|
||||
|
||||
if key in self._ATTRS:
|
||||
absolute_kwargs[key] = value
|
||||
elif key in self._ATTRS_PLURAL or key == 'weeks':
|
||||
relative_kwargs[key] = value
|
||||
elif key == 'week':
|
||||
raise AttributeError('setting absolute week is not supported')
|
||||
elif key !='tzinfo':
|
||||
raise AttributeError()
|
||||
|
||||
current = self._datetime.replace(**absolute_kwargs)
|
||||
current += relativedelta(**relative_kwargs)
|
||||
|
||||
tzinfo = kwargs.get('tzinfo')
|
||||
|
||||
if tzinfo is not None:
|
||||
tzinfo = self._get_tzinfo(tzinfo)
|
||||
current = current.replace(tzinfo=tzinfo)
|
||||
|
||||
return self.fromdatetime(current)
|
||||
|
||||
def to(self, tz):
|
||||
''' Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, converted to the target
|
||||
timezone.
|
||||
|
||||
:param tz: an expression representing a timezone.
|
||||
|
||||
Recognized timezone expressions:
|
||||
|
||||
- A ``tzinfo`` object.
|
||||
- A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
|
||||
- A ``str`` in ISO-8601 style, as in '+07:00'.
|
||||
- A ``str``, one of the following: 'local', 'utc', 'UTC'.
|
||||
|
||||
Usage::
|
||||
|
||||
>>> utc = arrow.utcnow()
|
||||
>>> utc
|
||||
<Arrow [2013-05-09T03:49:12.311072+00:00]>
|
||||
|
||||
>>> utc.to('US/Pacific')
|
||||
<Arrow [2013-05-08T20:49:12.311072-07:00]>
|
||||
|
||||
>>> utc.to(tz.tzlocal())
|
||||
<Arrow [2013-05-08T20:49:12.311072-07:00]>
|
||||
|
||||
>>> utc.to('-07:00')
|
||||
<Arrow [2013-05-08T20:49:12.311072-07:00]>
|
||||
|
||||
>>> utc.to('local')
|
||||
<Arrow [2013-05-08T20:49:12.311072-07:00]>
|
||||
|
||||
>>> utc.to('local').to('utc')
|
||||
<Arrow [2013-05-09T03:49:12.311072+00:00]>
|
||||
|
||||
'''
|
||||
|
||||
if not isinstance(tz, tzinfo):
|
||||
tz = parser.TzinfoParser.parse(tz)
|
||||
|
||||
dt = self._datetime.astimezone(tz)
|
||||
|
||||
return self.__class__(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
|
||||
dt.microsecond, dt.tzinfo)
|
||||
|
||||
def span(self, frame, count=1):
|
||||
''' Returns two new :class:`Arrow <arrow.arrow.Arrow>` objects, representing the timespan
|
||||
of the :class:`Arrow <arrow.arrow.Arrow>` object in a given timeframe.
|
||||
|
||||
:param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
|
||||
:param count: (optional) the number of frames to span.
|
||||
|
||||
Supported frame values: year, quarter, month, week, day, hour, minute, second
|
||||
|
||||
Usage::
|
||||
|
||||
>>> arrow.utcnow()
|
||||
<Arrow [2013-05-09T03:32:36.186203+00:00]>
|
||||
|
||||
>>> arrow.utcnow().span('hour')
|
||||
(<Arrow [2013-05-09T03:00:00+00:00]>, <Arrow [2013-05-09T03:59:59.999999+00:00]>)
|
||||
|
||||
>>> arrow.utcnow().span('day')
|
||||
(<Arrow [2013-05-09T00:00:00+00:00]>, <Arrow [2013-05-09T23:59:59.999999+00:00]>)
|
||||
|
||||
>>> arrow.utcnow().span('day', count=2)
|
||||
(<Arrow [2013-05-09T00:00:00+00:00]>, <Arrow [2013-05-10T23:59:59.999999+00:00]>)
|
||||
|
||||
'''
|
||||
|
||||
frame_absolute, frame_relative, relative_steps = self._get_frames(frame)
|
||||
|
||||
if frame_absolute == 'week':
|
||||
attr = 'day'
|
||||
elif frame_absolute == 'quarter':
|
||||
attr = 'month'
|
||||
else:
|
||||
attr = frame_absolute
|
||||
|
||||
index = self._ATTRS.index(attr)
|
||||
frames = self._ATTRS[:index + 1]
|
||||
|
||||
values = [getattr(self, f) for f in frames]
|
||||
|
||||
for i in range(3 - len(values)):
|
||||
values.append(1)
|
||||
|
||||
floor = self.__class__(*values, tzinfo=self.tzinfo)
|
||||
|
||||
if frame_absolute == 'week':
|
||||
floor = floor + relativedelta(days=-(self.isoweekday() - 1))
|
||||
elif frame_absolute == 'quarter':
|
||||
floor = floor + relativedelta(months=-((self.month - 1) % 3))
|
||||
|
||||
ceil = floor + relativedelta(
|
||||
**{frame_relative: count * relative_steps}) + relativedelta(microseconds=-1)
|
||||
|
||||
return floor, ceil
|
||||
|
||||
def floor(self, frame):
|
||||
''' Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, representing the "floor"
|
||||
of the timespan of the :class:`Arrow <arrow.arrow.Arrow>` object in a given timeframe.
|
||||
Equivalent to the first element in the 2-tuple returned by
|
||||
:func:`span <arrow.arrow.Arrow.span>`.
|
||||
|
||||
:param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
|
||||
|
||||
Usage::
|
||||
|
||||
>>> arrow.utcnow().floor('hour')
|
||||
<Arrow [2013-05-09T03:00:00+00:00]>
|
||||
'''
|
||||
|
||||
return self.span(frame)[0]
|
||||
|
||||
def ceil(self, frame):
|
||||
''' Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, representing the "ceiling"
|
||||
of the timespan of the :class:`Arrow <arrow.arrow.Arrow>` object in a given timeframe.
|
||||
Equivalent to the second element in the 2-tuple returned by
|
||||
:func:`span <arrow.arrow.Arrow.span>`.
|
||||
|
||||
:param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
|
||||
|
||||
Usage::
|
||||
|
||||
>>> arrow.utcnow().ceil('hour')
|
||||
<Arrow [2013-05-09T03:59:59.999999+00:00]>
|
||||
'''
|
||||
|
||||
return self.span(frame)[1]
|
||||
|
||||
|
||||
# string output and formatting.
|
||||
|
||||
def format(self, fmt='YYYY-MM-DD HH:mm:ssZZ', locale='en_us'):
|
||||
''' Returns a string representation of the :class:`Arrow <arrow.arrow.Arrow>` object,
|
||||
formatted according to a format string.
|
||||
|
||||
:param fmt: the format string.
|
||||
|
||||
Usage::
|
||||
|
||||
>>> arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ')
|
||||
'2013-05-09 03:56:47 -00:00'
|
||||
|
||||
>>> arrow.utcnow().format('X')
|
||||
'1368071882'
|
||||
|
||||
>>> arrow.utcnow().format('MMMM DD, YYYY')
|
||||
'May 09, 2013'
|
||||
|
||||
>>> arrow.utcnow().format()
|
||||
'2013-05-09 03:56:47 -00:00'
|
||||
|
||||
'''
|
||||
|
||||
return formatter.DateTimeFormatter(locale).format(self._datetime, fmt)
|
||||
|
||||
|
||||
def humanize(self, other=None, locale='en_us', only_distance=False):
|
||||
''' Returns a localized, humanized representation of a relative difference in time.
|
||||
|
||||
:param other: (optional) an :class:`Arrow <arrow.arrow.Arrow>` or ``datetime`` object.
|
||||
Defaults to now in the current :class:`Arrow <arrow.arrow.Arrow>` object's timezone.
|
||||
:param locale: (optional) a ``str`` specifying a locale. Defaults to 'en_us'.
|
||||
:param only_distance: (optional) returns only time difference eg: "11 seconds" without "in" or "ago" part.
|
||||
Usage::
|
||||
|
||||
>>> earlier = arrow.utcnow().replace(hours=-2)
|
||||
>>> earlier.humanize()
|
||||
'2 hours ago'
|
||||
|
||||
>>> later = later = earlier.replace(hours=4)
|
||||
>>> later.humanize(earlier)
|
||||
'in 4 hours'
|
||||
|
||||
'''
|
||||
|
||||
locale = locales.get_locale(locale)
|
||||
|
||||
if other is None:
|
||||
utc = datetime.utcnow().replace(tzinfo=dateutil_tz.tzutc())
|
||||
dt = utc.astimezone(self._datetime.tzinfo)
|
||||
|
||||
elif isinstance(other, Arrow):
|
||||
dt = other._datetime
|
||||
|
||||
elif isinstance(other, datetime):
|
||||
if other.tzinfo is None:
|
||||
dt = other.replace(tzinfo=self._datetime.tzinfo)
|
||||
else:
|
||||
dt = other.astimezone(self._datetime.tzinfo)
|
||||
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
delta = int(util.total_seconds(self._datetime - dt))
|
||||
sign = -1 if delta < 0 else 1
|
||||
diff = abs(delta)
|
||||
delta = diff
|
||||
|
||||
if diff < 10:
|
||||
return locale.describe('now', only_distance=only_distance)
|
||||
|
||||
if diff < 45:
|
||||
return locale.describe('seconds', sign, only_distance=only_distance)
|
||||
|
||||
elif diff < 90:
|
||||
return locale.describe('minute', sign, only_distance=only_distance)
|
||||
elif diff < 2700:
|
||||
minutes = sign * int(max(delta / 60, 2))
|
||||
return locale.describe('minutes', minutes, only_distance=only_distance)
|
||||
|
||||
elif diff < 5400:
|
||||
return locale.describe('hour', sign, only_distance=only_distance)
|
||||
elif diff < 79200:
|
||||
hours = sign * int(max(delta / 3600, 2))
|
||||
return locale.describe('hours', hours, only_distance=only_distance)
|
||||
|
||||
elif diff < 129600:
|
||||
return locale.describe('day', sign, only_distance=only_distance)
|
||||
elif diff < 2160000:
|
||||
days = sign * int(max(delta / 86400, 2))
|
||||
return locale.describe('days', days, only_distance=only_distance)
|
||||
|
||||
elif diff < 3888000:
|
||||
return locale.describe('month', sign, only_distance=only_distance)
|
||||
elif diff < 29808000:
|
||||
self_months = self._datetime.year * 12 + self._datetime.month
|
||||
other_months = dt.year * 12 + dt.month
|
||||
months = sign * abs(other_months - self_months)
|
||||
|
||||
return locale.describe('months', months, only_distance=only_distance)
|
||||
|
||||
elif diff < 47260800:
|
||||
return locale.describe('year', sign, only_distance=only_distance)
|
||||
else:
|
||||
years = sign * int(max(delta / 31536000, 2))
|
||||
return locale.describe('years', years, only_distance=only_distance)
|
||||
|
||||
|
||||
# math
|
||||
|
||||
def __add__(self, other):
|
||||
|
||||
if isinstance(other, (timedelta, relativedelta)):
|
||||
return self.fromdatetime(self._datetime + other, self._datetime.tzinfo)
|
||||
|
||||
raise TypeError()
|
||||
|
||||
def __radd__(self, other):
|
||||
return self.__add__(other)
|
||||
|
||||
def __sub__(self, other):
|
||||
|
||||
if isinstance(other, timedelta):
|
||||
return self.fromdatetime(self._datetime - other, self._datetime.tzinfo)
|
||||
|
||||
elif isinstance(other, datetime):
|
||||
return self._datetime - other
|
||||
|
||||
elif isinstance(other, Arrow):
|
||||
return self._datetime - other._datetime
|
||||
|
||||
raise TypeError()
|
||||
|
||||
def __rsub__(self, other):
|
||||
return self.__sub__(other)
|
||||
|
||||
|
||||
# comparisons
|
||||
|
||||
def _cmperror(self, other):
|
||||
raise TypeError('can\'t compare \'{0}\' to \'{1}\''.format(
|
||||
type(self), type(other)))
|
||||
|
||||
def __eq__(self, other):
|
||||
|
||||
if not isinstance(other, (Arrow, datetime)):
|
||||
return False
|
||||
|
||||
other = self._get_datetime(other)
|
||||
|
||||
return self._datetime == self._get_datetime(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
|
||||
if not isinstance(other, (Arrow, datetime)):
|
||||
self._cmperror(other)
|
||||
|
||||
return self._datetime > self._get_datetime(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
|
||||
if not isinstance(other, (Arrow, datetime)):
|
||||
self._cmperror(other)
|
||||
|
||||
return self._datetime >= self._get_datetime(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
|
||||
if not isinstance(other, (Arrow, datetime)):
|
||||
self._cmperror(other)
|
||||
|
||||
return self._datetime < self._get_datetime(other)
|
||||
|
||||
def __le__(self, other):
|
||||
|
||||
if not isinstance(other, (Arrow, datetime)):
|
||||
self._cmperror(other)
|
||||
|
||||
return self._datetime <= self._get_datetime(other)
|
||||
|
||||
|
||||
# datetime methods
|
||||
|
||||
def date(self):
|
||||
''' Returns a ``date`` object with the same year, month and day. '''
|
||||
|
||||
return self._datetime.date()
|
||||
|
||||
def time(self):
|
||||
''' Returns a ``time`` object with the same hour, minute, second, microsecond. '''
|
||||
|
||||
return self._datetime.time()
|
||||
|
||||
def timetz(self):
|
||||
''' Returns a ``time`` object with the same hour, minute, second, microsecond and tzinfo. '''
|
||||
|
||||
return self._datetime.timetz()
|
||||
|
||||
def astimezone(self, tz):
|
||||
''' Returns a ``datetime`` object, adjusted to the specified tzinfo.
|
||||
|
||||
:param tz: a ``tzinfo`` object.
|
||||
|
||||
'''
|
||||
|
||||
return self._datetime.astimezone(tz)
|
||||
|
||||
def utcoffset(self):
|
||||
''' Returns a ``timedelta`` object representing the whole number of minutes difference from UTC time. '''
|
||||
|
||||
return self._datetime.utcoffset()
|
||||
|
||||
def dst(self):
|
||||
''' Returns the daylight savings time adjustment. '''
|
||||
return self._datetime.dst()
|
||||
|
||||
def timetuple(self):
|
||||
''' Returns a ``time.struct_time``, in the current timezone. '''
|
||||
|
||||
return self._datetime.timetuple()
|
||||
|
||||
def utctimetuple(self):
|
||||
''' Returns a ``time.struct_time``, in UTC time. '''
|
||||
|
||||
return self._datetime.utctimetuple()
|
||||
|
||||
def toordinal(self):
|
||||
''' Returns the proleptic Gregorian ordinal of the date. '''
|
||||
|
||||
return self._datetime.toordinal()
|
||||
|
||||
def weekday(self):
|
||||
''' Returns the day of the week as an integer (0-6). '''
|
||||
|
||||
return self._datetime.weekday()
|
||||
|
||||
def isoweekday(self):
|
||||
''' Returns the ISO day of the week as an integer (1-7). '''
|
||||
|
||||
return self._datetime.isoweekday()
|
||||
|
||||
def isocalendar(self):
|
||||
''' Returns a 3-tuple, (ISO year, ISO week number, ISO weekday). '''
|
||||
|
||||
return self._datetime.isocalendar()
|
||||
|
||||
def isoformat(self, sep='T'):
|
||||
'''Returns an ISO 8601 formatted representation of the date and time. '''
|
||||
|
||||
return self._datetime.isoformat(sep)
|
||||
|
||||
def ctime(self):
|
||||
''' Returns a ctime formatted representation of the date and time. '''
|
||||
|
||||
return self._datetime.ctime()
|
||||
|
||||
def strftime(self, format):
|
||||
''' Formats in the style of ``datetime.strptime``.
|
||||
|
||||
:param format: the format string.
|
||||
|
||||
'''
|
||||
|
||||
return self._datetime.strftime(format)
|
||||
|
||||
def for_json(self):
|
||||
'''Serializes for the ``for_json`` protocol of simplejson.'''
|
||||
return self.isoformat()
|
||||
|
||||
# internal tools.
|
||||
|
||||
@staticmethod
|
||||
def _get_tzinfo(tz_expr):
|
||||
|
||||
if tz_expr is None:
|
||||
return dateutil_tz.tzutc()
|
||||
if isinstance(tz_expr, tzinfo):
|
||||
return tz_expr
|
||||
else:
|
||||
try:
|
||||
return parser.TzinfoParser.parse(tz_expr)
|
||||
except parser.ParserError:
|
||||
raise ValueError('\'{0}\' not recognized as a timezone'.format(
|
||||
tz_expr))
|
||||
|
||||
@classmethod
|
||||
def _get_datetime(cls, expr):
|
||||
|
||||
if isinstance(expr, Arrow):
|
||||
return expr.datetime
|
||||
|
||||
if isinstance(expr, datetime):
|
||||
return expr
|
||||
|
||||
try:
|
||||
expr = float(expr)
|
||||
return cls.utcfromtimestamp(expr).datetime
|
||||
except:
|
||||
raise ValueError(
|
||||
'\'{0}\' not recognized as a timestamp or datetime'.format(expr))
|
||||
|
||||
@classmethod
|
||||
def _get_frames(cls, name):
|
||||
|
||||
if name in cls._ATTRS:
|
||||
return name, '{0}s'.format(name), 1
|
||||
|
||||
elif name in ['week', 'weeks']:
|
||||
return 'week', 'weeks', 1
|
||||
elif name in ['quarter', 'quarters']:
|
||||
return 'quarter', 'months', 3
|
||||
|
||||
raise AttributeError()
|
||||
|
||||
@classmethod
|
||||
def _get_iteration_params(cls, end, limit):
|
||||
|
||||
if end is None:
|
||||
|
||||
if limit is None:
|
||||
raise Exception('one of \'end\' or \'limit\' is required')
|
||||
|
||||
return cls.max, limit
|
||||
|
||||
else:
|
||||
return end, sys.maxsize
|
||||
|
||||
@staticmethod
|
||||
def _get_timestamp_from_input(timestamp):
|
||||
|
||||
try:
|
||||
return float(timestamp)
|
||||
except:
|
||||
raise ValueError('cannot parse \'{0}\' as a timestamp'.format(timestamp))
|
||||
|
||||
Arrow.min = Arrow.fromdatetime(datetime.min)
|
||||
Arrow.max = Arrow.fromdatetime(datetime.max)
|
254
lib/arrow/factory.py
Normal file
254
lib/arrow/factory.py
Normal file
|
@ -0,0 +1,254 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Implements the :class:`ArrowFactory <arrow.factory.ArrowFactory>` class,
|
||||
providing factory methods for common :class:`Arrow <arrow.arrow.Arrow>`
|
||||
construction scenarios.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from arrow.arrow import Arrow
|
||||
from arrow import parser
|
||||
from arrow.util import is_timestamp, isstr
|
||||
|
||||
from datetime import datetime, tzinfo, date
|
||||
from dateutil import tz as dateutil_tz
|
||||
from time import struct_time
|
||||
import calendar
|
||||
|
||||
|
||||
class ArrowFactory(object):
|
||||
''' A factory for generating :class:`Arrow <arrow.arrow.Arrow>` objects.
|
||||
|
||||
:param type: (optional) the :class:`Arrow <arrow.arrow.Arrow>`-based class to construct from.
|
||||
Defaults to :class:`Arrow <arrow.arrow.Arrow>`.
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, type=Arrow):
|
||||
self.type = type
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
''' Returns an :class:`Arrow <arrow.arrow.Arrow>` object based on flexible inputs.
|
||||
|
||||
Usage::
|
||||
|
||||
>>> import arrow
|
||||
|
||||
**No inputs** to get current UTC time::
|
||||
|
||||
>>> arrow.get()
|
||||
<Arrow [2013-05-08T05:51:43.316458+00:00]>
|
||||
|
||||
**None** to also get current UTC time::
|
||||
|
||||
>>> arrow.get(None)
|
||||
<Arrow [2013-05-08T05:51:43.316458+00:00]>
|
||||
|
||||
**One** :class:`Arrow <arrow.arrow.Arrow>` object, to get a copy.
|
||||
|
||||
>>> arw = arrow.utcnow()
|
||||
>>> arrow.get(arw)
|
||||
<Arrow [2013-10-23T15:21:54.354846+00:00]>
|
||||
|
||||
**One** ``str``, ``float``, or ``int``, convertible to a floating-point timestamp, to get that timestamp in UTC::
|
||||
|
||||
>>> arrow.get(1367992474.293378)
|
||||
<Arrow [2013-05-08T05:54:34.293378+00:00]>
|
||||
|
||||
>>> arrow.get(1367992474)
|
||||
<Arrow [2013-05-08T05:54:34+00:00]>
|
||||
|
||||
>>> arrow.get('1367992474.293378')
|
||||
<Arrow [2013-05-08T05:54:34.293378+00:00]>
|
||||
|
||||
>>> arrow.get('1367992474')
|
||||
<Arrow [2013-05-08T05:54:34+0struct_time0:00]>
|
||||
|
||||
**One** ISO-8601-formatted ``str``, to parse it::
|
||||
|
||||
>>> arrow.get('2013-09-29T01:26:43.830580')
|
||||
<Arrow [2013-09-29T01:26:43.830580+00:00]>
|
||||
|
||||
**One** ``tzinfo``, to get the current time in that timezone::
|
||||
|
||||
>>> arrow.get(tz.tzlocal())
|
||||
<Arrow [2013-05-07T22:57:28.484717-07:00]>
|
||||
|
||||
**One** naive ``datetime``, to get that datetime in UTC::
|
||||
|
||||
>>> arrow.get(datetime(2013, 5, 5))
|
||||
<Arrow [2013-05-05T00:00:00+00:00]>
|
||||
|
||||
**One** aware ``datetime``, to get that datetime::
|
||||
|
||||
>>> arrow.get(datetime(2013, 5, 5, tzinfo=tz.tzlocal()))
|
||||
<Arrow [2013-05-05T00:00:00-07:00]>
|
||||
|
||||
**One** naive ``date``, to get that date in UTC::
|
||||
|
||||
>>> arrow.get(date(2013, 5, 5))
|
||||
<Arrow [2013-05-05T00:00:00+00:00]>
|
||||
|
||||
**Two** arguments, a naive or aware ``datetime``, and a timezone expression (as above)::
|
||||
|
||||
>>> arrow.get(datetime(2013, 5, 5), 'US/Pacific')
|
||||
<Arrow [2013-05-05T00:00:00-07:00]>
|
||||
|
||||
**Two** arguments, a naive ``date``, and a timezone expression (as above)::
|
||||
|
||||
>>> arrow.get(date(2013, 5, 5), 'US/Pacific')
|
||||
<Arrow [2013-05-05T00:00:00-07:00]>
|
||||
|
||||
**Two** arguments, both ``str``, to parse the first according to the format of the second::
|
||||
|
||||
>>> arrow.get('2013-05-05 12:30:45', 'YYYY-MM-DD HH:mm:ss')
|
||||
<Arrow [2013-05-05T12:30:45+00:00]>
|
||||
|
||||
**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'])
|
||||
<Arrow [2013-05-05T12:30:45+00:00]>
|
||||
|
||||
**Three or more** arguments, as for the constructor of a ``datetime``::
|
||||
|
||||
>>> arrow.get(2013, 5, 5, 12, 30, 45)
|
||||
<Arrow [2013-05-05T12:30:45+00:00]>
|
||||
|
||||
**One** time.struct time::
|
||||
>>> arrow.get(gmtime(0))
|
||||
<Arrow [1970-01-01T00:00:00+00:00]>
|
||||
|
||||
'''
|
||||
|
||||
arg_count = len(args)
|
||||
locale = kwargs.get('locale', 'en_us')
|
||||
tz = kwargs.get('tzinfo', None)
|
||||
|
||||
# () -> now, @ utc.
|
||||
if arg_count == 0:
|
||||
if isinstance(tz, tzinfo):
|
||||
return self.type.now(tz)
|
||||
return self.type.utcnow()
|
||||
|
||||
if arg_count == 1:
|
||||
arg = args[0]
|
||||
|
||||
# (None) -> now, @ utc.
|
||||
if arg is None:
|
||||
return self.type.utcnow()
|
||||
|
||||
# try (int, float, str(int), str(float)) -> utc, from timestamp.
|
||||
if is_timestamp(arg):
|
||||
return self.type.utcfromtimestamp(arg)
|
||||
|
||||
# (Arrow) -> from the object's datetime.
|
||||
if isinstance(arg, Arrow):
|
||||
return self.type.fromdatetime(arg.datetime)
|
||||
|
||||
# (datetime) -> from datetime.
|
||||
if isinstance(arg, datetime):
|
||||
return self.type.fromdatetime(arg)
|
||||
|
||||
# (date) -> from date.
|
||||
if isinstance(arg, date):
|
||||
return self.type.fromdate(arg)
|
||||
|
||||
# (tzinfo) -> now, @ tzinfo.
|
||||
elif isinstance(arg, tzinfo):
|
||||
return self.type.now(arg)
|
||||
|
||||
# (str) -> now, @ tzinfo.
|
||||
elif isstr(arg):
|
||||
dt = parser.DateTimeParser(locale).parse_iso(arg)
|
||||
return self.type.fromdatetime(dt)
|
||||
|
||||
# (struct_time) -> from struct_time
|
||||
elif isinstance(arg, struct_time):
|
||||
return self.type.utcfromtimestamp(calendar.timegm(arg))
|
||||
|
||||
else:
|
||||
raise TypeError('Can\'t parse single argument type of \'{0}\''.format(type(arg)))
|
||||
|
||||
elif arg_count == 2:
|
||||
|
||||
arg_1, arg_2 = args[0], args[1]
|
||||
|
||||
if isinstance(arg_1, datetime):
|
||||
|
||||
# (datetime, tzinfo) -> fromdatetime @ tzinfo/string.
|
||||
if isinstance(arg_2, tzinfo) or isstr(arg_2):
|
||||
return self.type.fromdatetime(arg_1, arg_2)
|
||||
else:
|
||||
raise TypeError('Can\'t parse two arguments of types \'datetime\', \'{0}\''.format(
|
||||
type(arg_2)))
|
||||
|
||||
# (date, tzinfo/str) -> fromdate @ tzinfo/string.
|
||||
elif isinstance(arg_1, date):
|
||||
|
||||
if isinstance(arg_2, tzinfo) or isstr(arg_2):
|
||||
return self.type.fromdate(arg_1, tzinfo=arg_2)
|
||||
else:
|
||||
raise TypeError('Can\'t parse two arguments of types \'date\', \'{0}\''.format(
|
||||
type(arg_2)))
|
||||
|
||||
# (str, format) -> parse.
|
||||
elif isstr(arg_1) and (isstr(arg_2) or isinstance(arg_2, list)):
|
||||
dt = parser.DateTimeParser(locale).parse(args[0], args[1])
|
||||
return self.type.fromdatetime(dt, tzinfo=tz)
|
||||
|
||||
else:
|
||||
raise TypeError('Can\'t parse two arguments of types \'{0}\', \'{1}\''.format(
|
||||
type(arg_1), type(arg_2)))
|
||||
|
||||
# 3+ args -> datetime-like via constructor.
|
||||
else:
|
||||
return self.type(*args, **kwargs)
|
||||
|
||||
def utcnow(self):
|
||||
'''Returns an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in UTC time.
|
||||
|
||||
Usage::
|
||||
|
||||
>>> import arrow
|
||||
>>> arrow.utcnow()
|
||||
<Arrow [2013-05-08T05:19:07.018993+00:00]>
|
||||
'''
|
||||
|
||||
return self.type.utcnow()
|
||||
|
||||
def now(self, tz=None):
|
||||
'''Returns an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now".
|
||||
|
||||
:param tz: (optional) An expression representing a timezone. Defaults to local time.
|
||||
|
||||
Recognized timezone expressions:
|
||||
|
||||
- A ``tzinfo`` object.
|
||||
- A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
|
||||
- A ``str`` in ISO-8601 style, as in '+07:00'.
|
||||
- A ``str``, one of the following: 'local', 'utc', 'UTC'.
|
||||
|
||||
Usage::
|
||||
|
||||
>>> import arrow
|
||||
>>> arrow.now()
|
||||
<Arrow [2013-05-07T22:19:11.363410-07:00]>
|
||||
|
||||
>>> arrow.now('US/Pacific')
|
||||
<Arrow [2013-05-07T22:19:15.251821-07:00]>
|
||||
|
||||
>>> arrow.now('+02:00')
|
||||
<Arrow [2013-05-08T07:19:25.618646+02:00]>
|
||||
|
||||
>>> arrow.now('local')
|
||||
<Arrow [2013-05-07T22:19:39.130059-07:00]>
|
||||
'''
|
||||
|
||||
if tz is None:
|
||||
tz = dateutil_tz.tzlocal()
|
||||
elif not isinstance(tz, tzinfo):
|
||||
tz = parser.TzinfoParser.parse(tz)
|
||||
|
||||
return self.type.now(tz)
|
105
lib/arrow/formatter.py
Normal file
105
lib/arrow/formatter.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
|
||||
import calendar
|
||||
import re
|
||||
from dateutil import tz as dateutil_tz
|
||||
from arrow import util, locales
|
||||
|
||||
|
||||
class DateTimeFormatter(object):
|
||||
|
||||
_FORMAT_RE = re.compile('(YYY?Y?|MM?M?M?|Do|DD?D?D?|d?dd?d?|HH?|hh?|mm?|ss?|SS?S?S?S?S?|ZZ?|a|A|X)')
|
||||
|
||||
def __init__(self, locale='en_us'):
|
||||
|
||||
self.locale = locales.get_locale(locale)
|
||||
|
||||
def format(cls, dt, fmt):
|
||||
|
||||
return cls._FORMAT_RE.sub(lambda m: cls._format_token(dt, m.group(0)), fmt)
|
||||
|
||||
def _format_token(self, dt, token):
|
||||
|
||||
if token == 'YYYY':
|
||||
return self.locale.year_full(dt.year)
|
||||
if token == 'YY':
|
||||
return self.locale.year_abbreviation(dt.year)
|
||||
|
||||
if token == 'MMMM':
|
||||
return self.locale.month_name(dt.month)
|
||||
if token == 'MMM':
|
||||
return self.locale.month_abbreviation(dt.month)
|
||||
if token == 'MM':
|
||||
return '{0:02d}'.format(dt.month)
|
||||
if token == 'M':
|
||||
return str(dt.month)
|
||||
|
||||
if token == 'DDDD':
|
||||
return '{0:03d}'.format(dt.timetuple().tm_yday)
|
||||
if token == 'DDD':
|
||||
return str(dt.timetuple().tm_yday)
|
||||
if token == 'DD':
|
||||
return '{0:02d}'.format(dt.day)
|
||||
if token == 'D':
|
||||
return str(dt.day)
|
||||
|
||||
if token == 'Do':
|
||||
return self.locale.ordinal_number(dt.day)
|
||||
|
||||
if token == 'dddd':
|
||||
return self.locale.day_name(dt.isoweekday())
|
||||
if token == 'ddd':
|
||||
return self.locale.day_abbreviation(dt.isoweekday())
|
||||
if token == 'd':
|
||||
return str(dt.isoweekday())
|
||||
|
||||
if token == 'HH':
|
||||
return '{0:02d}'.format(dt.hour)
|
||||
if token == 'H':
|
||||
return str(dt.hour)
|
||||
if token == 'hh':
|
||||
return '{0:02d}'.format(dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12))
|
||||
if token == 'h':
|
||||
return str(dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12))
|
||||
|
||||
if token == 'mm':
|
||||
return '{0:02d}'.format(dt.minute)
|
||||
if token == 'm':
|
||||
return str(dt.minute)
|
||||
|
||||
if token == 'ss':
|
||||
return '{0:02d}'.format(dt.second)
|
||||
if token == 's':
|
||||
return str(dt.second)
|
||||
|
||||
if token == 'SSSSSS':
|
||||
return str('{0:06d}'.format(int(dt.microsecond)))
|
||||
if token == 'SSSSS':
|
||||
return str('{0:05d}'.format(int(dt.microsecond / 10)))
|
||||
if token == 'SSSS':
|
||||
return str('{0:04d}'.format(int(dt.microsecond / 100)))
|
||||
if token == 'SSS':
|
||||
return str('{0:03d}'.format(int(dt.microsecond / 1000)))
|
||||
if token == 'SS':
|
||||
return str('{0:02d}'.format(int(dt.microsecond / 10000)))
|
||||
if token == 'S':
|
||||
return str(int(dt.microsecond / 100000))
|
||||
|
||||
if token == 'X':
|
||||
return str(calendar.timegm(dt.utctimetuple()))
|
||||
|
||||
if token in ['ZZ', 'Z']:
|
||||
separator = ':' if token == 'ZZ' else ''
|
||||
tz = dateutil_tz.tzutc() if dt.tzinfo is None else dt.tzinfo
|
||||
total_minutes = int(util.total_seconds(tz.utcoffset(dt)) / 60)
|
||||
|
||||
sign = '+' if total_minutes > 0 else '-'
|
||||
total_minutes = abs(total_minutes)
|
||||
hour, minute = divmod(total_minutes, 60)
|
||||
|
||||
return '{0}{1:02d}{2}{3:02d}'.format(sign, hour, separator, minute)
|
||||
|
||||
if token in ('a', 'A'):
|
||||
return self.locale.meridian(dt.hour, token)
|
||||
|
1703
lib/arrow/locales.py
Normal file
1703
lib/arrow/locales.py
Normal file
File diff suppressed because it is too large
Load diff
308
lib/arrow/parser.py
Normal file
308
lib/arrow/parser.py
Normal file
|
@ -0,0 +1,308 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil import tz
|
||||
import re
|
||||
|
||||
from arrow import locales
|
||||
|
||||
|
||||
class ParserError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class DateTimeParser(object):
|
||||
|
||||
_FORMAT_RE = re.compile('(YYY?Y?|MM?M?M?|Do|DD?D?D?|HH?|hh?|mm?|ss?|SS?S?S?S?S?|ZZ?Z?|a|A|X)')
|
||||
|
||||
_ONE_THROUGH_SIX_DIGIT_RE = re.compile('\d{1,6}')
|
||||
_ONE_THROUGH_FIVE_DIGIT_RE = re.compile('\d{1,5}')
|
||||
_ONE_THROUGH_FOUR_DIGIT_RE = re.compile('\d{1,4}')
|
||||
_ONE_TWO_OR_THREE_DIGIT_RE = re.compile('\d{1,3}')
|
||||
_ONE_OR_TWO_DIGIT_RE = re.compile('\d{1,2}')
|
||||
_FOUR_DIGIT_RE = re.compile('\d{4}')
|
||||
_TWO_DIGIT_RE = re.compile('\d{2}')
|
||||
_TZ_RE = re.compile('[+\-]?\d{2}:?\d{2}')
|
||||
_TZ_NAME_RE = re.compile('\w[\w+\-/]+')
|
||||
|
||||
|
||||
_BASE_INPUT_RE_MAP = {
|
||||
'YYYY': _FOUR_DIGIT_RE,
|
||||
'YY': _TWO_DIGIT_RE,
|
||||
'MM': _TWO_DIGIT_RE,
|
||||
'M': _ONE_OR_TWO_DIGIT_RE,
|
||||
'DD': _TWO_DIGIT_RE,
|
||||
'D': _ONE_OR_TWO_DIGIT_RE,
|
||||
'HH': _TWO_DIGIT_RE,
|
||||
'H': _ONE_OR_TWO_DIGIT_RE,
|
||||
'hh': _TWO_DIGIT_RE,
|
||||
'h': _ONE_OR_TWO_DIGIT_RE,
|
||||
'mm': _TWO_DIGIT_RE,
|
||||
'm': _ONE_OR_TWO_DIGIT_RE,
|
||||
'ss': _TWO_DIGIT_RE,
|
||||
's': _ONE_OR_TWO_DIGIT_RE,
|
||||
'X': re.compile('\d+'),
|
||||
'ZZZ': _TZ_NAME_RE,
|
||||
'ZZ': _TZ_RE,
|
||||
'Z': _TZ_RE,
|
||||
'SSSSSS': _ONE_THROUGH_SIX_DIGIT_RE,
|
||||
'SSSSS': _ONE_THROUGH_FIVE_DIGIT_RE,
|
||||
'SSSS': _ONE_THROUGH_FOUR_DIGIT_RE,
|
||||
'SSS': _ONE_TWO_OR_THREE_DIGIT_RE,
|
||||
'SS': _ONE_OR_TWO_DIGIT_RE,
|
||||
'S': re.compile('\d'),
|
||||
}
|
||||
|
||||
MARKERS = ['YYYY', 'MM', 'DD']
|
||||
SEPARATORS = ['-', '/', '.']
|
||||
|
||||
def __init__(self, locale='en_us'):
|
||||
|
||||
self.locale = locales.get_locale(locale)
|
||||
self._input_re_map = self._BASE_INPUT_RE_MAP.copy()
|
||||
self._input_re_map.update({
|
||||
'MMMM': self._choice_re(self.locale.month_names[1:], re.IGNORECASE),
|
||||
'MMM': self._choice_re(self.locale.month_abbreviations[1:],
|
||||
re.IGNORECASE),
|
||||
'Do': re.compile(self.locale.ordinal_day_re),
|
||||
'a': self._choice_re(
|
||||
(self.locale.meridians['am'], self.locale.meridians['pm'])
|
||||
),
|
||||
# note: 'A' token accepts both 'am/pm' and 'AM/PM' formats to
|
||||
# ensure backwards compatibility of this token
|
||||
'A': self._choice_re(self.locale.meridians.values())
|
||||
})
|
||||
|
||||
def parse_iso(self, string):
|
||||
|
||||
has_time = 'T' in string or ' ' in string.strip()
|
||||
space_divider = ' ' in string.strip()
|
||||
|
||||
if has_time:
|
||||
if space_divider:
|
||||
date_string, time_string = string.split(' ', 1)
|
||||
else:
|
||||
date_string, time_string = string.split('T', 1)
|
||||
time_parts = re.split('[+-]', time_string, 1)
|
||||
has_tz = len(time_parts) > 1
|
||||
has_seconds = time_parts[0].count(':') > 1
|
||||
has_subseconds = '.' in time_parts[0]
|
||||
|
||||
if has_subseconds:
|
||||
subseconds_token = 'S' * min(len(re.split('\D+', time_parts[0].split('.')[1], 1)[0]), 6)
|
||||
formats = ['YYYY-MM-DDTHH:mm:ss.%s' % subseconds_token]
|
||||
elif has_seconds:
|
||||
formats = ['YYYY-MM-DDTHH:mm:ss']
|
||||
else:
|
||||
formats = ['YYYY-MM-DDTHH:mm']
|
||||
else:
|
||||
has_tz = False
|
||||
# generate required formats: YYYY-MM-DD, YYYY-MM-DD, YYYY
|
||||
# using various separators: -, /, .
|
||||
l = len(self.MARKERS)
|
||||
formats = [separator.join(self.MARKERS[:l-i])
|
||||
for i in range(l)
|
||||
for separator in self.SEPARATORS]
|
||||
|
||||
if has_time and has_tz:
|
||||
formats = [f + 'Z' for f in formats]
|
||||
|
||||
if space_divider:
|
||||
formats = [item.replace('T', ' ', 1) for item in formats]
|
||||
|
||||
return self._parse_multiformat(string, formats)
|
||||
|
||||
def parse(self, string, fmt):
|
||||
|
||||
if isinstance(fmt, list):
|
||||
return self._parse_multiformat(string, fmt)
|
||||
|
||||
# fmt is a string of tokens like 'YYYY-MM-DD'
|
||||
# we construct a new string by replacing each
|
||||
# token by its pattern:
|
||||
# 'YYYY-MM-DD' -> '(?P<YYYY>\d{4})-(?P<MM>\d{2})-(?P<DD>\d{2})'
|
||||
fmt_pattern = fmt
|
||||
tokens = []
|
||||
offset = 0
|
||||
for m in self._FORMAT_RE.finditer(fmt):
|
||||
token = m.group(0)
|
||||
try:
|
||||
input_re = self._input_re_map[token]
|
||||
except KeyError:
|
||||
raise ParserError('Unrecognized token \'{0}\''.format(token))
|
||||
input_pattern = '(?P<{0}>{1})'.format(token, input_re.pattern)
|
||||
tokens.append(token)
|
||||
# a pattern doesn't have the same length as the token
|
||||
# it replaces! We keep the difference in the offset variable.
|
||||
# This works because the string is scanned left-to-right and matches
|
||||
# are returned in the order found by finditer.
|
||||
fmt_pattern = fmt_pattern[:m.start() + offset] + input_pattern + fmt_pattern[m.end() + offset:]
|
||||
offset += len(input_pattern) - (m.end() - m.start())
|
||||
match = re.search(fmt_pattern, string, flags=re.IGNORECASE)
|
||||
if match is None:
|
||||
raise ParserError('Failed to match \'{0}\' when parsing \'{1}\''.format(fmt_pattern, string))
|
||||
parts = {}
|
||||
for token in tokens:
|
||||
if token == 'Do':
|
||||
value = match.group('value')
|
||||
else:
|
||||
value = match.group(token)
|
||||
self._parse_token(token, value, parts)
|
||||
return self._build_datetime(parts)
|
||||
|
||||
def _parse_token(self, token, value, parts):
|
||||
|
||||
if token == 'YYYY':
|
||||
parts['year'] = int(value)
|
||||
elif token == 'YY':
|
||||
value = int(value)
|
||||
parts['year'] = 1900 + value if value > 68 else 2000 + value
|
||||
|
||||
elif token in ['MMMM', 'MMM']:
|
||||
parts['month'] = self.locale.month_number(value.lower())
|
||||
|
||||
elif token in ['MM', 'M']:
|
||||
parts['month'] = int(value)
|
||||
|
||||
elif token in ['DD', 'D']:
|
||||
parts['day'] = int(value)
|
||||
|
||||
elif token in ['Do']:
|
||||
parts['day'] = int(value)
|
||||
|
||||
elif token.upper() in ['HH', 'H']:
|
||||
parts['hour'] = int(value)
|
||||
|
||||
elif token in ['mm', 'm']:
|
||||
parts['minute'] = int(value)
|
||||
|
||||
elif token in ['ss', 's']:
|
||||
parts['second'] = int(value)
|
||||
|
||||
elif token == 'SSSSSS':
|
||||
parts['microsecond'] = int(value)
|
||||
elif token == 'SSSSS':
|
||||
parts['microsecond'] = int(value) * 10
|
||||
elif token == 'SSSS':
|
||||
parts['microsecond'] = int(value) * 100
|
||||
elif token == 'SSS':
|
||||
parts['microsecond'] = int(value) * 1000
|
||||
elif token == 'SS':
|
||||
parts['microsecond'] = int(value) * 10000
|
||||
elif token == 'S':
|
||||
parts['microsecond'] = int(value) * 100000
|
||||
|
||||
elif token == 'X':
|
||||
parts['timestamp'] = int(value)
|
||||
|
||||
elif token in ['ZZZ', 'ZZ', 'Z']:
|
||||
parts['tzinfo'] = TzinfoParser.parse(value)
|
||||
|
||||
elif token in ['a', 'A']:
|
||||
if value in (
|
||||
self.locale.meridians['am'],
|
||||
self.locale.meridians['AM']
|
||||
):
|
||||
parts['am_pm'] = 'am'
|
||||
elif value in (
|
||||
self.locale.meridians['pm'],
|
||||
self.locale.meridians['PM']
|
||||
):
|
||||
parts['am_pm'] = 'pm'
|
||||
|
||||
@staticmethod
|
||||
def _build_datetime(parts):
|
||||
|
||||
timestamp = parts.get('timestamp')
|
||||
|
||||
if timestamp:
|
||||
tz_utc = tz.tzutc()
|
||||
return datetime.fromtimestamp(timestamp, tz=tz_utc)
|
||||
|
||||
am_pm = parts.get('am_pm')
|
||||
hour = parts.get('hour', 0)
|
||||
|
||||
if am_pm == 'pm' and hour < 12:
|
||||
hour += 12
|
||||
elif am_pm == 'am' and hour == 12:
|
||||
hour = 0
|
||||
|
||||
return datetime(year=parts.get('year', 1), month=parts.get('month', 1),
|
||||
day=parts.get('day', 1), hour=hour, minute=parts.get('minute', 0),
|
||||
second=parts.get('second', 0), microsecond=parts.get('microsecond', 0),
|
||||
tzinfo=parts.get('tzinfo'))
|
||||
|
||||
def _parse_multiformat(self, string, formats):
|
||||
|
||||
_datetime = None
|
||||
|
||||
for fmt in formats:
|
||||
try:
|
||||
_datetime = self.parse(string, fmt)
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
if _datetime is None:
|
||||
raise ParserError('Could not match input to any of {0} on \'{1}\''.format(formats, string))
|
||||
|
||||
return _datetime
|
||||
|
||||
@staticmethod
|
||||
def _map_lookup(input_map, key):
|
||||
|
||||
try:
|
||||
return input_map[key]
|
||||
except KeyError:
|
||||
raise ParserError('Could not match "{0}" to {1}'.format(key, input_map))
|
||||
|
||||
@staticmethod
|
||||
def _try_timestamp(string):
|
||||
|
||||
try:
|
||||
return float(string)
|
||||
except:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _choice_re(choices, flags=0):
|
||||
return re.compile('({0})'.format('|'.join(choices)), flags=flags)
|
||||
|
||||
|
||||
class TzinfoParser(object):
|
||||
|
||||
_TZINFO_RE = re.compile('([+\-])?(\d\d):?(\d\d)')
|
||||
|
||||
@classmethod
|
||||
def parse(cls, string):
|
||||
|
||||
tzinfo = None
|
||||
|
||||
if string == 'local':
|
||||
tzinfo = tz.tzlocal()
|
||||
|
||||
elif string in ['utc', 'UTC']:
|
||||
tzinfo = tz.tzutc()
|
||||
|
||||
else:
|
||||
|
||||
iso_match = cls._TZINFO_RE.match(string)
|
||||
|
||||
if iso_match:
|
||||
sign, hours, minutes = iso_match.groups()
|
||||
seconds = int(hours) * 3600 + int(minutes) * 60
|
||||
|
||||
if sign == '-':
|
||||
seconds *= -1
|
||||
|
||||
tzinfo = tz.tzoffset(None, seconds)
|
||||
|
||||
else:
|
||||
tzinfo = tz.gettz(string)
|
||||
|
||||
if tzinfo is None:
|
||||
raise ParserError('Could not parse timezone expression "{0}"', string)
|
||||
|
||||
return tzinfo
|
45
lib/arrow/util.py
Normal file
45
lib/arrow/util.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
|
||||
# python 2.6 / 2.7 definitions for total_seconds function.
|
||||
|
||||
def _total_seconds_27(td): # pragma: no cover
|
||||
return td.total_seconds()
|
||||
|
||||
def _total_seconds_26(td):
|
||||
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 1e6) / 1e6
|
||||
|
||||
|
||||
# get version info and assign correct total_seconds function.
|
||||
|
||||
version = '{0}.{1}.{2}'.format(*sys.version_info[:3])
|
||||
|
||||
if version < '2.7': # pragma: no cover
|
||||
total_seconds = _total_seconds_26
|
||||
else: # pragma: no cover
|
||||
total_seconds = _total_seconds_27
|
||||
|
||||
def is_timestamp(value):
|
||||
try:
|
||||
float(value)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
# python 2.7 / 3.0+ definitions for isstr function.
|
||||
|
||||
try: # pragma: no cover
|
||||
basestring
|
||||
|
||||
def isstr(s):
|
||||
return isinstance(s, basestring)
|
||||
|
||||
except NameError: #pragma: no cover
|
||||
|
||||
def isstr(s):
|
||||
return isinstance(s, str)
|
||||
|
||||
|
||||
__all__ = ['total_seconds', 'is_timestamp', 'isstr']
|
Loading…
Add table
Add a link
Reference in a new issue