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