diff --git a/README.md b/README.md index f7126f2..d65208d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ Example key definitions as follows: | `%x %X` | 05/12/12 15:49:55 | | `iso` | 2012-05-12T15:49:55.287000+02:00 | +`iso` is almost equivalent to `%Y-%m-%dT%H:%M:%s.%f%z` (it is not possible to insert a char into %z). +`%x` and `%X` are representative for 'Locale’s appropriate time representation'. + ## Licence ## @@ -47,4 +50,4 @@ pytz is licenced under the **MIT licence**. [strptime]: http://docs.python.org/py3k/library/datetime.html#strftime-strptime-behavior "Python docs: 7.1.8. strftime() and strptime() Behavior" [pytz]: http://pytz.sourceforge.net/ "pytz - World Timezone Definitions for Python" -[pytz-down]: http://pypi.python.org/pypi/pytz#downloads "pytz - World Timezone Definitions for Python" +[pytz-down]: http://pypi.python.org/pypi/pytz#downloads "pytz : Python Package Index" diff --git a/insert_date.py b/insert_date.py index e88bc88..657e7ab 100644 --- a/insert_date.py +++ b/insert_date.py @@ -1,70 +1,127 @@ +import sublime import sublime_plugin from datetime import datetime, timedelta, tzinfo from functools import partial +import pytz +from pytz.exceptions import * -class LocalDatetime(object): +class LocalTimezone(tzinfo): """ - Wrapper class that uses the local timezone for formatting a datetime. + Helper class which extends datetime.tzinfo and implements the 'local timezone' + (a.k.a. 'capturing the platform's idea of local time'). + + Source: http://docs.python.org/library/datetime.html#tzinfo-objects """ - class LocalTimezone(tzinfo): - import time - """ - Helper class which extends datetime.tzinfo and supports the local timezone - (a.k.a. capturing the platform's idea of local time). + import time - Source: http://docs.python.org/library/datetime.html#tzinfo-objects""" + STDOFFSET = timedelta(seconds=-time.timezone) + if time.daylight: + DSTOFFSET = timedelta(seconds=-time.altzone) + else: + DSTOFFSET = STDOFFSET - STDOFFSET = timedelta(seconds=-time.timezone) - if time.daylight: - DSTOFFSET = timedelta(seconds=-time.altzone) - else: - DSTOFFSET = STDOFFSET + DSTDIFF = DSTOFFSET - STDOFFSET - DSTDIFF = DSTOFFSET - STDOFFSET + def utcoffset(self, dt): + if self._isdst(dt): + return self.DSTOFFSET + else: + return self.STDOFFSET - def utcoffset(self, dt): - if self._isdst(dt): - return self.DSTOFFSET - else: - return self.STDOFFSET + def dst(self, dt): + if self._isdst(dt): + return self.DSTDIFF + else: + return timedelta(0) - def dst(self, dt): - if self._isdst(dt): - return self.DSTDIFF - else: - return timedelta(0) - - def tzname(self, dt): - # TODO: This is buggy (at least for my system) - # tzname = self.time.tzname - # print tzname - # print("tzname: %s" % str(tzname[self._isdst(dt)])) - # return self.time.tzname[self._isdst(dt)] - return None - - def _isdst(self, dt): - tt = (dt.year, dt.month, dt.day, - dt.hour, dt.minute, dt.second, - dt.weekday(), 0, 0) - stamp = self.time.mktime(tt) - tt = self.time.localtime(stamp) - return tt.tm_isdst > 0 + def tzname(self, dt): + # TODO: This is buggy (I hate ASCII) + # print self.time.tzname + # print unicode(self.time.tzname[0]) + # return self.time.tzname[self._isdst(dt)].decode('utf-8') + return None - local = LocalTimezone() + def _isdst(self, dt): + tt = (dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second, + dt.weekday(), 0, 0) + stamp = self.time.mktime(tt) + tt = self.time.localtime(stamp) + return tt.tm_isdst > 0 - def __init__(self, dt=None): - if dt is None: - dt = datetime.now(tz=self.local) - if not isinstance(dt, datetime): - raise TypeError("Parameter is not instance of datetime.datetime") - self.dt = dt.astimezone(self.local) - print dt +class InsertDate(object): + local = LocalTimezone() + default = dict( + # TODO: be modifiable from settings + format="%Y-%m-%d %H:%M", + tz_in="Europe/Berlin" + ) + + def __init__(self, local=None, default=None): + if not local is None: + if isinstance(local, tzinfo): + self.local = local + else: + raise TypeError("Parameter 'local' is not instance of datetime.tzinfo") + + if not default is None: + try: + self.default.update(default) # just raise the error if it appears + except ValueError: + raise ValueError("Parameter 'default' is not iterable") + + def parse(self, format=None, tz_in=None, tz_out=None): + dt = self.date_gen(tz_in, tz_out) + return self.date_format(dt, format) + + def date_gen(self, tz_in=None, tz_out=None): + # gather tzinfo data and raise a few exceptions + if tz_in is None: + tz_in = self.default['tz_in'] + + if tz_in == "local": + tz_in = self.local + + if isinstance(tz_in, basestring): + try: + tz_in = pytz.timezone(tz_in) + except UnknownTimeZoneError: + raise UnknownTimeZoneError("Parameter %r=%r is not a valid timezone string" % ('tz_in', tz_in)) + + if not isinstance(tz_in, tzinfo): + raise TypeError("Parameter 'tz_in' is not instance of datetime.tzinfo") + + try: + tz_out = pytz.timezone(tz_out) if (tz_out is not None) else tz_in + except UnknownTimeZoneError: + raise UnknownTimeZoneError("Parameter %r=%r is not a valid timezone string" % ('tz_out', tz_out)) + + # get timedata + try: + dt = tz_in.localize(datetime.now()) + except AttributeError: + dt = datetime.now(tz=tz_in) + + # process timedata + # TODO: shift datetime here + if tz_out is tz_in: + return dt + + dt = dt.astimezone(tz_out) + try: + return tz_out.normalize(dt) + except AttributeError: + pass + + return dt + + def date_format(self, dt, format=None): + if format is None: + format = self.default['format'] - @staticmethod - def _format(dt, format): # 'iso:T' if format.startswith("iso"): sep = 'T' @@ -74,39 +131,42 @@ def _format(dt, format): return dt.strftime(format) - def format(self, format): - return LocalDatetime._format(self.dt, format) - - def __getattr__(self, attr): - return getattr(self.dt, attr) - -class InsertDateCommand(sublime_plugin.TextCommand): +class InsertDateCommand(sublime_plugin.TextCommand, InsertDate): """Prints Date according to given format string""" - default_format = "%Y-%m-%d %H:%M" - def run(self, edit, format=None, prompt=False): + def run(self, edit, format=None, prompt=False, tz_in=None, tz_out=None): if prompt: self.view.window().show_input_panel( "Date format string:", - format or '', + str(format) if format else '', # pass this function as callback - partial(self.run, edit), + partial(self.run, edit, tz_in=tz_in, tz_out=tz_out), None, None ) return # call already handled - elif format is None: - format = self.default_format + if format == '' or (isinstance(format, basestring) and format.isspace()): + # emtpy string or whitespaces entered in input panel + return + + # do the actual parse action + try: + text = self.parse(format, tz_in, tz_out) + except Exception as e: + sublime.error_message("[InsertDate]\n%s: %s" % (type(e).__name__, e)) + return - if not format: - # emtpy string entered in input panel + if text == '' or text.isspace(): + # don't bother replacing selections with actually nothing return - text = LocalDatetime().format(format) + if type(text) == str: + print text + text = text.decode('utf-8') for r in self.view.sel(): if r.empty(): - self.view.insert (edit, r.begin(), text) + self.view.insert (edit, r.a, text) else: - self.view.replace(edit, r, text) + self.view.replace(edit, r, text)