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