From 7ccc67c06d12a1390558636526e6750caf874680 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sun, 3 Jul 2011 12:14:59 -0700 Subject: Added XEP-0082 plugin. This should make things much easier for any stanza that uses timestamps. --- sleekxmpp/plugins/xep_0082.py | 202 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 sleekxmpp/plugins/xep_0082.py (limited to 'sleekxmpp/plugins/xep_0082.py') diff --git a/sleekxmpp/plugins/xep_0082.py b/sleekxmpp/plugins/xep_0082.py new file mode 100644 index 00000000..e36e062b --- /dev/null +++ b/sleekxmpp/plugins/xep_0082.py @@ -0,0 +1,202 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import datetime as dt +from dateutil import parser +from dateutil.tz import tzoffset, tzutc +from sleekxmpp.plugins.base import base_plugin + + +# ===================================================================== +# To make it easier for stanzas without direct access to plugin objects +# to use the XEP-0082 utility methods, we will define them as top-level +# functions and then just reference them in the plugin itself. + +def parse(time_str): + """ + Convert a string timestamp into a datetime object. + + Arguments: + time_str -- A formatted timestamp string. + """ + return parser.parse(time_str) + +def format_date(time_obj): + """ + Return a formatted string version of a date object. + + Format: + YYYY-MM-DD + + Arguments: + time_obj -- A date or datetime object. + """ + if isinstance(time_obj, dt.datetime): + time_obj = time_obj.date() + return time_obj.isoformat() + +def format_time(time_obj): + """ + Return a formatted string version of a time object. + + format: + hh:mm:ss[.sss][TZD + + arguments: + time_obj -- A time or datetime object. + """ + if isinstance(time_obj, dt.datetime): + time_obj = time_obj.timetz() + timestamp = time_obj.isoformat() + if time_obj.tzinfo == tzutc(): + timestamp = timestamp[:-6] + return '%sZ' % timestamp + return timestamp + +def format_datetime(time_obj): + """ + Return a formatted string version of a datetime object. + + Format: + YYYY-MM-DDThh:mm:ss[.sss]TZD + + arguments: + time_obj -- A datetime object. + """ + timestamp = time_obj.isoformat('T') + if time_obj.tzinfo == tzutc(): + timestamp = timestamp[:-6] + return '%sZ' % timestamp + return timestamp + +def date(year=None, month=None, day=None): + """ + Create a date only timestamp for the given instant. + + Unspecified components default to their current counterparts. + + Arguments: + year -- Integer value of the year (4 digits) + month -- Integer value of the month + day -- Integer value of the day of the month. + """ + today = dt.datetime.today() + if year is None: + year = today.year + if month is None: + month = today.month + if day is None: + day = today.day + return format_date(dt.date(year, month, day)) + +def time(hour=None, min=None, sec=None, micro=None, offset=None): + """ + Create a time only timestamp for the given instant. + + Unspecified components default to their current counterparts. + + Arguments: + hour -- Integer value of the hour. + min -- Integer value of the number of minutes. + sec -- Integer value of the number of seconds. + micro -- Integer value of the number of microseconds. + offset -- A positive or negative number of seconds to + offset from UTC to match a desired timezone. + """ + now = dt.datetime.utcnow() + if hour is None: + hour = now.hour + if min is None: + min = now.minute + if sec is None: + sec = now.second + if micro is None: + micro = now.microsecond + if offset is None: + offset = tzutc() + else: + offset = tzoffset(None, offset) + time = dt.time(hour, min, sec, micro, offset) + return format_time(time) + +def datetime(year=None, month=None, day=None, hour=None, + min=None, sec=None, micro=None, offset=None, + separators=True): + """ + Create a datetime timestamp for the given instant. + + Unspecified components default to their current counterparts. + + Arguments: + year -- Integer value of the year (4 digits) + month -- Integer value of the month + day -- Integer value of the day of the month. + hour -- Integer value of the hour. + min -- Integer value of the number of minutes. + sec -- Integer value of the number of seconds. + micro -- Integer value of the number of microseconds. + offset -- A positive or negative number of seconds to + offset from UTC to match a desired timezone. + """ + now = dt.datetime.utcnow() + if year is None: + year = now.year + if month is None: + month = now.month + if day is None: + day = now.day + if hour is None: + hour = now.hour + if min is None: + min = now.minute + if sec is None: + sec = now.second + if micro is None: + micro = now.microsecond + if offset is None: + offset = tzutc() + else: + offset = tzoffset(None, offset) + + date = dt.datetime(year, month, day, hour, + sec, min, micro, offset) + return format_datetime(date) + +class xep_0082(base_plugin): + + """ + XEP-0082: XMPP Date and Time Profiles + + XMPP uses a subset of the formats allowed by ISO 8601 as a matter of + pragmatism based on the relatively few formats historically used by + the XMPP. + + Also see . + + Methods: + date -- Create a time stamp using the Date profile. + datetime -- Create a time stamp using the DateTime profile. + time -- Create a time stamp using the Time profile. + format_date -- Format an existing date object. + format_datetime -- Format an existing datetime object. + format_time -- Format an existing time object. + parse -- Convert a time string into a Python datetime object. + """ + + def plugin_init(self): + """Start the XEP-0082 plugin.""" + self.xep = '0082' + self.description = 'XMPP Date and Time Profiles' + + self.date = date + self.datetime = datetime + self.time = time + self.format_date = format_date + self.format_datetime = format_datetime + self.format_time = format_time + self.parse = parse -- cgit v1.2.3 From c98f5d44509f5b6fdfdbf7408dae3282caccc9db Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sun, 3 Jul 2011 13:40:57 -0700 Subject: Fix some bugs in time handling. Namely, minutes and seconds were reversed. --- sleekxmpp/plugins/xep_0082.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'sleekxmpp/plugins/xep_0082.py') diff --git a/sleekxmpp/plugins/xep_0082.py b/sleekxmpp/plugins/xep_0082.py index e36e062b..785ba36b 100644 --- a/sleekxmpp/plugins/xep_0082.py +++ b/sleekxmpp/plugins/xep_0082.py @@ -105,8 +105,9 @@ def time(hour=None, min=None, sec=None, micro=None, offset=None): min -- Integer value of the number of minutes. sec -- Integer value of the number of seconds. micro -- Integer value of the number of microseconds. - offset -- A positive or negative number of seconds to - offset from UTC to match a desired timezone. + offset -- Either a positive or negative number of seconds + to offset from UTC to match a desired timezone, + or a tzinfo object. """ now = dt.datetime.utcnow() if hour is None: @@ -119,7 +120,7 @@ def time(hour=None, min=None, sec=None, micro=None, offset=None): micro = now.microsecond if offset is None: offset = tzutc() - else: + elif not isinstance(offset, dt.tzinfo): offset = tzoffset(None, offset) time = dt.time(hour, min, sec, micro, offset) return format_time(time) @@ -140,8 +141,9 @@ def datetime(year=None, month=None, day=None, hour=None, min -- Integer value of the number of minutes. sec -- Integer value of the number of seconds. micro -- Integer value of the number of microseconds. - offset -- A positive or negative number of seconds to - offset from UTC to match a desired timezone. + offset -- Either a positive or negative number of seconds + to offset from UTC to match a desired timezone, + or a tzinfo object. """ now = dt.datetime.utcnow() if year is None: @@ -160,11 +162,11 @@ def datetime(year=None, month=None, day=None, hour=None, micro = now.microsecond if offset is None: offset = tzutc() - else: + elif not isinstance(offset, dt.tzinfo): offset = tzoffset(None, offset) date = dt.datetime(year, month, day, hour, - sec, min, micro, offset) + min, sec, micro, offset) return format_datetime(date) class xep_0082(base_plugin): -- cgit v1.2.3 From a0767f6af61bc9c54b2526cd51aef7af4e383e90 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 4 Aug 2011 00:07:30 -0700 Subject: Sadly, dateutil is not actually part of the standard lib. Thus, using the XEP-0082 and XEP-0202 introduces a dependency on the dateutil package (installable using pip install python-dateutil). Maybe we'll be able to rework how these plugins work to avoid needing dateutil, but for now this will have to do. --- sleekxmpp/plugins/xep_0082.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'sleekxmpp/plugins/xep_0082.py') diff --git a/sleekxmpp/plugins/xep_0082.py b/sleekxmpp/plugins/xep_0082.py index 785ba36b..e78a50ad 100644 --- a/sleekxmpp/plugins/xep_0082.py +++ b/sleekxmpp/plugins/xep_0082.py @@ -6,11 +6,18 @@ See the file LICENSE for copying permission. """ +import logging import datetime as dt -from dateutil import parser -from dateutil.tz import tzoffset, tzutc + from sleekxmpp.plugins.base import base_plugin +try: + from dateutil import parser + from dateutil.tz import tzoffset, tzutc +except e: + log = logging.getLogger(__name__) + log.warning("XEP-0082 plugin requires dateutil") + # ===================================================================== # To make it easier for stanzas without direct access to plugin objects -- cgit v1.2.3 From 4d8933abdf4a190493f2762d423f469f6d8b30a9 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 4 Aug 2011 20:20:22 -0700 Subject: Actually, we can work around needing dateutil. If dateutil is present, we'll use that. If not, we'll use some regexes from the fixed_datetime module. --- sleekxmpp/plugins/xep_0082.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'sleekxmpp/plugins/xep_0082.py') diff --git a/sleekxmpp/plugins/xep_0082.py b/sleekxmpp/plugins/xep_0082.py index e78a50ad..d3c4cc56 100644 --- a/sleekxmpp/plugins/xep_0082.py +++ b/sleekxmpp/plugins/xep_0082.py @@ -10,13 +10,7 @@ import logging import datetime as dt from sleekxmpp.plugins.base import base_plugin - -try: - from dateutil import parser - from dateutil.tz import tzoffset, tzutc -except e: - log = logging.getLogger(__name__) - log.warning("XEP-0082 plugin requires dateutil") +from sleekxmpp.thirdparty import tzutc, tzoffset, parse_iso # ===================================================================== @@ -31,7 +25,8 @@ def parse(time_str): Arguments: time_str -- A formatted timestamp string. """ - return parser.parse(time_str) + return parse_iso(time_str) + def format_date(time_obj): """ @@ -52,7 +47,7 @@ def format_time(time_obj): Return a formatted string version of a time object. format: - hh:mm:ss[.sss][TZD + hh:mm:ss[.sss][TZD] arguments: time_obj -- A time or datetime object. -- cgit v1.2.3