mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-10 19:16:34 +03:00
New time/date locale-safe typecasting code.
This commit is contained in:
parent
770e7c34d3
commit
164eb32817
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -31,11 +31,13 @@ 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")) {
|
||||
if (str[0] == '-') {
|
||||
|
@ -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 **/
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
@ -22,15 +36,15 @@ def sleep(curs):
|
|||
# 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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user