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