New time/date locale-safe typecasting code.

This commit is contained in:
Federico Di Gregorio 2005-11-14 11:57:45 +00:00
parent 770e7c34d3
commit 164eb32817
6 changed files with 211 additions and 77 deletions

View File

@ -1,3 +1,9 @@
2005-11-14 Federico Di Gregorio <fog@initd.org>
* psycopg/typecast.c: added typecast_parse_date and typecast_parse_time
functions to do locale-safe date/time parsing. This would probably also
speed-up psycopg a little bit.
2005-11-07 Federico Di Gregorio <fog@initd.org>
* psycopg/pqpath.c: fixed problem with uninitialized value (all this was

View File

@ -47,6 +47,99 @@ skip_until_space2(char *s, int *len)
return s;
}
static int
typecast_parse_date(char* s, char** t, int* len,
int* year, int* month, int* day)
{
int acc = -1, cz = 0;
Dprintf("typecast_parse_date: len = %d, s = %s", *len, s);
while (cz < 3 && *len > 0 && *s) {
switch (*s) {
case '-':
case ' ':
case 'T':
if (cz == 0) *year = acc;
else if (cz == 1) *month = acc;
else if (cz == 2) *day = acc;
acc = -1; cz++;
break;
default:
acc = (acc == -1 ? 0 : acc*10) + ((int)*s - (int)'0');
break;
}
s++; (*len)--;
}
if (acc != -1) {
*day = acc;
cz += 1;
}
if (t != NULL) *t = s;
return cz;
}
static int
typecast_parse_time(char* s, char** t, int* len,
int* hh, int* mm, int* ss, int* us, int* tz)
{
int acc = -1, cz = 0;
int tzs = 1, tzhh = 0, tzmm = 0;
/* sets microseconds and timezone to 0 because they may be missing */
*us = *tz = 0;
Dprintf("typecast_parse_time: len = %d, s = %s", *len, s);
while (cz < 5 && *len > 0 && *s) {
switch (*s) {
case ':':
if (cz == 0) *hh = acc;
else if (cz == 1) *mm = acc;
else if (cz == 2) *ss = acc;
else if (cz == 3) *us = acc;
else if (cz == 4) tzhh = acc;
acc = -1; cz++;
break;
case '.':
/* we expect seconds and if we don't get them we return an error */
if (cz != 2) return -1;
*ss = acc;
acc = -1; cz++;
break;
case '+':
case '-':
/* seconds or microseconds here, anything else is an error */
if (cz < 2 || cz > 3) return -1;
if (*s == '-') tzs = -1;
if (cz == 2) *ss = acc;
else if (cz == 3) *us = acc;
else if (cz == 4) *tz = acc;
acc = -1; cz++;
break;
default:
acc = (acc == -1 ? 0 : acc*10) + ((int)*s - (int)'0');
break;
}
s++; (*len)--;
}
if (acc != -1) {
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;
}
if (t != NULL) *t = s;
*tz = tzs * tzhh*60 + tzmm;
return cz;
}
/** include casting objects **/
#include "psycopg/typecast_basic.c"

View File

@ -41,7 +41,6 @@ typecast_PYDATE_cast(char *str, int len, PyObject *curs)
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
/* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') {
obj = PyObject_GetAttrString(pyDateTypeP, "min");
@ -52,8 +51,10 @@ typecast_PYDATE_cast(char *str, int len, PyObject *curs)
}
else {
n = sscanf(str, "%d-%d-%d", &y, &m, &d);
n = typecast_parse_date(str, NULL, &len, &y, &m, &d);
Dprintf("typecast_PYDATE_cast: "
"n = %d, len = %d, y = %d, m = %d, d = %d",
n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
}
@ -71,10 +72,8 @@ typecast_PYDATETIME_cast(char *str, int len, PyObject *curs)
{
PyObject* obj = NULL;
int n, y=0, m=0, d=0;
int hh=0, mm=0;
int tzh=0, tzm=0;
double ss=0.0;
char tzs=0;
int hh=0, mm=0, ss=0, us=0, tz=0;
char *tp = NULL;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
@ -90,39 +89,45 @@ typecast_PYDATETIME_cast(char *str, int len, PyObject *curs)
else {
Dprintf("typecast_PYDATETIME_cast: s = %s", str);
n = sscanf(str, "%d-%d-%d %d:%d:%lf%c%d:%d",
&y, &m, &d, &hh, &mm, &ss, &tzs, &tzh, &tzm);
Dprintf("typecast_PYDATETIME_cast: date parsed, %d components", n);
if (n != 3 && n != 6 && n <= 7) {
n = typecast_parse_date(str, &tp, &len, &y, &m, &d);
Dprintf("typecast_PYDATE_cast: tp = %p "
"n = %d, len = %d, y = %d, m = %d, d = %d",
tp, n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
}
if (len > 0) {
n = typecast_parse_time(tp, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_PYDATETIME_cast: n = %d, len = %d, "
"hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
n, len, hh, mm, ss, us, tz);
if (n < 3 || n > 5) {
PyErr_SetString(DataError, "unable to parse time");
}
}
if (ss > 59) {
mm += 1;
ss -= 60;
}
if (n == 5 && ((cursorObject*)curs)->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);
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiiiO",
y, m, d, hh, mm, ss, us, tzinfo);
Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = %d",
tzinfo, tzinfo->ob_refcnt);
Py_XDECREF(tzinfo);
}
else {
double micro = (ss - floor(ss)) * 1000000.0;
int sec = (int)floor(ss);
if (sec > 59) {
mm += 1;
sec -= 60;
}
if (tzs && ((cursorObject*)curs)->tzinfo_factory != Py_None) {
/* we have a time zone, calculate minutes and create
appropriate tzinfo object calling the factory */
PyObject *tzinfo;
tzm += tzh*60;
if (tzs == '-') tzm = -tzm;
Dprintf("typecast_PYDATETIME_cast: UTC offset = %dm", tzm);
tzinfo = PyObject_CallFunction(
((cursorObject*)curs)->tzinfo_factory, "i", tzm);
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiiiO",
y, m, d, hh, mm, sec, (int)round(micro), tzinfo);
Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = %d",
tzinfo, tzinfo->ob_refcnt);
Py_XDECREF(tzinfo);
}
else {
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiii",
y, m, d, hh, mm, sec, (int)round(micro));
}
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiii",
y, m, d, hh, mm, ss, us);
}
}
return obj;
@ -134,25 +139,24 @@ static PyObject *
typecast_PYTIME_cast(char *str, int len, PyObject *curs)
{
PyObject* obj = NULL;
int n, hh=0, mm=0;
double ss=0.0;
int n, hh=0, mm=0, ss=0, us=0, tz=0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
n = sscanf(str, "%d:%d:%lf", &hh, &mm, &ss);
if (n != 3) {
n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_PYTIME_cast: n = %d, len = %d, "
"hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
n, len, hh, mm, ss, us, tz);
if (n < 3 || n > 5) {
PyErr_SetString(DataError, "unable to parse time");
}
else {
double micro = (ss - floor(ss)) * 1000000.0;
int sec = (int)floor(ss);
if (sec > 59) {
if (ss > 59) {
mm += 1;
sec -= 60;
ss -= 60;
}
obj = PyObject_CallFunction(pyTimeTypeP, "iiii",
hh, mm, sec, (int)round(micro));
obj = PyObject_CallFunction(pyTimeTypeP, "iiii", hh, mm, ss, us);
}
return obj;
}
@ -256,7 +260,7 @@ typecast_PYINTERVAL_cast(char *str, int len, PyObject *curs)
seconds += hundredths + minutes*60 + hours*3600;
}
/* calculates days */
/* calculates days */
days += years*365 + months*30;
micro = (seconds - floor(seconds)) * 1000000.0;

View File

@ -31,10 +31,12 @@ static PyObject *
typecast_MXDATE_cast(char *str, int len, PyObject *curs)
{
int n, y=0, m=0, d=0;
int hh=0, mm=0;
double ss=0.0;
int hh=0, mm=0, ss=0, us=0, tz=0;
char *tp = NULL;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
Dprintf("typecast_MXDATE_cast: s = %s", str);
/* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
@ -46,15 +48,27 @@ typecast_MXDATE_cast(char *str, int len, PyObject *curs)
}
}
Dprintf("typecast_MXDATE_cast: s = %s", str);
n = sscanf(str, "%d-%d-%d %d:%d:%lf", &y, &m, &d, &hh, &mm, &ss);
Dprintf("typecast_MXDATE_cast: date parsed, %d components", n);
if (n != 3 && n != 6) {
n = typecast_parse_date(str, &tp, &len, &y, &m, &d);
Dprintf("typecast_MXDATE_cast: tp = %p n = %d, len = %d, "
"y = %d, m = %d, d = %d", tp, n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
return NULL;
}
return mxDateTimeP->DateTime_FromDateAndTime(y, m, d, hh, mm, ss);
if (len > 0) {
n = typecast_parse_time(tp, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_MXDATE_cast: n = %d, len = %d, "
"hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
n, len, hh, mm, ss, us, tz);
if (n < 3 || n > 5) {
PyErr_SetString(DataError, "unable to parse time");
}
}
Dprintf("typecast_MXDATE_cast: fractionary seconds: %lf",
(double)ss + (double)us/(double)1000000.0);
return mxDateTimeP->DateTime_FromDateAndTime(y, m, d, hh, mm,
(double)ss + (double)us/(double)1000000.0);
}
/** TIME - parse time into an mx.DateTime object **/
@ -62,23 +76,26 @@ typecast_MXDATE_cast(char *str, int len, PyObject *curs)
static PyObject *
typecast_MXTIME_cast(char *str, int len, PyObject *curs)
{
int n, hh=0, mm=0;
double ss=0.0;
int n, hh=0, mm=0, ss=0, us=0, tz=0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
Dprintf("typecast_MXTIME_cast: s = %s", str);
n = sscanf(str, "%d:%d:%lf", &hh, &mm, &ss);
n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_MXTIME_cast: time parsed, %d components", n);
Dprintf("typecast_MXTIME_cast: hh = %d, mm = %d, ss = %f", hh, mm, ss);
Dprintf("typecast_MXTIME_cast: hh = %d, mm = %d, ss = %d, us = %d",
hh, mm, ss, us);
if (n != 3) {
if (n < 3 || n > 5) {
PyErr_SetString(DataError, "unable to parse time");
return NULL;
}
return mxDateTimeP->DateTimeDelta_FromTime(hh, mm ,ss);
Dprintf("typecast_MXTIME_cast: fractionary seconds: %lf",
(double)ss + (double)us/(double)1000000.0);
return mxDateTimeP->DateTimeDelta_FromTime(hh, mm,
(double)ss + (double)us/(double)1000000.0);
}
/** INTERVAL - parse an interval into an mx.DateTimeDelta **/

View File

@ -6,10 +6,24 @@ import psycopg2
#print d.days, d.seconds, d.microseconds
#print psycopg.adapt(d).getquoted()
conn = psycopg2.connect("dbname=test_unicode")
conn.set_client_encoding("xxx")
conn = psycopg2.connect("dbname=test")
#conn.set_client_encoding("xxx")
curs = conn.cursor()
#curs.execute("SELECT 1.0 AS foo")
curs.execute("SELECT '2005-2-12'::date AS foo")
print curs.fetchall()
curs.execute("SELECT '10:23:60'::time AS foo")
print curs.fetchall()
curs.execute("SELECT '10:23:59.895342'::time AS foo")
print curs.fetchall()
curs.execute("SELECT '0:0:12.31423'::time with time zone AS foo")
print curs.fetchall()
curs.execute("SELECT '0:0:12+01:30'::time with time zone AS foo")
print curs.fetchall()
curs.execute("SELECT '2005-2-12 10:23:59.895342'::timestamp AS foo")
print curs.fetchall()
curs.execute("SELECT '2005-2-12 10:23:59.895342'::timestamp with time zone AS foo")
print curs.fetchall()
#print curs.fetchmany(2)
#print curs.fetchall()
@ -17,20 +31,20 @@ def sleep(curs):
while not curs.isready():
print "."
time.sleep(.1)
#curs.execute("""
# DECLARE zz INSENSITIVE SCROLL CURSOR WITH HOLD FOR
# SELECT now();
# FOR READ ONLY;""", async = 1)
curs.execute("SELECT now() AS foo", async=1);
sleep(curs)
print curs.fetchall()
#curs.execute("SELECT now() AS foo", async=1);
#sleep(curs)
#print curs.fetchall()
#curs.execute("""
# FETCH FORWARD 1 FROM zz;""", async = 1)
curs.execute("SELECT now() AS bar", async=1);
print curs.fetchall()
#curs.execute("SELECT now() AS bar", async=1);
#print curs.fetchall()
curs.execute("SELECT now() AS bar");
sleep(curs)
#curs.execute("SELECT now() AS bar");
#sleep(curs)

View File

@ -1,5 +1,5 @@
[build_ext]
define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
define=PSYCOPG_DEBUG,PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
# PSYCOPG_EXTENSIONS enables extensions to PEP-249 (you really want this)
# PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower)
# HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4