1import calendar
2
3from datetime import (
4    date,
5    datetime,
6    timedelta,
7    tzinfo,
8    )
9
10from email.utils import (
11    formatdate,
12    mktime_tz,
13    parsedate_tz,
14    )
15
16import time
17
18from webob.compat import (
19    integer_types,
20    long,
21    native_,
22    text_type,
23    )
24
25__all__ = [
26    'UTC', 'timedelta_to_seconds',
27    'year', 'month', 'week', 'day', 'hour', 'minute', 'second',
28    'parse_date', 'serialize_date',
29    'parse_date_delta', 'serialize_date_delta',
30]
31
32_now = datetime.now # hook point for unit tests
33
34class _UTC(tzinfo):
35    def dst(self, dt):
36        return timedelta(0)
37    def utcoffset(self, dt):
38        return timedelta(0)
39    def tzname(self, dt):
40        return 'UTC'
41    def __repr__(self):
42        return 'UTC'
43
44UTC = _UTC()
45
46
47
48def timedelta_to_seconds(td):
49    """
50    Converts a timedelta instance to seconds.
51    """
52    return td.seconds + (td.days*24*60*60)
53
54day = timedelta(days=1)
55week = timedelta(weeks=1)
56hour = timedelta(hours=1)
57minute = timedelta(minutes=1)
58second = timedelta(seconds=1)
59# Estimate, I know; good enough for expirations
60month = timedelta(days=30)
61year = timedelta(days=365)
62
63
64def parse_date(value):
65    if not value:
66        return None
67    try:
68        value = native_(value)
69    except:
70        return None
71    t = parsedate_tz(value)
72    if t is None:
73        # Could not parse
74        return None
75    if t[-1] is None:
76        # No timezone given.  None would mean local time, but we'll force UTC
77        t = t[:9] + (0,)
78    t = mktime_tz(t)
79    return datetime.fromtimestamp(t, UTC)
80
81def serialize_date(dt):
82    if isinstance(dt, (bytes, text_type)):
83        return native_(dt)
84    if isinstance(dt, timedelta):
85        dt = _now() + dt
86    if isinstance(dt, (datetime, date)):
87        dt = dt.timetuple()
88    if isinstance(dt, (tuple, time.struct_time)):
89        dt = calendar.timegm(dt)
90    if not (isinstance(dt, float) or isinstance(dt, integer_types)):
91        raise ValueError(
92            "You must pass in a datetime, date, time tuple, or integer object, "
93            "not %r" % dt)
94    return formatdate(dt, usegmt=True)
95
96
97
98def parse_date_delta(value):
99    """
100    like parse_date, but also handle delta seconds
101    """
102    if not value:
103        return None
104    try:
105        value = int(value)
106    except ValueError:
107        return parse_date(value)
108    else:
109        return _now() + timedelta(seconds=value)
110
111
112def serialize_date_delta(value):
113    if isinstance(value, (float, int, long)):
114        return str(int(value))
115    else:
116        return serialize_date(value)
117