psycopg2/tests/test_dates.py
James Henstridge ba8be438bb * psycopg/typecast.c (typecast_parse_time): Fix up handling of
negative timezone offsets with a non-zero minutes field.

	* tests/test_dates.py (DatetimeTests): Add tests for time zone
	parsing.  The test for HH:MM:SS time zones is disabled because we
	don't currently support it.
2009-02-17 12:58:45 +09:00

297 lines
11 KiB
Python

#!/usr/bin/env python
import math
import unittest
import psycopg2
from psycopg2.tz import FixedOffsetTimezone
import tests
class CommonDatetimeTestsMixin:
def execute(self, *args):
conn = psycopg2.connect(tests.dsn)
curs = conn.cursor()
curs.execute(*args)
return curs.fetchone()[0]
def test_parse_date(self):
value = self.DATE('2007-01-01', None)
self.assertNotEqual(value, None)
self.assertEqual(value.year, 2007)
self.assertEqual(value.month, 1)
self.assertEqual(value.day, 1)
def test_parse_null_date(self):
value = self.DATE(None, None)
self.assertEqual(value, None)
def test_parse_incomplete_date(self):
self.assertRaises(psycopg2.DataError, self.DATE, '2007', None)
self.assertRaises(psycopg2.DataError, self.DATE, '2007-01', None)
def test_parse_time(self):
value = self.TIME('13:30:29', None)
self.assertNotEqual(value, None)
self.assertEqual(value.hour, 13)
self.assertEqual(value.minute, 30)
self.assertEqual(value.second, 29)
def test_parse_null_time(self):
value = self.TIME(None, None)
self.assertEqual(value, None)
def test_parse_incomplete_time(self):
self.assertRaises(psycopg2.DataError, self.TIME, '13', None)
self.assertRaises(psycopg2.DataError, self.TIME, '13:30', None)
def test_parse_datetime(self):
value = self.DATETIME('2007-01-01 13:30:29', None)
self.assertNotEqual(value, None)
self.assertEqual(value.year, 2007)
self.assertEqual(value.month, 1)
self.assertEqual(value.day, 1)
self.assertEqual(value.hour, 13)
self.assertEqual(value.minute, 30)
self.assertEqual(value.second, 29)
def test_parse_null_datetime(self):
value = self.DATETIME(None, None)
self.assertEqual(value, None)
def test_parse_incomplete_time(self):
self.assertRaises(psycopg2.DataError,
self.DATETIME, '2007', None)
self.assertRaises(psycopg2.DataError,
self.DATETIME, '2007-01', None)
self.assertRaises(psycopg2.DataError,
self.DATETIME, '2007-01-01 13', None)
self.assertRaises(psycopg2.DataError,
self.DATETIME, '2007-01-01 13:30', None)
self.assertRaises(psycopg2.DataError,
self.DATETIME, '2007-01-01 13:30:29+00:10:50', None)
def test_parse_null_interval(self):
value = self.INTERVAL(None, None)
self.assertEqual(value, None)
class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
"""Tests for the datetime based date handling in psycopg2."""
def setUp(self):
self.DATE = psycopg2._psycopg.PYDATE
self.TIME = psycopg2._psycopg.PYTIME
self.DATETIME = psycopg2._psycopg.PYDATETIME
self.INTERVAL = psycopg2._psycopg.PYINTERVAL
def test_parse_bc_date(self):
# datetime does not support BC dates
self.assertRaises(ValueError, self.DATE, '00042-01-01 BC', None)
def test_parse_bc_datetime(self):
# datetime does not support BC dates
self.assertRaises(ValueError, self.DATETIME,
'00042-01-01 13:30:29 BC', None)
def test_parse_time_microseconds(self):
value = self.TIME('13:30:29.123456', None)
self.assertEqual(value.second, 29)
self.assertEqual(value.microsecond, 123456)
def test_parse_datetime_microseconds(self):
value = self.DATETIME('2007-01-01 13:30:29.123456', None)
self.assertEqual(value.second, 29)
self.assertEqual(value.microsecond, 123456)
def check_timezone(self, curs, str_offset, offset):
from datetime import datetime, timedelta
base = datetime(2007, 1, 1, 13, 30, 29)
base_str = '2007-01-01 13:30:29'
value = self.DATETIME(base_str + str_offset, curs)
# tzinfo instance is correct.
self.assertTrue(isinstance(value.tzinfo, FixedOffsetTimezone),
"value's timezone is not a FixedOffsetTimezone")
self.assertEqual(value.tzinfo._offset, timedelta(seconds=offset))
# Datetime is correct.
self.assertEqual(value.replace(tzinfo=None), base)
# Offset from UTC is correct.
UTC = FixedOffsetTimezone(0, "UTC")
value_utc = value.astimezone(UTC).replace(tzinfo=None)
self.assertEqual(base - value_utc, timedelta(seconds=offset))
def test_parse_datetime_timezone_hours(self):
conn = psycopg2.connect(tests.dsn)
curs = conn.cursor()
self.check_timezone(curs, "+01", 3600)
self.check_timezone(curs, "-01", -3600)
def test_parse_datetime_timezone_hours_minutes(self):
conn = psycopg2.connect(tests.dsn)
curs = conn.cursor()
self.check_timezone(curs, "+01:15", 4500)
self.check_timezone(curs, "-01:15", -4500)
# This test is disabled because we don't support parsing second
# resolution timezone offsets and Python wouldn't handle them even
# if we did.
def disabled_test_parse_datetime_timezone_hours_minutes_seconds(self):
conn = psycopg2.connect(tests.dsn)
curs = conn.cursor()
self.check_timezone(curs, "+01:15:42", 4542)
self.check_timezone(curs, "-01:15:42", -4542)
def test_parse_interval(self):
value = self.INTERVAL('42 days 12:34:56.123456', None)
self.assertNotEqual(value, None)
self.assertEqual(value.days, 42)
self.assertEqual(value.seconds, 45296)
self.assertEqual(value.microseconds, 123456)
def test_parse_negative_interval(self):
value = self.INTERVAL('-42 days -12:34:56.123456', None)
self.assertNotEqual(value, None)
self.assertEqual(value.days, -43)
self.assertEqual(value.seconds, 41103)
self.assertEqual(value.microseconds, 876544)
def test_adapt_date(self):
from datetime import date
value = self.execute('select (%s)::date::text',
[date(2007, 1, 1)])
self.assertEqual(value, '2007-01-01')
def test_adapt_time(self):
from datetime import time
value = self.execute('select (%s)::time::text',
[time(13, 30, 29)])
self.assertEqual(value, '13:30:29')
def test_adapt_datetime(self):
from datetime import datetime
value = self.execute('select (%s)::timestamp::text',
[datetime(2007, 1, 1, 13, 30, 29)])
self.assertEqual(value, '2007-01-01 13:30:29')
def test_adapt_timedelta(self):
from datetime import timedelta
value = self.execute('select extract(epoch from (%s)::interval)',
[timedelta(days=42, seconds=45296,
microseconds=123456)])
seconds = math.floor(value)
self.assertEqual(seconds, 3674096)
self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
def test_adapt_megative_timedelta(self):
from datetime import timedelta
value = self.execute('select extract(epoch from (%s)::interval)',
[timedelta(days=-42, seconds=45296,
microseconds=123456)])
seconds = math.floor(value)
self.assertEqual(seconds, -3583504)
self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
# Only run the datetime tests if psycopg was compiled with support.
if not hasattr(psycopg2._psycopg, 'PYDATETIME'):
del DatetimeTests
class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
"""Tests for the mx.DateTime based date handling in psycopg2."""
def setUp(self):
self.DATE = psycopg2._psycopg.MXDATE
self.TIME = psycopg2._psycopg.MXTIME
self.DATETIME = psycopg2._psycopg.MXDATETIME
self.INTERVAL = psycopg2._psycopg.MXINTERVAL
def test_parse_bc_date(self):
value = self.DATE('00042-01-01 BC', None)
self.assertNotEqual(value, None)
# mx.DateTime numbers BC dates from 0 rather than 1.
self.assertEqual(value.year, -41)
self.assertEqual(value.month, 1)
self.assertEqual(value.day, 1)
def test_parse_bc_datetime(self):
value = self.DATETIME('00042-01-01 13:30:29 BC', None)
self.assertNotEqual(value, None)
# mx.DateTime numbers BC dates from 0 rather than 1.
self.assertEqual(value.year, -41)
self.assertEqual(value.month, 1)
self.assertEqual(value.day, 1)
self.assertEqual(value.hour, 13)
self.assertEqual(value.minute, 30)
self.assertEqual(value.second, 29)
def test_parse_time_microseconds(self):
value = self.TIME('13:30:29.123456', None)
self.assertEqual(math.floor(value.second), 29)
self.assertEqual(
int((value.second - math.floor(value.second)) * 1000000), 123456)
def test_parse_datetime_microseconds(self):
value = self.DATETIME('2007-01-01 13:30:29.123456', None)
self.assertEqual(math.floor(value.second), 29)
self.assertEqual(
int((value.second - math.floor(value.second)) * 1000000), 123456)
def test_parse_interval(self):
value = self.INTERVAL('42 days 05:50:05', None)
self.assertNotEqual(value, None)
self.assertEqual(value.day, 42)
self.assertEqual(value.hour, 5)
self.assertEqual(value.minute, 50)
self.assertEqual(value.second, 5)
def test_adapt_time(self):
from mx.DateTime import Time
value = self.execute('select (%s)::time::text',
[Time(13, 30, 29)])
self.assertEqual(value, '13:30:29')
def test_adapt_datetime(self):
from mx.DateTime import DateTime
value = self.execute('select (%s)::timestamp::text',
[DateTime(2007, 1, 1, 13, 30, 29.123456)])
self.assertEqual(value, '2007-01-01 13:30:29.123456')
def test_adapt_bc_datetime(self):
from mx.DateTime import DateTime
value = self.execute('select (%s)::timestamp::text',
[DateTime(-41, 1, 1, 13, 30, 29.123456)])
self.assertEqual(value, '0042-01-01 13:30:29.123456 BC')
def test_adapt_timedelta(self):
from mx.DateTime import DateTimeDeltaFrom
value = self.execute('select extract(epoch from (%s)::interval)',
[DateTimeDeltaFrom(days=42,
seconds=45296.123456)])
seconds = math.floor(value)
self.assertEqual(seconds, 3674096)
self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
def test_adapt_megative_timedelta(self):
from mx.DateTime import DateTimeDeltaFrom
value = self.execute('select extract(epoch from (%s)::interval)',
[DateTimeDeltaFrom(days=-42,
seconds=45296.123456)])
seconds = math.floor(value)
self.assertEqual(seconds, -3583504)
self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
# Only run the mx.DateTime tests if psycopg was compiled with support.
if not hasattr(psycopg2._psycopg, 'MXDATETIME'):
del mxDateTimeTests
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)