Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

Changeset 299

Show
Ignore:
Timestamp:
09/21/11 22:00:41
Author:
ltnooy
Message:

Adding postgres support for timestamps with timezone information.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/geniusql/dbtypes.py

    r296 r299  
    654654     
    655655    def dbtype_for_datetime_datetime(self, hints): 
     656        if hints.get('timezone_aware'): 
     657            for type in self.known_types['datetime']: 
     658                if getattr(type, 'timezone_aware', False): 
     659                    return type() 
     660 
    656661        return self.known_types['datetime'][0]() 
    657662     
  • trunk/geniusql/providers/postgres.py

    r292 r299  
    108108            h, m = 0, 0 
    109109        else: 
    110             h, m = divmod(value.tzinfo.utcoffset(value), 60) 
     110            offset = value.tzinfo.utcoffset(value) 
     111            m = (offset.days * 3600 + offset.seconds) / 60 
     112            h, m = divmod(m, 60) 
    111113         
    112114        if h < 0: 
     
    115117        else: 
    116118            sign = "+" 
    117          
     119  
    118120        return ("TIMESTAMP WITH TIME ZONE " 
    119                 "'%04d-%02d-%02d %02d:%02d:%02d%s%02d:%02d'" % 
     121                "'%04d-%02d-%02d %02d:%02d:%02d.%06d%s%02d:%02d'" % 
    120122                (value.year, value.month, value.day, 
    121                  value.hour, value.minute, value.second, 
     123                 value.hour, value.minute, value.second, value.microsecond, 
    122124                 sign, h, m)) 
    123125     
     
    599601 
    600602class TIMESTAMP(dbtypes.SQL92TIMESTAMP): 
    601     """A date and time.""" 
    602     pass 
     603    """A date and time. Timezone naive.""" 
     604    default_adapters = {datetime.datetime: datetime_to_PgTIMESTAMPTZ_Adapter()} 
    603605 
    604606class TIMESTAMPTZ(dbtypes.SQL92TIMESTAMP): 
    605     """A date and time.""" 
    606     default_adapters = {datetime.datetime: datetime_to_PgTIMESTAMPTZ_Adapter()} 
     607    """A date and time. Timezone aware.""" 
     608    default_adapters = {datetime.datetime: datetime_tz_to_PgTIMESTAMPTZ_Adapter()} 
     609    timezone_aware = True 
     610 
     611    def ddl(self): 
     612        return "TIMESTAMP WITH TIME ZONE" 
    607613 
    608614class DATE(dbtypes.SQL92DATE): 
  • trunk/geniusql/test/zoo_fixture.py

    r298 r299  
    5151        self.assertEqual(set(a), set(b)) 
    5252     
    53     def test_1_create_tables(self): 
     53    def test_01_create_tables(self): 
    5454        Animal = self.schema.table('Animal') 
    5555        Animal['ID'] = self.schema.column(int, autoincrement=True, key=True) 
     
    6161        Animal['PreviousZoos'] = self.schema.column(list, hints={'bytes': 8000}) 
    6262        Animal['LastEscape'] = self.schema.column(datetime.datetime) 
     63        Animal['Birthdate'] = self.schema.column(datetime.datetime, hints={'timezone_aware': True}) 
    6364        Animal['Lifespan'] = self.schema.column(float, hints={'precision': 4}) 
    6465        Animal['Age'] = self.schema.column(datetime.timedelta) 
     
    164165        self.schema['NothingToDoWithZoos'] = t 
    165166     
    166     def test_2_populate(self): 
     167    def test_02_populate(self): 
    167168        wap = self.schema['Zoo'].insert(Name='Wild Animal Park', 
    168169                           Founded=datetime.date(2000, 1, 1), 
     
    276277                PreferredFoodID=live_fish, AlternateFoodID=dead_fish) 
    277278     
    278     def test_3_Properties(self): 
     279    def test_03_Properties(self): 
    279280        # Zoos 
    280281        WAP = self.schema['Zoo'].select(Name='Wild Animal Park') 
     
    357358        self.assertEqual(float(tr['Acreage']), 4) 
    358359        self.assertEqual(tr['PettingAllowed'], False) 
    359      
    360     def test_4_Expressions(self): 
     360         
     361    def test_04_Expressions(self): 
    361362        def matches(lam, tkey='Animal'): 
    362363            data = self.schema[tkey].select_all(lam) 
     
    456457                     "but differ by %r instead." % (dt1, dt2, tolerance, diff)) 
    457458     
    458     def test_4a_Binary_Expressions(self): 
     459    def test_04a_Binary_Expressions(self): 
    459460         
    460461        def results(tablekey, fieldlist, restriction=None): 
     
    580581                                #for name, f in opens]) 
    581582     
    582     def test_5_Aggregates(self): 
     583    def test_05_Aggregates(self): 
    583584        try: 
    584585            Animal = self.schema['Animal'] 
     
    705706                             ) 
    706707            # Assert that the expected interval and actual interval are 
    707             # no more than 1 second apart. 
     708            # no more than 5 second apart. 
    708709            diff = abs(elapsed[4][1] - WAP_elapsed) 
    709             self.assert_(diff <= datetime.timedelta(0, 1), diff) 
     710            self.assert_(diff <= datetime.timedelta(0, 5), diff) 
    710711        finally: 
    711712            self.db.connections.commit() 
    712713     
    713     def test_6_Editing(self): 
     714    def test_06_Editing(self): 
    714715        Zoo = self.schema['Zoo'] 
    715716         
     
    762763                self.assertEqual(list(data)[0][0], d) 
    763764     
    764     def test_6a_Edit_expressions(self): 
     765    def test_06a_Edit_expressions(self): 
    765766        Vet = self.schema['Vet'] 
    766767         
     
    779780        self.assertEqual(gp['NameLen'], 5) 
    780781     
    781     def test_7_Multiselect(self): 
     782    def test_07_Multiselect(self): 
    782783        try: 
    783784            f = (lambda z, a: z.Name == 'San Diego Zoo') 
     
    863864            f = (lambda a, z, v: z.Name == 'Sea_World') 
    864865            entries = list(self.db.select((tree, [('ID',), ('ID',), ('ID', 'Name')], f))) 
    865             self.assertEqual(len(entries), 4
     866            self.assertEqual(len(entries), 2
    866867 
    867868            # Let's try three joined classes just for the sadistic fun of it. 
     
    885886            self.db.connections.commit() 
    886887 
    887     def test_8_CustomAssociations(self): 
     888    def test_08_CustomAssociations(self): 
    888889        try: 
    889890            # Try different association paths 
     
    901902            self.db.connections.commit() 
    902903     
    903     def test_9_delete(self): 
     904    def test_09_delete(self): 
    904905        ostrich = self.schema['Animal'].select(Species='Ostrich') 
    905906        self.assert_(ostrich is not None) 
     
    920921        ostrich = self.schema['Animal'].select(Species='Ostrich') 
    921922        self.assertEqual(ostrich, None) 
    922      
     923    
     924    def test_10_timezone_aware(self): 
     925 
     926        if self.db.__class__.__name__.endswith('PgDatabase'): 
     927            # Exercise the timezone aware dbtype supported by postgresql. 
     928            class Timezone(datetime.tzinfo): 
     929                def __init__(self, offset=0): 
     930                    self._offset = offset 
     931                    self._timedelta = datetime.timedelta(seconds=offset * 60) 
     932                def utcoffset(self, dt): 
     933                    return self._timedelta  
     934                def dst(self, dt): 
     935                    return datetime.timedelta(0) 
     936 
     937            eagle = self.schema['Animal'].insert( 
     938                Species='Eagle',  
     939                Birthdate=datetime.datetime(2011, 01, 01, 07, 31, 12, 100, tzinfo=Timezone(240)) 
     940            )['ID'] 
     941 
     942            eagle = self.schema['Animal'].select(Species='Eagle') 
     943            d = eagle['Birthdate'].astimezone(Timezone(240)) 
     944            self.assertEqual(2011, d.year) 
     945            self.assertEqual(01, d.month) 
     946            self.assertEqual(01, d.day) 
     947            self.assertEqual(07, d.hour) 
     948            self.assertEqual(31, d.minute) 
     949            self.assertEqual(12, d.second) 
     950            self.assertEqual(100, d.microsecond) 
     951  
    923952    def test_primary_key_support(self): 
    924953        # Drop and re-add the PK on, oh, how about the Animal table?