PostgreSQL Source Code git master
Loading...
Searching...
No Matches
interval.c
Go to the documentation of this file.
1/* src/interfaces/ecpg/pgtypeslib/interval.c */
2
3#include "postgres_fe.h"
4
5#include <time.h>
6#include <math.h>
7#include <limits.h>
8
9#include "common/string.h"
10#include "dt.h"
11#include "pgtypes_error.h"
12#include "pgtypes_interval.h"
13#include "pgtypeslib_extern.h"
14
15/* copy&pasted from .../src/backend/utils/adt/datetime.c
16 * and changed struct pg_tm to struct tm
17 */
18static void
19AdjustFractSeconds(double frac, struct /* pg_ */ tm *tm, fsec_t *fsec, int scale)
20{
21 int sec;
22
23 if (frac == 0)
24 return;
25 frac *= scale;
26 sec = (int) frac;
27 tm->tm_sec += sec;
28 frac -= sec;
29 *fsec += rint(frac * 1000000);
30}
31
32
33/* copy&pasted from .../src/backend/utils/adt/datetime.c
34 * and changed struct pg_tm to struct tm
35 */
36static void
37AdjustFractDays(double frac, struct /* pg_ */ tm *tm, fsec_t *fsec, int scale)
38{
39 int extra_days;
40
41 if (frac == 0)
42 return;
43 frac *= scale;
48}
49
50/* copy&pasted from .../src/backend/utils/adt/datetime.c */
51static int
52ParseISO8601Number(const char *str, char **endptr, int *ipart, double *fpart)
53{
54 double val;
55
56 if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
57 return DTERR_BAD_FORMAT;
58 errno = 0;
59 val = strtod(str, endptr);
60 /* did we not see anything that looks like a double? */
61 if (*endptr == str || errno != 0)
62 return DTERR_BAD_FORMAT;
63 /* watch out for overflow */
66 /* be very sure we truncate towards zero (cf dtrunc()) */
67 if (val >= 0)
68 *ipart = (int) floor(val);
69 else
70 *ipart = (int) -floor(-val);
71 *fpart = val - *ipart;
72 return 0;
73}
74
75/* copy&pasted from .../src/backend/utils/adt/datetime.c */
76static int
78{
79 /* We might have had a leading '-' */
80 if (*fieldstart == '-')
81 fieldstart++;
82 return strspn(fieldstart, "0123456789");
83}
84
85
86/* copy&pasted from .../src/backend/utils/adt/datetime.c
87 * and changed struct pg_tm to struct tm
88 */
89static inline void
90ClearPgTm(struct /* pg_ */ tm *tm, fsec_t *fsec)
91{
92 tm->tm_year = 0;
93 tm->tm_mon = 0;
94 tm->tm_mday = 0;
95 tm->tm_hour = 0;
96 tm->tm_min = 0;
97 tm->tm_sec = 0;
98 *fsec = 0;
99}
100
101/* copy&pasted from .../src/backend/utils/adt/datetime.c
102 *
103 * * changed struct pg_tm to struct tm
104 *
105 * * Made the function static
106 */
107static int
109 int *dtype, struct /* pg_ */ tm *tm, fsec_t *fsec)
110{
111 bool datepart = true;
112 bool havefield = false;
113
114 *dtype = DTK_DELTA;
115 ClearPgTm(tm, fsec);
116
117 if (strlen(str) < 2 || str[0] != 'P')
118 return DTERR_BAD_FORMAT;
119
120 str++;
121 while (*str)
122 {
123 char *fieldstart;
124 int val;
125 double fval;
126 char unit;
127 int dterr;
128
129 if (*str == 'T') /* T indicates the beginning of the time part */
130 {
131 datepart = false;
132 havefield = false;
133 str++;
134 continue;
135 }
136
137 fieldstart = str;
138 dterr = ParseISO8601Number(str, &str, &val, &fval);
139 if (dterr)
140 return dterr;
141
142 /*
143 * Note: we could step off the end of the string here. Code below
144 * *must* exit the loop if unit == '\0'.
145 */
146 unit = *str++;
147
148 if (datepart)
149 {
150 switch (unit) /* before T: Y M W D */
151 {
152 case 'Y':
153 tm->tm_year += val;
154 tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
155 break;
156 case 'M':
157 tm->tm_mon += val;
158 AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
159 break;
160 case 'W':
161 tm->tm_mday += val * 7;
162 AdjustFractDays(fval, tm, fsec, 7);
163 break;
164 case 'D':
165 tm->tm_mday += val;
166 AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
167 break;
168 case 'T': /* ISO 8601 4.4.3.3 Alternative Format / Basic */
169 case '\0':
171 {
172 tm->tm_year += val / 10000;
173 tm->tm_mon += (val / 100) % 100;
174 tm->tm_mday += val % 100;
175 AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
176 if (unit == '\0')
177 return 0;
178 datepart = false;
179 havefield = false;
180 continue;
181 }
182 /* Else fall through to extended alternative format */
184 case '-': /* ISO 8601 4.4.3.3 Alternative Format,
185 * Extended */
186 if (havefield)
187 return DTERR_BAD_FORMAT;
188
189 tm->tm_year += val;
190 tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
191 if (unit == '\0')
192 return 0;
193 if (unit == 'T')
194 {
195 datepart = false;
196 havefield = false;
197 continue;
198 }
199
200 dterr = ParseISO8601Number(str, &str, &val, &fval);
201 if (dterr)
202 return dterr;
203 tm->tm_mon += val;
204 AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
205 if (*str == '\0')
206 return 0;
207 if (*str == 'T')
208 {
209 datepart = false;
210 havefield = false;
211 continue;
212 }
213 if (*str != '-')
214 return DTERR_BAD_FORMAT;
215 str++;
216
217 dterr = ParseISO8601Number(str, &str, &val, &fval);
218 if (dterr)
219 return dterr;
220 tm->tm_mday += val;
221 AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
222 if (*str == '\0')
223 return 0;
224 if (*str == 'T')
225 {
226 datepart = false;
227 havefield = false;
228 continue;
229 }
230 return DTERR_BAD_FORMAT;
231 default:
232 /* not a valid date unit suffix */
233 return DTERR_BAD_FORMAT;
234 }
235 }
236 else
237 {
238 switch (unit) /* after T: H M S */
239 {
240 case 'H':
241 tm->tm_hour += val;
242 AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
243 break;
244 case 'M':
245 tm->tm_min += val;
247 break;
248 case 'S':
249 tm->tm_sec += val;
250 AdjustFractSeconds(fval, tm, fsec, 1);
251 break;
252 case '\0': /* ISO 8601 4.4.3.3 Alternative Format */
254 {
255 tm->tm_hour += val / 10000;
256 tm->tm_min += (val / 100) % 100;
257 tm->tm_sec += val % 100;
258 AdjustFractSeconds(fval, tm, fsec, 1);
259 return 0;
260 }
261 /* Else fall through to extended alternative format */
263 case ':': /* ISO 8601 4.4.3.3 Alternative Format,
264 * Extended */
265 if (havefield)
266 return DTERR_BAD_FORMAT;
267
268 tm->tm_hour += val;
269 AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
270 if (unit == '\0')
271 return 0;
272
273 dterr = ParseISO8601Number(str, &str, &val, &fval);
274 if (dterr)
275 return dterr;
276 tm->tm_min += val;
278 if (*str == '\0')
279 return 0;
280 if (*str != ':')
281 return DTERR_BAD_FORMAT;
282 str++;
283
284 dterr = ParseISO8601Number(str, &str, &val, &fval);
285 if (dterr)
286 return dterr;
287 tm->tm_sec += val;
288 AdjustFractSeconds(fval, tm, fsec, 1);
289 if (*str == '\0')
290 return 0;
291 return DTERR_BAD_FORMAT;
292
293 default:
294 /* not a valid time unit suffix */
295 return DTERR_BAD_FORMAT;
296 }
297 }
298
299 havefield = true;
300 }
301
302 return 0;
303}
304
305
306
307/* copy&pasted from .../src/backend/utils/adt/datetime.c
308 * with 3 exceptions
309 *
310 * * changed struct pg_tm to struct tm
311 *
312 * * ECPG code called this without a 'range' parameter
313 * removed 'int range' from the argument list and
314 * places where DecodeTime is called; and added
315 * int range = INTERVAL_FULL_RANGE;
316 *
317 * * ECPG seems not to have a global IntervalStyle
318 * so added
319 * int IntervalStyle = INTSTYLE_POSTGRES;
320 */
321int
322DecodeInterval(char **field, int *ftype, int nf, /* int range, */
323 int *dtype, struct /* pg_ */ tm *tm, fsec_t *fsec)
324{
327 bool is_before = false;
328 char *cp;
329 int fmask = 0,
330 tmask,
331 type;
332 int i;
333 int dterr;
334 int val;
335 double fval;
336
337 *dtype = DTK_DELTA;
339 ClearPgTm(tm, fsec);
340
341 /* read through list backwards to pick up units before values */
342 for (i = nf - 1; i >= 0; i--)
343 {
344 switch (ftype[i])
345 {
346 case DTK_TIME:
347 dterr = DecodeTime(field[i], /* range, */
348 &tmask, tm, fsec);
349 if (dterr)
350 return dterr;
351 type = DTK_DAY;
352 break;
353
354 case DTK_TZ:
355
356 /*
357 * Timezone is a token with a leading sign character and at
358 * least one digit; there could be ':', '.', '-' embedded in
359 * it as well.
360 */
361 Assert(*field[i] == '-' || *field[i] == '+');
362
363 /*
364 * Try for hh:mm or hh:mm:ss. If not, fall through to
365 * DTK_NUMBER case, which can handle signed float numbers and
366 * signed year-month values.
367 */
368 if (strchr(field[i] + 1, ':') != NULL &&
369 DecodeTime(field[i] + 1, /* INTERVAL_FULL_RANGE, */
370 &tmask, tm, fsec) == 0)
371 {
372 if (*field[i] == '-')
373 {
374 /* flip the sign on all fields */
375 tm->tm_hour = -tm->tm_hour;
376 tm->tm_min = -tm->tm_min;
377 tm->tm_sec = -tm->tm_sec;
378 *fsec = -(*fsec);
379 }
380
381 /*
382 * Set the next type to be a day, if units are not
383 * specified. This handles the case of '1 +02:03' since we
384 * are reading right to left.
385 */
386 type = DTK_DAY;
387 tmask = DTK_M(TZ);
388 break;
389 }
391
392 case DTK_DATE:
393 case DTK_NUMBER:
394 if (type == IGNORE_DTF)
395 {
396 /* use typmod to decide what rightmost field is */
397 switch (range)
398 {
399 case INTERVAL_MASK(YEAR):
400 type = DTK_YEAR;
401 break;
402 case INTERVAL_MASK(MONTH):
404 type = DTK_MONTH;
405 break;
406 case INTERVAL_MASK(DAY):
407 type = DTK_DAY;
408 break;
409 case INTERVAL_MASK(HOUR):
413 type = DTK_HOUR;
414 break;
415 case INTERVAL_MASK(MINUTE):
418 break;
419 case INTERVAL_MASK(SECOND):
423 break;
424 default:
426 break;
427 }
428 }
429
430 errno = 0;
431 val = strtoint(field[i], &cp, 10);
432 if (errno == ERANGE)
434
435 if (*cp == '-')
436 {
437 /* SQL "years-months" syntax */
438 int val2;
439
440 val2 = strtoint(cp + 1, &cp, 10);
443 if (*cp != '\0')
444 return DTERR_BAD_FORMAT;
445 type = DTK_MONTH;
446 if (*field[i] == '-')
447 val2 = -val2;
449 fval = 0;
450 }
451 else if (*cp == '.')
452 {
453 errno = 0;
454 fval = strtod(cp, &cp);
455 if (*cp != '\0' || errno != 0)
456 return DTERR_BAD_FORMAT;
457
458 if (*field[i] == '-')
459 fval = -fval;
460 }
461 else if (*cp == '\0')
462 fval = 0;
463 else
464 return DTERR_BAD_FORMAT;
465
466 tmask = 0; /* DTK_M(type); */
467
468 switch (type)
469 {
470 case DTK_MICROSEC:
471 *fsec += rint(val + fval);
473 break;
474
475 case DTK_MILLISEC:
476 *fsec += rint((val + fval) * 1000);
478 break;
479
480 case DTK_SECOND:
481 tm->tm_sec += val;
482 *fsec += rint(fval * 1000000);
483
484 /*
485 * If any subseconds were specified, consider this
486 * microsecond and millisecond input as well.
487 */
488 if (fval == 0)
489 tmask = DTK_M(SECOND);
490 else
492 break;
493
494 case DTK_MINUTE:
495 tm->tm_min += val;
497 tmask = DTK_M(MINUTE);
498 break;
499
500 case DTK_HOUR:
501 tm->tm_hour += val;
502 AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
503 tmask = DTK_M(HOUR);
504 type = DTK_DAY;
505 break;
506
507 case DTK_DAY:
508 tm->tm_mday += val;
509 AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
510 tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
511 break;
512
513 case DTK_WEEK:
514 tm->tm_mday += val * 7;
515 AdjustFractDays(fval, tm, fsec, 7);
516 tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
517 break;
518
519 case DTK_MONTH:
520 tm->tm_mon += val;
521 AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
522 tmask = DTK_M(MONTH);
523 break;
524
525 case DTK_YEAR:
526 tm->tm_year += val;
527 tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
528 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
529 break;
530
531 case DTK_DECADE:
532 tm->tm_year += val * 10;
533 tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 10);
534 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
535 break;
536
537 case DTK_CENTURY:
538 tm->tm_year += val * 100;
539 tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 100);
540 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
541 break;
542
543 case DTK_MILLENNIUM:
544 tm->tm_year += val * 1000;
545 tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 1000);
546 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
547 break;
548
549 default:
550 return DTERR_BAD_FORMAT;
551 }
552 break;
553
554 case DTK_STRING:
555 case DTK_SPECIAL:
556 type = DecodeUnits(i, field[i], &val);
557 if (type == IGNORE_DTF)
558 continue;
559
560 tmask = 0; /* DTK_M(type); */
561 switch (type)
562 {
563 case UNITS:
564 type = val;
565 break;
566
567 case AGO:
568 is_before = true;
569 type = val;
570 break;
571
572 case RESERV:
574 *dtype = val;
575 break;
576
577 default:
578 return DTERR_BAD_FORMAT;
579 }
580 break;
581
582 default:
583 return DTERR_BAD_FORMAT;
584 }
585
586 if (tmask & fmask)
587 return DTERR_BAD_FORMAT;
588 fmask |= tmask;
589 }
590
591 /* ensure that at least one time field has been found */
592 if (fmask == 0)
593 return DTERR_BAD_FORMAT;
594
595 /* ensure fractional seconds are fractional */
596 if (*fsec != 0)
597 {
598 int sec;
599
600 sec = *fsec / USECS_PER_SEC;
601 *fsec -= sec * USECS_PER_SEC;
602 tm->tm_sec += sec;
603 }
604
605 /*----------
606 * The SQL standard defines the interval literal
607 * '-1 1:00:00'
608 * to mean "negative 1 days and negative 1 hours", while Postgres
609 * traditionally treats this as meaning "negative 1 days and positive
610 * 1 hours". In SQL_STANDARD intervalstyle, we apply the leading sign
611 * to all fields if there are no other explicit signs.
612 *
613 * We leave the signs alone if there are additional explicit signs.
614 * This protects us against misinterpreting postgres-style dump output,
615 * since the postgres-style output code has always put an explicit sign on
616 * all fields following a negative field. But note that SQL-spec output
617 * is ambiguous and can be misinterpreted on load! (So it's best practice
618 * to dump in postgres style, not SQL style.)
619 *----------
620 */
621 if (IntervalStyle == INTSTYLE_SQL_STANDARD && *field[0] == '-')
622 {
623 /* Check for additional explicit signs */
624 bool more_signs = false;
625
626 for (i = 1; i < nf; i++)
627 {
628 if (*field[i] == '-' || *field[i] == '+')
629 {
630 more_signs = true;
631 break;
632 }
633 }
634
635 if (!more_signs)
636 {
637 /*
638 * Rather than re-determining which field was field[0], just force
639 * 'em all negative.
640 */
641 if (*fsec > 0)
642 *fsec = -(*fsec);
643 if (tm->tm_sec > 0)
644 tm->tm_sec = -tm->tm_sec;
645 if (tm->tm_min > 0)
646 tm->tm_min = -tm->tm_min;
647 if (tm->tm_hour > 0)
648 tm->tm_hour = -tm->tm_hour;
649 if (tm->tm_mday > 0)
650 tm->tm_mday = -tm->tm_mday;
651 if (tm->tm_mon > 0)
652 tm->tm_mon = -tm->tm_mon;
653 if (tm->tm_year > 0)
654 tm->tm_year = -tm->tm_year;
655 }
656 }
657
658 /* finally, AGO negates everything */
659 if (is_before)
660 {
661 *fsec = -(*fsec);
662 tm->tm_sec = -tm->tm_sec;
663 tm->tm_min = -tm->tm_min;
664 tm->tm_hour = -tm->tm_hour;
665 tm->tm_mday = -tm->tm_mday;
666 tm->tm_mon = -tm->tm_mon;
667 tm->tm_year = -tm->tm_year;
668 }
669
670 return 0;
671}
672
673
674/* copy&pasted from .../src/backend/utils/adt/datetime.c */
675static char *
676AddVerboseIntPart(char *cp, int value, const char *units,
677 bool *is_zero, bool *is_before)
678{
679 if (value == 0)
680 return cp;
681 /* first nonzero value sets is_before */
682 if (*is_zero)
683 {
684 *is_before = (value < 0);
685 value = abs(value);
686 }
687 else if (*is_before)
688 value = -value;
689 sprintf(cp, " %d %s%s", value, units, (value == 1) ? "" : "s");
690 *is_zero = false;
691 return cp + strlen(cp);
692}
693
694/* copy&pasted from .../src/backend/utils/adt/datetime.c */
695static char *
696AddPostgresIntPart(char *cp, int value, const char *units,
697 bool *is_zero, bool *is_before)
698{
699 if (value == 0)
700 return cp;
701 sprintf(cp, "%s%s%d %s%s",
702 (!*is_zero) ? " " : "",
703 (*is_before && value > 0) ? "+" : "",
704 value,
705 units,
706 (value != 1) ? "s" : "");
707
708 /*
709 * Each nonzero field sets is_before for (only) the next one. This is a
710 * tad bizarre but it's how it worked before...
711 */
712 *is_before = (value < 0);
713 *is_zero = false;
714 return cp + strlen(cp);
715}
716
717/* copy&pasted from .../src/backend/utils/adt/datetime.c */
718static char *
720{
721 if (value == 0)
722 return cp;
723 sprintf(cp, "%d%c", value, units);
724 return cp + strlen(cp);
725}
726
727/* copy&pasted from .../src/backend/utils/adt/datetime.c */
728static void
729AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
730{
731 if (fsec == 0)
732 {
733 if (fillzeros)
734 sprintf(cp, "%02d", abs(sec));
735 else
736 sprintf(cp, "%d", abs(sec));
737 }
738 else
739 {
740 if (fillzeros)
741 sprintf(cp, "%02d.%0*d", abs(sec), precision, abs(fsec));
742 else
743 sprintf(cp, "%d.%0*d", abs(sec), precision, abs(fsec));
745 }
746}
747
748
749/* copy&pasted from .../src/backend/utils/adt/datetime.c
750 *
751 * Change pg_tm to tm
752 */
753
754void
755EncodeInterval(struct /* pg_ */ tm *tm, fsec_t fsec, int style, char *str)
756{
757 char *cp = str;
758 int year = tm->tm_year;
759 int mon = tm->tm_mon;
760 int mday = tm->tm_mday;
761 int hour = tm->tm_hour;
762 int min = tm->tm_min;
763 int sec = tm->tm_sec;
764 bool is_before = false;
765 bool is_zero = true;
766
767 /*
768 * The sign of year and month are guaranteed to match, since they are
769 * stored internally as "month". But we'll need to check for is_before and
770 * is_zero when determining the signs of day and hour/minute/seconds
771 * fields.
772 */
773 switch (style)
774 {
775 /* SQL Standard interval format */
777 {
778 bool has_negative = year < 0 || mon < 0 ||
779 mday < 0 || hour < 0 ||
780 min < 0 || sec < 0 || fsec < 0;
781 bool has_positive = year > 0 || mon > 0 ||
782 mday > 0 || hour > 0 ||
783 min > 0 || sec > 0 || fsec > 0;
784 bool has_year_month = year != 0 || mon != 0;
785 bool has_day_time = mday != 0 || hour != 0 ||
786 min != 0 || sec != 0 || fsec != 0;
787 bool has_day = mday != 0;
790
791 /*
792 * SQL Standard wants only 1 "<sign>" preceding the whole
793 * interval ... but can't do that if mixed signs.
794 */
796 {
797 *cp++ = '-';
798 year = -year;
799 mon = -mon;
800 mday = -mday;
801 hour = -hour;
802 min = -min;
803 sec = -sec;
804 fsec = -fsec;
805 }
806
807 if (!has_negative && !has_positive)
808 {
809 sprintf(cp, "0");
810 }
811 else if (!sql_standard_value)
812 {
813 /*
814 * For non sql-standard interval values, force outputting
815 * the signs to avoid ambiguities with intervals with
816 * mixed sign components.
817 */
818 char year_sign = (year < 0 || mon < 0) ? '-' : '+';
819 char day_sign = (mday < 0) ? '-' : '+';
820 char sec_sign = (hour < 0 || min < 0 ||
821 sec < 0 || fsec < 0) ? '-' : '+';
822
823 sprintf(cp, "%c%d-%d %c%d %c%d:%02d:",
824 year_sign, abs(year), abs(mon),
825 day_sign, abs(mday),
826 sec_sign, abs(hour), abs(min));
827 cp += strlen(cp);
828 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
829 }
830 else if (has_year_month)
831 {
832 sprintf(cp, "%d-%d", year, mon);
833 }
834 else if (has_day)
835 {
836 sprintf(cp, "%d %d:%02d:", mday, hour, min);
837 cp += strlen(cp);
838 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
839 }
840 else
841 {
842 sprintf(cp, "%d:%02d:", hour, min);
843 cp += strlen(cp);
844 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
845 }
846 }
847 break;
848
849 /* ISO 8601 "time-intervals by duration only" */
851 /* special-case zero to avoid printing nothing */
852 if (year == 0 && mon == 0 && mday == 0 &&
853 hour == 0 && min == 0 && sec == 0 && fsec == 0)
854 {
855 sprintf(cp, "PT0S");
856 break;
857 }
858 *cp++ = 'P';
859 cp = AddISO8601IntPart(cp, year, 'Y');
860 cp = AddISO8601IntPart(cp, mon, 'M');
861 cp = AddISO8601IntPart(cp, mday, 'D');
862 if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
863 *cp++ = 'T';
864 cp = AddISO8601IntPart(cp, hour, 'H');
865 cp = AddISO8601IntPart(cp, min, 'M');
866 if (sec != 0 || fsec != 0)
867 {
868 if (sec < 0 || fsec < 0)
869 *cp++ = '-';
870 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
871 cp += strlen(cp);
872 *cp++ = 'S';
873 *cp = '\0';
874 }
875 break;
876
877 /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
879 cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
880 cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
882 if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
883 {
884 bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
885
886 sprintf(cp, "%s%s%02d:%02d:",
887 is_zero ? "" : " ",
888 (minus ? "-" : (is_before ? "+" : "")),
889 abs(hour), abs(min));
890 cp += strlen(cp);
891 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
892 }
893 break;
894
895 /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
897 default:
898 strcpy(cp, "@");
899 cp++;
900 cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
901 cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
904 cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
905 if (sec != 0 || fsec != 0)
906 {
907 *cp++ = ' ';
908 if (sec < 0 || (sec == 0 && fsec < 0))
909 {
910 if (is_zero)
911 is_before = true;
912 else if (!is_before)
913 *cp++ = '-';
914 }
915 else if (is_before)
916 *cp++ = '-';
917 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
918 cp += strlen(cp);
919 /* We output "ago", not negatives, so use abs(). */
920 sprintf(cp, " sec%s",
921 (abs(sec) != 1 || fsec != 0) ? "s" : "");
922 is_zero = false;
923 }
924 /* identically zero? then put in a unitless zero... */
925 if (is_zero)
926 strcat(cp, " 0");
927 if (is_before)
928 strcat(cp, " ago");
929 break;
930 }
931}
932
933
934/* interval2tm()
935 * Convert an interval data type to a tm structure.
936 */
937static int
939{
940 int64 time;
941
942 if (span.month != 0)
943 {
944 tm->tm_year = span.month / MONTHS_PER_YEAR;
945 tm->tm_mon = span.month % MONTHS_PER_YEAR;
946 }
947 else
948 {
949 tm->tm_year = 0;
950 tm->tm_mon = 0;
951 }
952
953 time = span.time;
954
955 tm->tm_mday = time / USECS_PER_DAY;
956 time -= tm->tm_mday * USECS_PER_DAY;
957 tm->tm_hour = time / USECS_PER_HOUR;
958 time -= tm->tm_hour * USECS_PER_HOUR;
959 tm->tm_min = time / USECS_PER_MINUTE;
960 time -= tm->tm_min * USECS_PER_MINUTE;
961 tm->tm_sec = time / USECS_PER_SEC;
962 *fsec = time - (tm->tm_sec * USECS_PER_SEC);
963
964 return 0;
965} /* interval2tm() */
966
967static int
969{
970 if ((double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon > INT_MAX ||
971 (double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon < INT_MIN)
972 return -1;
973 span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
974 span->time = (((((((tm->tm_mday * INT64CONST(24)) +
975 tm->tm_hour) * INT64CONST(60)) +
976 tm->tm_min) * INT64CONST(60)) +
977 tm->tm_sec) * USECS_PER_SEC) + fsec;
978
979 return 0;
980} /* tm2interval() */
981
982interval *
984{
985 interval *result;
986
987 result = (interval *) pgtypes_alloc(sizeof(interval));
988 /* result can be NULL if we run out of memory */
989 return result;
990}
991
992void
997
998interval *
999PGTYPESinterval_from_asc(char *str, char **endptr)
1000{
1001 interval *result = NULL;
1002 fsec_t fsec;
1003 struct tm tt,
1004 *tm = &tt;
1005 int dtype;
1006 int nf;
1007 char *field[MAXDATEFIELDS];
1008 int ftype[MAXDATEFIELDS];
1010 char *realptr;
1011 char **ptr = (endptr != NULL) ? endptr : &realptr;
1012
1013 tm->tm_year = 0;
1014 tm->tm_mon = 0;
1015 tm->tm_mday = 0;
1016 tm->tm_hour = 0;
1017 tm->tm_min = 0;
1018 tm->tm_sec = 0;
1019 fsec = 0;
1020
1021 if (strlen(str) > MAXDATELEN)
1022 {
1024 return NULL;
1025 }
1026
1027 if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
1028 (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0 &&
1029 DecodeISO8601Interval(str, &dtype, tm, &fsec) != 0))
1030 {
1032 return NULL;
1033 }
1034
1035 result = (interval *) pgtypes_alloc(sizeof(interval));
1036 if (!result)
1037 return NULL;
1038
1039 if (dtype != DTK_DELTA)
1040 {
1042 free(result);
1043 return NULL;
1044 }
1045
1046 if (tm2interval(tm, fsec, result) != 0)
1047 {
1049 free(result);
1050 return NULL;
1051 }
1052
1053 errno = 0;
1054 return result;
1055}
1056
1057char *
1059{
1060 struct tm tt,
1061 *tm = &tt;
1062 fsec_t fsec;
1063 char buf[MAXDATELEN + 1];
1065
1066 if (interval2tm(*span, tm, &fsec) != 0)
1067 {
1069 return NULL;
1070 }
1071
1073
1074 return pgtypes_strdup(buf);
1075}
1076
1077int
1079{
1080 intvldest->time = intvlsrc->time;
1081 intvldest->month = intvlsrc->month;
1082
1083 return 0;
1084}
int DecodeUnits(int field, const char *lowtoken, int *val)
Definition datetime.c:4170
int ParseDateTime(const char *timestr, char *workbuf, size_t buflen, char **field, int *ftype, int maxfields, int *numfields)
Definition datetime.c:774
static int DecodeTime(char *str, int fmask, int range, int *tmask, struct pg_tm *tm, fsec_t *fsec)
Definition datetime.c:2726
#define INT64CONST(x)
Definition c.h:632
#define Assert(condition)
Definition c.h:945
int64_t int64
Definition c.h:615
#define pg_fallthrough
Definition c.h:152
#define SECS_PER_HOUR
Definition timestamp.h:127
int32 fsec_t
Definition timestamp.h:41
#define USECS_PER_HOUR
Definition timestamp.h:132
#define MONTHS_PER_YEAR
Definition timestamp.h:108
#define MAX_INTERVAL_PRECISION
Definition timestamp.h:93
#define SECS_PER_MINUTE
Definition timestamp.h:128
#define USECS_PER_DAY
Definition timestamp.h:131
#define USECS_PER_SEC
Definition timestamp.h:134
#define USECS_PER_MINUTE
Definition timestamp.h:133
#define DAYS_PER_MONTH
Definition timestamp.h:116
#define SECS_PER_DAY
Definition timestamp.h:126
void TrimTrailingZeros(char *str)
Definition dt_common.c:722
int IntervalStyle
Definition globals.c:127
const char * str
#define MAXDATEFIELDS
Definition datetime.h:202
#define DTK_SPECIAL
Definition datetime.h:149
#define DTK_TIME
Definition datetime.h:145
#define DTK_DECADE
Definition datetime.h:168
#define DTK_SECOND
Definition datetime.h:160
#define DTK_NUMBER
Definition datetime.h:141
#define DTK_STRING
Definition datetime.h:142
#define MONTH
Definition datetime.h:91
#define DTK_DELTA
Definition datetime.h:159
#define IGNORE_DTF
Definition datetime.h:98
#define MICROSECOND
Definition datetime.h:104
#define DTK_TIME_M
Definition datetime.h:192
#define DTK_M(t)
Definition datetime.h:187
#define HOUR
Definition datetime.h:100
#define DAY
Definition datetime.h:93
#define YEAR
Definition datetime.h:92
#define DTK_DATE
Definition datetime.h:144
#define DTK_CENTURY
Definition datetime.h:169
#define TZ
Definition datetime.h:95
#define MILLISECOND
Definition datetime.h:103
#define DTK_DAY
Definition datetime.h:163
#define RESERV
Definition datetime.h:90
#define DTERR_BAD_FORMAT
Definition datetime.h:282
#define DTK_DATE_M
Definition datetime.h:191
#define DTK_MILLENNIUM
Definition datetime.h:170
#define MAXDATELEN
Definition datetime.h:200
#define SECOND
Definition datetime.h:102
#define DTK_ALL_SECS_M
Definition datetime.h:190
#define DTK_TZ
Definition datetime.h:146
#define DTK_HOUR
Definition datetime.h:162
#define DTK_WEEK
Definition datetime.h:164
#define MINUTE
Definition datetime.h:101
#define DTK_MICROSEC
Definition datetime.h:172
#define DTK_YEAR
Definition datetime.h:167
#define AGO
Definition datetime.h:110
#define DTK_MILLISEC
Definition datetime.h:171
#define DTK_MONTH
Definition datetime.h:165
#define DTERR_FIELD_OVERFLOW
Definition datetime.h:283
#define DTK_MINUTE
Definition datetime.h:161
#define UNITS
Definition datetime.h:107
long val
Definition informix.c:689
static struct @174 value
char * pgtypes_strdup(const char *str)
Definition common.c:20
char * pgtypes_alloc(long size)
Definition common.c:10
interval * PGTYPESinterval_new(void)
Definition interval.c:983
void PGTYPESinterval_free(interval *intvl)
Definition interval.c:993
static char * AddISO8601IntPart(char *cp, int value, char units)
Definition interval.c:719
static void AdjustFractDays(double frac, struct tm *tm, fsec_t *fsec, int scale)
Definition interval.c:37
static int DecodeISO8601Interval(char *str, int *dtype, struct tm *tm, fsec_t *fsec)
Definition interval.c:108
int PGTYPESinterval_copy(interval *intvlsrc, interval *intvldest)
Definition interval.c:1078
static int interval2tm(interval span, struct tm *tm, fsec_t *fsec)
Definition interval.c:938
static int tm2interval(struct tm *tm, fsec_t fsec, interval *span)
Definition interval.c:968
void EncodeInterval(struct tm *tm, fsec_t fsec, int style, char *str)
Definition interval.c:755
static int ParseISO8601Number(const char *str, char **endptr, int *ipart, double *fpart)
Definition interval.c:52
static void ClearPgTm(struct tm *tm, fsec_t *fsec)
Definition interval.c:90
static char * AddPostgresIntPart(char *cp, int value, const char *units, bool *is_zero, bool *is_before)
Definition interval.c:696
static void AdjustFractSeconds(double frac, struct tm *tm, fsec_t *fsec, int scale)
Definition interval.c:19
static void AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
Definition interval.c:729
int DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm *tm, fsec_t *fsec)
Definition interval.c:322
interval * PGTYPESinterval_from_asc(char *str, char **endptr)
Definition interval.c:999
static char * AddVerboseIntPart(char *cp, int value, const char *units, bool *is_zero, bool *is_before)
Definition interval.c:676
char * PGTYPESinterval_to_asc(interval *span)
Definition interval.c:1058
static int ISO8601IntegerWidth(const char *fieldstart)
Definition interval.c:77
int i
Definition isn.c:77
static struct pg_tm tm
Definition localtime.c:104
#define INTSTYLE_SQL_STANDARD
Definition miscadmin.h:259
#define INTSTYLE_POSTGRES_VERBOSE
Definition miscadmin.h:258
#define INTSTYLE_ISO_8601
Definition miscadmin.h:260
#define INTSTYLE_POSTGRES
Definition miscadmin.h:257
static char buf[DEFAULT_XLOG_SEG_SIZE]
static int scale
Definition pgbench.c:182
#define PGTYPES_INTVL_BAD_INTERVAL
#define sprintf
Definition port.h:262
static int fb(int x)
static struct cvec * range(struct vars *v, chr a, chr b, int cases)
#define free(a)
int strtoint(const char *pg_restrict str, char **pg_restrict endptr, int base)
Definition string.c:50
int tm_hour
Definition pgtime.h:38
int tm_mday
Definition pgtime.h:39
int tm_mon
Definition pgtime.h:40
int tm_min
Definition pgtime.h:37
int tm_sec
Definition pgtime.h:36
int tm_year
Definition pgtime.h:41
#define INTERVAL_FULL_RANGE
Definition timestamp.h:76
#define INTERVAL_MASK(b)
Definition timestamp.h:73
const char * type