1"""
2    Slixmpp: The Slick XMPP Library
3    Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
4    This file is part of Slixmpp.
5
6    See the file LICENSE for copying permission.
7"""
8
9import datetime as dt
10
11from slixmpp.plugins import BasePlugin, register_plugin
12from slixmpp.thirdparty import tzutc, tzoffset, parse_iso
13
14
15# =====================================================================
16# To make it easier for stanzas without direct access to plugin objects
17# to use the XEP-0082 utility methods, we will define them as top-level
18# functions and then just reference them in the plugin itself.
19
20def parse(time_str):
21    """
22    Convert a string timestamp into a datetime object.
23
24    Arguments:
25        time_str -- A formatted timestamp string.
26    """
27    return parse_iso(time_str)
28
29
30def format_date(time_obj):
31    """
32    Return a formatted string version of a date object.
33
34    Format:
35        YYYY-MM-DD
36
37    Arguments:
38        time_obj -- A date or datetime object.
39    """
40    if isinstance(time_obj, dt.datetime):
41        time_obj = time_obj.date()
42    return time_obj.isoformat()
43
44
45def format_time(time_obj):
46    """
47    Return a formatted string version of a time object.
48
49    format:
50        hh:mm:ss[.sss][TZD]
51
52    arguments:
53        time_obj -- A time or datetime object.
54    """
55    if isinstance(time_obj, dt.datetime):
56        time_obj = time_obj.timetz()
57    timestamp = time_obj.isoformat()
58    if time_obj.tzinfo == tzutc():
59        timestamp = timestamp[:-6]
60        return '%sZ' % timestamp
61    return timestamp
62
63
64def format_datetime(time_obj):
65    """
66    Return a formatted string version of a datetime object.
67
68    Format:
69        YYYY-MM-DDThh:mm:ss[.sss]TZD
70
71    arguments:
72        time_obj -- A datetime object.
73    """
74    timestamp = time_obj.isoformat('T')
75    if time_obj.tzinfo == tzutc():
76        timestamp = timestamp[:-6]
77        return '%sZ' % timestamp
78    return timestamp
79
80
81def date(year=None, month=None, day=None, obj=False):
82    """
83    Create a date only timestamp for the given instant.
84
85    Unspecified components default to their current counterparts.
86
87    Arguments:
88        year   -- Integer value of the year (4 digits)
89        month  -- Integer value of the month
90        day    -- Integer value of the day of the month.
91        obj    -- If True, return the date object instead
92                  of a formatted string. Defaults to False.
93    """
94    today = dt.datetime.utcnow()
95    if year is None:
96        year = today.year
97    if month is None:
98        month = today.month
99    if day is None:
100        day = today.day
101    value = dt.date(year, month, day)
102    if obj:
103        return value
104    return format_date(value)
105
106
107def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False):
108    """
109    Create a time only timestamp for the given instant.
110
111    Unspecified components default to their current counterparts.
112
113    Arguments:
114        hour   -- Integer value of the hour.
115        min    -- Integer value of the number of minutes.
116        sec    -- Integer value of the number of seconds.
117        micro  -- Integer value of the number of microseconds.
118        offset -- Either a positive or negative number of seconds
119                  to offset from UTC to match a desired timezone,
120                  or a tzinfo object.
121        obj    -- If True, return the time object instead
122                  of a formatted string. Defaults to False.
123    """
124    now = dt.datetime.utcnow()
125    if hour is None:
126        hour = now.hour
127    if min is None:
128        min = now.minute
129    if sec is None:
130        sec = now.second
131    if micro is None:
132        micro = now.microsecond
133    if offset in (None, 0):
134        offset = tzutc()
135    elif not isinstance(offset, dt.tzinfo):
136        offset = tzoffset(None, offset)
137    value = dt.time(hour, min, sec, micro, offset)
138    if obj:
139        return value
140    return format_time(value)
141
142
143def datetime(year=None, month=None, day=None, hour=None,
144             min=None, sec=None, micro=None, offset=None,
145             separators=True, obj=False):
146    """
147    Create a datetime timestamp for the given instant.
148
149    Unspecified components default to their current counterparts.
150
151    Arguments:
152        year   -- Integer value of the year (4 digits)
153        month  -- Integer value of the month
154        day    -- Integer value of the day of the month.
155        hour   -- Integer value of the hour.
156        min    -- Integer value of the number of minutes.
157        sec    -- Integer value of the number of seconds.
158        micro  -- Integer value of the number of microseconds.
159        offset -- Either a positive or negative number of seconds
160                  to offset from UTC to match a desired timezone,
161                  or a tzinfo object.
162        obj    -- If True, return the datetime object instead
163                  of a formatted string. Defaults to False.
164    """
165    now = dt.datetime.utcnow()
166    if year is None:
167        year = now.year
168    if month is None:
169        month = now.month
170    if day is None:
171        day = now.day
172    if hour is None:
173        hour = now.hour
174    if min is None:
175        min = now.minute
176    if sec is None:
177        sec = now.second
178    if micro is None:
179        micro = now.microsecond
180    if offset in (None, 0):
181        offset = tzutc()
182    elif not isinstance(offset, dt.tzinfo):
183        offset = tzoffset(None, offset)
184
185    value = dt.datetime(year, month, day, hour,
186                       min, sec, micro, offset)
187    if obj:
188        return value
189    return format_datetime(value)
190
191
192class XEP_0082(BasePlugin):
193
194    """
195    XEP-0082: XMPP Date and Time Profiles
196
197    XMPP uses a subset of the formats allowed by ISO 8601 as a matter of
198    pragmatism based on the relatively few formats historically used by
199    the XMPP.
200
201    Also see <http://www.xmpp.org/extensions/xep-0082.html>.
202
203    Methods:
204        date            -- Create a time stamp using the Date profile.
205        datetime        -- Create a time stamp using the DateTime profile.
206        time            -- Create a time stamp using the Time profile.
207        format_date     -- Format an existing date object.
208        format_datetime -- Format an existing datetime object.
209        format_time     -- Format an existing time object.
210        parse           -- Convert a time string into a Python datetime object.
211    """
212
213    name = 'xep_0082'
214    description = 'XEP-0082: XMPP Date and Time Profiles'
215    dependencies = set()
216
217    def plugin_init(self):
218        """Start the XEP-0082 plugin."""
219        self.date = date
220        self.datetime = datetime
221        self.time = time
222        self.format_date = format_date
223        self.format_datetime = format_datetime
224        self.format_time = format_time
225        self.parse = parse
226
227
228register_plugin(XEP_0082)
229