Mercurial > hg > ltpdarepo
changeset 244:e6ed4e03074f stable
Fix timezone handling in database queries.
author | Daniele Nicolodi <daniele@grinta.net> |
---|---|
date | Fri, 16 Dec 2011 19:09:37 +0100 |
parents | 826a29703975 |
children | ce09aed4a90b |
files | src/ltpdarepo/__init__.py src/ltpdarepo/utils.py src/ltpdarepo/views/browse.py |
diffstat | 3 files changed, 67 insertions(+), 48 deletions(-) [+] |
line wrap: on
line diff
--- a/src/ltpdarepo/__init__.py Fri Dec 16 19:09:37 2011 +0100 +++ b/src/ltpdarepo/__init__.py Fri Dec 16 19:09:37 2011 +0100 @@ -16,6 +16,7 @@ import dateutil.tz from .security import secure, require, authenticate +from .utils import datetimetz from .views.browse import module as browse from .views.databases import module as databases from .views.feed import url_for_atom_feed, module as feed @@ -27,24 +28,26 @@ SCHEMA = 31 -class datetimeutc(datetime): - # subclass of `datetime.datetime` with default string - # representation including the timezone name - def __str__(self): - return self.strftime('%Y-%m-%d %H:%M:%S %Z') - - -# customize mysql types conversion for datetime fields to return -# timezone aware objects in the UTC timezone -def datetime_or_none_utc(s): +# customize mysql types conversion from and to DATETIME fields to +# return timezone aware datetime objects in the UTC timezone and +# correclty convert timezone aware datetime objects to UTC timezone +def datetime_or_none(s): value = converters.DateTime_or_None(s) if value is not None: - value = datetimeutc(value.year, value.month, value.day, value.hour, - value.minute, value.second, value.microsecond, - tzinfo=dateutil.tz.tzutc()) + value = datetimetz(value.year, value.month, value.day, value.hour, + value.minute, value.second, value.microsecond, + tzinfo=dateutil.tz.tzutc()) return value + +def datetime_to_literal(value, c): + if value.tzinfo is not None: + value = value.astimezone(dateutil.tz.tzutc()) + return converters.DateTime2literal(value, c) + conversions = converters.conversions.copy() -conversions[mysql.constants.FIELD_TYPE.DATETIME] = datetime_or_none_utc +conversions[mysql.constants.FIELD_TYPE.DATETIME] = datetime_or_none +conversions[datetime] = datetime_to_literal +conversions[datetimetz] = datetime_to_literal def before_request():
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ltpdarepo/utils.py Fri Dec 16 19:09:37 2011 +0100 @@ -0,0 +1,40 @@ +# Copyright 2011 Daniele Nicolodi <nicolodi@science.unitn.it> +# +# This software may be used and distributed according to the terms of +# the GNU Affero General Public License version 3 or any later version. + + +from __future__ import absolute_import +from datetime import * + +import dateutil.parser +import dateutil.tz + + +class datetimetz(datetime): + # subclass of `datetime.datetime` with default string + # representation including the timezone name + def __str__(self): + return self.strftime('%Y-%m-%d %H:%M:%S %Z') + + +def parsedatetime(string): + # parse datetime string representation and returns a timezone + # aware subclass of datetime with default string representation + # including the timezone name + + # parsing default is midnight today in UTC timezone + default = datetime.utcnow().replace( + tzinfo=dateutil.tz.tzutc(), hour=0, minute=0, second=0, microsecond=0) + + value = dateutil.parser.parse(string, dayfirst=True, yearfirst=True, default=default) + return datetimetz(value.year, value.month, value.day, value.hour, + value.minute, value.second, value.microsecond, value.tzinfo) + + +def toDATETIME(value): + if not isinstance(value, datetime): + return value + if value.tzinfo is not None: + value = value.astimezone(dateutil.tz.tzutc()) + return value.strftime('%Y-%m-%d %H:%M:%S')
--- a/src/ltpdarepo/views/browse.py Fri Dec 16 19:09:37 2011 +0100 +++ b/src/ltpdarepo/views/browse.py Fri Dec 16 19:09:37 2011 +0100 @@ -7,20 +7,18 @@ from datetime import datetime from dateutil.relativedelta import relativedelta -import dateutil.tz -import dateutil.parser - -from flask import Blueprint, Markup, abort, g, request, render_template, json, make_response, url_for, redirect +from flask import Blueprint, abort, g, request, render_template, make_response, url_for, redirect from MySQLdb.cursors import DictCursor from wtforms import Form from wtforms.fields import Field from wtforms.widgets import TextInput from wtforms.validators import ValidationError, Optional +from ltpdarepo.database import Database +from ltpdarepo.pagination import Pagination +from ltpdarepo.query import Query from ltpdarepo.security import require, view -from ltpdarepo.database import Database -from ltpdarepo.query import Query -from ltpdarepo.pagination import Pagination +from ltpdarepo.utils import parsedatetime, toDATETIME try: from collections import OrderedDict @@ -135,8 +133,8 @@ self._end = None def timespan(self, start=None, end=None): - self._start = start - self._end = end + self._start = toDATETIME(start) + self._end = toDATETIME(end) return self @property @@ -220,24 +218,6 @@ return column, kind, values -class datetimefield(datetime): - """Timezone aware subclass of `datetime.datetime` with a new - constructor that parses strings and a default string - representation including the timezone name.""" - - def __new__(cls, string): - # parsing default is midnight today in UTC timezone - default = datetime.utcnow().replace( - tzinfo=dateutil.tz.tzutc(), hour=0, minute=0, second=0, microsecond=0) - - value = dateutil.parser.parse(string, dayfirst=True, yearfirst=True, default=default) - return datetime.__new__(cls, value.year, value.month, value.day, value.hour, - value.minute, value.second, value.microsecond, value.tzinfo) - - def __str__(self): - return self.strftime('%Y-%m-%d %H:%M:%S %Z') - - class Request(object): """Retrieves and validates query parameters from the request""" @@ -246,7 +226,7 @@ 'double': float, 'text': unicode, 'enum': unicode, - 'datetime': datetimefield} + 'datetime': parsedatetime} def __init__(self, formdata, columns, indexes={}): # incoming data @@ -331,7 +311,7 @@ def _value(self): if self.data: - return self.data.strftime('%Y-%m-%d %H:%M:%S %Z') + return str(self.data) return self.raw_data and u' '.join(self.raw_data) or u'' def process_formdata(self, valuelist): @@ -340,12 +320,8 @@ if not datestr: self.data = None raise ValidationError(self.gettext(u'Input a datetime value')) - - # parsing default is midnight today in UTC timezone - default = datetime.utcnow().replace( - tzinfo=dateutil.tz.tzutc(), hour=0, minute=0, second=0, microsecond=0) try: - self.data = dateutil.parser.parse(datestr, default=default, **self.parseargs) + self.data = parsedatetime(datestr) except ValueError: self.data = None raise ValidationError(self.gettext(u'Invalid datetime input'))