psycopg2/psycopg/typecast_datetime.c
Jason Erickson b075017ad9 Pulled down changes from dvarrazzo branch on gh
Pulled the master branch from of Daniele's psycopg branch on github and
merged the changes.
2011-02-10 15:59:31 -07:00

334 lines
10 KiB
C

/* typecast_datetime.c - date and time typecasting functions to python types
*
* Copyright (C) 2001-2010 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#include <math.h>
#include "datetime.h"
static int
psyco_typecast_datetime_init(void)
{
Dprintf("psyco_typecast_datetime_init: datetime init");
PyDateTime_IMPORT;
if (!PyDateTimeAPI) {
PyErr_SetString(PyExc_ImportError, "datetime initialization failed");
return -1;
}
return 0;
}
/** DATE - cast a date into a date python object **/
static PyObject *
typecast_PYDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
{
PyObject* obj = NULL;
int n, y=0, m=0, d=0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') {
obj = PyObject_GetAttrString(
(PyObject*)PyDateTimeAPI->DateType, "min");
}
else {
obj = PyObject_GetAttrString(
(PyObject*)PyDateTimeAPI->DateType, "max");
}
}
else {
n = typecast_parse_date(str, NULL, &len, &y, &m, &d);
Dprintf("typecast_PYDATE_cast: "
"n = %d, len = " FORMAT_CODE_PY_SSIZE_T ", "
"y = %d, m = %d, d = %d",
n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
return NULL;
}
else {
if (y > 9999) y = 9999;
obj = PyObject_CallFunction(
(PyObject*)PyDateTimeAPI->DateType, "iii", y, m, d);
}
}
return obj;
}
/** DATETIME - cast a timestamp into a datetime python object **/
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;
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(
(PyObject*)PyDateTimeAPI->DateTimeType, "min");
}
else {
obj = PyObject_GetAttrString(
(PyObject*)PyDateTimeAPI->DateTimeType, "max");
}
}
else {
Dprintf("typecast_PYDATETIME_cast: s = %s", str);
n = typecast_parse_date(str, &tp, &len, &y, &m, &d);
Dprintf("typecast_PYDATE_cast: tp = %p "
"n = %d, len = " FORMAT_CODE_PY_SSIZE_T ","
" y = %d, m = %d, d = %d",
tp, n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
return NULL;
}
if (len > 0) {
n = typecast_parse_time(tp, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_PYDATETIME_cast: n = %d,"
" 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 > 6) {
PyErr_SetString(DataError, "unable to parse time");
return NULL;
}
}
if (ss > 59) {
mm += 1;
ss -= 60;
}
if (y > 9999)
y = 9999;
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_PYDATETIME_cast: UTC offset = %ds", tz);
/* The datetime module requires that time zone offsets be
a whole number of minutes, so truncate the seconds to the
closest minute. */
// printf("%d %d %d\n", tz, tzmin, round(tz / 60.0));
tzinfo = PyObject_CallFunction(tzinfo_factory, "i",
(int)round(tz / 60.0));
} else {
Py_INCREF(Py_None);
tzinfo = Py_None;
}
if (tzinfo != NULL) {
obj = PyObject_CallFunction(
(PyObject*)PyDateTimeAPI->DateTimeType, "iiiiiiiO",
y, m, d, hh, mm, ss, us, tzinfo);
Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
tzinfo, Py_REFCNT(tzinfo)
);
Py_DECREF(tzinfo);
}
}
return obj;
}
/** TIME - parse time into a time object **/
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;}
n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_PYTIME_cast: n = %d, 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 > 6) {
PyErr_SetString(DataError, "unable to parse time");
return NULL;
}
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 truncate the seconds to the
closest minute. */
tzinfo = PyObject_CallFunction(tzinfo_factory, "i",
(int)round(tz / 60.0));
} else {
Py_INCREF(Py_None);
tzinfo = Py_None;
}
if (tzinfo != NULL) {
obj = PyObject_CallFunction((PyObject*)PyDateTimeAPI->TimeType, "iiiiO",
hh, mm, ss, us, tzinfo);
Py_DECREF(tzinfo);
}
return obj;
}
/** INTERVAL - parse an interval into a timedelta object **/
static PyObject *
typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
{
long years = 0, months = 0, days = 0;
double hours = 0.0, minutes = 0.0, seconds = 0.0, hundredths = 0.0;
double v = 0.0, sign = 1.0, denominator = 1.0;
int part = 0, sec;
double micro;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
Dprintf("typecast_PYINTERVAL_cast: s = %s", str);
while (len-- > 0 && *str) {
switch (*str) {
case '-':
sign = -1.0;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
v = v*10 + (double)*str - (double)'0';
if (part == 6){
denominator *= 10;
}
break;
case 'y':
if (part == 0) {
years = (long)(v*sign);
str = skip_until_space2(str, &len);
v = 0.0; sign = 1.0; part = 1;
}
break;
case 'm':
if (part <= 1) {
months = (long)(v*sign);
str = skip_until_space2(str, &len);
v = 0.0; sign = 1.0; part = 2;
}
break;
case 'd':
if (part <= 2) {
days = (long)(v*sign);
str = skip_until_space2(str, &len);
v = 0.0; sign = 1.0; part = 3;
}
break;
case ':':
if (part <= 3) {
hours = v;
v = 0.0; part = 4;
}
else if (part == 4) {
minutes = v;
v = 0.0; part = 5;
}
break;
case '.':
if (part == 5) {
seconds = v;
v = 0.0; part = 6;
}
break;
default:
break;
}
str++;
}
/* manage last value, be it minutes or seconds or hundredths of a second */
if (part == 4) {
minutes = v;
}
else if (part == 5) {
seconds = v;
}
else if (part == 6) {
hundredths = v;
hundredths = hundredths/denominator;
}
/* calculates seconds */
if (sign < 0.0) {
seconds = - (hundredths + seconds + minutes*60 + hours*3600);
}
else {
seconds += hundredths + minutes*60 + hours*3600;
}
/* calculates days */
days += years*365 + months*30;
micro = (seconds - floor(seconds)) * 1000000.0;
sec = (int)floor(seconds);
return PyObject_CallFunction((PyObject*)PyDateTimeAPI->DeltaType, "iii",
days, sec, (int)round(micro));
}
/* psycopg defaults to using python datetime types */
#ifdef PSYCOPG_DEFAULT_PYDATETIME
#define typecast_DATE_cast typecast_PYDATE_cast
#define typecast_TIME_cast typecast_PYTIME_cast
#define typecast_INTERVAL_cast typecast_PYINTERVAL_cast
#define typecast_DATETIME_cast typecast_PYDATETIME_cast
#endif