mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 17:06:33 +03:00
Merge from trunk
This commit is contained in:
commit
49bdaf92f6
35
ChangeLog
35
ChangeLog
|
@ -1,3 +1,38 @@
|
|||
2009-02-17 James Henstridge <james@jamesh.id.au>
|
||||
|
||||
* psycopg/utils.c (psycopg_escape_string): same here.
|
||||
|
||||
* psycopg/adapter_binary.c (binary_escape): simplify PostgreSQL
|
||||
version check.
|
||||
|
||||
* setup.py (psycopg_build_ext.finalize_options): use a single
|
||||
define of the PostgreSQL version in a form that can easily be used
|
||||
by #ifdefs.
|
||||
|
||||
* tests/test_dates.py (DatetimeTests, mxDateTimeTests): full test
|
||||
coverage for datetime and time strings with and without time zone
|
||||
information.
|
||||
|
||||
* psycopg/typecast_datetime.c (typecast_PYDATETIME_cast): adjust
|
||||
to handle the changes in typecast_parse_time.
|
||||
(typecast_PYTIME_cast): add support for time zone aware time
|
||||
values.
|
||||
|
||||
* psycopg/typecast_mxdatetime.c (typecast_MXDATE_cast): make sure
|
||||
that values with time zones are correctly processed (even though
|
||||
that means ignoring the time zone value).
|
||||
(typecast_MXTIME_cast): same here.
|
||||
|
||||
* psycopg/typecast.c (typecast_parse_time): Update method to parse
|
||||
second resolution timezone offsets.
|
||||
|
||||
* 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-16 Federico Di Gregorio <fog@initd.org>
|
||||
|
||||
* FreeBSD now has round(). Modified config.h as suggested by
|
||||
|
|
|
@ -42,9 +42,7 @@ static unsigned char *
|
|||
binary_escape(unsigned char *from, size_t from_length,
|
||||
size_t *to_length, PGconn *conn)
|
||||
{
|
||||
#if PG_MAJOR_VERSION > 8 || \
|
||||
(PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION > 1) || \
|
||||
(PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION == 1 && PG_PATCH_VERSION >= 4)
|
||||
#if PG_VERSION_HEX >= 0x080104
|
||||
if (conn)
|
||||
return PQescapeByteaConn(conn, from, from_length, to_length);
|
||||
else
|
||||
|
|
|
@ -96,7 +96,7 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
|
|||
int* hh, int* mm, int* ss, int* us, int* tz)
|
||||
{
|
||||
int acc = -1, cz = 0;
|
||||
int tzs = 1, tzhh = 0, tzmm = 0;
|
||||
int tzsign = 1, tzhh = 0, tzmm = 0, tzss = 0;
|
||||
int usd = 0;
|
||||
|
||||
/* sets microseconds and timezone to 0 because they may be missing */
|
||||
|
@ -105,7 +105,7 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
|
|||
Dprintf("typecast_parse_time: len = " FORMAT_CODE_PY_SSIZE_T ", s = %s",
|
||||
*len, s);
|
||||
|
||||
while (cz < 6 && *len > 0 && *s) {
|
||||
while (cz < 7 && *len > 0 && *s) {
|
||||
switch (*s) {
|
||||
case ':':
|
||||
if (cz == 0) *hh = acc;
|
||||
|
@ -113,6 +113,7 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
|
|||
else if (cz == 2) *ss = acc;
|
||||
else if (cz == 3) *us = acc;
|
||||
else if (cz == 4) tzhh = acc;
|
||||
else if (cz == 5) tzmm = acc;
|
||||
acc = -1; cz++;
|
||||
break;
|
||||
case '.':
|
||||
|
@ -125,7 +126,7 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
|
|||
case '-':
|
||||
/* seconds or microseconds here, anything else is an error */
|
||||
if (cz < 2 || cz > 3) return -1;
|
||||
if (*s == '-') tzs = -1;
|
||||
if (*s == '-') tzsign = -1;
|
||||
if (cz == 2) *ss = acc;
|
||||
else if (cz == 3) *us = acc;
|
||||
acc = -1; cz = 4;
|
||||
|
@ -151,11 +152,12 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
|
|||
else if (cz == 2) { *ss = acc; cz += 1; }
|
||||
else if (cz == 3) { *us = acc; cz += 1; }
|
||||
else if (cz == 4) { tzhh = acc; cz += 1; }
|
||||
else if (cz == 5) tzmm = acc;
|
||||
else if (cz == 5) { tzmm = acc; cz += 1; }
|
||||
else if (cz == 6) tzss = acc;
|
||||
}
|
||||
if (t != NULL) *t = s;
|
||||
|
||||
*tz = tzs * tzhh*60 + tzmm;
|
||||
*tz = tzsign * (3600 * tzhh + 60 * tzmm + tzss);
|
||||
|
||||
if (*us != 0) {
|
||||
while (usd++ < 6) *us *= 10;
|
||||
|
|
|
@ -74,6 +74,8 @@ static PyObject *
|
|||
typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||
{
|
||||
PyObject* obj = NULL;
|
||||
PyObject *tzinfo = NULL;
|
||||
PyObject *tzinfo_factory;
|
||||
int n, y=0, m=0, d=0;
|
||||
int hh=0, mm=0, ss=0, us=0, tz=0;
|
||||
const char *tp = NULL;
|
||||
|
@ -108,7 +110,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
" len = " FORMAT_CODE_PY_SSIZE_T ","
|
||||
" hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
|
||||
n, len, hh, mm, ss, us, tz);
|
||||
if (n < 3 || n > 5) {
|
||||
if (n < 3 || n > 6) {
|
||||
PyErr_SetString(DataError, "unable to parse time");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -121,24 +123,34 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
if (y > 9999)
|
||||
y = 9999;
|
||||
|
||||
if (n == 5 && ((cursorObject*)curs)->tzinfo_factory != Py_None) {
|
||||
tzinfo_factory = ((cursorObject *)curs)->tzinfo_factory;
|
||||
if (n >= 5 && tzinfo_factory != Py_None) {
|
||||
/* we have a time zone, calculate minutes and create
|
||||
appropriate tzinfo object calling the factory */
|
||||
PyObject *tzinfo;
|
||||
Dprintf("typecast_PYDATETIME_cast: UTC offset = %dm", tz);
|
||||
tzinfo = PyObject_CallFunction(
|
||||
((cursorObject*)curs)->tzinfo_factory, "i", tz);
|
||||
Dprintf("typecast_PYDATETIME_cast: UTC offset = %ds", tz);
|
||||
|
||||
/* The datetime module requires that time zone offsets be
|
||||
a whole number of minutes, so fail if we have a time
|
||||
zone with a seconds offset.
|
||||
*/
|
||||
if (tz % 60 != 0) {
|
||||
PyErr_Format(PyExc_ValueError, "time zone offset %d is not "
|
||||
"a whole number of minutes", tz);
|
||||
return NULL;
|
||||
}
|
||||
tzinfo = PyObject_CallFunction(tzinfo_factory, "i", tz / 60);
|
||||
} else {
|
||||
Py_INCREF(Py_None);
|
||||
tzinfo = Py_None;
|
||||
}
|
||||
if (tzinfo != NULL) {
|
||||
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiiiO",
|
||||
y, m, d, hh, mm, ss, us, tzinfo);
|
||||
Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = "
|
||||
FORMAT_CODE_PY_SSIZE_T,
|
||||
tzinfo, tzinfo->ob_refcnt
|
||||
);
|
||||
Py_XDECREF(tzinfo);
|
||||
}
|
||||
else {
|
||||
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiii",
|
||||
y, m, d, hh, mm, ss, us);
|
||||
Py_DECREF(tzinfo);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
|
@ -150,6 +162,8 @@ static PyObject *
|
|||
typecast_PYTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||
{
|
||||
PyObject* obj = NULL;
|
||||
PyObject *tzinfo = NULL;
|
||||
PyObject *tzinfo_factory;
|
||||
int n, hh=0, mm=0, ss=0, us=0, tz=0;
|
||||
|
||||
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
|
||||
|
@ -159,16 +173,38 @@ typecast_PYTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
"hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
|
||||
n, len, hh, mm, ss, us, tz);
|
||||
|
||||
if (n < 3 || n > 5) {
|
||||
if (n < 3 || n > 6) {
|
||||
PyErr_SetString(DataError, "unable to parse time");
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
if (ss > 59) {
|
||||
mm += 1;
|
||||
ss -= 60;
|
||||
if (ss > 59) {
|
||||
mm += 1;
|
||||
ss -= 60;
|
||||
}
|
||||
tzinfo_factory = ((cursorObject *)curs)->tzinfo_factory;
|
||||
if (n >= 5 && tzinfo_factory != Py_None) {
|
||||
/* we have a time zone, calculate minutes and create
|
||||
appropriate tzinfo object calling the factory */
|
||||
Dprintf("typecast_PYTIME_cast: UTC offset = %ds", tz);
|
||||
|
||||
/* The datetime module requires that time zone offsets be
|
||||
a whole number of minutes, so fail if we have a time
|
||||
zone with a seconds offset.
|
||||
*/
|
||||
if (tz % 60 != 0) {
|
||||
PyErr_Format(PyExc_ValueError, "time zone offset %d is not "
|
||||
"a whole number of minutes", tz);
|
||||
return NULL;
|
||||
}
|
||||
obj = PyObject_CallFunction(pyTimeTypeP, "iiii", hh, mm, ss, us);
|
||||
tzinfo = PyObject_CallFunction(tzinfo_factory, "i", tz / 60);
|
||||
} else {
|
||||
Py_INCREF(Py_None);
|
||||
tzinfo = Py_None;
|
||||
}
|
||||
if (tzinfo != NULL) {
|
||||
obj = PyObject_CallFunction(pyTimeTypeP, "iiiiO",
|
||||
hh, mm, ss, us, tzinfo);
|
||||
Py_DECREF(tzinfo);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ typecast_MXDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
" len = " FORMAT_CODE_PY_SSIZE_T ","
|
||||
" hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
|
||||
n, len, hh, mm, ss, us, tz);
|
||||
if (n != 0 && (n < 3 || n > 5)) {
|
||||
if (n != 0 && (n < 3 || n > 6)) {
|
||||
PyErr_SetString(DataError, "unable to parse time");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ typecast_MXTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
Dprintf("typecast_MXTIME_cast: hh = %d, mm = %d, ss = %d, us = %d",
|
||||
hh, mm, ss, us);
|
||||
|
||||
if (n < 3 || n > 5) {
|
||||
if (n < 3 || n > 6) {
|
||||
PyErr_SetString(DataError, "unable to parse time");
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -31,9 +31,7 @@ psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len,
|
|||
|
||||
#ifndef PSYCOPG_OWN_QUOTING
|
||||
{
|
||||
#if PG_MAJOR_VERSION > 8 || \
|
||||
(PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION > 1) || \
|
||||
(PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION == 1 && PG_PATCH_VERSION >= 4)
|
||||
#if PG_VERSION_HEX >= 0x080104
|
||||
int err;
|
||||
if (conn && conn->pgconn)
|
||||
ql = PQescapeStringConn(conn->pgconn, to+eq+1, from, len, &err);
|
||||
|
|
9
setup.py
9
setup.py
|
@ -204,12 +204,11 @@ class psycopg_build_ext(build_ext):
|
|||
# *at least* PostgreSQL 7.4 is available (this is the only
|
||||
# 7.x series supported by psycopg 2)
|
||||
pgversion = self.get_pg_config("version").split()[1]
|
||||
pgmajor, pgminor, pgpatch = pgversion.split('.')
|
||||
except:
|
||||
pgmajor, pgminor, pgpatch = 7, 4, 0
|
||||
define_macros.append(("PG_MAJOR_VERSION", pgmajor))
|
||||
define_macros.append(("PG_MINOR_VERSION", pgminor))
|
||||
define_macros.append(("PG_PATCH_VERSION", pgpatch))
|
||||
pgversion = "7.4.0"
|
||||
pgmajor, pgminor, pgpatch = pgversion.split('.')
|
||||
define_macros.append(("PG_VERSION_HEX", "0x%02X%02X%02X" %
|
||||
(int(pgmajor), int(pgminor), int(pgpatch))))
|
||||
except Warning, w:
|
||||
if self.pg_config == self.DEFAULT_PG_CONFIG:
|
||||
sys.stderr.write("Warning: %s" % str(w))
|
||||
|
|
|
@ -3,49 +3,48 @@ 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]
|
||||
self.curs.execute(*args)
|
||||
return self.curs.fetchone()[0]
|
||||
|
||||
def test_parse_date(self):
|
||||
value = self.DATE('2007-01-01', None)
|
||||
value = self.DATE('2007-01-01', self.curs)
|
||||
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)
|
||||
value = self.DATE(None, self.curs)
|
||||
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)
|
||||
self.assertRaises(psycopg2.DataError, self.DATE, '2007', self.curs)
|
||||
self.assertRaises(psycopg2.DataError, self.DATE, '2007-01', self.curs)
|
||||
|
||||
def test_parse_time(self):
|
||||
value = self.TIME('13:30:29', None)
|
||||
value = self.TIME('13:30:29', self.curs)
|
||||
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)
|
||||
value = self.TIME(None, self.curs)
|
||||
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)
|
||||
self.assertRaises(psycopg2.DataError, self.TIME, '13', self.curs)
|
||||
self.assertRaises(psycopg2.DataError, self.TIME, '13:30', self.curs)
|
||||
|
||||
def test_parse_datetime(self):
|
||||
value = self.DATETIME('2007-01-01 13:30:29', None)
|
||||
value = self.DATETIME('2007-01-01 13:30:29', self.curs)
|
||||
self.assertNotEqual(value, None)
|
||||
self.assertEqual(value.year, 2007)
|
||||
self.assertEqual(value.month, 1)
|
||||
|
@ -55,23 +54,21 @@ class CommonDatetimeTestsMixin:
|
|||
self.assertEqual(value.second, 29)
|
||||
|
||||
def test_parse_null_datetime(self):
|
||||
value = self.DATETIME(None, None)
|
||||
value = self.DATETIME(None, self.curs)
|
||||
self.assertEqual(value, None)
|
||||
|
||||
def test_parse_incomplete_time(self):
|
||||
self.assertRaises(psycopg2.DataError,
|
||||
self.DATETIME, '2007', None)
|
||||
self.DATETIME, '2007', self.curs)
|
||||
self.assertRaises(psycopg2.DataError,
|
||||
self.DATETIME, '2007-01', None)
|
||||
self.DATETIME, '2007-01', self.curs)
|
||||
self.assertRaises(psycopg2.DataError,
|
||||
self.DATETIME, '2007-01-01 13', None)
|
||||
self.DATETIME, '2007-01-01 13', self.curs)
|
||||
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)
|
||||
self.DATETIME, '2007-01-01 13:30', self.curs)
|
||||
|
||||
def test_parse_null_interval(self):
|
||||
value = self.INTERVAL(None, None)
|
||||
value = self.INTERVAL(None, self.curs)
|
||||
self.assertEqual(value, None)
|
||||
|
||||
|
||||
|
@ -79,39 +76,137 @@ class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
|
|||
"""Tests for the datetime based date handling in psycopg2."""
|
||||
|
||||
def setUp(self):
|
||||
self.conn = psycopg2.connect(tests.dsn)
|
||||
self.curs = self.conn.cursor()
|
||||
self.DATE = psycopg2._psycopg.PYDATE
|
||||
self.TIME = psycopg2._psycopg.PYTIME
|
||||
self.DATETIME = psycopg2._psycopg.PYDATETIME
|
||||
self.INTERVAL = psycopg2._psycopg.PYINTERVAL
|
||||
|
||||
def tearDown(self):
|
||||
self.conn.close()
|
||||
|
||||
def test_parse_bc_date(self):
|
||||
# datetime does not support BC dates
|
||||
self.assertRaises(ValueError, self.DATE, '00042-01-01 BC', None)
|
||||
self.assertRaises(ValueError, self.DATE, '00042-01-01 BC', self.curs)
|
||||
|
||||
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)
|
||||
'00042-01-01 13:30:29 BC', self.curs)
|
||||
|
||||
def test_parse_time_microseconds(self):
|
||||
value = self.TIME('13:30:29.123456', None)
|
||||
value = self.TIME('13:30:29.123456', self.curs)
|
||||
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)
|
||||
value = self.DATETIME('2007-01-01 13:30:29.123456', self.curs)
|
||||
self.assertEqual(value.second, 29)
|
||||
self.assertEqual(value.microsecond, 123456)
|
||||
|
||||
def check_time_tz(self, str_offset, offset):
|
||||
from datetime import time, timedelta
|
||||
base = time(13, 30, 29)
|
||||
base_str = '13:30:29'
|
||||
|
||||
value = self.TIME(base_str + str_offset, self.curs)
|
||||
|
||||
# Value has time zone info and correct UTC offset.
|
||||
self.assertNotEqual(value.tzinfo, None),
|
||||
self.assertEqual(value.utcoffset(), timedelta(seconds=offset))
|
||||
|
||||
# Time portion is correct.
|
||||
self.assertEqual(value.replace(tzinfo=None), base)
|
||||
|
||||
def test_parse_time_timezone(self):
|
||||
self.check_time_tz("+01", 3600)
|
||||
self.check_time_tz("-01", -3600)
|
||||
self.check_time_tz("+01:15", 4500)
|
||||
self.check_time_tz("-01:15", -4500)
|
||||
# The Python datetime module does not support time zone
|
||||
# offsets that are not a whole number of minutes, so we get an
|
||||
# error here. Check that we are generating an understandable
|
||||
# error message.
|
||||
try:
|
||||
self.check_time_tz("+01:15:42", 4542)
|
||||
except ValueError, exc:
|
||||
self.assertEqual(exc.message, "time zone offset 4542 is not a "
|
||||
"whole number of minutes")
|
||||
else:
|
||||
self.fail("Expected ValueError")
|
||||
|
||||
try:
|
||||
self.check_time_tz("-01:15:42", -4542)
|
||||
except ValueError, exc:
|
||||
self.assertEqual(exc.message, "time zone offset -4542 is not a "
|
||||
"whole number of minutes")
|
||||
else:
|
||||
self.fail("Expected ValueError")
|
||||
|
||||
def check_datetime_tz(self, 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, self.curs)
|
||||
|
||||
# Value has time zone info and correct UTC offset.
|
||||
self.assertNotEqual(value.tzinfo, None),
|
||||
self.assertEqual(value.utcoffset(), timedelta(seconds=offset))
|
||||
|
||||
# Datetime is correct.
|
||||
self.assertEqual(value.replace(tzinfo=None), base)
|
||||
|
||||
# Conversion to UTC produces the expected offset.
|
||||
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(self):
|
||||
self.check_datetime_tz("+01", 3600)
|
||||
self.check_datetime_tz("-01", -3600)
|
||||
self.check_datetime_tz("+01:15", 4500)
|
||||
self.check_datetime_tz("-01:15", -4500)
|
||||
# The Python datetime module does not support time zone
|
||||
# offsets that are not a whole number of minutes, so we get an
|
||||
# error here. Check that we are generating an understandable
|
||||
# error message.
|
||||
try:
|
||||
self.check_datetime_tz("+01:15:42", 4542)
|
||||
except ValueError, exc:
|
||||
self.assertEqual(exc.message, "time zone offset 4542 is not a "
|
||||
"whole number of minutes")
|
||||
else:
|
||||
self.fail("Expected ValueError")
|
||||
|
||||
try:
|
||||
self.check_datetime_tz("-01:15:42", -4542)
|
||||
except ValueError, exc:
|
||||
self.assertEqual(exc.message, "time zone offset -4542 is not a "
|
||||
"whole number of minutes")
|
||||
else:
|
||||
self.fail("Expected ValueError")
|
||||
|
||||
def test_parse_time_no_timezone(self):
|
||||
self.assertEqual(self.TIME("13:30:29", self.curs).tzinfo, None)
|
||||
self.assertEqual(self.TIME("13:30:29.123456", self.curs).tzinfo, None)
|
||||
|
||||
def test_parse_datetime_no_timezone(self):
|
||||
self.assertEqual(
|
||||
self.DATETIME("2007-01-01 13:30:29", self.curs).tzinfo, None)
|
||||
self.assertEqual(
|
||||
self.DATETIME("2007-01-01 13:30:29.123456", self.curs).tzinfo, None)
|
||||
|
||||
def test_parse_interval(self):
|
||||
value = self.INTERVAL('42 days 12:34:56.123456', None)
|
||||
value = self.INTERVAL('42 days 12:34:56.123456', self.curs)
|
||||
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)
|
||||
value = self.INTERVAL('-42 days -12:34:56.123456', self.curs)
|
||||
self.assertNotEqual(value, None)
|
||||
self.assertEqual(value.days, -43)
|
||||
self.assertEqual(value.seconds, 41103)
|
||||
|
@ -163,13 +258,18 @@ class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
|
|||
"""Tests for the mx.DateTime based date handling in psycopg2."""
|
||||
|
||||
def setUp(self):
|
||||
self.conn = psycopg2.connect(tests.dsn)
|
||||
self.curs = self.conn.cursor()
|
||||
self.DATE = psycopg2._psycopg.MXDATE
|
||||
self.TIME = psycopg2._psycopg.MXTIME
|
||||
self.DATETIME = psycopg2._psycopg.MXDATETIME
|
||||
self.INTERVAL = psycopg2._psycopg.MXINTERVAL
|
||||
|
||||
def tearDown(self):
|
||||
self.conn.close()
|
||||
|
||||
def test_parse_bc_date(self):
|
||||
value = self.DATE('00042-01-01 BC', None)
|
||||
value = self.DATE('00042-01-01 BC', self.curs)
|
||||
self.assertNotEqual(value, None)
|
||||
# mx.DateTime numbers BC dates from 0 rather than 1.
|
||||
self.assertEqual(value.year, -41)
|
||||
|
@ -177,7 +277,7 @@ class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
|
|||
self.assertEqual(value.day, 1)
|
||||
|
||||
def test_parse_bc_datetime(self):
|
||||
value = self.DATETIME('00042-01-01 13:30:29 BC', None)
|
||||
value = self.DATETIME('00042-01-01 13:30:29 BC', self.curs)
|
||||
self.assertNotEqual(value, None)
|
||||
# mx.DateTime numbers BC dates from 0 rather than 1.
|
||||
self.assertEqual(value.year, -41)
|
||||
|
@ -188,19 +288,47 @@ class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
|
|||
self.assertEqual(value.second, 29)
|
||||
|
||||
def test_parse_time_microseconds(self):
|
||||
value = self.TIME('13:30:29.123456', None)
|
||||
value = self.TIME('13:30:29.123456', self.curs)
|
||||
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)
|
||||
value = self.DATETIME('2007-01-01 13:30:29.123456', self.curs)
|
||||
self.assertEqual(math.floor(value.second), 29)
|
||||
self.assertEqual(
|
||||
int((value.second - math.floor(value.second)) * 1000000), 123456)
|
||||
|
||||
def test_parse_time_timezone(self):
|
||||
# Time zone information is ignored.
|
||||
from mx.DateTime import Time
|
||||
expected = Time(13, 30, 29)
|
||||
self.assertEqual(expected, self.TIME("13:30:29+01", self.curs))
|
||||
self.assertEqual(expected, self.TIME("13:30:29-01", self.curs))
|
||||
self.assertEqual(expected, self.TIME("13:30:29+01:15", self.curs))
|
||||
self.assertEqual(expected, self.TIME("13:30:29-01:15", self.curs))
|
||||
self.assertEqual(expected, self.TIME("13:30:29+01:15:42", self.curs))
|
||||
self.assertEqual(expected, self.TIME("13:30:29-01:15:42", self.curs))
|
||||
|
||||
def test_parse_datetime_timezone(self):
|
||||
# Time zone information is ignored.
|
||||
from mx.DateTime import DateTime
|
||||
expected = DateTime(2007, 1, 1, 13, 30, 29)
|
||||
self.assertEqual(
|
||||
expected, self.DATETIME("2007-01-01 13:30:29+01", self.curs))
|
||||
self.assertEqual(
|
||||
expected, self.DATETIME("2007-01-01 13:30:29-01", self.curs))
|
||||
self.assertEqual(
|
||||
expected, self.DATETIME("2007-01-01 13:30:29+01:15", self.curs))
|
||||
self.assertEqual(
|
||||
expected, self.DATETIME("2007-01-01 13:30:29-01:15", self.curs))
|
||||
self.assertEqual(
|
||||
expected, self.DATETIME("2007-01-01 13:30:29+01:15:42", self.curs))
|
||||
self.assertEqual(
|
||||
expected, self.DATETIME("2007-01-01 13:30:29-01:15:42", self.curs))
|
||||
|
||||
def test_parse_interval(self):
|
||||
value = self.INTERVAL('42 days 05:50:05', None)
|
||||
value = self.INTERVAL('42 days 05:50:05', self.curs)
|
||||
self.assertNotEqual(value, None)
|
||||
self.assertEqual(value.day, 42)
|
||||
self.assertEqual(value.hour, 5)
|
||||
|
|
Loading…
Reference in New Issue
Block a user