mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-23 01:16:34 +03:00
* psycopg/typecast.c (typecast_parse_time): give the correct
return value for partially parsed time values. * psycopg/typecast_mxdatetime.c (typecast_MXDATE_cast): return NULL after setting DataError. Also, don't treat it as an error if typecast_parse_time() returns 0 (as might happen if the remainder of the string is " BC"). * psycopg/typecast_datetime.c (typecast_PYDATE_cast): return NULL after setting DataError. (typecast_PYDATETIME_cast): same here. (typecast_PYTIME_cast): same here. * tests/test_dates.py (CommonDatetimeTestsMixin.test_parse_incomplete_date): test that parsing incomplete date values results in DataError. (CommonDatetimeTestsMixin.test_parse_incomplete_time): same for times. (CommonDatetimeTestsMixin.test_parse_incomplete_time): same for datetimes.
This commit is contained in:
parent
1ea0cd4980
commit
cceaa7331b
23
ChangeLog
23
ChangeLog
|
@ -1,3 +1,26 @@
|
||||||
|
2008-03-17 James Henstridge <james@jamesh.id.au>
|
||||||
|
|
||||||
|
* psycopg/typecast.c (typecast_parse_time): give the correct
|
||||||
|
return value for partially parsed time values.
|
||||||
|
|
||||||
|
* psycopg/typecast_mxdatetime.c (typecast_MXDATE_cast): return
|
||||||
|
NULL after setting DataError. Also, don't treat it as an error if
|
||||||
|
typecast_parse_time() returns 0 (as might happen if the remainder
|
||||||
|
of the string is " BC").
|
||||||
|
|
||||||
|
* psycopg/typecast_datetime.c (typecast_PYDATE_cast): return NULL
|
||||||
|
after setting DataError.
|
||||||
|
(typecast_PYDATETIME_cast): same here.
|
||||||
|
(typecast_PYTIME_cast): same here.
|
||||||
|
|
||||||
|
* tests/test_dates.py
|
||||||
|
(CommonDatetimeTestsMixin.test_parse_incomplete_date): test that
|
||||||
|
parsing incomplete date values results in DataError.
|
||||||
|
(CommonDatetimeTestsMixin.test_parse_incomplete_time): same for
|
||||||
|
times.
|
||||||
|
(CommonDatetimeTestsMixin.test_parse_incomplete_time): same for
|
||||||
|
datetimes.
|
||||||
|
|
||||||
2008-03-07 Jason Erickson <jerickso@stickpeople.com>
|
2008-03-07 Jason Erickson <jerickso@stickpeople.com>
|
||||||
|
|
||||||
* psycopg/pqpath.c (pq_raise): if PSYCOPG_EXTENSIONS is not
|
* psycopg/pqpath.c (pq_raise): if PSYCOPG_EXTENSIONS is not
|
||||||
|
|
|
@ -146,7 +146,9 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acc != -1) {
|
if (acc != -1) {
|
||||||
if (cz == 2) { *ss = acc; cz += 1; }
|
if (cz == 0) { *hh = acc; cz += 1; }
|
||||||
|
else if (cz == 1) { *mm = acc; cz += 1; }
|
||||||
|
else if (cz == 2) { *ss = acc; cz += 1; }
|
||||||
else if (cz == 3) { *us = acc; cz += 1; }
|
else if (cz == 3) { *us = acc; cz += 1; }
|
||||||
else if (cz == 4) { tzhh = acc; cz += 1; }
|
else if (cz == 4) { tzhh = acc; cz += 1; }
|
||||||
else if (cz == 5) tzmm = acc;
|
else if (cz == 5) tzmm = acc;
|
||||||
|
|
|
@ -58,6 +58,7 @@ typecast_PYDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
n, len, y, m, d);
|
n, len, y, m, d);
|
||||||
if (n != 3) {
|
if (n != 3) {
|
||||||
PyErr_SetString(DataError, "unable to parse date");
|
PyErr_SetString(DataError, "unable to parse date");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (y > 9999) y = 9999;
|
if (y > 9999) y = 9999;
|
||||||
|
@ -98,6 +99,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
tp, n, len, y, m, d);
|
tp, n, len, y, m, d);
|
||||||
if (n != 3) {
|
if (n != 3) {
|
||||||
PyErr_SetString(DataError, "unable to parse date");
|
PyErr_SetString(DataError, "unable to parse date");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
|
@ -108,6 +110,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
n, len, hh, mm, ss, us, tz);
|
n, len, hh, mm, ss, us, tz);
|
||||||
if (n < 3 || n > 5) {
|
if (n < 3 || n > 5) {
|
||||||
PyErr_SetString(DataError, "unable to parse time");
|
PyErr_SetString(DataError, "unable to parse time");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,6 +161,7 @@ typecast_PYTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
|
|
||||||
if (n < 3 || n > 5) {
|
if (n < 3 || n > 5) {
|
||||||
PyErr_SetString(DataError, "unable to parse time");
|
PyErr_SetString(DataError, "unable to parse time");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (ss > 59) {
|
if (ss > 59) {
|
||||||
|
|
|
@ -54,6 +54,7 @@ typecast_MXDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
" y = %d, m = %d, d = %d", tp, n, len, y, m, d);
|
" y = %d, m = %d, d = %d", tp, n, len, y, m, d);
|
||||||
if (n != 3) {
|
if (n != 3) {
|
||||||
PyErr_SetString(DataError, "unable to parse date");
|
PyErr_SetString(DataError, "unable to parse date");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
|
@ -62,8 +63,9 @@ typecast_MXDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
" len = " FORMAT_CODE_PY_SSIZE_T ","
|
" len = " FORMAT_CODE_PY_SSIZE_T ","
|
||||||
" hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
|
" hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
|
||||||
n, len, hh, mm, ss, us, tz);
|
n, len, hh, mm, ss, us, tz);
|
||||||
if (n < 3 || n > 5) {
|
if (n != 0 && (n < 3 || n > 5)) {
|
||||||
PyErr_SetString(DataError, "unable to parse time");
|
PyErr_SetString(DataError, "unable to parse time");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,10 @@ class CommonDatetimeTestsMixin:
|
||||||
value = self.DATE(None, None)
|
value = self.DATE(None, None)
|
||||||
self.assertEqual(value, 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):
|
def test_parse_time(self):
|
||||||
value = self.TIME('13:30:29', None)
|
value = self.TIME('13:30:29', None)
|
||||||
self.assertNotEqual(value, None)
|
self.assertNotEqual(value, None)
|
||||||
|
@ -36,6 +40,10 @@ class CommonDatetimeTestsMixin:
|
||||||
value = self.TIME(None, None)
|
value = self.TIME(None, None)
|
||||||
self.assertEqual(value, 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):
|
def test_parse_datetime(self):
|
||||||
value = self.DATETIME('2007-01-01 13:30:29', None)
|
value = self.DATETIME('2007-01-01 13:30:29', None)
|
||||||
self.assertNotEqual(value, None)
|
self.assertNotEqual(value, None)
|
||||||
|
@ -49,240 +57,18 @@ class CommonDatetimeTestsMixin:
|
||||||
def test_parse_null_datetime(self):
|
def test_parse_null_datetime(self):
|
||||||
value = self.DATETIME(None, None)
|
value = self.DATETIME(None, None)
|
||||||
self.assertEqual(value, None)
|
self.assertEqual(value, None)
|
||||||
|
|
||||||
def test_parse_null_interval(self):
|
def test_parse_incomplete_time(self):
|
||||||
value = self.INTERVAL(None, None)
|
self.assertRaises(psycopg2.DataError,
|
||||||
self.assertEqual(value, None)
|
self.DATETIME, '2007', None)
|
||||||
|
self.assertRaises(psycopg2.DataError,
|
||||||
|
self.DATETIME, '2007-01', None)
|
||||||
class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
|
self.assertRaises(psycopg2.DataError,
|
||||||
"""Tests for the datetime based date handling in psycopg2."""
|
self.DATETIME, '2007-01-01 13', None)
|
||||||
|
self.assertRaises(psycopg2.DataError,
|
||||||
def setUp(self):
|
self.DATETIME, '2007-01-01 13:30', None)
|
||||||
self.DATE = psycopg2._psycopg.PYDATE
|
self.assertRaises(psycopg2.DataError,
|
||||||
self.TIME = psycopg2._psycopg.PYTIME
|
self.DATETIME, '2007-01-01 13:30:29+00:10:50', None)
|
||||||
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 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__)
|
|
||||||
|
|
||||||
#!/usr/bin/env python
|
|
||||||
import math
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import psycopg2
|
|
||||||
import tests
|
|
||||||
|
|
||||||
|
|
||||||
class CommonDatetimeTestsMixin:
|
|
||||||
|
|
||||||
def execute(self, *args):
|
|
||||||
conn = psycopg2.connect("dbname=%s" % tests.dbname)
|
|
||||||
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_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_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_null_interval(self):
|
def test_parse_null_interval(self):
|
||||||
value = self.INTERVAL(None, None)
|
value = self.INTERVAL(None, None)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user