]> granicus.if.org Git - postgresql/blob - src/interfaces/ecpg/pgtypeslib/interval.c
Remove more extraneous parentheses in date/time functions.
[postgresql] / src / interfaces / ecpg / pgtypeslib / interval.c
1 #include "postgres_fe.h"
2 #include <time.h>
3 #include <math.h>
4
5 #ifdef __FAST_MATH__
6 #error -ffast-math is known to break this code
7 #endif
8
9 #include "extern.h"
10 #include "dt.h"
11 #include "pgtypes_error.h"
12 #include "pgtypes_interval.h"
13
14 /* TrimTrailingZeros()
15  * ... resulting from printing numbers with full precision.
16  */
17 static void
18 TrimTrailingZeros(char *str)
19 {
20         int                     len = strlen(str);
21
22         /* chop off trailing zeros... but leave at least 2 fractional digits */
23         while (*(str + len - 1) == '0' && *(str + len - 3) != '.')
24         {
25                 len--;
26                 *(str + len) = '\0';
27         }
28 }
29
30 /* DecodeTime()
31  * Decode time string which includes delimiters.
32  * Only check the lower limit on hours, since this same code
33  *      can be used to represent time spans.
34  */
35 static int
36 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
37 {
38         char       *cp;
39
40         *tmask = DTK_TIME_M;
41
42         tm->tm_hour = strtol(str, &cp, 10);
43         if (*cp != ':')
44                 return -1;
45         str = cp + 1;
46         tm->tm_min = strtol(str, &cp, 10);
47         if (*cp == '\0')
48         {
49                 tm->tm_sec = 0;
50                 *fsec = 0;
51         }
52         else if (*cp != ':')
53                 return -1;
54         else
55         {
56                 str = cp + 1;
57                 tm->tm_sec = strtol(str, &cp, 10);
58                 if (*cp == '\0')
59                         *fsec = 0;
60                 else if (*cp == '.')
61                 {
62 #ifdef HAVE_INT64_TIMESTAMP
63                         char            fstr[MAXDATELEN + 1];
64
65                         /*
66                          * OK, we have at most six digits to work with. Let's
67                          * construct a string and then do the conversion to an
68                          * integer.
69                          */
70                         strncpy(fstr, (cp + 1), 7);
71                         strcpy(fstr + strlen(fstr), "000000");
72                         *(fstr + 6) = '\0';
73                         *fsec = strtol(fstr, &cp, 10);
74 #else
75                         str = cp;
76                         *fsec = strtod(str, &cp);
77 #endif
78                         if (*cp != '\0')
79                                 return -1;
80                 }
81                 else
82                         return -1;
83         }
84
85         /* do a sanity check */
86 #ifdef HAVE_INT64_TIMESTAMP
87         if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
88                 tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= USECS_PER_SEC)
89                 return -1;
90 #else
91         if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
92                 tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= 1)
93                 return -1;
94 #endif
95
96         return 0;
97 }       /* DecodeTime() */
98
99 /* DecodeInterval()
100  * Interpret previously parsed fields for general time interval.
101  * Return 0 if decoded and -1 if problems.
102  *
103  * Allow "date" field DTK_DATE since this could be just
104  *      an unsigned floating point number. - thomas 1997-11-16
105  *
106  * Allow ISO-style time span, with implicit units on number of days
107  *      preceding an hh:mm:ss field. - thomas 1998-04-30
108  */
109 int
110 DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec)
111 {
112         int                     is_before = FALSE;
113
114         char       *cp;
115         int                     fmask = 0,
116                                 tmask,
117                                 type;
118         int                     i;
119         int                     val;
120         double          fval;
121
122         *dtype = DTK_DELTA;
123
124         type = IGNORE_DTF;
125         tm->tm_year = 0;
126         tm->tm_mon = 0;
127         tm->tm_mday = 0;
128         tm->tm_hour = 0;
129         tm->tm_min = 0;
130         tm->tm_sec = 0;
131         *fsec = 0;
132
133         /* read through list backwards to pick up units before values */
134         for (i = nf - 1; i >= 0; i--)
135         {
136                 switch (ftype[i])
137                 {
138                         case DTK_TIME:
139                                 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
140                                         return -1;
141                                 type = DTK_DAY;
142                                 break;
143
144                         case DTK_TZ:
145
146                                 /*
147                                  * Timezone is a token with a leading sign character and
148                                  * otherwise the same as a non-signed time field
149                                  */
150
151                                 /*
152                                  * A single signed number ends up here, but will be
153                                  * rejected by DecodeTime(). So, work this out to drop
154                                  * through to DTK_NUMBER, which *can* tolerate this.
155                                  */
156                                 cp = field[i] + 1;
157                                 while (*cp != '\0' && *cp != ':' && *cp != '.')
158                                         cp++;
159                                 if (*cp == ':' && DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0)
160                                 {
161                                         if (*field[i] == '-')
162                                         {
163                                                 /* flip the sign on all fields */
164                                                 tm->tm_hour = -tm->tm_hour;
165                                                 tm->tm_min = -tm->tm_min;
166                                                 tm->tm_sec = -tm->tm_sec;
167                                                 *fsec = -(*fsec);
168                                         }
169
170                                         /*
171                                          * Set the next type to be a day, if units are not
172                                          * specified. This handles the case of '1 +02:03'
173                                          * since we are reading right to left.
174                                          */
175                                         type = DTK_DAY;
176                                         tmask = DTK_M(TZ);
177                                         break;
178                                 }
179                                 else if (type == IGNORE_DTF)
180                                 {
181                                         if (*cp == '.')
182                                         {
183                                                 /*
184                                                  * Got a decimal point? Then assume some sort of
185                                                  * seconds specification
186                                                  */
187                                                 type = DTK_SECOND;
188                                         }
189                                         else if (*cp == '\0')
190                                         {
191                                                 /*
192                                                  * Only a signed integer? Then must assume a
193                                                  * timezone-like usage
194                                                  */
195                                                 type = DTK_HOUR;
196                                         }
197                                 }
198                                 /* DROP THROUGH */
199
200                         case DTK_DATE:
201                         case DTK_NUMBER:
202                                 val = strtol(field[i], &cp, 10);
203
204                                 if (type == IGNORE_DTF)
205                                         type = DTK_SECOND;
206
207                                 if (*cp == '.')
208                                 {
209                                         fval = strtod(cp, &cp);
210                                         if (*cp != '\0')
211                                                 return -1;
212
213                                         if (val < 0)
214                                                 fval = -fval;
215                                 }
216                                 else if (*cp == '\0')
217                                         fval = 0;
218                                 else
219                                         return -1;
220
221                                 tmask = 0;              /* DTK_M(type); */
222
223                                 switch (type)
224                                 {
225                                         case DTK_MICROSEC:
226 #ifdef HAVE_INT64_TIMESTAMP
227                                                 *fsec += val + fval;
228 #else
229                                                 *fsec += (val + fval) * 1e-6;
230 #endif
231                                                 break;
232
233                                         case DTK_MILLISEC:
234 #ifdef HAVE_INT64_TIMESTAMP
235                                                 *fsec += (val + fval) * 1000;
236 #else
237                                                 *fsec += (val + fval) * 1e-3;
238 #endif
239                                                 break;
240
241                                         case DTK_SECOND:
242                                                 tm->tm_sec += val;
243 #ifdef HAVE_INT64_TIMESTAMP
244                                                 *fsec += fval * 1000000;
245 #else
246                                                 *fsec += fval;
247 #endif
248                                                 tmask = DTK_M(SECOND);
249                                                 break;
250
251                                         case DTK_MINUTE:
252                                                 tm->tm_min += val;
253                                                 if (fval != 0)
254                                                 {
255                                                         int                     sec;
256
257                                                         fval *= 60;
258                                                         sec = fval;
259                                                         tm->tm_sec += sec;
260 #ifdef HAVE_INT64_TIMESTAMP
261                                                         *fsec += ((fval - sec) * 1000000);
262 #else
263                                                         *fsec += (fval - sec);
264 #endif
265                                                 }
266                                                 tmask = DTK_M(MINUTE);
267                                                 break;
268
269                                         case DTK_HOUR:
270                                                 tm->tm_hour += val;
271                                                 if (fval != 0)
272                                                 {
273                                                         int                     sec;
274
275                                                         fval *= 3600;
276                                                         sec = fval;
277                                                         tm->tm_sec += sec;
278 #ifdef HAVE_INT64_TIMESTAMP
279                                                         *fsec += (fval - sec) * 1000000;
280 #else
281                                                         *fsec += (fval - sec);
282 #endif
283                                                 }
284                                                 tmask = DTK_M(HOUR);
285                                                 break;
286
287                                         case DTK_DAY:
288                                                 tm->tm_mday += val;
289                                                 if (fval != 0)
290                                                 {
291                                                         int                     sec;
292
293                                                         fval *= SECS_PER_DAY;
294                                                         sec = fval;
295                                                         tm->tm_sec += sec;
296 #ifdef HAVE_INT64_TIMESTAMP
297                                                         *fsec += (fval - sec) * 1000000;
298 #else
299                                                         *fsec += (fval - sec);
300 #endif
301                                                 }
302                                                 tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
303                                                 break;
304
305                                         case DTK_WEEK:
306                                                 tm->tm_mday += val * 7;
307                                                 if (fval != 0)
308                                                 {
309                                                         int                     sec;
310
311                                                         fval *= 7 * SECS_PER_DAY;
312                                                         sec = fval;
313                                                         tm->tm_sec += sec;
314 #ifdef HAVE_INT64_TIMESTAMP
315                                                         *fsec += (fval - sec) * 1000000;
316 #else
317                                                         *fsec += (fval - sec);
318 #endif
319                                                 }
320                                                 tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
321                                                 break;
322
323                                         case DTK_MONTH:
324                                                 tm->tm_mon += val;
325                                                 if (fval != 0)
326                                                 {
327                                                         int                     sec;
328
329                                                         fval *= 30 * SECS_PER_DAY;
330                                                         sec = fval;
331                                                         tm->tm_sec += sec;
332 #ifdef HAVE_INT64_TIMESTAMP
333                                                         *fsec += (fval - sec) * 1000000;
334 #else
335                                                         *fsec += (fval - sec);
336 #endif
337                                                 }
338                                                 tmask = DTK_M(MONTH);
339                                                 break;
340
341                                         case DTK_YEAR:
342                                                 tm->tm_year += val;
343                                                 if (fval != 0)
344                                                         tm->tm_mon += fval * 12;
345                                                 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
346                                                 break;
347
348                                         case DTK_DECADE:
349                                                 tm->tm_year += val * 10;
350                                                 if (fval != 0)
351                                                         tm->tm_mon += fval * 120;
352                                                 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
353                                                 break;
354
355                                         case DTK_CENTURY:
356                                                 tm->tm_year += val * 100;
357                                                 if (fval != 0)
358                                                         tm->tm_mon += fval * 1200;
359                                                 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
360                                                 break;
361
362                                         case DTK_MILLENNIUM:
363                                                 tm->tm_year += val * 1000;
364                                                 if (fval != 0)
365                                                         tm->tm_mon += fval * 12000;
366                                                 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
367                                                 break;
368
369                                         default:
370                                                 return -1;
371                                 }
372                                 break;
373
374                         case DTK_STRING:
375                         case DTK_SPECIAL:
376                                 type = DecodeUnits(i, field[i], &val);
377                                 if (type == IGNORE_DTF)
378                                         continue;
379
380                                 tmask = 0;              /* DTK_M(type); */
381                                 switch (type)
382                                 {
383                                         case UNITS:
384                                                 type = val;
385                                                 break;
386
387                                         case AGO:
388                                                 is_before = TRUE;
389                                                 type = val;
390                                                 break;
391
392                                         case RESERV:
393                                                 tmask = (DTK_DATE_M || DTK_TIME_M);
394                                                 *dtype = val;
395                                                 break;
396
397                                         default:
398                                                 return -1;
399                                 }
400                                 break;
401
402                         default:
403                                 return -1;
404                 }
405
406                 if (tmask & fmask)
407                         return -1;
408                 fmask |= tmask;
409         }
410
411         if (*fsec != 0)
412         {
413                 int                     sec;
414
415 #ifdef HAVE_INT64_TIMESTAMP
416                 sec = (*fsec / USECS_PER_SEC);
417                 *fsec -= (sec * USECS_PER_SEC);
418 #else
419                 TMODULO(*fsec, sec, 1e0);
420 #endif
421                 tm->tm_sec += sec;
422         }
423
424         if (is_before)
425         {
426                 *fsec = -(*fsec);
427                 tm->tm_sec = -(tm->tm_sec);
428                 tm->tm_min = -(tm->tm_min);
429                 tm->tm_hour = -(tm->tm_hour);
430                 tm->tm_mday = -(tm->tm_mday);
431                 tm->tm_mon = -(tm->tm_mon);
432                 tm->tm_year = -(tm->tm_year);
433         }
434
435         /* ensure that at least one time field has been found */
436         return (fmask != 0) ? 0 : -1;
437 }       /* DecodeInterval() */
438
439 /* EncodeInterval()
440  * Interpret time structure as a delta time and convert to string.
441  *
442  * Support "traditional Postgres" and ISO-8601 styles.
443  * Actually, afaik ISO does not address time interval formatting,
444  *      but this looks similar to the spec for absolute date/time.
445  * - thomas 1998-04-30
446  */
447 int
448 EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
449 {
450         int                     is_before = FALSE;
451         int                     is_nonzero = FALSE;
452         char       *cp = str;
453
454         /*
455          * The sign of year and month are guaranteed to match, since they are
456          * stored internally as "month". But we'll need to check for is_before
457          * and is_nonzero when determining the signs of hour/minute/seconds
458          * fields.
459          */
460         switch (style)
461         {
462                         /* compatible with ISO date formats */
463                 case USE_ISO_DATES:
464                         if (tm->tm_year != 0)
465                         {
466                                 sprintf(cp, "%d year%s",
467                                                 tm->tm_year, (tm->tm_year != 1) ? "s" : "");
468                                 cp += strlen(cp);
469                                 is_before = (tm->tm_year < 0);
470                                 is_nonzero = TRUE;
471                         }
472
473                         if (tm->tm_mon != 0)
474                         {
475                                 sprintf(cp, "%s%s%d mon%s", is_nonzero ? " " : "",
476                                                 (is_before && tm->tm_mon > 0) ? "+" : "",
477                                                 tm->tm_mon, (tm->tm_mon != 1) ? "s" : "");
478                                 cp += strlen(cp);
479                                 is_before = (tm->tm_mon < 0);
480                                 is_nonzero = TRUE;
481                         }
482
483                         if (tm->tm_mday != 0)
484                         {
485                                 sprintf(cp, "%s%s%d day%s", is_nonzero ? " " : "",
486                                                 (is_before && tm->tm_mday > 0) ? "+" : "",
487                                                 tm->tm_mday, (tm->tm_mday != 1) ? "s" : "");
488                                 cp += strlen(cp);
489                                 is_before = (tm->tm_mday < 0);
490                                 is_nonzero = TRUE;
491                         }
492                         if (!is_nonzero || tm->tm_hour != 0 || tm->tm_min != 0 ||
493                                 tm->tm_sec != 0 || fsec != 0)
494                         {
495                                 int                     minus = tm->tm_hour < 0 || tm->tm_min < 0 ||
496                                                                         tm->tm_sec < 0 || fsec < 0;
497
498                                 sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""),
499                                                 (minus ? "-" : (is_before ? "+" : "")),
500                                                 abs(tm->tm_hour), abs(tm->tm_min));
501                                 cp += strlen(cp);
502                                 /* Mark as "non-zero" since the fields are now filled in */
503                                 is_nonzero = TRUE;
504
505                                 /* fractional seconds? */
506                                 if (fsec != 0)
507                                 {
508 #ifdef HAVE_INT64_TIMESTAMP
509                                         sprintf(cp, ":%02d", abs(tm->tm_sec));
510                                         cp += strlen(cp);
511                                         sprintf(cp, ".%06d", (fsec >= 0) ? fsec : -(fsec));
512 #else
513                                         fsec += tm->tm_sec;
514                                         sprintf(cp, ":%013.10f", fabs(fsec));
515 #endif
516                                         TrimTrailingZeros(cp);
517                                         cp += strlen(cp);
518                                         is_nonzero = TRUE;
519                                 }
520                                 /* otherwise, integer seconds only? */
521                                 else if (tm->tm_sec != 0)
522                                 {
523                                         sprintf(cp, ":%02d", abs(tm->tm_sec));
524                                         cp += strlen(cp);
525                                         is_nonzero = TRUE;
526                                 }
527                         }
528                         break;
529
530                 case USE_POSTGRES_DATES:
531                 default:
532                         strcpy(cp, "@ ");
533                         cp += strlen(cp);
534
535                         if (tm->tm_year != 0)
536                         {
537                                 int                     year = tm->tm_year;
538
539                                 if (tm->tm_year < 0)
540                                         year = -year;
541
542                                 sprintf(cp, "%d year%s", year,
543                                                 (year != 1) ? "s" : "");
544                                 cp += strlen(cp);
545                                 is_before = (tm->tm_year < 0);
546                                 is_nonzero = TRUE;
547                         }
548
549                         if (tm->tm_mon != 0)
550                         {
551                                 int                     mon = tm->tm_mon;
552
553                                 if (is_before || (!is_nonzero && tm->tm_mon < 0))
554                                         mon = -mon;
555
556                                 sprintf(cp, "%s%d mon%s", is_nonzero ? " " : "", mon,
557                                                 (mon != 1) ? "s" : "");
558                                 cp += strlen(cp);
559                                 if (!is_nonzero)
560                                         is_before = (tm->tm_mon < 0);
561                                 is_nonzero = TRUE;
562                         }
563
564                         if (tm->tm_mday != 0)
565                         {
566                                 int                     day = tm->tm_mday;
567
568                                 if (is_before || (!is_nonzero && tm->tm_mday < 0))
569                                         day = -day;
570
571                                 sprintf(cp, "%s%d day%s", is_nonzero ? " " : "", day,
572                                                 (day != 1) ? "s" : "");
573                                 cp += strlen(cp);
574                                 if (!is_nonzero)
575                                         is_before = (tm->tm_mday < 0);
576                                 is_nonzero = TRUE;
577                         }
578                         if (tm->tm_hour != 0)
579                         {
580                                 int                     hour = tm->tm_hour;
581
582                                 if (is_before || (!is_nonzero && tm->tm_hour < 0))
583                                         hour = -hour;
584
585                                 sprintf(cp, "%s%d hour%s", is_nonzero ? " " : "", hour,
586                                                 (hour != 1) ? "s" : "");
587                                 cp += strlen(cp);
588                                 if (!is_nonzero)
589                                         is_before = (tm->tm_hour < 0);
590                                 is_nonzero = TRUE;
591                         }
592
593                         if (tm->tm_min != 0)
594                         {
595                                 int                     min = tm->tm_min;
596
597                                 if (is_before || (!is_nonzero && tm->tm_min < 0))
598                                         min = -min;
599
600                                 sprintf(cp, "%s%d min%s", is_nonzero ? " " : "", min,
601                                                 (min != 1) ? "s" : "");
602                                 cp += strlen(cp);
603                                 if (!is_nonzero)
604                                         is_before = (tm->tm_min < 0);
605                                 is_nonzero = TRUE;
606                         }
607
608                         /* fractional seconds? */
609                         if (fsec != 0)
610                         {
611 #ifdef HAVE_INT64_TIMESTAMP
612                                 if (is_before || (!is_nonzero && tm->tm_sec < 0))
613                                         tm->tm_sec = -tm->tm_sec;
614                                 sprintf(cp, "%s%d.%02d secs", is_nonzero ? " " : "",
615                                                 tm->tm_sec, ((int) fsec) / 10000);
616                                 cp += strlen(cp);
617                                 if (!is_nonzero)
618                                         is_before = (fsec < 0);
619 #else
620                                 fsec_t          sec;
621
622                                 fsec += tm->tm_sec;
623                                 sec = fsec;
624                                 if (is_before || (!is_nonzero && fsec < 0))
625                                         sec = -sec;
626
627                                 sprintf(cp, "%s%.2f secs", is_nonzero ? " " : "", sec);
628                                 cp += strlen(cp);
629                                 if (!is_nonzero)
630                                         is_before = (fsec < 0);
631 #endif
632                                 is_nonzero = TRUE;
633
634                                 /* otherwise, integer seconds only? */
635                         }
636                         else if (tm->tm_sec != 0)
637                         {
638                                 int                     sec = tm->tm_sec;
639
640                                 if (is_before || (!is_nonzero && tm->tm_sec < 0))
641                                         sec = -sec;
642
643                                 sprintf(cp, "%s%d sec%s", is_nonzero ? " " : "", sec,
644                                                 (sec != 1) ? "s" : "");
645                                 cp += strlen(cp);
646                                 if (!is_nonzero)
647                                         is_before = (tm->tm_sec < 0);
648                                 is_nonzero = TRUE;
649                         }
650                         break;
651         }
652
653         /* identically zero? then put in a unitless zero... */
654         if (!is_nonzero)
655         {
656                 strcat(cp, "0");
657                 cp += strlen(cp);
658         }
659
660         if (is_before && (style != USE_ISO_DATES))
661         {
662                 strcat(cp, " ago");
663                 cp += strlen(cp);
664         }
665
666         return 0;
667 }       /* EncodeInterval() */
668
669 /* interval2tm()
670  * Convert a interval data type to a tm structure.
671  */
672 static int
673 interval2tm(interval span, struct tm * tm, fsec_t *fsec)
674 {
675 #ifdef HAVE_INT64_TIMESTAMP
676         int64           time;
677
678 #else
679         double          time;
680 #endif
681
682         if (span.month != 0)
683         {
684                 tm->tm_year = span.month / 12;
685                 tm->tm_mon = span.month % 12;
686
687         }
688         else
689         {
690                 tm->tm_year = 0;
691                 tm->tm_mon = 0;
692         }
693
694         time = span.time;
695
696 #ifdef HAVE_INT64_TIMESTAMP
697         tm->tm_mday = (time / USECS_PER_DAY);
698         time -= (tm->tm_mday * USECS_PER_DAY);
699         tm->tm_hour = (time / USECS_PER_HOUR);
700         time -= (tm->tm_hour * USECS_PER_HOUR);
701         tm->tm_min = (time / USECS_PER_MINUTE);
702         time -= (tm->tm_min * USECS_PER_MINUTE);
703         tm->tm_sec = (time / USECS_PER_SEC);
704         *fsec = (time - (tm->tm_sec * USECS_PER_SEC));
705 #else
706         TMODULO(time, tm->tm_mday, (double)SECS_PER_DAY);
707         TMODULO(time, tm->tm_hour, 3600e0);
708         TMODULO(time, tm->tm_min, 60e0);
709         TMODULO(time, tm->tm_sec, 1e0);
710         *fsec = time;
711 #endif
712
713         return 0;
714 }       /* interval2tm() */
715
716 static int
717 tm2interval(struct tm * tm, fsec_t fsec, interval *span)
718 {
719         span->month = tm->tm_year * 12 + tm->tm_mon;
720 #ifdef HAVE_INT64_TIMESTAMP
721         span->time = (((((((tm->tm_mday * INT64CONST(24)) +
722                                                 tm->tm_hour) * INT64CONST(60)) +
723                                                 tm->tm_min) * INT64CONST(60)) +
724                                                 tm->tm_sec) * USECS_PER_SEC) + fsec;
725 #else
726         span->time = (((((tm->tm_mday * 24.0) +
727                                                 tm->tm_hour) * 60.0) +
728                                                 tm->tm_min) * 60.0) +
729                                                 tm->tm_sec;
730         span->time = JROUND(span->time + fsec);
731 #endif
732
733         return 0;
734 }       /* tm2interval() */
735
736 interval *
737 PGTYPESinterval_from_asc(char *str, char **endptr)
738 {
739         interval   *result = NULL;
740         fsec_t          fsec;
741         struct tm       tt,
742                            *tm = &tt;
743         int                     dtype;
744         int                     nf;
745         char       *field[MAXDATEFIELDS];
746         int                     ftype[MAXDATEFIELDS];
747         char            lowstr[MAXDATELEN + MAXDATEFIELDS];
748         char       *realptr;
749         char      **ptr = (endptr != NULL) ? endptr : &realptr;
750
751         tm->tm_year = 0;
752         tm->tm_mon = 0;
753         tm->tm_mday = 0;
754         tm->tm_hour = 0;
755         tm->tm_min = 0;
756         tm->tm_sec = 0;
757         fsec = 0;
758
759         if (strlen(str) >= sizeof(lowstr))
760         {
761                 errno = PGTYPES_INTVL_BAD_INTERVAL;
762                 return NULL;
763         }
764
765         if (ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0 ||
766                 DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0)
767         {
768                 errno = PGTYPES_INTVL_BAD_INTERVAL;
769                 return NULL;
770         }
771
772         result = (interval *) pgtypes_alloc(sizeof(interval));
773         if (!result)
774                 return NULL;
775
776         if (dtype != DTK_DELTA)
777         {
778                 errno = PGTYPES_INTVL_BAD_INTERVAL;
779                 free(result);
780                 return NULL;
781         }
782
783         if (tm2interval(tm, fsec, result) != 0)
784         {
785                 errno = PGTYPES_INTVL_BAD_INTERVAL;
786                 free(result);
787                 return NULL;
788         }
789
790         return result;
791 }
792
793 char *
794 PGTYPESinterval_to_asc(interval *span)
795 {
796         struct tm       tt,
797                            *tm = &tt;
798         fsec_t          fsec;
799         char            buf[MAXDATELEN + 1];
800         int                     DateStyle = 0;
801
802         if (interval2tm(*span, tm, &fsec) != 0)
803         {
804                 errno = PGTYPES_INTVL_BAD_INTERVAL;
805                 return NULL;
806         }
807
808         if (EncodeInterval(tm, fsec, DateStyle, buf) != 0)
809         {
810                 errno = PGTYPES_INTVL_BAD_INTERVAL;
811                 return NULL;
812         }
813
814         return pgtypes_strdup(buf);
815 }
816
817 int
818 PGTYPESinterval_copy(interval *intvlsrc, interval *intrcldest)
819 {
820         intrcldest->time = intvlsrc->time;
821         intrcldest->month = intvlsrc->month;
822
823         return 0;
824 }