PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
interval.c File Reference
#include "postgres_fe.h"
#include <time.h>
#include <math.h>
#include <limits.h>
#include "common/string.h"
#include "dt.h"
#include "pgtypes_error.h"
#include "pgtypes_interval.h"
#include "pgtypeslib_extern.h"
Include dependency graph for interval.c:

Go to the source code of this file.

Functions

static void AdjustFractSeconds (double frac, struct tm *tm, fsec_t *fsec, int scale)
 
static void AdjustFractDays (double frac, struct tm *tm, fsec_t *fsec, int scale)
 
static int ParseISO8601Number (const char *str, char **endptr, int *ipart, double *fpart)
 
static int ISO8601IntegerWidth (const char *fieldstart)
 
static void ClearPgTm (struct tm *tm, fsec_t *fsec)
 
static int DecodeISO8601Interval (char *str, int *dtype, struct tm *tm, fsec_t *fsec)
 
int DecodeInterval (char **field, int *ftype, int nf, int *dtype, struct tm *tm, fsec_t *fsec)
 
static char * AddVerboseIntPart (char *cp, int value, const char *units, bool *is_zero, bool *is_before)
 
static char * AddPostgresIntPart (char *cp, int value, const char *units, bool *is_zero, bool *is_before)
 
static char * AddISO8601IntPart (char *cp, int value, char units)
 
static void AppendSeconds (char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
 
void EncodeInterval (struct tm *tm, fsec_t fsec, int style, char *str)
 
static int interval2tm (interval span, struct tm *tm, fsec_t *fsec)
 
static int tm2interval (struct tm *tm, fsec_t fsec, interval *span)
 
intervalPGTYPESinterval_new (void)
 
void PGTYPESinterval_free (interval *intvl)
 
intervalPGTYPESinterval_from_asc (char *str, char **endptr)
 
char * PGTYPESinterval_to_asc (interval *span)
 
int PGTYPESinterval_copy (interval *intvlsrc, interval *intvldest)
 

Function Documentation

◆ AddISO8601IntPart()

static char * AddISO8601IntPart ( char *  cp,
int  value,
char  units 
)
static

Definition at line 723 of file interval.c.

724{
725 if (value == 0)
726 return cp;
727 sprintf(cp, "%d%c", value, units);
728 return cp + strlen(cp);
729}
static struct @162 value
#define sprintf
Definition: port.h:240

References sprintf, and value.

Referenced by EncodeInterval().

◆ AddPostgresIntPart()

static char * AddPostgresIntPart ( char *  cp,
int  value,
const char *  units,
bool *  is_zero,
bool *  is_before 
)
static

Definition at line 700 of file interval.c.

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}

References sprintf, and value.

Referenced by EncodeInterval().

◆ AddVerboseIntPart()

static char * AddVerboseIntPart ( char *  cp,
int  value,
const char *  units,
bool *  is_zero,
bool *  is_before 
)
static

Definition at line 680 of file interval.c.

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}

References sprintf, and value.

Referenced by EncodeInterval().

◆ AdjustFractDays()

static void AdjustFractDays ( double  frac,
struct tm tm,
fsec_t fsec,
int  scale 
)
static

Definition at line 41 of file interval.c.

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;
52}
#define SECS_PER_DAY
Definition: timestamp.h:126
static void AdjustFractSeconds(double frac, struct tm *tm, fsec_t *fsec, int scale)
Definition: interval.c:23
static struct pg_tm tm
Definition: localtime.c:104
static int scale
Definition: pgbench.c:181
int tm_mday
Definition: pgtime.h:39

References AdjustFractSeconds(), scale, SECS_PER_DAY, tm, and pg_tm::tm_mday.

Referenced by DecodeInterval(), and DecodeISO8601Interval().

◆ AdjustFractSeconds()

static void AdjustFractSeconds ( double  frac,
struct tm tm,
fsec_t fsec,
int  scale 
)
static

Definition at line 23 of file interval.c.

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}
int tm_sec
Definition: pgtime.h:36

References scale, tm, and pg_tm::tm_sec.

Referenced by AdjustFractDays(), DecodeInterval(), and DecodeISO8601Interval().

◆ AppendSeconds()

static void AppendSeconds ( char *  cp,
int  sec,
fsec_t  fsec,
int  precision,
bool  fillzeros 
)
static

Definition at line 733 of file interval.c.

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));
749 }
750}
void TrimTrailingZeros(char *str)
Definition: dt_common.c:722

References sprintf, and TrimTrailingZeros().

Referenced by EncodeInterval().

◆ ClearPgTm()

static void ClearPgTm ( struct tm tm,
fsec_t fsec 
)
inlinestatic

Definition at line 94 of file interval.c.

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}
int tm_hour
Definition: pgtime.h:38
int tm_mon
Definition: pgtime.h:40
int tm_min
Definition: pgtime.h:37
int tm_year
Definition: pgtime.h:41

References tm, pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, and pg_tm::tm_year.

Referenced by DecodeInterval(), and DecodeISO8601Interval().

◆ DecodeInterval()

int DecodeInterval ( char **  field,
int *  ftype,
int  nf,
int *  dtype,
struct tm tm,
fsec_t fsec 
)

Definition at line 326 of file interval.c.

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;
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):
422 break;
423 case INTERVAL_MASK(SECOND):
427 break;
428 default:
430 break;
431 }
432 }
433
434 errno = 0;
435 val = strtoint(field[i], &cp, 10);
436 if (errno == ERANGE)
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)
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;
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}
int DecodeUnits(int field, const char *lowtoken, int *val)
Definition: datetime.c:4163
static int DecodeTime(char *str, int fmask, int range, int *tmask, struct pg_tm *tm, fsec_t *fsec)
Definition: datetime.c:2716
#define Assert(condition)
Definition: c.h:815
#define SECS_PER_HOUR
Definition: timestamp.h:127
#define MONTHS_PER_YEAR
Definition: timestamp.h:108
#define SECS_PER_MINUTE
Definition: timestamp.h:128
#define USECS_PER_SEC
Definition: timestamp.h:134
#define DAYS_PER_MONTH
Definition: timestamp.h:116
int IntervalStyle
Definition: globals.c:126
#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 SECOND
Definition: datetime.h:102
#define DTK_ALL_SECS_M
Definition: datetime.h:190
#define DTK_TZ
Definition: datetime.h:146
#define DTK_HOUR
Definition: datetime.h:162
#define DTK_WEEK
Definition: datetime.h:164
#define MINUTE
Definition: datetime.h:101
#define DTK_MICROSEC
Definition: datetime.h:172
#define DTK_YEAR
Definition: datetime.h:167
#define AGO
Definition: datetime.h:110
#define DTK_MILLISEC
Definition: datetime.h:171
#define DTK_MONTH
Definition: datetime.h:165
#define DTERR_FIELD_OVERFLOW
Definition: datetime.h:283
#define DTK_MINUTE
Definition: datetime.h:161
#define UNITS
Definition: datetime.h:107
long val
Definition: informix.c:689
static void AdjustFractDays(double frac, struct tm *tm, fsec_t *fsec, int scale)
Definition: interval.c:41
static void ClearPgTm(struct tm *tm, fsec_t *fsec)
Definition: interval.c:94
int i
Definition: isn.c:72
#define INTSTYLE_SQL_STANDARD
Definition: miscadmin.h:258
#define INTSTYLE_POSTGRES_VERBOSE
Definition: miscadmin.h:257
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:50
#define INTERVAL_FULL_RANGE
Definition: timestamp.h:76
#define INTERVAL_MASK(b)
Definition: timestamp.h:73
const char * type

References AdjustFractDays(), AdjustFractSeconds(), AGO, Assert, ClearPgTm(), DAY, DAYS_PER_MONTH, DecodeTime(), DecodeUnits(), DTERR_BAD_FORMAT, DTERR_FIELD_OVERFLOW, DTK_ALL_SECS_M, DTK_CENTURY, DTK_DATE, DTK_DATE_M, DTK_DAY, DTK_DECADE, DTK_DELTA, DTK_HOUR, DTK_M, DTK_MICROSEC, DTK_MILLENNIUM, DTK_MILLISEC, DTK_MINUTE, DTK_MONTH, DTK_NUMBER, DTK_SECOND, DTK_SPECIAL, DTK_STRING, DTK_TIME, DTK_TIME_M, DTK_TZ, DTK_WEEK, DTK_YEAR, HOUR, i, IGNORE_DTF, INTERVAL_FULL_RANGE, INTERVAL_MASK, IntervalStyle, INTSTYLE_POSTGRES_VERBOSE, INTSTYLE_SQL_STANDARD, MICROSECOND, MILLISECOND, MINUTE, MONTH, MONTHS_PER_YEAR, range(), RESERV, SECOND, SECS_PER_DAY, SECS_PER_HOUR, SECS_PER_MINUTE, strtoint(), tm, pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, pg_tm::tm_year, type, TZ, UNITS, USECS_PER_SEC, val, and YEAR.

Referenced by PGTYPESinterval_from_asc().

◆ DecodeISO8601Interval()

static int DecodeISO8601Interval ( char *  str,
int *  dtype,
struct tm tm,
fsec_t fsec 
)
static

Definition at line 112 of file interval.c.

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;
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;
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}
const char * str
static int ParseISO8601Number(const char *str, char **endptr, int *ipart, double *fpart)
Definition: interval.c:56
static int ISO8601IntegerWidth(const char *fieldstart)
Definition: interval.c:81

References AdjustFractDays(), AdjustFractSeconds(), ClearPgTm(), DAYS_PER_MONTH, DTERR_BAD_FORMAT, DTK_DELTA, ISO8601IntegerWidth(), MONTHS_PER_YEAR, ParseISO8601Number(), SECS_PER_DAY, SECS_PER_HOUR, SECS_PER_MINUTE, str, tm, pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, pg_tm::tm_year, and val.

Referenced by PGTYPESinterval_from_asc().

◆ EncodeInterval()

void EncodeInterval ( struct tm tm,
fsec_t  fsec,
int  style,
char *  str 
)

Definition at line 759 of file interval.c.

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" */
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' */
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}
#define MAX_INTERVAL_PRECISION
Definition: timestamp.h:93
static char * AddISO8601IntPart(char *cp, int value, char units)
Definition: interval.c:723
static char * AddPostgresIntPart(char *cp, int value, const char *units, bool *is_zero, bool *is_before)
Definition: interval.c:700
static void AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
Definition: interval.c:733
static char * AddVerboseIntPart(char *cp, int value, const char *units, bool *is_zero, bool *is_before)
Definition: interval.c:680
#define INTSTYLE_ISO_8601
Definition: miscadmin.h:259
#define INTSTYLE_POSTGRES
Definition: miscadmin.h:256

References AddISO8601IntPart(), AddPostgresIntPart(), AddVerboseIntPart(), AppendSeconds(), INTSTYLE_ISO_8601, INTSTYLE_POSTGRES, INTSTYLE_POSTGRES_VERBOSE, INTSTYLE_SQL_STANDARD, MAX_INTERVAL_PRECISION, sprintf, str, tm, pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, and pg_tm::tm_year.

Referenced by PGTYPESinterval_to_asc().

◆ interval2tm()

static int interval2tm ( interval  span,
struct tm tm,
fsec_t fsec 
)
static

Definition at line 942 of file interval.c.

943{
944 int64 time;
945
946 if (span.month != 0)
947 {
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() */
int64_t int64
Definition: c.h:485
#define USECS_PER_HOUR
Definition: timestamp.h:132
#define USECS_PER_DAY
Definition: timestamp.h:131
#define USECS_PER_MINUTE
Definition: timestamp.h:133

References interval::month, MONTHS_PER_YEAR, interval::time, tm, pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, pg_tm::tm_year, USECS_PER_DAY, USECS_PER_HOUR, USECS_PER_MINUTE, and USECS_PER_SEC.

Referenced by PGTYPESinterval_to_asc().

◆ ISO8601IntegerWidth()

static int ISO8601IntegerWidth ( const char *  fieldstart)
static

Definition at line 81 of file interval.c.

82{
83 /* We might have had a leading '-' */
84 if (*fieldstart == '-')
85 fieldstart++;
86 return strspn(fieldstart, "0123456789");
87}

Referenced by DecodeISO8601Interval().

◆ ParseISO8601Number()

static int ParseISO8601Number ( const char *  str,
char **  endptr,
int *  ipart,
double *  fpart 
)
static

Definition at line 56 of file interval.c.

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)
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}

References DTERR_BAD_FORMAT, DTERR_FIELD_OVERFLOW, str, and val.

Referenced by DecodeISO8601Interval().

◆ PGTYPESinterval_copy()

int PGTYPESinterval_copy ( interval intvlsrc,
interval intvldest 
)

Definition at line 1082 of file interval.c.

1083{
1084 intvldest->time = intvlsrc->time;
1085 intvldest->month = intvlsrc->month;
1086
1087 return 0;
1088}

References interval::month, and interval::time.

Referenced by ecpg_get_data(), and main().

◆ PGTYPESinterval_free()

void PGTYPESinterval_free ( interval intvl)

Definition at line 997 of file interval.c.

998{
999 free(intvl);
1000}
#define free(a)
Definition: header.h:65

References free.

Referenced by main().

◆ PGTYPESinterval_from_asc()

interval * PGTYPESinterval_from_asc ( char *  str,
char **  endptr 
)

Definition at line 1003 of file interval.c.

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}
int ParseDateTime(const char *timestr, char *workbuf, size_t buflen, char **field, int *ftype, int maxfields, int *numfields)
Definition: datetime.c:764
int32 fsec_t
Definition: timestamp.h:41
#define MAXDATEFIELDS
Definition: datetime.h:202
#define MAXDATELEN
Definition: datetime.h:200
char * pgtypes_alloc(long size)
Definition: common.c:10
static int DecodeISO8601Interval(char *str, int *dtype, struct tm *tm, fsec_t *fsec)
Definition: interval.c:112
static int tm2interval(struct tm *tm, fsec_t fsec, interval *span)
Definition: interval.c:972
int DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm *tm, fsec_t *fsec)
Definition: interval.c:326
#define PGTYPES_INTVL_BAD_INTERVAL
Definition: pgtypes_error.h:18

References DecodeInterval(), DecodeISO8601Interval(), DTK_DELTA, free, MAXDATEFIELDS, MAXDATELEN, ParseDateTime(), pgtypes_alloc(), PGTYPES_INTVL_BAD_INTERVAL, str, tm, tm2interval(), pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, and pg_tm::tm_year.

Referenced by ecpg_get_data(), and main().

◆ PGTYPESinterval_new()

interval * PGTYPESinterval_new ( void  )

Definition at line 987 of file interval.c.

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}

References pgtypes_alloc().

Referenced by main().

◆ PGTYPESinterval_to_asc()

char * PGTYPESinterval_to_asc ( interval span)

Definition at line 1062 of file interval.c.

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}
char * pgtypes_strdup(const char *str)
Definition: common.c:20
static int interval2tm(interval span, struct tm *tm, fsec_t *fsec)
Definition: interval.c:942
void EncodeInterval(struct tm *tm, fsec_t fsec, int style, char *str)
Definition: interval.c:759
static char * buf
Definition: pg_test_fsync.c:72

References buf, EncodeInterval(), interval2tm(), IntervalStyle, INTSTYLE_POSTGRES_VERBOSE, MAXDATELEN, PGTYPES_INTVL_BAD_INTERVAL, pgtypes_strdup(), and tm.

Referenced by ecpg_store_input(), intoasc(), and main().

◆ tm2interval()

static int tm2interval ( struct tm tm,
fsec_t  fsec,
interval span 
)
static

Definition at line 972 of file interval.c.

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() */
#define INT64CONST(x)
Definition: c.h:502

References INT64CONST, interval::month, MONTHS_PER_YEAR, interval::time, tm, pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, pg_tm::tm_year, and USECS_PER_SEC.

Referenced by PGTYPESinterval_from_asc().