Changeset 849


Ignore:
Timestamp:
Feb 9, 2016, 8:14:26 AM (4 years ago)
Author:
cito
Message:

Make timetz and timestamptz work properly with Python 2

Python 2 has no concrete timezone class, therefore we had so far returned only
naive datetimes in this case. This patch adds a simple concrete timezone class,
so we can return timetz and timestamptz with timezones in Python 2 as well.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/pg.py

    r833 r849  
    2828# supporting documentation.
    2929
    30 from __future__ import print_function
     30from __future__ import print_function, division
    3131
    3232from _pg import *
     
    3737import warnings
    3838
    39 from datetime import date, time, datetime, timedelta
     39from datetime import date, time, datetime, timedelta, tzinfo
    4040from decimal import Decimal
    4141from math import isnan, isinf
     
    158158
    159159try:
    160     if datetime.strptime('+0100', '%z') is None:
    161         raise ValueError
    162 except ValueError:  # Python < 3.2
    163     timezones = None
     160    from datetime import timezone
     161except ImportError:  # Python < 3.2
     162
     163    class timezone(tzinfo):
     164        """Simple timezone implementation."""
     165
     166        def __init__(self, offset, name=None):
     167            self.offset = offset
     168            if not name:
     169                minutes = self.offset.days * 1440 + self.offset.seconds // 60
     170                if minutes < 0:
     171                    hours, minutes = divmod(-minutes, 60)
     172                    hours = -hours
     173                else:
     174                    hours, minutes = divmod(minutes, 60)
     175                name = 'UTC%+03d:%02d' % (hours, minutes)
     176            self.name = name
     177
     178        def utcoffset(self, dt):
     179            return self.offset
     180
     181        def tzname(self, dt):
     182            return self.name
     183
     184        def dst(self, dt):
     185            return None
     186
     187    timezone.utc = timezone(timedelta(0), 'UTC')
     188
     189    _has_timezone = False
    164190else:
    165     # time zones used in Postgres timestamptz output
    166     timezones = dict(CET='+0100', EET='+0200', EST='-0500',
    167         GMT='+0000', HST='-1000', MET='+0100', MST='-0700',
    168         UCT='+0000', UTC='+0000', WET='+0000')
     191    _has_timezone = True
     192
     193# time zones used in Postgres timestamptz output
     194_timezones = dict(CET='+0100', EET='+0200', EST='-0500',
     195    GMT='+0000', HST='-1000', MET='+0100', MST='-0700',
     196    UCT='+0000', UTC='+0000', WET='+0000')
     197
     198
     199def _timezone_as_offset(tz):
     200    if tz.startswith(('+', '-')):
     201        if len(tz) < 5:
     202            return tz + '00'
     203        return tz.replace(':', '')
     204    return _timezones.get(tz, '+0000')
     205
     206
     207def _get_timezone(tz):
     208    tz = _timezone_as_offset(tz)
     209    minutes = 60 * int(tz[1:3]) + int(tz[3:5])
     210    if tz[0] == '-':
     211        minutes = -minutes
     212    return timezone(timedelta(minutes=minutes), tz)
    169213
    170214
     
    674718        tz = '+0000'
    675719    fmt = '%H:%M:%S.%f' if len(value) > 8 else '%H:%M:%S'
    676     if timezones:
    677         if tz.startswith(('+', '-')):
    678             if len(tz) < 5:
    679                 tz += '00'
    680             else:
    681                 tz = tz.replace(':', '')
    682         elif tz in timezones:
    683             tz = timezones[tz]
    684         else:
    685             tz = '+0000'
    686         value += tz
     720    if _has_timezone:
     721        value += _timezone_as_offset(tz)
    687722        fmt += '%z'
    688     return datetime.strptime(value, fmt).timetz()
     723        return datetime.strptime(value, fmt).timetz()
     724    return datetime.strptime(value, fmt).timetz().replace(
     725        tzinfo=_get_timezone(tz))
    689726
    690727
     
    741778            return datetime.max
    742779        fmt = [fmt, '%H:%M:%S.%f' if len(value[1]) > 8 else '%H:%M:%S']
    743     if timezones:
    744         if tz.startswith(('+', '-')):
    745             if len(tz) < 5:
    746                 tz += '00'
    747             else:
    748                 tz = tz.replace(':', '')
    749         elif tz in timezones:
    750             tz = timezones[tz]
    751         else:
    752             tz = '+0000'
    753         value.append(tz)
     780    if _has_timezone:
     781        value.append(_timezone_as_offset(tz))
    754782        fmt.append('%z')
    755     return datetime.strptime(' '.join(value), ' '.join(fmt))
     783        return datetime.strptime(' '.join(value), ' '.join(fmt))
     784    return datetime.strptime(' '.join(value), ' '.join(fmt)).replace(
     785        tzinfo=_get_timezone(tz))
     786
    756787
    757788_re_interval_sql_standard = regex(
  • trunk/pgdb.py

    r847 r849  
    6464"""
    6565
    66 from __future__ import print_function
     66from __future__ import print_function, division
    6767
    6868from _pg import *
     
    7070__version__ = version
    7171
    72 from datetime import date, time, datetime, timedelta
     72from datetime import date, time, datetime, timedelta, tzinfo
    7373from time import localtime
    7474from decimal import Decimal
     
    129129
    130130try:
    131     if datetime.strptime('+0100', '%z') is None:
    132         raise ValueError
    133 except ValueError:  # Python < 3.2
    134     timezones = None
     131    from datetime import timezone
     132except ImportError:  # Python < 3.2
     133
     134    class timezone(tzinfo):
     135        """Simple timezone implementation."""
     136
     137        def __init__(self, offset, name=None):
     138            self.offset = offset
     139            if not name:
     140                minutes = self.offset.days * 1440 + self.offset.seconds // 60
     141                if minutes < 0:
     142                    hours, minutes = divmod(-minutes, 60)
     143                    hours = -hours
     144                else:
     145                    hours, minutes = divmod(minutes, 60)
     146                name = 'UTC%+03d:%02d' % (hours, minutes)
     147            self.name = name
     148
     149        def utcoffset(self, dt):
     150            return self.offset
     151
     152        def tzname(self, dt):
     153            return self.name
     154
     155        def dst(self, dt):
     156            return None
     157
     158    timezone.utc = timezone(timedelta(0), 'UTC')
     159
     160    _has_timezone = False
    135161else:
    136     # time zones used in Postgres timestamptz output
    137     timezones = dict(CET='+0100', EET='+0200', EST='-0500',
    138         GMT='+0000', HST='-1000', MET='+0100', MST='-0700',
    139         UCT='+0000', UTC='+0000', WET='+0000')
     162    _has_timezone = True
     163
     164# time zones used in Postgres timestamptz output
     165_timezones = dict(CET='+0100', EET='+0200', EST='-0500',
     166    GMT='+0000', HST='-1000', MET='+0100', MST='-0700',
     167    UCT='+0000', UTC='+0000', WET='+0000')
     168
     169
     170def _timezone_as_offset(tz):
     171    if tz.startswith(('+', '-')):
     172        if len(tz) < 5:
     173            return tz + '00'
     174        return tz.replace(':', '')
     175    return _timezones.get(tz, '+0000')
     176
     177
     178def _get_timezone(tz):
     179    tz = _timezone_as_offset(tz)
     180    minutes = 60 * int(tz[1:3]) + int(tz[3:5])
     181    if tz[0] == '-':
     182        minutes = -minutes
     183    return timezone(timedelta(minutes=minutes), tz)
    140184
    141185
     
    208252        tz = '+0000'
    209253    fmt = '%H:%M:%S.%f' if len(value) > 8 else '%H:%M:%S'
    210     if timezones:
    211         if tz.startswith(('+', '-')):
    212             if len(tz) < 5:
    213                 tz += '00'
    214             else:
    215                 tz = tz.replace(':', '')
    216         elif tz in timezones:
    217             tz = timezones[tz]
    218         else:
    219             tz = '+0000'
    220         value += tz
     254    if _has_timezone:
     255        value += _timezone_as_offset(tz)
    221256        fmt += '%z'
    222     return datetime.strptime(value, fmt).timetz()
     257        return datetime.strptime(value, fmt).timetz()
     258    return datetime.strptime(value, fmt).timetz().replace(
     259        tzinfo=_get_timezone(tz))
    223260
    224261
     
    275312            return datetime.max
    276313        fmt = [fmt, '%H:%M:%S.%f' if len(value[1]) > 8 else '%H:%M:%S']
    277     if timezones:
    278         if tz.startswith(('+', '-')):
    279             if len(tz) < 5:
    280                 tz += '00'
    281             else:
    282                 tz = tz.replace(':', '')
    283         elif tz in timezones:
    284             tz = timezones[tz]
    285         else:
    286             tz = '+0000'
    287         value.append(tz)
     314    if _has_timezone:
     315        value.append(_timezone_as_offset(tz))
    288316        fmt.append('%z')
    289     return datetime.strptime(' '.join(value), ' '.join(fmt))
     317        return datetime.strptime(' '.join(value), ' '.join(fmt))
     318    return datetime.strptime(' '.join(value), ' '.join(fmt)).replace(
     319        tzinfo=_get_timezone(tz))
     320
    290321
    291322_re_interval_sql_standard = regex(
  • trunk/tests/test_classic_dbwrapper.py

    r833 r849  
    35733573        timezones = dict(CET=1, EET=2, EST=-5, UTC=0)
    35743574        for timezone in sorted(timezones):
    3575             offset = timezones[timezone]
     3575            tz = '%+03d00' % timezones[timezone]
    35763576            try:
    3577                 tzinfo = datetime.strptime('%+03d00' % offset, '%z').tzinfo
    3578             except ValueError:  # Python < 3.3
    3579                 tzinfo = None
     3577                tzinfo = datetime.strptime(tz, '%z').tzinfo
     3578            except ValueError:  # Python < 3.2
     3579                tzinfo = pg._get_timezone(tz)
    35803580            self.db.set_parameter('timezone', timezone)
    35813581            d = time(15, 9, 26, tzinfo=tzinfo)
     
    36283628        timezones = dict(CET=1, EET=2, EST=-5, UTC=0)
    36293629        for timezone in sorted(timezones):
    3630             offset = timezones[timezone]
     3630            tz = '%+03d00' % timezones[timezone]
    36313631            try:
    3632                 tzinfo = datetime.strptime('%+03d00' % offset, '%z').tzinfo
    3633             except ValueError:  # Python < 3.3
    3634                 tzinfo = None
     3632                tzinfo = datetime.strptime(tz, '%z').tzinfo
     3633            except ValueError:  # Python < 3.2
     3634                tzinfo = pg._get_timezone(tz)
    36353635            self.db.set_parameter('timezone', timezone)
    36363636            for datestyle in ('ISO', 'Postgres, MDY', 'Postgres, DMY',
  • trunk/tests/test_dbapi20.py

    r848 r849  
    3131from datetime import date, time, datetime, timedelta
    3232from uuid import UUID as Uuid
    33 
    34 try:
    35     from datetime import timezone
    36 except ImportError:  # Python < 3.2
    37     timezone = None
    3833
    3934try:
     
    579574                values = [dt.date(), dt.time(), dt,
    580575                    dt.time(), dt]
    581                 if timezone:
    582                     values[3] = values[3].replace(tzinfo=timezone.utc)
    583                     values[4] = values[4].replace(tzinfo=timezone.utc)
     576                values[3] = values[3].replace(tzinfo=pgdb.timezone.utc)
     577                values[4] = values[4].replace(tzinfo=pgdb.timezone.utc)
    584578                if n == 0:  # input as objects
    585579                    params = values
     
    589583                    d = (dt.year, dt.month, dt.day)
    590584                    t = (dt.hour, dt.minute, dt.second, dt.microsecond)
    591                     z = (timezone.utc,) if timezone else ()
     585                    z = (pgdb.timezone.utc,)
    592586                    params = [pgdb.Date(*d), pgdb.Time(*t),
    593587                            pgdb.Timestamp(*(d + t)), pgdb.Time(*(t + z)),
Note: See TracChangeset for help on using the changeset viewer.