|
28 | 28 | #endif |
29 | 29 | #endif |
30 | 30 |
|
| 31 | +#define MAX_TZDISP_HOUR 15 /* maximum allowed hour part */ |
| 32 | +#define SECS_PER_MINUTE 60 |
| 33 | +#define MINS_PER_HOUR 60 |
| 34 | + |
31 | 35 | const char *PROGRAM_NAME = NULL; |
32 | 36 |
|
33 | 37 | const char *pgut_dbname = NULL; |
@@ -496,19 +500,91 @@ parse_uint64(const char *value, uint64 *result) |
496 | 500 | * Convert ISO-8601 format string to time_t value. |
497 | 501 | */ |
498 | 502 | bool |
499 | | -parse_time(const char *value, time_t *time) |
| 503 | +parse_time(const char *value, time_t *result) |
500 | 504 | { |
501 | 505 | size_t len; |
| 506 | + int fields_num, |
| 507 | + tz = 0, |
| 508 | + i; |
502 | 509 | char *tmp; |
503 | | - int i; |
504 | 510 | struct tm tm; |
505 | 511 | char junk[2]; |
506 | 512 |
|
507 | 513 | /* tmp = replace( value, !isalnum, ' ' ) */ |
508 | 514 | tmp = pgut_malloc(strlen(value) + + 1); |
509 | 515 | len = 0; |
510 | | - for (i = 0; value[i]; i++) |
511 | | - tmp[len++] = (IsAlnum(value[i]) ? value[i] : ' '); |
| 516 | + fields_num = 1; |
| 517 | + |
| 518 | + while (*value) |
| 519 | + { |
| 520 | + if (IsAlnum(*value)) |
| 521 | + { |
| 522 | + tmp[len++] = *value; |
| 523 | + value++; |
| 524 | + } |
| 525 | + else if (fields_num < 6) |
| 526 | + { |
| 527 | + fields_num++; |
| 528 | + tmp[len++] = ' '; |
| 529 | + value++; |
| 530 | + } |
| 531 | + /* timezone field is 7th */ |
| 532 | + else if ((*value == '-' || *value == '+') && fields_num == 6) |
| 533 | + { |
| 534 | + int hr, |
| 535 | + min, |
| 536 | + sec = 0; |
| 537 | + char *cp; |
| 538 | + |
| 539 | + errno = 0; |
| 540 | + hr = strtol(value + 1, &cp, 10); |
| 541 | + if ((value + 1) == cp || errno == ERANGE) |
| 542 | + return false; |
| 543 | + |
| 544 | + /* explicit delimiter? */ |
| 545 | + if (*cp == ':') |
| 546 | + { |
| 547 | + errno = 0; |
| 548 | + min = strtol(cp + 1, &cp, 10); |
| 549 | + if (errno == ERANGE) |
| 550 | + return false; |
| 551 | + if (*cp == ':') |
| 552 | + { |
| 553 | + errno = 0; |
| 554 | + sec = strtol(cp + 1, &cp, 10); |
| 555 | + if (errno == ERANGE) |
| 556 | + return false; |
| 557 | + } |
| 558 | + } |
| 559 | + /* otherwise, might have run things together... */ |
| 560 | + else if (*cp == '\0' && strlen(value) > 3) |
| 561 | + { |
| 562 | + min = hr % 100; |
| 563 | + hr = hr / 100; |
| 564 | + /* we could, but don't, support a run-together hhmmss format */ |
| 565 | + } |
| 566 | + else |
| 567 | + min = 0; |
| 568 | + |
| 569 | + /* Range-check the values; see notes in datatype/timestamp.h */ |
| 570 | + if (hr < 0 || hr > MAX_TZDISP_HOUR) |
| 571 | + return false; |
| 572 | + if (min < 0 || min >= MINS_PER_HOUR) |
| 573 | + return false; |
| 574 | + if (sec < 0 || sec >= SECS_PER_MINUTE) |
| 575 | + return false; |
| 576 | + |
| 577 | + tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec; |
| 578 | + if (*value == '-') |
| 579 | + tz = -tz; |
| 580 | + |
| 581 | + fields_num++; |
| 582 | + value = cp; |
| 583 | + } |
| 584 | + /* wrong format */ |
| 585 | + else if (!IsSpace(*value)) |
| 586 | + return false; |
| 587 | + } |
512 | 588 | tmp[len] = '\0'; |
513 | 589 |
|
514 | 590 | /* parse for "YYYY-MM-DD HH:MI:SS" */ |
@@ -540,7 +616,25 @@ parse_time(const char *value, time_t *time) |
540 | 616 | /* determine whether Daylight Saving Time is in effect */ |
541 | 617 | tm.tm_isdst = -1; |
542 | 618 |
|
543 | | - *time = mktime(&tm); |
| 619 | + *result = mktime(&tm); |
| 620 | + |
| 621 | + /* adjust time zone */ |
| 622 | + if (tz != 0) |
| 623 | + { |
| 624 | + time_t ltime = time(NULL); |
| 625 | + struct tm *ptm = gmtime(<ime); |
| 626 | + time_t gmt = mktime(ptm); |
| 627 | + time_t offset; |
| 628 | + |
| 629 | + /* UTC time */ |
| 630 | + *result -= tz; |
| 631 | + |
| 632 | + /* Get local time */ |
| 633 | + ptm = localtime(<ime); |
| 634 | + offset = ltime - gmt + (ptm->tm_isdst ? 3600 : 0); |
| 635 | + |
| 636 | + *result += offset; |
| 637 | + } |
544 | 638 |
|
545 | 639 | return true; |
546 | 640 | } |
|
0 commit comments