00001
00002
00003 #include "postgres_fe.h"
00004 #include <time.h>
00005 #include <math.h>
00006 #include <limits.h>
00007
00008 #ifdef __FAST_MATH__
00009 #error -ffast-math is known to break this code
00010 #endif
00011
00012 #include "extern.h"
00013 #include "dt.h"
00014 #include "pgtypes_error.h"
00015 #include "pgtypes_interval.h"
00016
00017
00018 static int
00019 strtoi(const char *nptr, char **endptr, int base)
00020 {
00021 long val;
00022
00023 val = strtol(nptr, endptr, base);
00024 #ifdef HAVE_LONG_INT_64
00025 if (val != (long) ((int32) val))
00026 errno = ERANGE;
00027 #endif
00028 return (int) val;
00029 }
00030
00031
00032
00033
00034 static void
00035 AdjustFractSeconds(double frac, struct tm * tm, fsec_t *fsec, int scale)
00036 {
00037 int sec;
00038
00039 if (frac == 0)
00040 return;
00041 frac *= scale;
00042 sec = (int) frac;
00043 tm->tm_sec += sec;
00044 frac -= sec;
00045 #ifdef HAVE_INT64_TIMESTAMP
00046 *fsec += rint(frac * 1000000);
00047 #else
00048 *fsec += frac;
00049 #endif
00050 }
00051
00052
00053
00054
00055
00056 static void
00057 AdjustFractDays(double frac, struct tm * tm, fsec_t *fsec, int scale)
00058 {
00059 int extra_days;
00060
00061 if (frac == 0)
00062 return;
00063 frac *= scale;
00064 extra_days = (int) frac;
00065 tm->tm_mday += extra_days;
00066 frac -= extra_days;
00067 AdjustFractSeconds(frac, tm, fsec, SECS_PER_DAY);
00068 }
00069
00070
00071 static int
00072 ParseISO8601Number(char *str, char **endptr, int *ipart, double *fpart)
00073 {
00074 double val;
00075
00076 if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
00077 return DTERR_BAD_FORMAT;
00078 errno = 0;
00079 val = strtod(str, endptr);
00080
00081 if (*endptr == str || errno != 0)
00082 return DTERR_BAD_FORMAT;
00083
00084 if (val < INT_MIN || val > INT_MAX)
00085 return DTERR_FIELD_OVERFLOW;
00086
00087 if (val >= 0)
00088 *ipart = (int) floor(val);
00089 else
00090 *ipart = (int) -floor(-val);
00091 *fpart = val - *ipart;
00092 return 0;
00093 }
00094
00095
00096 static int
00097 ISO8601IntegerWidth(char *fieldstart)
00098 {
00099
00100 if (*fieldstart == '-')
00101 fieldstart++;
00102 return strspn(fieldstart, "0123456789");
00103 }
00104
00105
00106
00107
00108
00109 static inline void
00110 ClearPgTm(struct tm * tm, fsec_t *fsec)
00111 {
00112 tm->tm_year = 0;
00113 tm->tm_mon = 0;
00114 tm->tm_mday = 0;
00115 tm->tm_hour = 0;
00116 tm->tm_min = 0;
00117 tm->tm_sec = 0;
00118 *fsec = 0;
00119 }
00120
00121
00122
00123
00124
00125
00126
00127 static int
00128 DecodeISO8601Interval(char *str,
00129 int *dtype, struct tm * tm, fsec_t *fsec)
00130 {
00131 bool datepart = true;
00132 bool havefield = false;
00133
00134 *dtype = DTK_DELTA;
00135 ClearPgTm(tm, fsec);
00136
00137 if (strlen(str) < 2 || str[0] != 'P')
00138 return DTERR_BAD_FORMAT;
00139
00140 str++;
00141 while (*str)
00142 {
00143 char *fieldstart;
00144 int val;
00145 double fval;
00146 char unit;
00147 int dterr;
00148
00149 if (*str == 'T')
00150 {
00151 datepart = false;
00152 havefield = false;
00153 str++;
00154 continue;
00155 }
00156
00157 fieldstart = str;
00158 dterr = ParseISO8601Number(str, &str, &val, &fval);
00159 if (dterr)
00160 return dterr;
00161
00162
00163
00164
00165
00166 unit = *str++;
00167
00168 if (datepart)
00169 {
00170 switch (unit)
00171 {
00172 case 'Y':
00173 tm->tm_year += val;
00174 tm->tm_mon += (fval * 12);
00175 break;
00176 case 'M':
00177 tm->tm_mon += val;
00178 AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
00179 break;
00180 case 'W':
00181 tm->tm_mday += val * 7;
00182 AdjustFractDays(fval, tm, fsec, 7);
00183 break;
00184 case 'D':
00185 tm->tm_mday += val;
00186 AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
00187 break;
00188 case 'T':
00189 case '\0':
00190 if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
00191 {
00192 tm->tm_year += val / 10000;
00193 tm->tm_mon += (val / 100) % 100;
00194 tm->tm_mday += val % 100;
00195 AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
00196 if (unit == '\0')
00197 return 0;
00198 datepart = false;
00199 havefield = false;
00200 continue;
00201 }
00202
00203 case '-':
00204
00205 if (havefield)
00206 return DTERR_BAD_FORMAT;
00207
00208 tm->tm_year += val;
00209 tm->tm_mon += (fval * 12);
00210 if (unit == '\0')
00211 return 0;
00212 if (unit == 'T')
00213 {
00214 datepart = false;
00215 havefield = false;
00216 continue;
00217 }
00218
00219 dterr = ParseISO8601Number(str, &str, &val, &fval);
00220 if (dterr)
00221 return dterr;
00222 tm->tm_mon += val;
00223 AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
00224 if (*str == '\0')
00225 return 0;
00226 if (*str == 'T')
00227 {
00228 datepart = false;
00229 havefield = false;
00230 continue;
00231 }
00232 if (*str != '-')
00233 return DTERR_BAD_FORMAT;
00234 str++;
00235
00236 dterr = ParseISO8601Number(str, &str, &val, &fval);
00237 if (dterr)
00238 return dterr;
00239 tm->tm_mday += val;
00240 AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
00241 if (*str == '\0')
00242 return 0;
00243 if (*str == 'T')
00244 {
00245 datepart = false;
00246 havefield = false;
00247 continue;
00248 }
00249 return DTERR_BAD_FORMAT;
00250 default:
00251
00252 return DTERR_BAD_FORMAT;
00253 }
00254 }
00255 else
00256 {
00257 switch (unit)
00258 {
00259 case 'H':
00260 tm->tm_hour += val;
00261 AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
00262 break;
00263 case 'M':
00264 tm->tm_min += val;
00265 AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
00266 break;
00267 case 'S':
00268 tm->tm_sec += val;
00269 AdjustFractSeconds(fval, tm, fsec, 1);
00270 break;
00271 case '\0':
00272 if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
00273 {
00274 tm->tm_hour += val / 10000;
00275 tm->tm_min += (val / 100) % 100;
00276 tm->tm_sec += val % 100;
00277 AdjustFractSeconds(fval, tm, fsec, 1);
00278 return 0;
00279 }
00280
00281 case ':':
00282
00283 if (havefield)
00284 return DTERR_BAD_FORMAT;
00285
00286 tm->tm_hour += val;
00287 AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
00288 if (unit == '\0')
00289 return 0;
00290
00291 dterr = ParseISO8601Number(str, &str, &val, &fval);
00292 if (dterr)
00293 return dterr;
00294 tm->tm_min += val;
00295 AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
00296 if (*str == '\0')
00297 return 0;
00298 if (*str != ':')
00299 return DTERR_BAD_FORMAT;
00300 str++;
00301
00302 dterr = ParseISO8601Number(str, &str, &val, &fval);
00303 if (dterr)
00304 return dterr;
00305 tm->tm_sec += val;
00306 AdjustFractSeconds(fval, tm, fsec, 1);
00307 if (*str == '\0')
00308 return 0;
00309 return DTERR_BAD_FORMAT;
00310
00311 default:
00312
00313 return DTERR_BAD_FORMAT;
00314 }
00315 }
00316
00317 havefield = true;
00318 }
00319
00320 return 0;
00321 }
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341 int
00342 DecodeInterval(char **field, int *ftype, int nf,
00343 int *dtype, struct tm * tm, fsec_t *fsec)
00344 {
00345 int IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
00346 int range = INTERVAL_FULL_RANGE;
00347 bool is_before = FALSE;
00348 char *cp;
00349 int fmask = 0,
00350 tmask,
00351 type;
00352 int i;
00353 int dterr;
00354 int val;
00355 double fval;
00356
00357 *dtype = DTK_DELTA;
00358 type = IGNORE_DTF;
00359 ClearPgTm(tm, fsec);
00360
00361
00362 for (i = nf - 1; i >= 0; i--)
00363 {
00364 switch (ftype[i])
00365 {
00366 case DTK_TIME:
00367 dterr = DecodeTime(field[i],
00368 &tmask, tm, fsec);
00369 if (dterr)
00370 return dterr;
00371 type = DTK_DAY;
00372 break;
00373
00374 case DTK_TZ:
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388 if (strchr(field[i] + 1, ':') != NULL &&
00389 DecodeTime(field[i] + 1,
00390 &tmask, tm, fsec) == 0)
00391 {
00392 if (*field[i] == '-')
00393 {
00394
00395 tm->tm_hour = -tm->tm_hour;
00396 tm->tm_min = -tm->tm_min;
00397 tm->tm_sec = -tm->tm_sec;
00398 *fsec = -(*fsec);
00399 }
00400
00401
00402
00403
00404
00405
00406 type = DTK_DAY;
00407 tmask = DTK_M(TZ);
00408 break;
00409 }
00410
00411
00412 case DTK_DATE:
00413 case DTK_NUMBER:
00414 if (type == IGNORE_DTF)
00415 {
00416
00417 switch (range)
00418 {
00419 case INTERVAL_MASK(YEAR):
00420 type = DTK_YEAR;
00421 break;
00422 case INTERVAL_MASK(MONTH):
00423 case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
00424 type = DTK_MONTH;
00425 break;
00426 case INTERVAL_MASK(DAY):
00427 type = DTK_DAY;
00428 break;
00429 case INTERVAL_MASK(HOUR):
00430 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
00431 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
00432 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
00433 type = DTK_HOUR;
00434 break;
00435 case INTERVAL_MASK(MINUTE):
00436 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
00437 type = DTK_MINUTE;
00438 break;
00439 case INTERVAL_MASK(SECOND):
00440 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
00441 case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
00442 type = DTK_SECOND;
00443 break;
00444 default:
00445 type = DTK_SECOND;
00446 break;
00447 }
00448 }
00449
00450 errno = 0;
00451 val = strtoi(field[i], &cp, 10);
00452 if (errno == ERANGE)
00453 return DTERR_FIELD_OVERFLOW;
00454
00455 if (*cp == '-')
00456 {
00457
00458 int val2;
00459
00460 val2 = strtoi(cp + 1, &cp, 10);
00461 if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
00462 return DTERR_FIELD_OVERFLOW;
00463 if (*cp != '\0')
00464 return DTERR_BAD_FORMAT;
00465 type = DTK_MONTH;
00466 if (*field[i] == '-')
00467 val2 = -val2;
00468 val = val * MONTHS_PER_YEAR + val2;
00469 fval = 0;
00470 }
00471 else if (*cp == '.')
00472 {
00473 errno = 0;
00474 fval = strtod(cp, &cp);
00475 if (*cp != '\0' || errno != 0)
00476 return DTERR_BAD_FORMAT;
00477
00478 if (*field[i] == '-')
00479 fval = -fval;
00480 }
00481 else if (*cp == '\0')
00482 fval = 0;
00483 else
00484 return DTERR_BAD_FORMAT;
00485
00486 tmask = 0;
00487
00488 switch (type)
00489 {
00490 case DTK_MICROSEC:
00491 #ifdef HAVE_INT64_TIMESTAMP
00492 *fsec += rint(val + fval);
00493 #else
00494 *fsec += (val + fval) * 1e-6;
00495 #endif
00496 tmask = DTK_M(MICROSECOND);
00497 break;
00498
00499 case DTK_MILLISEC:
00500 #ifdef HAVE_INT64_TIMESTAMP
00501 *fsec += rint((val + fval) * 1000);
00502 #else
00503 *fsec += (val + fval) * 1e-3;
00504 #endif
00505 tmask = DTK_M(MILLISECOND);
00506 break;
00507
00508 case DTK_SECOND:
00509 tm->tm_sec += val;
00510 #ifdef HAVE_INT64_TIMESTAMP
00511 *fsec += rint(fval * 1000000);
00512 #else
00513 *fsec += fval;
00514 #endif
00515
00516
00517
00518
00519
00520 if (fval == 0)
00521 tmask = DTK_M(SECOND);
00522 else
00523 tmask = DTK_ALL_SECS_M;
00524 break;
00525
00526 case DTK_MINUTE:
00527 tm->tm_min += val;
00528 AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
00529 tmask = DTK_M(MINUTE);
00530 break;
00531
00532 case DTK_HOUR:
00533 tm->tm_hour += val;
00534 AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
00535 tmask = DTK_M(HOUR);
00536 type = DTK_DAY;
00537 break;
00538
00539 case DTK_DAY:
00540 tm->tm_mday += val;
00541 AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
00542 tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
00543 break;
00544
00545 case DTK_WEEK:
00546 tm->tm_mday += val * 7;
00547 AdjustFractDays(fval, tm, fsec, 7);
00548 tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
00549 break;
00550
00551 case DTK_MONTH:
00552 tm->tm_mon += val;
00553 AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
00554 tmask = DTK_M(MONTH);
00555 break;
00556
00557 case DTK_YEAR:
00558 tm->tm_year += val;
00559 if (fval != 0)
00560 tm->tm_mon += fval * MONTHS_PER_YEAR;
00561 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
00562 break;
00563
00564 case DTK_DECADE:
00565 tm->tm_year += val * 10;
00566 if (fval != 0)
00567 tm->tm_mon += fval * MONTHS_PER_YEAR * 10;
00568 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
00569 break;
00570
00571 case DTK_CENTURY:
00572 tm->tm_year += val * 100;
00573 if (fval != 0)
00574 tm->tm_mon += fval * MONTHS_PER_YEAR * 100;
00575 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
00576 break;
00577
00578 case DTK_MILLENNIUM:
00579 tm->tm_year += val * 1000;
00580 if (fval != 0)
00581 tm->tm_mon += fval * MONTHS_PER_YEAR * 1000;
00582 tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
00583 break;
00584
00585 default:
00586 return DTERR_BAD_FORMAT;
00587 }
00588 break;
00589
00590 case DTK_STRING:
00591 case DTK_SPECIAL:
00592 type = DecodeUnits(i, field[i], &val);
00593 if (type == IGNORE_DTF)
00594 continue;
00595
00596 tmask = 0;
00597 switch (type)
00598 {
00599 case UNITS:
00600 type = val;
00601 break;
00602
00603 case AGO:
00604 is_before = TRUE;
00605 type = val;
00606 break;
00607
00608 case RESERV:
00609 tmask = (DTK_DATE_M | DTK_TIME_M);
00610 *dtype = val;
00611 break;
00612
00613 default:
00614 return DTERR_BAD_FORMAT;
00615 }
00616 break;
00617
00618 default:
00619 return DTERR_BAD_FORMAT;
00620 }
00621
00622 if (tmask & fmask)
00623 return DTERR_BAD_FORMAT;
00624 fmask |= tmask;
00625 }
00626
00627
00628 if (fmask == 0)
00629 return DTERR_BAD_FORMAT;
00630
00631
00632 if (*fsec != 0)
00633 {
00634 int sec;
00635
00636 #ifdef HAVE_INT64_TIMESTAMP
00637 sec = *fsec / USECS_PER_SEC;
00638 *fsec -= sec * USECS_PER_SEC;
00639 #else
00640 TMODULO(*fsec, sec, 1.0);
00641 #endif
00642 tm->tm_sec += sec;
00643 }
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661 if (IntervalStyle == INTSTYLE_SQL_STANDARD && *field[0] == '-')
00662 {
00663
00664 bool more_signs = false;
00665
00666 for (i = 1; i < nf; i++)
00667 {
00668 if (*field[i] == '-' || *field[i] == '+')
00669 {
00670 more_signs = true;
00671 break;
00672 }
00673 }
00674
00675 if (!more_signs)
00676 {
00677
00678
00679
00680
00681 if (*fsec > 0)
00682 *fsec = -(*fsec);
00683 if (tm->tm_sec > 0)
00684 tm->tm_sec = -tm->tm_sec;
00685 if (tm->tm_min > 0)
00686 tm->tm_min = -tm->tm_min;
00687 if (tm->tm_hour > 0)
00688 tm->tm_hour = -tm->tm_hour;
00689 if (tm->tm_mday > 0)
00690 tm->tm_mday = -tm->tm_mday;
00691 if (tm->tm_mon > 0)
00692 tm->tm_mon = -tm->tm_mon;
00693 if (tm->tm_year > 0)
00694 tm->tm_year = -tm->tm_year;
00695 }
00696 }
00697
00698
00699 if (is_before)
00700 {
00701 *fsec = -(*fsec);
00702 tm->tm_sec = -tm->tm_sec;
00703 tm->tm_min = -tm->tm_min;
00704 tm->tm_hour = -tm->tm_hour;
00705 tm->tm_mday = -tm->tm_mday;
00706 tm->tm_mon = -tm->tm_mon;
00707 tm->tm_year = -tm->tm_year;
00708 }
00709
00710 return 0;
00711 }
00712
00713
00714
00715 static char *
00716 AddVerboseIntPart(char *cp, int value, const char *units,
00717 bool *is_zero, bool *is_before)
00718 {
00719 if (value == 0)
00720 return cp;
00721
00722 if (*is_zero)
00723 {
00724 *is_before = (value < 0);
00725 value = abs(value);
00726 }
00727 else if (*is_before)
00728 value = -value;
00729 sprintf(cp, " %d %s%s", value, units, (value == 1) ? "" : "s");
00730 *is_zero = FALSE;
00731 return cp + strlen(cp);
00732 }
00733
00734
00735 static char *
00736 AddPostgresIntPart(char *cp, int value, const char *units,
00737 bool *is_zero, bool *is_before)
00738 {
00739 if (value == 0)
00740 return cp;
00741 sprintf(cp, "%s%s%d %s%s",
00742 (!*is_zero) ? " " : "",
00743 (*is_before && value > 0) ? "+" : "",
00744 value,
00745 units,
00746 (value != 1) ? "s" : "");
00747
00748
00749
00750
00751
00752 *is_before = (value < 0);
00753 *is_zero = FALSE;
00754 return cp + strlen(cp);
00755 }
00756
00757
00758 static char *
00759 AddISO8601IntPart(char *cp, int value, char units)
00760 {
00761 if (value == 0)
00762 return cp;
00763 sprintf(cp, "%d%c", value, units);
00764 return cp + strlen(cp);
00765 }
00766
00767
00768 static void
00769 AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
00770 {
00771 if (fsec == 0)
00772 {
00773 if (fillzeros)
00774 sprintf(cp, "%02d", abs(sec));
00775 else
00776 sprintf(cp, "%d", abs(sec));
00777 }
00778 else
00779 {
00780 #ifdef HAVE_INT64_TIMESTAMP
00781 if (fillzeros)
00782 sprintf(cp, "%02d.%0*d", abs(sec), precision, (int) Abs(fsec));
00783 else
00784 sprintf(cp, "%d.%0*d", abs(sec), precision, (int) Abs(fsec));
00785 #else
00786 if (fillzeros)
00787 sprintf(cp, "%0*.*f", precision + 3, precision, fabs(sec + fsec));
00788 else
00789 sprintf(cp, "%.*f", precision, fabs(sec + fsec));
00790 #endif
00791 TrimTrailingZeros(cp);
00792 }
00793 }
00794
00795
00796
00797
00798
00799
00800
00801 int
00802 EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
00803 {
00804
00805 char *cp = str;
00806 int year = tm->tm_year;
00807 int mon = tm->tm_mon;
00808 int mday = tm->tm_mday;
00809 int hour = tm->tm_hour;
00810 int min = tm->tm_min;
00811 int sec = tm->tm_sec;
00812 bool is_before = FALSE;
00813 bool is_zero = TRUE;
00814
00815
00816
00817
00818
00819
00820
00821 switch (style)
00822 {
00823
00824 case INTSTYLE_SQL_STANDARD:
00825 {
00826 bool has_negative = year < 0 || mon < 0 ||
00827 mday < 0 || hour < 0 ||
00828 min < 0 || sec < 0 || fsec < 0;
00829 bool has_positive = year > 0 || mon > 0 ||
00830 mday > 0 || hour > 0 ||
00831 min > 0 || sec > 0 || fsec > 0;
00832 bool has_year_month = year != 0 || mon != 0;
00833 bool has_day_time = mday != 0 || hour != 0 ||
00834 min != 0 || sec != 0 || fsec != 0;
00835 bool has_day = mday != 0;
00836 bool sql_standard_value = !(has_negative && has_positive) &&
00837 !(has_year_month && has_day_time);
00838
00839
00840
00841
00842
00843 if (has_negative && sql_standard_value)
00844 {
00845 *cp++ = '-';
00846 year = -year;
00847 mon = -mon;
00848 mday = -mday;
00849 hour = -hour;
00850 min = -min;
00851 sec = -sec;
00852 fsec = -fsec;
00853 }
00854
00855 if (!has_negative && !has_positive)
00856 {
00857 sprintf(cp, "0");
00858 }
00859 else if (!sql_standard_value)
00860 {
00861
00862
00863
00864
00865
00866 char year_sign = (year < 0 || mon < 0) ? '-' : '+';
00867 char day_sign = (mday < 0) ? '-' : '+';
00868 char sec_sign = (hour < 0 || min < 0 ||
00869 sec < 0 || fsec < 0) ? '-' : '+';
00870
00871 sprintf(cp, "%c%d-%d %c%d %c%d:%02d:",
00872 year_sign, abs(year), abs(mon),
00873 day_sign, abs(mday),
00874 sec_sign, abs(hour), abs(min));
00875 cp += strlen(cp);
00876 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
00877 }
00878 else if (has_year_month)
00879 {
00880 sprintf(cp, "%d-%d", year, mon);
00881 }
00882 else if (has_day)
00883 {
00884 sprintf(cp, "%d %d:%02d:", mday, hour, min);
00885 cp += strlen(cp);
00886 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
00887 }
00888 else
00889 {
00890 sprintf(cp, "%d:%02d:", hour, min);
00891 cp += strlen(cp);
00892 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
00893 }
00894 }
00895 break;
00896
00897
00898 case INTSTYLE_ISO_8601:
00899
00900 if (year == 0 && mon == 0 && mday == 0 &&
00901 hour == 0 && min == 0 && sec == 0 && fsec == 0)
00902 {
00903 sprintf(cp, "PT0S");
00904 break;
00905 }
00906 *cp++ = 'P';
00907 cp = AddISO8601IntPart(cp, year, 'Y');
00908 cp = AddISO8601IntPart(cp, mon, 'M');
00909 cp = AddISO8601IntPart(cp, mday, 'D');
00910 if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
00911 *cp++ = 'T';
00912 cp = AddISO8601IntPart(cp, hour, 'H');
00913 cp = AddISO8601IntPart(cp, min, 'M');
00914 if (sec != 0 || fsec != 0)
00915 {
00916 if (sec < 0 || fsec < 0)
00917 *cp++ = '-';
00918 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
00919 cp += strlen(cp);
00920 *cp++ = 'S';
00921 *cp = '\0';
00922 }
00923 break;
00924
00925
00926 case INTSTYLE_POSTGRES:
00927 cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
00928 cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
00929 cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
00930 if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
00931 {
00932 bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
00933
00934 sprintf(cp, "%s%s%02d:%02d:",
00935 is_zero ? "" : " ",
00936 (minus ? "-" : (is_before ? "+" : "")),
00937 abs(hour), abs(min));
00938 cp += strlen(cp);
00939 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
00940 }
00941 break;
00942
00943
00944 case INTSTYLE_POSTGRES_VERBOSE:
00945 default:
00946 strcpy(cp, "@");
00947 cp++;
00948 cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
00949 cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
00950 cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
00951 cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
00952 cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
00953 if (sec != 0 || fsec != 0)
00954 {
00955 *cp++ = ' ';
00956 if (sec < 0 || (sec == 0 && fsec < 0))
00957 {
00958 if (is_zero)
00959 is_before = TRUE;
00960 else if (!is_before)
00961 *cp++ = '-';
00962 }
00963 else if (is_before)
00964 *cp++ = '-';
00965 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
00966 cp += strlen(cp);
00967 sprintf(cp, " sec%s",
00968 (abs(sec) != 1 || fsec != 0) ? "s" : "");
00969 is_zero = FALSE;
00970 }
00971
00972 if (is_zero)
00973 strcat(cp, " 0");
00974 if (is_before)
00975 strcat(cp, " ago");
00976 break;
00977 }
00978
00979 return 0;
00980 }
00981
00982
00983
00984
00985
00986 static int
00987 interval2tm(interval span, struct tm * tm, fsec_t *fsec)
00988 {
00989 #ifdef HAVE_INT64_TIMESTAMP
00990 int64 time;
00991 #else
00992 double time;
00993 #endif
00994
00995 if (span.month != 0)
00996 {
00997 tm->tm_year = span.month / MONTHS_PER_YEAR;
00998 tm->tm_mon = span.month % MONTHS_PER_YEAR;
00999
01000 }
01001 else
01002 {
01003 tm->tm_year = 0;
01004 tm->tm_mon = 0;
01005 }
01006
01007 time = span.time;
01008
01009 #ifdef HAVE_INT64_TIMESTAMP
01010 tm->tm_mday = time / USECS_PER_DAY;
01011 time -= tm->tm_mday * USECS_PER_DAY;
01012 tm->tm_hour = time / USECS_PER_HOUR;
01013 time -= tm->tm_hour * USECS_PER_HOUR;
01014 tm->tm_min = time / USECS_PER_MINUTE;
01015 time -= tm->tm_min * USECS_PER_MINUTE;
01016 tm->tm_sec = time / USECS_PER_SEC;
01017 *fsec = time - (tm->tm_sec * USECS_PER_SEC);
01018 #else
01019 recalc:
01020 TMODULO(time, tm->tm_mday, (double) SECS_PER_DAY);
01021 TMODULO(time, tm->tm_hour, (double) SECS_PER_HOUR);
01022 TMODULO(time, tm->tm_min, (double) SECS_PER_MINUTE);
01023 TMODULO(time, tm->tm_sec, 1.0);
01024 time = TSROUND(time);
01025
01026 if (time >= 1.0)
01027 {
01028 time = ceil(span.time);
01029 goto recalc;
01030 }
01031 *fsec = time;
01032 #endif
01033
01034 return 0;
01035 }
01036
01037 static int
01038 tm2interval(struct tm * tm, fsec_t fsec, interval * span)
01039 {
01040 span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
01041 #ifdef HAVE_INT64_TIMESTAMP
01042 span->time = (((((((tm->tm_mday * INT64CONST(24)) +
01043 tm->tm_hour) * INT64CONST(60)) +
01044 tm->tm_min) * INT64CONST(60)) +
01045 tm->tm_sec) * USECS_PER_SEC) + fsec;
01046 #else
01047 span->time = (((((tm->tm_mday * (double) HOURS_PER_DAY) +
01048 tm->tm_hour) * (double) MINS_PER_HOUR) +
01049 tm->tm_min) * (double) SECS_PER_MINUTE) +
01050 tm->tm_sec + fsec;
01051 #endif
01052
01053 return 0;
01054 }
01055
01056 interval *
01057 PGTYPESinterval_new(void)
01058 {
01059 interval *result;
01060
01061 result = (interval *) pgtypes_alloc(sizeof(interval));
01062
01063 return result;
01064 }
01065
01066 void
01067 PGTYPESinterval_free(interval * intvl)
01068 {
01069 free(intvl);
01070 }
01071
01072 interval *
01073 PGTYPESinterval_from_asc(char *str, char **endptr)
01074 {
01075 interval *result = NULL;
01076 fsec_t fsec;
01077 struct tm tt,
01078 *tm = &tt;
01079 int dtype;
01080 int nf;
01081 char *field[MAXDATEFIELDS];
01082 int ftype[MAXDATEFIELDS];
01083 char lowstr[MAXDATELEN + MAXDATEFIELDS];
01084 char *realptr;
01085 char **ptr = (endptr != NULL) ? endptr : &realptr;
01086
01087 tm->tm_year = 0;
01088 tm->tm_mon = 0;
01089 tm->tm_mday = 0;
01090 tm->tm_hour = 0;
01091 tm->tm_min = 0;
01092 tm->tm_sec = 0;
01093 fsec = 0;
01094
01095 if (strlen(str) >= sizeof(lowstr))
01096 {
01097 errno = PGTYPES_INTVL_BAD_INTERVAL;
01098 return NULL;
01099 }
01100
01101 if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
01102 (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0 &&
01103 DecodeISO8601Interval(str, &dtype, tm, &fsec) != 0))
01104 {
01105 errno = PGTYPES_INTVL_BAD_INTERVAL;
01106 return NULL;
01107 }
01108
01109 result = (interval *) pgtypes_alloc(sizeof(interval));
01110 if (!result)
01111 return NULL;
01112
01113 if (dtype != DTK_DELTA)
01114 {
01115 errno = PGTYPES_INTVL_BAD_INTERVAL;
01116 free(result);
01117 return NULL;
01118 }
01119
01120 if (tm2interval(tm, fsec, result) != 0)
01121 {
01122 errno = PGTYPES_INTVL_BAD_INTERVAL;
01123 free(result);
01124 return NULL;
01125 }
01126
01127 errno = 0;
01128 return result;
01129 }
01130
01131 char *
01132 PGTYPESinterval_to_asc(interval * span)
01133 {
01134 struct tm tt,
01135 *tm = &tt;
01136 fsec_t fsec;
01137 char buf[MAXDATELEN + 1];
01138 int IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
01139
01140 if (interval2tm(*span, tm, &fsec) != 0)
01141 {
01142 errno = PGTYPES_INTVL_BAD_INTERVAL;
01143 return NULL;
01144 }
01145
01146 if (EncodeInterval(tm, fsec, IntervalStyle, buf) != 0)
01147 {
01148 errno = PGTYPES_INTVL_BAD_INTERVAL;
01149 return NULL;
01150 }
01151
01152 return pgtypes_strdup(buf);
01153 }
01154
01155 int
01156 PGTYPESinterval_copy(interval * intvlsrc, interval * intvldest)
01157 {
01158 intvldest->time = intvlsrc->time;
01159 intvldest->month = intvlsrc->month;
01160
01161 return 0;
01162 }