Changeset 814 for trunk/tests


Ignore:
Timestamp:
Feb 3, 2016, 3:23:20 PM (3 years ago)
Author:
cito
Message:

Add typecasting of dates, times, timestamps, intervals

So far, PyGreSQL has returned these types only as strings (in various
formats depending on the DateStyle? setting) and left it to the user
to parse and interpret the strings. These types are now properly cast
into the corresponding detetime types of Python, and this works with
any setting of DatesStyle?, even if you change DateStyle? in the middle
of a database session.

To implement this, a fast method for getting the datestyle (cached and
without roundtrip to the database) has been added. Also, the typecast
mechanism has been extended so that typecast functions can optionally
also take the connection as argument.

The date and time typecast functions have been implemented in Python
using the new typecast registry and added to both pg and pgdb. Some
duplication of code in the two modules was unavoidable, since we don't
want the modules to be dependent of each other or install additional
helper modules. One day we might want to change this, put everything
in one package and factor out some of the functionality.

Location:
trunk/tests
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/test_classic_connection.py

    r813 r814  
    121121
    122122    def testAllConnectMethods(self):
    123         methods = '''cancel close endcopy
     123        methods = '''cancel close date_format endcopy
    124124            escape_bytea escape_identifier escape_literal escape_string
    125125            fileno get_cast_hook get_notice_receiver getline getlo getnotify
     
    915915        r = self.c.query(q, (value,)).getresult()[0][0]
    916916        self.assertIsInstance(r, pytype)
    917         if isinstance(value, (bytes, str)):
    918             if not value or '{':
     917        if isinstance(value, str):
     918            if not value or ' ' in value or '{' in value:
    919919                value = '"%s"' % value
    920920        value = '{%s}' % value
    921921        r = self.c.query(q + '[]', (value,)).getresult()[0][0]
    922         self.assertIsInstance(r, list)
    923         self.assertEqual(len(r), 1)
    924         self.assertIsInstance(r[0], pytype)
     922        if pgtype.startswith(('date', 'time', 'interval')):
     923            # arrays of these are casted by the DB wrapper only
     924            self.assertEqual(r, value)
     925        else:
     926            self.assertIsInstance(r, list)
     927            self.assertEqual(len(r), 1)
     928            self.assertIsInstance(r[0], pytype)
    925929
    926930    def testInt(self):
     
    956960    def testDate(self):
    957961        self.assert_proper_cast('1956-01-31', 'date', str)
    958         self.assert_proper_cast('0', 'interval', str)
    959         self.assert_proper_cast('08:42', 'time', str)
    960         self.assert_proper_cast('08:42', 'timetz', str)
    961         self.assert_proper_cast('1956-01-31 08:42', 'timestamp', str)
    962         self.assert_proper_cast('1956-01-31 08:42', 'timestamptz', str)
     962        self.assert_proper_cast('10:20:30', 'interval', str)
     963        self.assert_proper_cast('08:42:15', 'time', str)
     964        self.assert_proper_cast('08:42:15+00', 'timetz', str)
     965        self.assert_proper_cast('1956-01-31 08:42:15', 'timestamp', str)
     966        self.assert_proper_cast('1956-01-31 08:42:15+00', 'timestamptz', str)
    963967
    964968    def testText(self):
  • trunk/tests/test_classic_dbwrapper.py

    r804 r814  
    1818import os
    1919import sys
     20import json
    2021import tempfile
    21 import json
    2222
    2323import pg  # the module under test
    2424
    2525from decimal import Decimal
    26 from datetime import date
     26from datetime import date, time, datetime, timedelta
     27from time import strftime
    2728from operator import itemgetter
    2829
     
    181182            'begin',
    182183            'cancel', 'clear', 'close', 'commit',
    183             'db', 'dbname', 'dbtypes',
     184            'date_format', 'db', 'dbname', 'dbtypes',
    184185            'debug', 'decode_json', 'delete',
    185186            'encode_json', 'end', 'endcopy', 'error',
     
    15061507                        if item[0] in expect)
    15071508            ts = expect.get('ts')
    1508             if ts == 'current_timestamp':
    1509                 ts = expect['ts'] = data['ts']
    1510                 if len(ts) > 19:
    1511                     self.assertEqual(ts[19], '.')
    1512                     ts = ts[:19]
     1509            if ts:
     1510                if ts == 'current_timestamp':
     1511                    ts = data['ts']
     1512                    self.assertIsInstance(ts, datetime)
     1513                    self.assertEqual(ts.strftime('%Y-%m-%d'),
     1514                        strftime('%Y-%m-%d'))
    15131515                else:
    1514                     self.assertEqual(len(ts), 19)
    1515                 self.assertTrue(ts[:4].isdigit())
    1516                 self.assertEqual(ts[4], '-')
    1517                 self.assertEqual(ts[10], ' ')
    1518                 self.assertTrue(ts[11:13].isdigit())
    1519                 self.assertEqual(ts[13], ':')
     1516                    ts = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S')
     1517                expect['ts'] = ts
    15201518            self.assertEqual(data, expect)
    15211519            data = query(
     
    35243522        self.assertIsInstance(p.age, int)
    35253523
     3524    def testDate(self):
     3525        query = self.db.query
     3526        for datestyle in ('ISO', 'Postgres, MDY', 'Postgres, DMY',
     3527                'SQL, MDY', 'SQL, DMY', 'German'):
     3528            self.db.set_parameter('datestyle', datestyle)
     3529            d = date(2016, 3, 14)
     3530            q = "select '2016-03-14'::date"
     3531            r = query(q).getresult()[0][0]
     3532            self.assertIsInstance(r, date)
     3533            self.assertEqual(r, d)
     3534            q = "select '10000-08-01'::date, '0099-01-08 BC'::date"
     3535            r = query(q).getresult()[0]
     3536            self.assertIsInstance(r[0], date)
     3537            self.assertIsInstance(r[1], date)
     3538            self.assertEqual(r[0], date.max)
     3539            self.assertEqual(r[1], date.min)
     3540        q = "select 'infinity'::date, '-infinity'::date"
     3541        r = query(q).getresult()[0]
     3542        self.assertIsInstance(r[0], date)
     3543        self.assertIsInstance(r[1], date)
     3544        self.assertEqual(r[0], date.max)
     3545        self.assertEqual(r[1], date.min)
     3546
     3547    def testTime(self):
     3548        query = self.db.query
     3549        d = time(15, 9, 26)
     3550        q = "select '15:09:26'::time"
     3551        r = query(q).getresult()[0][0]
     3552        self.assertIsInstance(r, time)
     3553        self.assertEqual(r, d)
     3554        d = time(15, 9, 26, 535897)
     3555        q = "select '15:09:26.535897'::time"
     3556        r = query(q).getresult()[0][0]
     3557        self.assertIsInstance(r, time)
     3558        self.assertEqual(r, d)
     3559
     3560    def testTimetz(self):
     3561        query = self.db.query
     3562        timezones = dict(CET=1, EET=2, EST=-5, UTC=0)
     3563        for timezone in sorted(timezones):
     3564            offset = timezones[timezone]
     3565            try:
     3566                tzinfo = datetime.strptime('%+03d00' % offset, '%z').tzinfo
     3567            except ValueError:  # Python < 3.3
     3568                tzinfo = None
     3569            self.db.set_parameter('timezone', timezone)
     3570            d = time(15, 9, 26, tzinfo=tzinfo)
     3571            q = "select '15:09:26'::timetz"
     3572            r = query(q).getresult()[0][0]
     3573            self.assertIsInstance(r, time)
     3574            self.assertEqual(r, d)
     3575            d = time(15, 9, 26, 535897, tzinfo)
     3576            q = "select '15:09:26.535897'::timetz"
     3577            r = query(q).getresult()[0][0]
     3578            self.assertIsInstance(r, time)
     3579            self.assertEqual(r, d)
     3580
     3581    def testTimestamp(self):
     3582        query = self.db.query
     3583        for datestyle in ('ISO', 'Postgres, MDY', 'Postgres, DMY',
     3584                'SQL, MDY', 'SQL, DMY', 'German'):
     3585            self.db.set_parameter('datestyle', datestyle)
     3586            d = datetime(2016, 3, 14)
     3587            q = "select '2016-03-14'::timestamp"
     3588            r = query(q).getresult()[0][0]
     3589            self.assertIsInstance(r, datetime)
     3590            self.assertEqual(r, d)
     3591            d = datetime(2016, 3, 14, 15, 9, 26)
     3592            q = "select '2016-03-14 15:09:26'::timestamp"
     3593            r = query(q).getresult()[0][0]
     3594            self.assertIsInstance(r, datetime)
     3595            self.assertEqual(r, d)
     3596            d = datetime(2016, 3, 14, 15, 9, 26, 535897)
     3597            q = "select '2016-03-14 15:09:26.535897'::timestamp"
     3598            r = query(q).getresult()[0][0]
     3599            self.assertIsInstance(r, datetime)
     3600            self.assertEqual(r, d)
     3601            q = ("select '10000-08-01 AD'::timestamp,"
     3602                " '0099-01-08 BC'::timestamp")
     3603            r = query(q).getresult()[0]
     3604            self.assertIsInstance(r[0], datetime)
     3605            self.assertIsInstance(r[1], datetime)
     3606            self.assertEqual(r[0], datetime.max)
     3607            self.assertEqual(r[1], datetime.min)
     3608        q = "select 'infinity'::timestamp, '-infinity'::timestamp"
     3609        r = query(q).getresult()[0]
     3610        self.assertIsInstance(r[0], datetime)
     3611        self.assertIsInstance(r[1], datetime)
     3612        self.assertEqual(r[0], datetime.max)
     3613        self.assertEqual(r[1], datetime.min)
     3614
     3615    def testTimestamptz(self):
     3616        query = self.db.query
     3617        timezones = dict(CET=1, EET=2, EST=-5, UTC=0)
     3618        for timezone in sorted(timezones):
     3619            offset = timezones[timezone]
     3620            try:
     3621                tzinfo = datetime.strptime('%+03d00' % offset, '%z').tzinfo
     3622            except ValueError:  # Python < 3.3
     3623                tzinfo = None
     3624            self.db.set_parameter('timezone', timezone)
     3625            for datestyle in ('ISO', 'Postgres, MDY', 'Postgres, DMY',
     3626                    'SQL, MDY', 'SQL, DMY', 'German'):
     3627                self.db.set_parameter('datestyle', datestyle)
     3628                d = datetime(2016, 3, 14, tzinfo=tzinfo)
     3629                q = "select '2016-03-14'::timestamptz"
     3630                r = query(q).getresult()[0][0]
     3631                self.assertIsInstance(r, datetime)
     3632                self.assertEqual(r, d)
     3633                d = datetime(2016, 3, 14, 15, 9, 26, tzinfo=tzinfo)
     3634                q = "select '2016-03-14 15:09:26'::timestamptz"
     3635                r = query(q).getresult()[0][0]
     3636                self.assertIsInstance(r, datetime)
     3637                self.assertEqual(r, d)
     3638                d = datetime(2016, 3, 14, 15, 9, 26, 535897, tzinfo)
     3639                q = "select '2016-03-14 15:09:26.535897'::timestamptz"
     3640                r = query(q).getresult()[0][0]
     3641                self.assertIsInstance(r, datetime)
     3642                self.assertEqual(r, d)
     3643                q = ("select '10000-08-01 AD'::timestamptz,"
     3644                    " '0099-01-08 BC'::timestamptz")
     3645                r = query(q).getresult()[0]
     3646                self.assertIsInstance(r[0], datetime)
     3647                self.assertIsInstance(r[1], datetime)
     3648                self.assertEqual(r[0], datetime.max)
     3649                self.assertEqual(r[1], datetime.min)
     3650        q = "select 'infinity'::timestamptz, '-infinity'::timestamptz"
     3651        r = query(q).getresult()[0]
     3652        self.assertIsInstance(r[0], datetime)
     3653        self.assertIsInstance(r[1], datetime)
     3654        self.assertEqual(r[0], datetime.max)
     3655        self.assertEqual(r[1], datetime.min)
     3656
     3657    def testInterval(self):
     3658        query = self.db.query
     3659        for intervalstyle in (
     3660                'sql_standard', 'postgres', 'postgres_verbose', 'iso_8601'):
     3661            self.db.set_parameter('intervalstyle', intervalstyle)
     3662            q = "select '2016-03-14'::timestamp - '2016-03-11'::timestamp"
     3663            r = query(q).getresult()[0][0]
     3664            self.assertIsInstance(r, timedelta)
     3665            d = timedelta(3)
     3666            self.assertEqual(r, d)
     3667            q = "select '2016-03-14'::timestamp - '2016-04-13'::timestamp"
     3668            r = query(q).getresult()[0][0]
     3669            self.assertIsInstance(r, timedelta)
     3670            d = timedelta(-30)
     3671            self.assertEqual(r, d)
     3672            q = ("select '2016-03-14 15:31:42.5678'::timestamp"
     3673                 " - '2016-03-14 12:00:00'::timestamp")
     3674            r = query(q).getresult()[0][0]
     3675            self.assertIsInstance(r, timedelta)
     3676            d = timedelta(hours=3, minutes=31, seconds=42, microseconds=5678)
     3677            self.assertEqual(r, d)
     3678
     3679    def testDateAndTimeArrays(self):
     3680        q = "select ARRAY['2016-03-14'::date], ARRAY['15:09:26'::time]"
     3681        r = self.db.query(q).getresult()[0]
     3682        d = r[0]
     3683        self.assertIsInstance(d, list)
     3684        d = d[0]
     3685        self.assertIsInstance(d, date)
     3686        self.assertEqual(d, date(2016, 3, 14))
     3687        d = r[1]
     3688        self.assertIsInstance(d, list)
     3689        d = d[0]
     3690        self.assertIsInstance(d, time)
     3691        self.assertEqual(d, time(15, 9, 26))
     3692
    35263693    def testDbTypesInfo(self):
    35273694        dbtypes = self.db.dbtypes
  • trunk/tests/test_classic_functions.py

    r813 r814  
    2020
    2121import pg  # the module under test
     22
     23from datetime import timedelta
    2224
    2325try:
     
    615617
    616618
     619class TestCastInterval(unittest.TestCase):
     620    """Test the interval typecast function."""
     621
     622    intervals = [
     623        ((0, 0, 0, 1, 0, 0, 0),
     624            ('1:00:00', '01:00:00', '@ 1 hour', 'PT1H')),
     625        ((0, 0, 0, -1, 0, 0, 0),
     626            ('-1:00:00', '-01:00:00', '@ -1 hour', 'PT-1H')),
     627        ((0, 0, 0, 1, 0, 0, 0),
     628            ('0-0 0 1:00:00', '0 years 0 mons 0 days 01:00:00',
     629            '@ 0 years 0 mons 0 days 1 hour', 'P0Y0M0DT1H')),
     630        ((0, 0, 0, -1, 0, 0, 0),
     631            ('-0-0 -1:00:00', '0 years 0 mons 0 days -01:00:00',
     632            '@ 0 years 0 mons 0 days -1 hour', 'P0Y0M0DT-1H')),
     633        ((0, 0, 1, 0, 0, 0, 0),
     634            ('1 0:00:00', '1 day', '@ 1 day', 'P1D')),
     635        ((0, 0, -1, 0, 0, 0, 0),
     636            ('-1 0:00:00', '-1 day', '@ -1 day', 'P-1D')),
     637        ((0, 1, 0, 0, 0, 0, 0),
     638            ('0-1', '1 mon', '@ 1 mon', 'P1M')),
     639        ((1, 0, 0, 0, 0, 0, 0),
     640            ('1-0', '1 year', '@ 1 year', 'P1Y')),
     641        ((0, 0, 0, 2, 0, 0, 0),
     642            ('2:00:00', '02:00:00', '@ 2 hours', 'PT2H')),
     643        ((0, 0, 2, 0, 0, 0, 0),
     644            ('2 0:00:00', '2 days', '@ 2 days', 'P2D')),
     645        ((0, 2, 0, 0, 0, 0, 0),
     646            ('0-2', '2 mons', '@ 2 mons', 'P2M')),
     647        ((2, 0, 0, 0, 0, 0, 0),
     648            ('2-0', '2 years', '@ 2 years', 'P2Y')),
     649        ((0, 0, 0, -3, 0, 0, 0),
     650            ('-3:00:00', '-03:00:00', '@ 3 hours ago', 'PT-3H')),
     651        ((0, 0, -3, 0, 0, 0, 0),
     652            ('-3 0:00:00', '-3 days', '@ 3 days ago', 'P-3D')),
     653        ((0, -3, 0, 0, 0, 0, 0),
     654            ('-0-3', '-3 mons', '@ 3 mons ago', 'P-3M')),
     655        ((-3, 0, 0, 0, 0, 0, 0),
     656            ('-3-0', '-3 years', '@ 3 years ago', 'P-3Y')),
     657        ((0, 0, 0, 0, 1, 0, 0),
     658            ('0:01:00', '00:01:00', '@ 1 min', 'PT1M')),
     659        ((0, 0, 0, 0, 0, 1, 0),
     660            ('0:00:01', '00:00:01', '@ 1 sec', 'PT1S')),
     661        ((0, 0, 0, 0, 0, 0, 1),
     662            ('0:00:00.000001', '00:00:00.000001',
     663             '@ 0.000001 secs', 'PT0.000001S')),
     664        ((0, 0, 0, 0, 2, 0, 0),
     665            ('0:02:00', '00:02:00', '@ 2 mins', 'PT2M')),
     666        ((0, 0, 0, 0, 0, 2, 0),
     667            ('0:00:02', '00:00:02', '@ 2 secs', 'PT2S')),
     668        ((0, 0, 0, 0, 0, 0, 2),
     669            ('0:00:00.000002', '00:00:00.000002',
     670             '@ 0.000002 secs', 'PT0.000002S')),
     671        ((0, 0, 0, 0, -3, 0, 0),
     672            ('-0:03:00', '-00:03:00', '@ 3 mins ago', 'PT-3M')),
     673        ((0, 0, 0, 0, 0, -3, 0),
     674            ('-0:00:03', '-00:00:03', '@ 3 secs ago', 'PT-3S')),
     675        ((0, 0, 0, 0, 0, 0, -3),
     676            ('-0:00:00.000003', '-00:00:00.000003',
     677             '@ 0.000003 secs ago', 'PT-0.000003S')),
     678        ((1, 2, 0, 0, 0, 0, 0),
     679            ('1-2', '1 year 2 mons', '@ 1 year 2 mons', 'P1Y2M')),
     680        ((0, 0, 3, 4, 5, 6, 0),
     681            ('3 4:05:06', '3 days 04:05:06',
     682             '@ 3 days 4 hours 5 mins 6 secs', 'P3DT4H5M6S')),
     683        ((1, 2, 3, 4, 5, 6, 0),
     684            ('+1-2 +3 +4:05:06', '1 year 2 mons 3 days 04:05:06',
     685             '@ 1 year 2 mons 3 days 4 hours 5 mins 6 secs',
     686             'P1Y2M3DT4H5M6S')),
     687        ((1, 2, 3, -4, -5, -6, 0),
     688            ('+1-2 +3 -4:05:06', '1 year 2 mons 3 days -04:05:06',
     689             '@ 1 year 2 mons 3 days -4 hours -5 mins -6 secs',
     690             'P1Y2M3DT-4H-5M-6S')),
     691        ((1, 2, 3, -4, 5, 6, 0),
     692            ('+1-2 +3 -3:54:54', '1 year 2 mons 3 days -03:54:54',
     693             '@ 1 year 2 mons 3 days -3 hours -54 mins -54 secs',
     694             'P1Y2M3DT-3H-54M-54S')),
     695        ((-1, -2, 3, -4, -5, -6, 0),
     696            ('-1-2 +3 -4:05:06', '-1 years -2 mons +3 days -04:05:06',
     697             '@ 1 year 2 mons -3 days 4 hours 5 mins 6 secs ago',
     698             'P-1Y-2M3DT-4H-5M-6S')),
     699        ((1, 2, -3, 4, 5, 6, 0),
     700            ('+1-2 -3 +4:05:06', '1 year 2 mons -3 days +04:05:06',
     701             '@ 1 year 2 mons -3 days 4 hours 5 mins 6 secs',
     702             'P1Y2M-3DT4H5M6S')),
     703        ((0, 0, 0, 1, 30, 0, 0),
     704            ('1:30:00', '01:30:00', '@ 1 hour 30 mins', 'PT1H30M')),
     705        ((0, 0, 0, 3, 15, 45, 123456),
     706            ('3:15:45.123456', '03:15:45.123456',
     707             '@ 3 hours 15 mins 45.123456 secs', 'PT3H15M45.123456S')),
     708        ((0, 0, 0, 3, 15, -5, 123),
     709            ('3:14:55.000123', '03:14:55.000123',
     710             '@ 3 hours 14 mins 55.000123 secs', 'PT3H14M55.000123S')),
     711        ((0, 0, 0, 3, -5, 15, -12345),
     712            ('2:55:14.987655', '02:55:14.987655',
     713             '@ 2 hours 55 mins 14.987655 secs', 'PT2H55M14.987655S')),
     714        ((0, 0, 0, 2, -1, 0, 0),
     715            ('1:59:00', '01:59:00', '@ 1 hour 59 mins', 'PT1H59M')),
     716        ((0, 0, 0, -1, 2, 0, 0),
     717            ('-0:58:00', '-00:58:00', '@ 58 mins ago', 'PT-58M')),
     718        ((1, 11, 0, 0, 0, 0, 0),
     719            ('1-11', '1 year 11 mons', '@ 1 year 11 mons', 'P1Y11M')),
     720        ((0, -10, 0, 0, 0, 0, 0),
     721            ('-0-10', '-10 mons', '@ 10 mons ago', 'P-10M')),
     722        ((0, 0, 2, -1, 0, 0, 0),
     723            ('+0-0 +2 -1:00:00', '2 days -01:00:00',
     724             '@ 2 days -1 hours', 'P2DT-1H')),
     725        ((0, 0, -1, 2, 0, 0, 0),
     726            ('+0-0 -1 +2:00:00', '-1 days +02:00:00',
     727             '@ 1 day -2 hours ago', 'P-1DT2H')),
     728        ((0, 0, 1, 0, 0, 0, 1),
     729            ('1 0:00:00.000001', '1 day 00:00:00.000001',
     730             '@ 1 day 0.000001 secs', 'P1DT0.000001S')),
     731        ((0, 0, 1, 0, 0, 1, 0),
     732            ('1 0:00:01', '1 day 00:00:01', '@ 1 day 1 sec', 'P1DT1S')),
     733        ((0, 0, 1, 0, 1, 0, 0),
     734            ('1 0:01:00', '1 day 00:01:00', '@ 1 day 1 min', 'P1DT1M')),
     735        ((0, 0, 0, 0, 1, 0, -1),
     736            ('0:00:59.999999', '00:00:59.999999',
     737             '@ 59.999999 secs', 'PT59.999999S')),
     738        ((0, 0, 0, 0, -1, 0, 1),
     739            ('-0:00:59.999999', '-00:00:59.999999',
     740             '@ 59.999999 secs ago', 'PT-59.999999S')),
     741        ((0, 0, 0, 0, -1, 1, 1),
     742            ('-0:00:58.999999', '-00:00:58.999999',
     743             '@ 58.999999 secs ago', 'PT-58.999999S')),
     744        ((0, 0, 42, 0, 0, 0, 0),
     745            ('42 0:00:00', '42 days', '@ 42 days', 'P42D')),
     746        ((0, 0, -7, 0, 0, 0, 0),
     747            ('-7 0:00:00', '-7 days', '@ 7 days ago', 'P-7D')),
     748        ((1, 1, 1, 1, 1, 0, 0),
     749            ('+1-1 +1 +1:01:00', '1 year 1 mon 1 day 01:01:00',
     750             '@ 1 year 1 mon 1 day 1 hour 1 min', 'P1Y1M1DT1H1M')),
     751        ((0, -11, -1, -1, 1, 0, 0),
     752            ('-0-11 -1 -0:59:00', '-11 mons -1 days -00:59:00',
     753             '@ 11 mons 1 day 59 mins ago', 'P-11M-1DT-59M')),
     754        ((-1, -1, -1, -1, -1, 0, 0),
     755            ('-1-1 -1 -1:01:00', '-1 years -1 mons -1 days -01:01:00',
     756             '@ 1 year 1 mon 1 day 1 hour 1 min ago', 'P-1Y-1M-1DT-1H-1M')),
     757        ((-1, 0, -3, 1, 0, 0, 0),
     758            ('-1-0 -3 +1:00:00', '-1 years -3 days +01:00:00',
     759             '@ 1 year 3 days -1 hours ago', 'P-1Y-3DT1H')),
     760        ((1, 0, 0, 0, 0, 0, 1),
     761            ('+1-0 +0 +0:00:00.000001', '1 year 00:00:00.000001',
     762             '@ 1 year 0.000001 secs', 'P1YT0.000001S')),
     763        ((1, 0, 0, 0, 0, 0, -1),
     764            ('+1-0 +0 -0:00:00.000001', '1 year -00:00:00.000001',
     765             '@ 1 year -0.000001 secs', 'P1YT-0.000001S')),
     766        ((1, 2, 3, 4, 5, 6, 7),
     767            ('+1-2 +3 +4:05:06.000007',
     768             '1 year 2 mons 3 days 04:05:06.000007',
     769             '@ 1 year 2 mons 3 days 4 hours 5 mins 6.000007 secs',
     770             'P1Y2M3DT4H5M6.000007S')),
     771        ((0, 10, 3, -4, 5, -6, 7),
     772            ('+0-10 +3 -3:55:05.999993', '10 mons 3 days -03:55:05.999993',
     773             '@ 10 mons 3 days -3 hours -55 mins -5.999993 secs',
     774             'P10M3DT-3H-55M-5.999993S')),
     775        ((0, -10, -3, 4, -5, 6, -7),
     776            ('-0-10 -3 +3:55:05.999993',
     777             '-10 mons -3 days +03:55:05.999993',
     778             '@ 10 mons 3 days -3 hours -55 mins -5.999993 secs ago',
     779             'P-10M-3DT3H55M5.999993S'))]
     780
     781    def testCastInterval(self):
     782        for result, values in self.intervals:
     783            f = pg.cast_interval
     784            years, mons, days, hours, mins, secs, usecs = result
     785            days += 365 * years + 30 * mons
     786            interval = timedelta(days=days, hours=hours, minutes=mins,
     787                seconds=secs, microseconds=usecs)
     788            for value in values:
     789                self.assertEqual(f(value), interval)
     790
     791
    617792class TestEscapeFunctions(unittest.TestCase):
    618793    """Test pg escape and unescape functions.
     
    677852    """
    678853
     854    def testGetDatestyle(self):
     855        self.assertIsNone(pg.get_datestyle())
     856
     857    def testGetDatestyle(self):
     858        datestyle = pg.get_datestyle()
     859        try:
     860            pg.set_datestyle('ISO, YMD')
     861            self.assertEqual(pg.get_datestyle(), 'ISO, YMD')
     862            pg.set_datestyle('Postgres, MDY')
     863            self.assertEqual(pg.get_datestyle(), 'Postgres, MDY')
     864            pg.set_datestyle('Postgres, DMY')
     865            self.assertEqual(pg.get_datestyle(), 'Postgres, DMY')
     866            pg.set_datestyle('SQL, MDY')
     867            self.assertEqual(pg.get_datestyle(), 'SQL, MDY')
     868            pg.set_datestyle('SQL, DMY')
     869            self.assertEqual(pg.get_datestyle(), 'SQL, DMY')
     870            pg.set_datestyle('German, DMY')
     871            self.assertEqual(pg.get_datestyle(), 'German, DMY')
     872            pg.set_datestyle(None)
     873            self.assertIsNone(pg.get_datestyle())
     874        finally:
     875            pg.set_datestyle(datestyle)
     876
    679877    def testGetDecimalPoint(self):
    680878        r = pg.get_decimal_point()
  • trunk/tests/test_dbapi20.py

    r799 r814  
    2929        pass
    3030
    31 from datetime import datetime
     31from datetime import date, time, datetime, timedelta
     32
     33try:
     34    from datetime import timezone
     35except ImportError:  # Python < 3.2
     36    timezone = None
    3237
    3338try:
     
    402407        values = ('test', pgdb.Binary(b'\xff\x52\xb2'),
    403408            True, 5, 6, 5.7, Decimal('234.234234'), Decimal('75.45'),
    404             '2011-07-17', '15:47:42', '2008-10-20 15:25:35', '15:31:05',
    405             7897234)
    406         table = self.table_prefix + 'booze'
    407         con = self._connect()
    408         try:
    409             cur = con.cursor()
    410             cur.execute("set datestyle to 'iso'")
     409            pgdb.Date(2011, 7, 17), pgdb.Time(15, 47, 42),
     410            pgdb.Timestamp(2008, 10, 20, 15, 25, 35),
     411            pgdb.Interval(15, 31, 5), 7897234)
     412        table = self.table_prefix + 'booze'
     413        con = self._connect()
     414        try:
     415            cur = con.cursor()
     416            cur.execute("set datestyle to iso")
    411417            cur.execute("create table %s ("
    412418                "stringtest varchar,"
     
    444450            self.assertIsInstance(row0[6], Decimal)
    445451            self.assertIsInstance(row0[7], Decimal)
    446             self.assertIsInstance(row0[8], str)
    447             self.assertIsInstance(row0[9], str)
    448             self.assertIsInstance(row0[10], str)
    449             self.assertIsInstance(row0[11], str)
     452            self.assertIsInstance(row0[8], date)
     453            self.assertIsInstance(row0[9], time)
     454            self.assertIsInstance(row0[10], datetime)
     455            self.assertIsInstance(row0[11], timedelta)
    450456        finally:
    451457            con.close()
     
    504510
    505511    def test_datetime(self):
    506         values = ['2011-07-17 15:47:42', datetime(2016, 1, 20, 20, 15, 51)]
    507         table = self.table_prefix + 'booze'
    508         con = self._connect()
    509         try:
    510             cur = con.cursor()
    511             cur.execute("set datestyle to 'iso'")
    512             cur.execute(
    513                 "create table %s (n smallint, ts timestamp)" % table)
    514             params = enumerate(values)
    515             cur.executemany("insert into %s values (%%d,%%s)" % table, params)
    516             cur.execute("select ts from %s order by n" % table)
    517             rows = cur.fetchall()
    518             self.assertEqual(cur.description[0].type_code, pgdb.DATETIME)
    519             self.assertNotEqual(cur.description[0].type_code, pgdb.ARRAY)
    520             self.assertNotEqual(cur.description[0].type_code, pgdb.RECORD)
    521         finally:
    522             con.close()
    523         self.assertEqual(len(rows), len(values))
    524         rows = [row[0] for row in rows]
    525         for inval, outval in zip(values, rows):
    526             if isinstance(inval, datetime):
    527                 inval = inval.strftime('%Y-%m-%d %H:%M:%S')
    528             self.assertEqual(inval, outval)
     512        dt = datetime(2011, 7, 17, 15, 47, 42, 317509)
     513        td = dt - datetime(1970, 1, 1)
     514        table = self.table_prefix + 'booze'
     515        con = self._connect()
     516        try:
     517            cur = con.cursor()
     518            cur.execute("set datestyle to iso")
     519            cur.execute("set datestyle to iso")
     520            cur.execute("create table %s ("
     521                "d date, t time,  ts timestamp,"
     522                "tz timetz, tsz timestamptz, i interval)" % table)
     523            for n in range(3):
     524                values = [dt.date(), dt.time(), dt,
     525                    dt.time(), dt, td]
     526                if timezone:
     527                    values[3] = values[3].replace(tzinfo=timezone.utc)
     528                    values[4] = values[4].replace(tzinfo=timezone.utc)
     529                if n == 0:  # input as objects
     530                    params = values
     531                if n == 1:  # input as text
     532                    params = [v.isoformat() for v in values[:5]]  # as text
     533                    params.append('%d days %d seconds %d microseconds '
     534                        % (td.days, td.seconds, td.microseconds))
     535                elif n == 2:  # input using type helpers
     536                    d = (dt.year, dt.month, dt.day)
     537                    t = (dt.hour, dt.minute, dt.second, dt.microsecond)
     538                    i = (td.days, 0, 0, td.seconds, td.microseconds)
     539                    params = [pgdb.Date(*d), pgdb.Time(*t),
     540                            pgdb.Timestamp(*(d + t)), pgdb.Time(*t),
     541                            pgdb.Timestamp(*(d + t)), pgdb.Interval(*i)]
     542                cur.execute("insert into %s"
     543                    " values (%%s,%%s,%%s,%%s,%%s,%%s)" % table, params)
     544                for datestyle in ('iso', 'postgres, mdy', 'postgres, dmy',
     545                        'sql, mdy', 'sql, dmy', 'german'):
     546                    cur.execute("set datestyle to %s" % datestyle)
     547                    cur.execute("select * from %s" % table)
     548                    d = cur.description
     549                    for i in range(6):
     550                        self.assertEqual(d[i].type_code, pgdb.DATETIME)
     551                        self.assertNotEqual(d[i].type_code, pgdb.STRING)
     552                        self.assertNotEqual(d[i].type_code, pgdb.ARRAY)
     553                        self.assertNotEqual(d[i].type_code, pgdb.RECORD)
     554                    self.assertEqual(d[0].type_code, pgdb.DATE)
     555                    self.assertEqual(d[1].type_code, pgdb.TIME)
     556                    self.assertEqual(d[2].type_code, pgdb.TIMESTAMP)
     557                    self.assertEqual(d[3].type_code, pgdb.TIME)
     558                    self.assertEqual(d[4].type_code, pgdb.TIMESTAMP)
     559                    self.assertEqual(d[5].type_code, pgdb.INTERVAL)
     560                    row = cur.fetchone()
     561                    self.assertEqual(row, tuple(values))
     562                cur.execute("delete from %s" % table)
     563        finally:
     564            con.close()
    529565
    530566    def test_insert_array(self):
Note: See TracChangeset for help on using the changeset viewer.