Skip to content

Commit

Permalink
Implement pytz timezone conversation and separate command- and data-p…
Browse files Browse the repository at this point in the history
…rocessing classes
  • Loading branch information
FichteFoll committed May 13, 2012
1 parent c879a70 commit 822f4bf
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 69 deletions.
5 changes: 4 additions & 1 deletion README.md
Expand Up @@ -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 ##

Expand All @@ -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"
196 changes: 128 additions & 68 deletions 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'
Expand All @@ -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')

This comment has been minimized.

Copy link
@vovkkk

vovkkk Jun 24, 2013

@FichteFoll
date format: %y-%b-%d %H:%M or just %b
env: Windows 7 with Russian l10n, ST 2.0.1 2217
error:

Traceback (most recent call last):
  File ".\sublime_plugin.py", line 356, in run_
  File ".\insert_date.py", line 38, in run
  File ".\encodings\utf_8.py", line 16, in decode
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 4-6: invalid data

If I replace utf-8 with cp1251 then it works as it should.

I'm not sure what way is best but here I've used this:

sys_enc = locale.getpreferredencoding()
text = text.decode(sys_enc) 

seems to work

This comment has been minimized.

Copy link
@FichteFoll

FichteFoll Jun 24, 2013

Author Owner

locale.getpreferredencoding() indeed looks like it can solve this issue, I can fallback to utf8 if that fails.

But you should create an issue next time for bugs like these (with a link to the relevant source for instance). ;)


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)

0 comments on commit 822f4bf

Please sign in to comment.