summaryrefslogtreecommitdiffstats
path: root/kittystore/storm/hack_datetime.py
blob: 0e87e71a797926e11fe6ec7722a04e24530e44b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# -*- coding: utf-8 -*-
#
# hack around a bug in storm: support for timezones is missing
# https://bugs.launchpad.net/storm/+bug/280708
#
# pylint: disable=C,W,R

import datetime
import re
from storm.locals import *
from storm.variables import _parse_date, _parse_time


RE_TIME = re.compile(r"""^
                          (?P<year>\d{4})\-(?P<month>\d{2})\-(?P<day>\d{2})        # pattern matching date
                          T                                                        # seperator
                          (?P<hour>\d{2})\:(?P<minutes>\d{2})\:(?P<seconds>\d{2})  # pattern matching time
                          (\.(?P<microseconds>\d{6}))?                             # pattern matching optional microseconds
                          (?P<tz_offset>[\-\+]\d{2}\:\d{2})?                       # pattern matching optional timezone offset
                         $""", re.VERBOSE)

def parse_time(time_str):
    x = RE_TIME.match(time_str)
    if not x:
        raise ValueError
    d = datetime.datetime(int(x.group("year")), int(x.group("month")),
        int(x.group("day")), int(x.group("hour")), int(x.group("minutes")),
        int(x.group("seconds")))
    if x.group("microseconds"):
        d = d.replace(microsecond=int(x.group("microseconds")))
    if x.group("tz_offset"):
        d = d.replace(tzinfo=TimeZone(x.group("tz_offset")))
    return d

class DateTimeVariableHack(DateTime.variable_class):

    def parse_set(self, value, from_db):
        if from_db:
            if isinstance(value, datetime.datetime):
                pass
            elif isinstance(value, (str, unicode)):
                if value.count(":") == 3: #additional timezone info
                    value = value.replace(" ", "T")
                    value = parse_time(value)
                else:
                    if " " not in value:
                        raise ValueError("Unknown date/time format: %r" % value)
                    date_str, time_str = value.split(" ")
                    value = datetime.datetime(*(_parse_date(date_str) +
                                       _parse_time(time_str)))
            else:
                raise TypeError("Expected datetime, found %s" % repr(value))
            if self._tzinfo is not None:
                if value.tzinfo is None:
                    value = value.replace(tzinfo=self._tzinfo)
                else:
                    value = value.astimezone(self._tzinfo)
        else:
            if type(value) in (int, long, float):
                value = datetime.datetime.utcfromtimestamp(value)
            elif not isinstance(value, datetime.datetime):
                raise TypeError("Expected datetime, found %s" % repr(value))
            if self._tzinfo is not None:
                value = value.astimezone(self._tzinfo)
        return value



class DateTime(DateTime):
    # Overwrite DateTime in Storm's locals
    # pylint: disable-msg=E0102
    variable_class = DateTimeVariableHack



class TimeZone(datetime.tzinfo):

    def __init__(self, tz_string):
        hours, minutes = tz_string.lstrip("-+").split(":")
        self.stdoffset = datetime.timedelta(hours=int(hours), minutes=int(minutes))
        if tz_string.startswith("-"):
            self.stdoffset *= -1

    def __repr__(self):
        return "TimeZone(%s)" %(self.stdoffset.days*24*60*60 + self.stdoffset.seconds)

    def utcoffset(self, dt):
        return self.stdoffset

    def dst(self, dt):
        return datetime.timedelta(0)