...
[plcapi.git] / psycopg2 / psycopg / adapter_mxdatetime.c
1 /* adapter_mxdatetime.c - mx date/time objects
2  *
3  * Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
4  *
5  * This file is part of psycopg.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2,
10  * or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #include <Python.h>
23 #include <structmember.h>
24 #include <stringobject.h>
25 #include <mxDateTime.h>
26 #include <string.h>
27
28 #define PSYCOPG_MODULE
29 #include "psycopg/config.h"
30 #include "psycopg/python.h"
31 #include "psycopg/psycopg.h"
32 #include "psycopg/adapter_mxdatetime.h"
33 #include "psycopg/microprotocols_proto.h"
34
35 /* the pointer to the mxDateTime API is initialized by the module init code,
36    we just need to grab it */
37 extern mxDateTimeModule_APIObject *mxDateTimeP;
38
39
40 /* mxdatetime_str, mxdatetime_getquoted - return result of quoting */
41
42 static PyObject *
43 mxdatetime_str(mxdatetimeObject *self)
44 {
45     PyObject *str = NULL, *res = NULL;
46     
47     switch (self->type) {
48
49     case PSYCO_MXDATETIME_DATE:
50     case PSYCO_MXDATETIME_TIMESTAMP:
51         str = PyObject_Str(self->wrapped);
52         
53         /* given the limitation of the mx.DateTime module that uses the same
54            type for both date and timestamp values we need to do some black
55            magic and make sure we're not using an adapt()ed timestamp as a
56            simple date */
57         if (strncmp(&(PyString_AsString(str)[11]), "00:00:00.000", 12) == 0) {
58             PyObject *tmp = 
59                 PyString_FromStringAndSize(PyString_AsString(str), 10);
60             Py_DECREF(str);
61             str = tmp; 
62         }
63         break;
64
65     case PSYCO_MXDATETIME_TIME:
66     case PSYCO_MXDATETIME_INTERVAL:
67         str = PyObject_Str(self->wrapped);
68
69         /* given the limitation of the mx.DateTime module that uses the same
70            type for both time and delta values we need to do some black magic
71            and make sure we're not using an adapt()ed interval as a simple
72            time */                    
73         if (PyString_Size(str) > 8 && PyString_AsString(str)[8] == ':') {
74             mxDateTimeDeltaObject *obj = (mxDateTimeDeltaObject*)self->wrapped;
75         
76             char buffer[8];
77             int i, j, x;
78             
79             double ss = obj->hour*3600.0 + obj->minute*60.0 + obj->second;
80             int us = (int)((ss - floor(ss))*1000000);
81                 
82             for (i=1000000, j=0; i > 0 ; i /= 10) {
83                 x = us/i;
84                 us -= x*i;
85                 buffer[j++] = '0'+x;
86             }
87             buffer[j] = '\0';
88             
89             res = PyString_FromFormat("'%ld days %d.%s seconds'",
90                 obj->day, (int)round(ss), buffer);
91         }
92         break;
93     }
94
95     if (str != NULL && res == NULL) {
96         res = PyString_FromFormat("'%s'", PyString_AsString(str));
97     }
98     Py_XDECREF(str);   
99
100     return res;
101 }
102
103 PyObject *
104 mxdatetime_getquoted(mxdatetimeObject *self, PyObject *args)
105 {
106     if (!PyArg_ParseTuple(args, "")) return NULL;
107     return mxdatetime_str(self);
108 }
109
110 PyObject *
111 mxdatetime_conform(mxdatetimeObject *self, PyObject *args)
112 {
113     PyObject *res, *proto;
114     
115     if (!PyArg_ParseTuple(args, "O", &proto)) return NULL;
116
117     if (proto == (PyObject*)&isqlquoteType)
118         res = (PyObject*)self;
119     else
120         res = Py_None;
121     
122     Py_INCREF(res);
123     return res;
124 }
125
126 /** the MxDateTime object **/
127
128 /* object member list */
129
130 static struct PyMemberDef mxdatetimeObject_members[] = {
131     {"adapted", T_OBJECT, offsetof(mxdatetimeObject, wrapped), RO},
132     {"type", T_INT, offsetof(mxdatetimeObject, type), RO},
133     {NULL}
134 };
135
136 /* object method table */
137
138 static PyMethodDef mxdatetimeObject_methods[] = {
139     {"getquoted", (PyCFunction)mxdatetime_getquoted, METH_VARARGS,
140      "getquoted() -> wrapped object value as SQL date/time"},
141     {"__conform__", (PyCFunction)mxdatetime_conform, METH_VARARGS, NULL},
142     {NULL}  /* Sentinel */
143 };
144
145 /* initialization and finalization methods */
146
147 static int
148 mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
149 {
150     Dprintf("mxdatetime_setup: init mxdatetime object at %p, refcnt = %d",
151             self, ((PyObject *)self)->ob_refcnt);
152
153     self->type = type;
154     self->wrapped = obj;
155     Py_INCREF(self->wrapped);
156     
157     Dprintf("mxdatetime_setup: good mxdatetime object at %p, refcnt = %d",
158             self, ((PyObject *)self)->ob_refcnt);
159     return 0;
160 }
161
162 static void
163 mxdatetime_dealloc(PyObject* obj)
164 {
165     mxdatetimeObject *self = (mxdatetimeObject *)obj;
166
167     Py_XDECREF(self->wrapped);
168     
169     Dprintf("mxdatetime_dealloc: deleted mxdatetime object at %p, refcnt = %d",
170             obj, obj->ob_refcnt);
171     
172     obj->ob_type->tp_free(obj);
173 }
174
175 static int
176 mxdatetime_init(PyObject *obj, PyObject *args, PyObject *kwds)
177 {
178     PyObject *mx;
179     int type = -1; /* raise an error if type was not passed! */
180     
181     if (!PyArg_ParseTuple(args, "O|i", &mx, &type))
182         return -1;
183
184     return mxdatetime_setup((mxdatetimeObject *)obj, mx, type);
185 }
186
187 static PyObject *
188 mxdatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
189 {    
190     return type->tp_alloc(type, 0);
191 }
192
193 static void
194 mxdatetime_del(PyObject* self)
195 {
196     PyObject_Del(self);
197 }
198
199 static PyObject *
200 mxdatetime_repr(mxdatetimeObject *self)
201 {
202     return PyString_FromFormat("<psycopg2._psycopg.MxDateTime object at %p>",
203                                 self);
204 }
205
206 /* object type */
207
208 #define mxdatetimeType_doc \
209 "MxDateTime(mx, type) -> new mx.DateTime wrapper object"
210
211 PyTypeObject mxdatetimeType = {
212     PyObject_HEAD_INIT(NULL)
213     0,
214     "psycopg2._psycopg.MxDateTime",
215     sizeof(mxdatetimeObject),
216     0,
217     mxdatetime_dealloc, /*tp_dealloc*/
218     0,          /*tp_print*/
219     0,          /*tp_getattr*/
220     0,          /*tp_setattr*/   
221
222     0,          /*tp_compare*/
223     (reprfunc)mxdatetime_repr, /*tp_repr*/
224     0,          /*tp_as_number*/
225     0,          /*tp_as_sequence*/
226     0,          /*tp_as_mapping*/
227     0,          /*tp_hash */
228
229     0,          /*tp_call*/
230     (reprfunc)mxdatetime_str, /*tp_str*/
231     0,          /*tp_getattro*/
232     0,          /*tp_setattro*/
233     0,          /*tp_as_buffer*/
234
235     Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
236
237     mxdatetimeType_doc, /*tp_doc*/
238     
239     0,          /*tp_traverse*/
240     0,          /*tp_clear*/
241
242     0,          /*tp_richcompare*/
243     0,          /*tp_weaklistoffset*/
244
245     0,          /*tp_iter*/
246     0,          /*tp_iternext*/
247
248     /* Attribute descriptor and subclassing stuff */
249
250     mxdatetimeObject_methods, /*tp_methods*/
251     mxdatetimeObject_members, /*tp_members*/
252     0,          /*tp_getset*/
253     0,          /*tp_base*/
254     0,          /*tp_dict*/
255     
256     0,          /*tp_descr_get*/
257     0,          /*tp_descr_set*/
258     0,          /*tp_dictoffset*/
259     
260     mxdatetime_init, /*tp_init*/
261     PyType_GenericAlloc, /*tp_alloc*/
262     mxdatetime_new, /*tp_new*/
263     (freefunc)mxdatetime_del, /*tp_free  Low-level free-memory routine */
264     0,          /*tp_is_gc For PyObject_IS_GC */
265     0,          /*tp_bases*/
266     0,          /*tp_mro method resolution order */
267     0,          /*tp_cache*/
268     0,          /*tp_subclasses*/
269     0           /*tp_weaklist*/
270 };
271
272
273 /** module-level functions **/
274
275 #ifdef PSYCOPG_DEFAULT_MXDATETIME
276
277 PyObject *
278 psyco_Date(PyObject *self, PyObject *args) 
279 {
280         PyObject *res, *mx;
281         int year, month, day;
282         
283         if (!PyArg_ParseTuple(args, "iii", &year, &month, &day))
284             return NULL;
285                                          
286         mx = mxDateTimeP->DateTime_FromDateAndTime(year, month, day, 0, 0, 0.0);
287     if (mx == NULL) return NULL;
288
289     res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
290                                 PSYCO_MXDATETIME_DATE);
291     Py_DECREF(mx);
292     return res;
293 }
294
295 PyObject *
296 psyco_Time(PyObject *self, PyObject *args) 
297 {
298         PyObject *res, *mx;
299     int hours, minutes=0;
300         double seconds=0.0;
301
302     if (!PyArg_ParseTuple(args, "iid", &hours, &minutes, &seconds))
303             return NULL;
304                                          
305         mx = mxDateTimeP->DateTimeDelta_FromTime(hours, minutes, seconds);
306     if (mx == NULL) return NULL;
307
308     res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
309                                 PSYCO_MXDATETIME_TIME);
310     Py_DECREF(mx);
311     return res;    
312 }
313
314 PyObject *
315 psyco_Timestamp(PyObject *self, PyObject *args) 
316 {
317         PyObject *res, *mx;
318     int year, month, day;
319         int hour=0, minute=0; /* default to midnight */
320         double second=0.0;
321     
322     if (!PyArg_ParseTuple(args, "lii|iid", &year, &month, &day,
323                           &hour, &minute, &second))
324             return NULL;
325
326     mx = mxDateTimeP->DateTime_FromDateAndTime(year, month, day,
327                                                hour, minute, second);
328     if (mx == NULL) return NULL;
329
330     res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
331                                 PSYCO_MXDATETIME_TIMESTAMP);
332     Py_DECREF(mx);
333     return res;     
334 }
335
336 PyObject *
337 psyco_DateFromTicks(PyObject *self, PyObject *args)
338 {
339     PyObject *res, *mx;
340     double ticks;
341
342     if (!PyArg_ParseTuple(args,"d", &ticks))
343         return NULL;
344                                          
345     if (!(mx = mxDateTimeP->DateTime_FromTicks(ticks)))
346         return NULL;
347     
348     res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
349                                 PSYCO_MXDATETIME_DATE);
350     Py_DECREF(mx);
351     return res; 
352 }
353
354 PyObject *
355 psyco_TimeFromTicks(PyObject *self, PyObject *args)
356 {
357     PyObject *res, *mx, *dt;
358     double ticks;
359
360     if (!PyArg_ParseTuple(args,"d", &ticks))
361         return NULL;
362                                          
363     if (!(dt = mxDateTimeP->DateTime_FromTicks(ticks)))
364         return NULL;
365
366     if (!(mx = mxDateTimeP->DateTimeDelta_FromDaysAndSeconds(
367             0, ((mxDateTimeObject*)dt)->abstime)))
368     {
369         Py_DECREF(dt);
370         return NULL;
371     }
372
373     Py_DECREF(dt);
374     res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
375                                 PSYCO_MXDATETIME_TIME);
376     Py_DECREF(mx);
377     return res; 
378 }
379
380 PyObject *
381 psyco_TimestampFromTicks(PyObject *self, PyObject *args)
382 {
383     PyObject *mx, *res;
384     double ticks;
385
386     if (!PyArg_ParseTuple(args, "d", &ticks))
387         return NULL;
388                                          
389     if (!(mx = mxDateTimeP->DateTime_FromTicks(ticks)))
390         return NULL;
391
392     res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
393                                  PSYCO_MXDATETIME_TIMESTAMP);
394     Py_DECREF(mx);
395     return res; 
396 }
397
398 #endif
399
400 PyObject *
401 psyco_DateFromMx(PyObject *self, PyObject *args)
402 {
403     PyObject *mx;
404
405     if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx))
406         return NULL;
407     
408     return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
409                                  PSYCO_MXDATETIME_DATE);
410 }
411
412 PyObject *
413 psyco_TimeFromMx(PyObject *self, PyObject *args)
414 {
415     PyObject *mx;
416
417     if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTimeDelta_Type, &mx))
418         return NULL;
419     
420     return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
421                                  PSYCO_MXDATETIME_TIME);
422 }
423
424 PyObject *
425 psyco_TimestampFromMx(PyObject *self, PyObject *args)
426 {
427     PyObject *mx;
428
429     if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx))
430         return NULL;
431     
432     return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
433                                  PSYCO_MXDATETIME_TIMESTAMP);
434 }
435
436 PyObject *
437 psyco_IntervalFromMx(PyObject *self, PyObject *args)
438 {
439     PyObject *mx;
440
441     if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx))
442         return NULL;
443     
444     return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
445                                  PSYCO_MXDATETIME_INTERVAL);
446 }