]> granicus.if.org Git - postgresql/blob - src/interfaces/ecpg/pgtypeslib/timestamp.c
- Added Dave patch for Informix handling of numeric/int conversion.
[postgresql] / src / interfaces / ecpg / pgtypeslib / timestamp.c
1 #include "postgres_fe.h"
2 #include <time.h>
3 #include <float.h>
4 #include <math.h>
5
6 #ifdef __FAST_MATH__
7 #error -ffast-math is known to break this code
8 #endif
9
10 #include "extern.h"
11 #include "dt.h"
12 #include "pgtypes_timestamp.h"
13 #include "pgtypes_date.h"
14 #include "datetime.h"
15
16 int PGTYPEStimestamp_defmt_scan(char **, char *, timestamp *, int *, int *, int *,
17                                                         int *, int *, int *, int *);
18
19 #ifdef HAVE_INT64_TIMESTAMP
20 static int64
21 time2t(const int hour, const int min, const int sec, const fsec_t fsec)
22 {
23         return ((((((hour * 60) + min) * 60) + sec) * INT64CONST(1000000)) + fsec);
24 }       /* time2t() */
25
26 #else
27 static double
28 time2t(const int hour, const int min, const int sec, const fsec_t fsec)
29 {
30         return ((((hour * 60) + min) * 60) + sec + fsec);
31 }       /* time2t() */
32 #endif
33
34 static timestamp
35 dt2local(timestamp dt, int tz)
36 {
37 #ifdef HAVE_INT64_TIMESTAMP
38         dt -= (tz * INT64CONST(1000000));
39 #else
40         dt -= tz;
41         dt = JROUND(dt);
42 #endif
43         return dt;
44 }       /* dt2local() */
45
46 /* tm2timestamp()
47  * Convert a tm structure to a timestamp data type.
48  * Note that year is _not_ 1900-based, but is an explicit full value.
49  * Also, month is one-based, _not_ zero-based.
50  *
51  * Returns -1 on failure (overflow).
52  */
53 int
54 tm2timestamp(struct tm * tm, fsec_t fsec, int *tzp, timestamp *result)
55 {
56 #ifdef HAVE_INT64_TIMESTAMP
57         int             dDate;
58         int64           time;
59
60 #else
61         double          dDate, time;
62 #endif
63
64         /* Julian day routines are not correct for negative Julian days */
65         if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
66                 return -1;
67
68         dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
69         time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
70 #ifdef HAVE_INT64_TIMESTAMP
71         *result = (date * INT64CONST(86400000000)) + time;
72         /* check for major overflow */
73         if ((*result - time) / INT64CONST(86400000000) != date)
74                 return -1;
75         /* check for just-barely overflow (okay except time-of-day wraps) */
76         if ((*result < 0) ? (date >= 0) : (date < 0))
77                 return -1;
78 #else
79         *result = ((dDate * 86400) + time);
80 #endif
81         if (tzp != NULL)
82                 *result = dt2local(*result, -(*tzp));
83
84         return 0;
85 }       /* tm2timestamp() */
86
87 static timestamp
88 SetEpochTimestamp(void)
89 {
90         timestamp       dt;
91         struct tm       tt,
92                            *tm = &tt;
93
94         GetEpochTime(tm);
95         tm2timestamp(tm, 0, NULL, &dt);
96         return dt;
97 }       /* SetEpochTimestamp() */
98
99 static void
100 dt2time(timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
101 {
102 #ifdef HAVE_INT64_TIMESTAMP
103         int64           time;
104
105 #else
106         double          time;
107 #endif
108
109         time = jd;
110
111 #ifdef HAVE_INT64_TIMESTAMP
112         *hour = (time / INT64CONST(3600000000));
113         time -= ((*hour) * INT64CONST(3600000000));
114         *min = (time / INT64CONST(60000000));
115         time -= ((*min) * INT64CONST(60000000));
116         *sec = (time / INT64CONST(1000000));
117         *fsec = (time - (*sec * INT64CONST(1000000)));
118         *sec = (time / INT64CONST(1000000));
119         *fsec = (time - (*sec * INT64CONST(1000000)));
120 #else
121         *hour = (time / 3600);
122         time -= ((*hour) * 3600);
123         *min = (time / 60);
124         time -= ((*min) * 60);
125         *sec = time;
126         *fsec = JROUND(time - *sec);
127 #endif
128         return;
129 }       /* dt2time() */
130
131 /* timestamp2tm()
132  * Convert timestamp data type to POSIX time structure.
133  * Note that year is _not_ 1900-based, but is an explicit full value.
134  * Also, month is one-based, _not_ zero-based.
135  * Returns:
136  *       0 on success
137  *      -1 on out of range
138  *
139  * For dates within the system-supported time_t range, convert to the
140  *      local time zone. If out of this range, leave as GMT. - tgl 97/05/27
141  */
142 static int
143 timestamp2tm(timestamp dt, int *tzp, struct tm * tm, fsec_t *fsec, char **tzn)
144 {
145 #ifdef HAVE_INT64_TIMESTAMP
146         int             dDate, date0;
147         int64           time;
148
149 #else
150         double          dDate, date0;
151         double          time;
152 #endif
153         time_t          utime;
154
155 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
156         struct tm  *tx;
157 #endif
158
159         date0 = date2j(2000, 1, 1);
160
161         time = dt;
162 #ifdef HAVE_INT64_TIMESTAMP
163         TMODULO(time, dDate, INT64CONST(86400000000));
164
165         if (time < INT64CONST(0))
166         {
167                 time += INT64CONST(86400000000);
168                 dDate -= 1;
169         }
170 #else
171         TMODULO(time, dDate, 86400e0);
172
173         if (time < 0)
174         {
175                 time += 86400;
176                 dDate -= 1;
177         }
178 #endif
179
180         /* Julian day routine does not work for negative Julian days */
181         if (dDate < -date0)
182                 return -1;
183
184         /* add offset to go from J2000 back to standard Julian date */
185         dDate += date0;
186
187         j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
188         dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
189
190         if (tzp != NULL)
191         {
192                 /*
193                  * Does this fall within the capabilities of the localtime()
194                  * interface? Then use this to rotate to the local time zone.
195                  */
196                 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
197                 {
198 #ifdef HAVE_INT64_TIMESTAMP
199                         utime = ((dt / INT64CONST(1000000))
200                                    + ((date0 - date2j(1970, 1, 1)) * INT64CONST(86400)));
201 #else
202                         utime = (dt + ((date0 - date2j(1970, 1, 1)) * 86400));
203 #endif
204
205 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
206                         tx = localtime(&utime);
207                         tm->tm_year = tx->tm_year + 1900;
208                         tm->tm_mon = tx->tm_mon + 1;
209                         tm->tm_mday = tx->tm_mday;
210                         tm->tm_hour = tx->tm_hour;
211                         tm->tm_min = tx->tm_min;
212                         tm->tm_isdst = tx->tm_isdst;
213
214 #if defined(HAVE_TM_ZONE)
215                         tm->tm_gmtoff = tx->tm_gmtoff;
216                         tm->tm_zone = tx->tm_zone;
217
218                         *tzp = -(tm->tm_gmtoff);        /* tm_gmtoff is Sun/DEC-ism */
219                         if (tzn != NULL)
220                                 *tzn = (char *) tm->tm_zone;
221 #elif defined(HAVE_INT_TIMEZONE)
222                         *tzp = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
223                         if (tzn != NULL)
224                                 *tzn = tzname[(tm->tm_isdst > 0)];
225 #endif
226
227 #else                                                   /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
228                         *tzp = 0;
229                         /* Mark this as *no* time zone available */
230                         tm->tm_isdst = -1;
231                         if (tzn != NULL)
232                                 *tzn = NULL;
233 #endif
234
235                         dt = dt2local(dt, *tzp);
236                 }
237                 else
238                 {
239                         *tzp = 0;
240                         /* Mark this as *no* time zone available */
241                         tm->tm_isdst = -1;
242                         if (tzn != NULL)
243                                 *tzn = NULL;
244                 }
245         }
246         else
247         {
248                 tm->tm_isdst = -1;
249                 if (tzn != NULL)
250                         *tzn = NULL;
251         }
252
253         return 0;
254 }       /* timestamp2tm() */
255
256 /* EncodeSpecialTimestamp()
257  *      * Convert reserved timestamp data type to string.
258  *       */
259 static int
260 EncodeSpecialTimestamp(timestamp dt, char *str)
261 {
262         if (TIMESTAMP_IS_NOBEGIN(dt))
263                 strcpy(str, EARLY);
264         else if (TIMESTAMP_IS_NOEND(dt))
265                 strcpy(str, LATE);
266         else
267                 return FALSE;
268
269         return TRUE;
270 }       /* EncodeSpecialTimestamp() */
271
272 timestamp
273 PGTYPEStimestamp_from_asc(char *str, char **endptr)
274 {
275         timestamp       result;
276
277 #ifdef HAVE_INT64_TIMESTAMP
278         int64           noresult = 0;
279
280 #else
281         double          noresult = 0.0;
282 #endif
283         fsec_t          fsec;
284         struct tm       tt,
285                            *tm = &tt;
286         int                     tz;
287         int                     dtype;
288         int                     nf;
289         char       *field[MAXDATEFIELDS];
290         int                     ftype[MAXDATEFIELDS];
291         char            lowstr[MAXDATELEN + MAXDATEFIELDS];
292         char       *realptr;
293         char      **ptr = (endptr != NULL) ? endptr : &realptr;
294
295         errno = 0;
296         if (strlen(str) >= sizeof(lowstr))
297         {
298                 errno = PGTYPES_TS_BAD_TIMESTAMP;
299                 return (noresult);
300         }
301
302         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0)
303         || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz, 0) != 0))
304         {
305                 errno = PGTYPES_TS_BAD_TIMESTAMP;
306                 return (noresult);
307         }
308
309         switch (dtype)
310         {
311                 case DTK_DATE:
312                         if (tm2timestamp(tm, fsec, NULL, &result) != 0)
313                         {
314                                 errno = PGTYPES_TS_BAD_TIMESTAMP;
315                                 return (noresult);
316                         }
317                         break;
318
319                 case DTK_EPOCH:
320                         result = SetEpochTimestamp();
321                         break;
322
323                 case DTK_LATE:
324                         TIMESTAMP_NOEND(result);
325                         break;
326
327                 case DTK_EARLY:
328                         TIMESTAMP_NOBEGIN(result);
329                         break;
330
331                 case DTK_INVALID:
332                         errno = PGTYPES_TS_BAD_TIMESTAMP;
333                         return (noresult);
334
335                 default:
336                         errno = PGTYPES_TS_BAD_TIMESTAMP;
337                         return (noresult);
338         }
339
340         /* AdjustTimestampForTypmod(&result, typmod); */
341
342         return result;
343 }
344
345 char *
346 PGTYPEStimestamp_to_asc(timestamp tstamp)
347 {
348         struct tm       tt,
349                            *tm = &tt;
350         char            buf[MAXDATELEN + 1];
351         char       *tzn = NULL;
352         fsec_t          fsec;
353         int                     DateStyle = 1;  /* this defaults to ISO_DATES, shall we
354                                                                  * make it an option? */
355
356         if (TIMESTAMP_NOT_FINITE(tstamp))
357                 EncodeSpecialTimestamp(tstamp, buf);
358         else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
359                 EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf, 0);
360         else
361         {
362                 errno = PGTYPES_TS_BAD_TIMESTAMP;
363                 return NULL;
364         }
365         return pgtypes_strdup(buf);
366 }
367
368 void
369 PGTYPEStimestamp_current(timestamp *ts)
370 {
371         struct tm       tm;
372
373         GetCurrentDateTime(&tm);
374         tm2timestamp(&tm, 0, NULL, ts);
375         return;
376 }
377
378 static int
379 dttofmtasc_replace(timestamp *ts, date dDate, int dow, struct tm * tm,
380                                    char *output, int *pstr_len, char *fmtstr)
381 {
382         union un_fmt_comb replace_val;
383         int                     replace_type;
384         int                     i;
385         char       *p = fmtstr;
386         char       *q = output;
387
388         while (*p)
389         {
390                 if (*p == '%')
391                 {
392                         p++;
393                         /* fix compiler warning */
394                         replace_type = PGTYPES_TYPE_NOTHING;
395                         switch (*p)
396                         {
397                                 case 'a':
398                                         replace_val.str_val = pgtypes_date_weekdays_short[dow];
399                                         replace_type = PGTYPES_TYPE_STRING_CONSTANT;
400                                         break;
401                                 case 'A':
402                                         replace_val.str_val = days[dow];
403                                         replace_type = PGTYPES_TYPE_STRING_CONSTANT;
404                                         break;
405                                 case 'b':
406                                 case 'h':
407                                         replace_val.str_val = months[tm->tm_mon];
408                                         replace_type = PGTYPES_TYPE_STRING_CONSTANT;
409                                         break;
410                                 case 'B':
411                                         replace_val.str_val = pgtypes_date_months[tm->tm_mon];
412                                         replace_type = PGTYPES_TYPE_STRING_CONSTANT;
413                                         break;
414                                 case 'c':
415                                         /* XXX */
416                                         break;
417                                 case 'C':
418                                         replace_val.uint_val = tm->tm_year / 100;
419                                         replace_type = PGTYPES_TYPE_UINT_2_LZ;
420                                         break;
421                                 case 'd':
422                                         replace_val.uint_val = tm->tm_mday;
423                                         replace_type = PGTYPES_TYPE_UINT_2_LZ;
424                                         break;
425                                 case 'D':
426
427                                         /*
428                                          * ts, dDate, dow, tm is information about the
429                                          * timestamp
430                                          *
431                                          * q is the start of the current output buffer
432                                          *
433                                          * pstr_len is a pointer to the remaining size of output,
434                                          * i.e. the size of q
435                                          */
436                                         i = dttofmtasc_replace(ts, dDate, dow, tm,
437                                                                                    q, pstr_len,
438                                                                                    "%m/%d/%y");
439                                         if (i)
440                                                 return i;
441                                         break;
442                                 case 'e':
443                                         replace_val.uint_val = tm->tm_mday;
444                                         replace_type = PGTYPES_TYPE_UINT_2_LS;
445                                         break;
446                                 case 'E':
447                                         {
448                                                 char            tmp[4] = "%Ex";
449
450                                                 p++;
451                                                 if (*p == '\0')
452                                                         return -1;
453                                                 tmp[2] = *p;
454                                                 /* XXX: fall back to strftime */
455
456                                                 /*
457                                                  * strftime's month is 0 based, ours is 1 based
458                                                  */
459                                                 tm->tm_mon -= 1;
460                                                 i = strftime(q, *pstr_len, tmp, tm);
461                                                 if (i == 0)
462                                                         return -1;
463                                                 while (*q)
464                                                 {
465                                                         q++;
466                                                         (*pstr_len)--;
467                                                 }
468                                                 tm->tm_mon += 1;
469                                                 replace_type = PGTYPES_TYPE_NOTHING;
470                                                 break;
471                                         }
472                                 case 'G':
473                                         /* XXX: fall back to strftime */
474                                         tm->tm_mon -= 1;
475                                         i = strftime(q, *pstr_len, "%G", tm);
476                                         if (i == 0)
477                                                 return -1;
478                                         while (*q)
479                                         {
480                                                 q++;
481                                                 (*pstr_len)--;
482                                         }
483                                         tm->tm_mon += 1;
484                                         replace_type = PGTYPES_TYPE_NOTHING;
485                                         break;
486                                 case 'g':
487                                         /* XXX: fall back to strftime */
488                                         {
489                                                 char       *fmt = "%g"; /* Keep compiler quiet
490                                                                                                  * about 2-digit year */
491
492                                                 tm->tm_mon -= 1;
493                                                 i = strftime(q, *pstr_len, fmt, tm);
494                                                 if (i == 0)
495                                                         return -1;
496                                                 while (*q)
497                                                 {
498                                                         q++;
499                                                         (*pstr_len)--;
500                                                 }
501                                                 tm->tm_mon += 1;
502                                                 replace_type = PGTYPES_TYPE_NOTHING;
503                                         }
504                                         break;
505                                 case 'H':
506                                         replace_val.uint_val = tm->tm_hour;
507                                         replace_type = PGTYPES_TYPE_UINT_2_LZ;
508                                         break;
509                                 case 'I':
510                                         replace_val.uint_val = tm->tm_hour % 12;
511                                         replace_type = PGTYPES_TYPE_UINT_2_LZ;
512                                         break;
513                                 case 'j':
514                                         replace_val.uint_val = tm->tm_yday;
515                                         replace_type = PGTYPES_TYPE_UINT_3_LZ;
516                                         break;
517                                 case 'k':
518                                         replace_val.uint_val = tm->tm_hour;
519                                         replace_type = PGTYPES_TYPE_UINT_2_LS;
520                                         break;
521                                 case 'l':
522                                         replace_val.uint_val = tm->tm_hour % 12;
523                                         replace_type = PGTYPES_TYPE_UINT_2_LS;
524                                         break;
525                                 case 'm':
526                                         replace_val.uint_val = tm->tm_mon;
527                                         replace_type = PGTYPES_TYPE_UINT_2_LZ;
528                                         break;
529                                 case 'M':
530                                         replace_val.uint_val = tm->tm_min;
531                                         replace_type = PGTYPES_TYPE_UINT_2_LZ;
532                                         break;
533                                 case 'n':
534                                         replace_val.char_val = '\n';
535                                         replace_type = PGTYPES_TYPE_CHAR;
536                                         break;
537                                 case 'p':
538                                         if (tm->tm_hour < 12)
539                                                 replace_val.str_val = "AM";
540                                         else
541                                                 replace_val.str_val = "PM";
542                                         replace_type = PGTYPES_TYPE_STRING_CONSTANT;
543                                         break;
544                                 case 'P':
545                                         if (tm->tm_hour < 12)
546                                                 replace_val.str_val = "am";
547                                         else
548                                                 replace_val.str_val = "pm";
549                                         replace_type = PGTYPES_TYPE_STRING_CONSTANT;
550                                         break;
551                                 case 'r':
552                                         i = dttofmtasc_replace(ts, dDate, dow, tm,
553                                                                                    q, pstr_len,
554                                                                                    "%I:%M:%S %p");
555                                         if (i)
556                                                 return i;
557                                         break;
558                                 case 'R':
559                                         i = dttofmtasc_replace(ts, dDate, dow, tm,
560                                                                                    q, pstr_len,
561                                                                                    "%H:%M");
562                                         if (i)
563                                                 return i;
564                                         break;
565                                 case 's':
566 #ifdef HAVE_INT64_TIMESTAMP
567                                         replace_val.int64_val = ((*ts - SetEpochTimestamp()) / 1000000e0);
568                                         replace_type = PGTYPES_TYPE_INT64;
569 #else
570                                         replace_val.double_val = *ts - SetEpochTimestamp();
571                                         replace_type = PGTYPES_TYPE_DOUBLE_NF;
572 #endif
573                                         break;
574                                 case 'S':
575                                         replace_val.uint_val = tm->tm_sec;
576                                         replace_type = PGTYPES_TYPE_UINT;
577                                         break;
578                                 case 't':
579                                         replace_val.char_val = '\t';
580                                         replace_type = PGTYPES_TYPE_CHAR;
581                                         break;
582                                 case 'T':
583                                         i = dttofmtasc_replace(ts, dDate, dow, tm,
584                                                                                    q, pstr_len,
585                                                                                    "%H:%M:%S");
586                                         if (i)
587                                                 return i;
588                                         break;
589                                 case 'u':
590                                         if (dow == 0)
591                                                 dow = 7;
592                                         replace_val.uint_val = dow;
593                                         replace_type = PGTYPES_TYPE_UINT;
594                                         break;
595                                 case 'U':
596                                         /* XXX: fall back to strftime */
597                                         tm->tm_mon -= 1;
598                                         i = strftime(q, *pstr_len, "%U", tm);
599                                         if (i == 0)
600                                                 return -1;
601                                         while (*q)
602                                         {
603                                                 q++;
604                                                 (*pstr_len)--;
605                                         }
606                                         tm->tm_mon += 1;
607                                         replace_type = PGTYPES_TYPE_NOTHING;
608                                         break;
609                                 case 'V':
610                                         /* XXX: fall back to strftime */
611                                         i = strftime(q, *pstr_len, "%V", tm);
612                                         if (i == 0)
613                                                 return -1;
614                                         while (*q)
615                                         {
616                                                 q++;
617                                                 (*pstr_len)--;
618                                         }
619                                         replace_type = PGTYPES_TYPE_NOTHING;
620                                         break;
621                                 case 'w':
622                                         replace_val.uint_val = dow;
623                                         replace_type = PGTYPES_TYPE_UINT;
624                                         break;
625                                 case 'W':
626                                         /* XXX: fall back to strftime */
627                                         tm->tm_mon -= 1;
628                                         i = strftime(q, *pstr_len, "%U", tm);
629                                         if (i == 0)
630                                                 return -1;
631                                         while (*q)
632                                         {
633                                                 q++;
634                                                 (*pstr_len)--;
635                                         }
636                                         tm->tm_mon += 1;
637                                         replace_type = PGTYPES_TYPE_NOTHING;
638                                         break;
639                                 case 'x':
640                                         /* XXX: fall back to strftime */
641                                         {
642                                                 char       *fmt = "%x"; /* Keep compiler quiet
643                                                                                                  * about 2-digit year */
644
645                                                 tm->tm_mon -= 1;
646                                                 i = strftime(q, *pstr_len, fmt, tm);
647                                                 if (i == 0)
648                                                         return -1;
649                                                 while (*q)
650                                                 {
651                                                         q++;
652                                                         (*pstr_len)--;
653                                                 }
654                                                 tm->tm_mon += 1;
655                                                 replace_type = PGTYPES_TYPE_NOTHING;
656                                         }
657                                         break;
658                                 case 'X':
659                                         /* XXX: fall back to strftime */
660                                         tm->tm_mon -= 1;
661                                         i = strftime(q, *pstr_len, "%X", tm);
662                                         if (i == 0)
663                                                 return -1;
664                                         while (*q)
665                                         {
666                                                 q++;
667                                                 (*pstr_len)--;
668                                         }
669                                         tm->tm_mon += 1;
670                                         replace_type = PGTYPES_TYPE_NOTHING;
671                                         break;
672                                 case 'y':
673                                         replace_val.uint_val = tm->tm_year % 100;
674                                         replace_type = PGTYPES_TYPE_UINT_2_LZ;
675                                         break;
676                                 case 'Y':
677                                         replace_val.uint_val = tm->tm_year;
678                                         replace_type = PGTYPES_TYPE_UINT;
679                                         break;
680                                 case 'z':
681                                         /* XXX: fall back to strftime */
682                                         tm->tm_mon -= 1;
683                                         i = strftime(q, *pstr_len, "%z", tm);
684                                         if (i == 0)
685                                                 return -1;
686                                         while (*q)
687                                         {
688                                                 q++;
689                                                 (*pstr_len)--;
690                                         }
691                                         tm->tm_mon += 1;
692                                         replace_type = PGTYPES_TYPE_NOTHING;
693                                         break;
694                                 case 'Z':
695                                         /* XXX: fall back to strftime */
696                                         tm->tm_mon -= 1;
697                                         i = strftime(q, *pstr_len, "%Z", tm);
698                                         if (i == 0)
699                                                 return -1;
700                                         while (*q)
701                                         {
702                                                 q++;
703                                                 (*pstr_len)--;
704                                         }
705                                         tm->tm_mon += 1;
706                                         replace_type = PGTYPES_TYPE_NOTHING;
707                                         break;
708                                 case '%':
709                                         replace_val.char_val = '%';
710                                         replace_type = PGTYPES_TYPE_CHAR;
711                                         break;
712                                 case '\0':
713                                         /* fmtstr: blabla%' */
714
715                                         /*
716                                          * this is not compliant to the specification
717                                          */
718                                         return -1;
719                                 default:
720
721                                         /*
722                                          * if we don't know the pattern, we just copy it
723                                          */
724                                         if (*pstr_len > 1)
725                                         {
726                                                 *q = '%';
727                                                 q++;
728                                                 (*pstr_len)--;
729                                                 if (*pstr_len > 1)
730                                                 {
731                                                         *q = *p;
732                                                         q++;
733                                                         (*pstr_len)--;
734                                                 }
735                                                 else
736                                                 {
737                                                         *q = '\0';
738                                                         return -1;
739                                                 }
740                                                 *q = '\0';
741                                         }
742                                         else
743                                                 return -1;
744                                         break;
745                         }
746                         i = pgtypes_fmt_replace(replace_val, replace_type, &q, pstr_len);
747                         if (i)
748                                 return i;
749                 }
750                 else
751                 {
752                         if (*pstr_len > 1)
753                         {
754                                 *q = *p;
755                                 (*pstr_len)--;
756                                 q++;
757                                 *q = '\0';
758                         }
759                         else
760                                 return -1;
761                 }
762                 p++;
763         }
764         return 0;
765 }
766
767
768 int
769 PGTYPEStimestamp_fmt_asc(timestamp *ts, char *output, int str_len, char *fmtstr)
770 {
771         struct tm       tm;
772         fsec_t          fsec;
773         date            dDate;
774         int                     dow;
775
776         dDate = PGTYPESdate_from_timestamp(*ts);
777         dow = PGTYPESdate_dayofweek(dDate);
778         timestamp2tm(*ts, NULL, &tm, &fsec, NULL);
779
780         return dttofmtasc_replace(ts, dDate, dow, &tm, output, &str_len, fmtstr);
781 }
782
783 int
784 PGTYPEStimestamp_sub(timestamp *ts1, timestamp *ts2, interval *iv)
785 {
786         if (TIMESTAMP_NOT_FINITE(*ts1) || TIMESTAMP_NOT_FINITE(*ts2))
787                 return PGTYPES_TS_ERR_EINFTIME;
788         else
789 #ifdef HAVE_INT64_TIMESTAMP
790                 iv->time = (ts1 - ts2);
791 #else
792                 iv->time = JROUND(ts1 - ts2);
793 #endif
794
795         iv->month = 0;
796
797         return 0;
798 }
799
800 int
801 PGTYPEStimestamp_defmt_asc(char *str, char *fmt, timestamp *d)
802 {
803         int                     year,
804                                 month,
805                                 day;
806         int                     hour,
807                                 minute,
808                                 second;
809         int                     tz;
810
811         int                     i;
812         char       *mstr;
813         char       *mfmt;
814
815         if (!fmt)
816                 fmt = "%Y-%m-%d %H:%M:%S";
817         if (!fmt[0])
818                 return 1;
819
820         mstr = pgtypes_strdup(str);
821         mfmt = pgtypes_strdup(fmt);
822
823         /*
824          * initialize with impossible values so that we can see if the fields
825          * where specified at all
826          */
827         /* XXX ambiguity with 1 BC for year? */
828         year = -1;
829         month = -1;
830         day = -1;
831         hour = 0;
832         minute = -1;
833         second = -1;
834         tz = 0;
835
836         i = PGTYPEStimestamp_defmt_scan(&mstr, mfmt, d, &year, &month, &day, &hour, &minute, &second, &tz);
837         free(mstr);
838         free(mfmt);
839         return i;
840 }