Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2cf1642

Browse files
author
Thomas G. Lockhart
committed
Fix INTERVAL output when year/month has different sign as day/hour etc.
Previously, all fields were unsigned, with only a trailing "ago" to indicate negative intervals. Now, ISO format does not use "ago", and and the traditional PostgreSQL format has the first numeric field unsigned with "ago" supporting that field. So "1 month - 2 days ago" is two days less than a month in the past. Fix interval arithmetic across daylight savings time boundaries. Previously, most math across boundaries introduced a one hour offset. Allow some date/time functions to return NULL if called with NULL args. Implement functions for AT TIME ZONE support. Support "SAT" as an Australian time zone if USE_AUSTRALIAN_RULES is defined.
1 parent df9462a commit 2cf1642

File tree

2 files changed

+210
-108
lines changed

2 files changed

+210
-108
lines changed

src/backend/utils/adt/datetime.c

Lines changed: 129 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.54 2000/10/29 13:17:33 petere Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.55 2000/11/06 15:57:00 thomas Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -199,7 +199,11 @@ static datetkn datetktbl[] = {
199199
{"pst", TZ, NEG(48)}, /* Pacific Standard Time */
200200
{"sadt", DTZ, 63}, /* S. Australian Dayl. Time */
201201
{"sast", TZ, 57}, /* South Australian Std Time */
202+
#if USE_AUSTRALIAN_RULES
203+
{"sat", TZ, 57},
204+
#else
202205
{"sat", DOW, 6},
206+
#endif
203207
{"saturday", DOW, 6},
204208
{"sep", MONTH, 9},
205209
{"sept", MONTH, 9},
@@ -218,8 +222,7 @@ static datetkn datetktbl[] = {
218222
{"tue", DOW, 2},
219223
{"tues", DOW, 2},
220224
{"tuesday", DOW, 2},
221-
{"undefined", RESERV, DTK_INVALID}, /* "undefined" pre-v6.1 invalid
222-
* time */
225+
{"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
223226
{"ut", TZ, 0},
224227
{"utc", TZ, 0},
225228
{"wadt", DTZ, 48}, /* West Australian DST */
@@ -235,10 +238,10 @@ static datetkn datetktbl[] = {
235238
{"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */
236239
{YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
237240
{"yst", TZ, NEG(54)}, /* Yukon Standard Time */
241+
{"z", RESERV, DTK_ZULU}, /* 00:00:00 */
238242
{"zp4", TZ, NEG(24)}, /* GMT +4 hours. */
239243
{"zp5", TZ, NEG(30)}, /* GMT +5 hours. */
240244
{"zp6", TZ, NEG(36)}, /* GMT +6 hours. */
241-
{"z", RESERV, DTK_ZULU}, /* 00:00:00 */
242245
{ZULU, RESERV, DTK_ZULU}, /* 00:00:00 */
243246
};
244247

@@ -466,25 +469,6 @@ ParseDateTime(char *timestr, char *lowstr,
466469
*/
467470
if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
468471
{
469-
#if 0
470-
471-
/*
472-
* special case of Posix timezone "GMT-0800" Note that
473-
* other sign (e.g. "GMT+0800" is recognized as two
474-
* separate fields and handled later. XXX There is no room
475-
* for a delimiter between the "GMT" and the "-0800", so
476-
* we are going to just swallow the "GMT". But this leads
477-
* to other troubles with the definition of signs, so we
478-
* have to flip - thomas 2000-02-06
479-
*/
480-
if ((*cp == '-') && isdigit(*(cp + 1))
481-
&& (strncmp(field[nf], "gmt", 3) == 0))
482-
{
483-
*cp = '+';
484-
continue;
485-
}
486-
#endif
487-
488472
ftype[nf] = DTK_DATE;
489473
while (isdigit((int) *cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
490474
*lp++ = tolower(*cp++);
@@ -1667,8 +1651,7 @@ DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, do
16671651
tmask,
16681652
type;
16691653
int i;
1670-
int flen,
1671-
val;
1654+
int val;
16721655
double fval;
16731656
double sec;
16741657

@@ -1695,14 +1678,40 @@ DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, do
16951678
break;
16961679

16971680
case DTK_TZ:
1698-
16991681
/*
17001682
* Timezone is a token with a leading sign character and
1701-
* otherwise the same as a non-signed numeric field
1683+
* otherwise the same as a non-signed time field
17021684
*/
1685+
Assert((*field[i] == '-') || (*field[i] == '+'));
1686+
/* A single signed number ends up here, but will be rejected by DecodeTime().
1687+
* So, work this out to drop through to DTK_NUMBER, which *can* tolerate this.
1688+
*/
1689+
cp = field[i]+1;
1690+
while ((*cp != '\0') && (*cp != ':'))
1691+
cp++;
1692+
if ((*cp == ':')
1693+
&& (DecodeTime((field[i]+1), fmask, &tmask, tm, fsec) == 0)) {
1694+
if (*field[i] == '-') {
1695+
/* flip the sign on all fields */
1696+
tm->tm_hour = -tm->tm_hour;
1697+
tm->tm_min = -tm->tm_min;
1698+
tm->tm_sec = -tm->tm_sec;
1699+
*fsec = -(*fsec);
1700+
}
1701+
1702+
/* Set the next type to be a day, if units are not specified.
1703+
* This handles the case of '1 +02:03' since we are reading right to left.
1704+
*/
1705+
type = DTK_DAY;
1706+
tmask = DTK_M(TZ);
1707+
break;
1708+
}
1709+
/* DROP THROUGH */
1710+
17031711
case DTK_DATE:
17041712
case DTK_NUMBER:
17051713
val = strtol(field[i], &cp, 10);
1714+
17061715
if (*cp == '.')
17071716
{
17081717
fval = strtod(cp, &cp);
@@ -1717,7 +1726,6 @@ DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, do
17171726
else
17181727
return -1;
17191728

1720-
flen = strlen(field[i]);
17211729
tmask = 0; /* DTK_M(type); */
17221730

17231731
switch (type)
@@ -2193,98 +2201,126 @@ EncodeTimeSpan(struct tm * tm, double fsec, int style, char *str)
21932201
int is_nonzero = FALSE;
21942202
char *cp = str;
21952203

2204+
/* The sign of year and month are guaranteed to match,
2205+
* since they are stored internally as "month".
2206+
* But we'll need to check for is_before and is_nonzero
2207+
* when determining the signs of hour/minute/seconds fields.
2208+
*/
21962209
switch (style)
21972210
{
21982211
/* compatible with ISO date formats */
21992212
case USE_ISO_DATES:
2200-
break;
2201-
2202-
default:
2203-
strcpy(cp, "@ ");
2204-
cp += strlen(cp);
2205-
break;
2206-
}
2207-
2208-
if (tm->tm_year != 0)
2209-
{
2210-
is_before |= (tm->tm_year < 0);
2211-
sprintf(cp, "%d year%s",
2212-
abs(tm->tm_year), ((abs(tm->tm_year) != 1) ? "s" : ""));
2213-
cp += strlen(cp);
2214-
is_nonzero = TRUE;
2215-
}
2216-
2217-
if (tm->tm_mon != 0)
2218-
{
2219-
is_before |= (tm->tm_mon < 0);
2220-
sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""),
2221-
abs(tm->tm_mon), ((abs(tm->tm_mon) != 1) ? "s" : ""));
2222-
cp += strlen(cp);
2223-
is_nonzero = TRUE;
2224-
}
2225-
2226-
switch (style)
2227-
{
2228-
/* compatible with ISO date formats */
2229-
case USE_ISO_DATES:
2230-
if (tm->tm_mday != 0)
2213+
if (tm->tm_year != 0)
22312214
{
2232-
is_before |= (tm->tm_mday < 0);
2233-
sprintf(cp, "%s%d", (is_nonzero ? " " : ""), abs(tm->tm_mday));
2215+
sprintf(cp, "%d year%s",
2216+
tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
22342217
cp += strlen(cp);
22352218
is_nonzero = TRUE;
22362219
}
2237-
is_before |= ((tm->tm_hour < 0) || (tm->tm_min < 0));
2238-
sprintf(cp, "%s%02d:%02d", (is_nonzero ? " " : ""),
2239-
abs(tm->tm_hour), abs(tm->tm_min));
2240-
cp += strlen(cp);
2241-
/* Mark as "non-zero" since the fields are now filled in */
2242-
is_nonzero = TRUE;
22432220

2244-
/* fractional seconds? */
2245-
if (fsec != 0)
2221+
if (tm->tm_mon != 0)
22462222
{
2247-
fsec += tm->tm_sec;
2248-
is_before |= (fsec < 0);
2249-
sprintf(cp, ":%05.2f", fabs(fsec));
2223+
sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""),
2224+
tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
22502225
cp += strlen(cp);
22512226
is_nonzero = TRUE;
2227+
}
22522228

2253-
/* otherwise, integer seconds only? */
2229+
if (tm->tm_mday != 0)
2230+
{
2231+
sprintf(cp, "%s%d", (is_nonzero ? " " : ""), tm->tm_mday);
2232+
cp += strlen(cp);
2233+
is_nonzero = TRUE;
22542234
}
2255-
else if (tm->tm_sec != 0)
22562235
{
2257-
is_before |= (tm->tm_sec < 0);
2258-
sprintf(cp, ":%02d", abs(tm->tm_sec));
2236+
int minus = ((tm->tm_hour < 0) || (tm->tm_min < 0)
2237+
|| (tm->tm_sec < 0) || (fsec < 0));
2238+
2239+
sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""),
2240+
(minus ? "-" : "+"),
2241+
abs(tm->tm_hour), abs(tm->tm_min));
22592242
cp += strlen(cp);
2243+
/* Mark as "non-zero" since the fields are now filled in */
22602244
is_nonzero = TRUE;
2245+
2246+
/* fractional seconds? */
2247+
if (fsec != 0)
2248+
{
2249+
fsec += tm->tm_sec;
2250+
sprintf(cp, ":%05.2f", fabs(fsec));
2251+
cp += strlen(cp);
2252+
is_nonzero = TRUE;
2253+
2254+
/* otherwise, integer seconds only? */
2255+
}
2256+
else if (tm->tm_sec != 0)
2257+
{
2258+
sprintf(cp, ":%02d", abs(tm->tm_sec));
2259+
cp += strlen(cp);
2260+
is_nonzero = TRUE;
2261+
}
22612262
}
22622263
break;
22632264

22642265
case USE_POSTGRES_DATES:
22652266
default:
2267+
strcpy(cp, "@ ");
2268+
cp += strlen(cp);
2269+
2270+
if (tm->tm_year != 0)
2271+
{
2272+
is_before = (tm->tm_year < 0);
2273+
if (is_before)
2274+
tm->tm_year = -tm->tm_year;
2275+
sprintf(cp, "%d year%s",
2276+
tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
2277+
cp += strlen(cp);
2278+
is_nonzero = TRUE;
2279+
}
2280+
2281+
if (tm->tm_mon != 0)
2282+
{
2283+
if (! is_nonzero)
2284+
is_before = (tm->tm_mon < 0);
2285+
if (is_before)
2286+
tm->tm_mon = -tm->tm_mon;
2287+
sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""),
2288+
tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
2289+
cp += strlen(cp);
2290+
is_nonzero = TRUE;
2291+
}
2292+
22662293
if (tm->tm_mday != 0)
22672294
{
2268-
is_before |= (tm->tm_mday < 0);
2295+
if (! is_nonzero)
2296+
is_before = (tm->tm_mday < 0);
2297+
if (is_before)
2298+
tm->tm_mday = -tm->tm_mday;
22692299
sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""),
2270-
abs(tm->tm_mday), ((abs(tm->tm_mday) != 1) ? "s" : ""));
2300+
tm->tm_mday, ((tm->tm_mday != 1) ? "s" : ""));
22712301
cp += strlen(cp);
22722302
is_nonzero = TRUE;
22732303
}
22742304
if (tm->tm_hour != 0)
22752305
{
2276-
is_before |= (tm->tm_hour < 0);
2306+
if (! is_nonzero)
2307+
is_before = (tm->tm_hour < 0);
2308+
if (is_before)
2309+
tm->tm_hour = -tm->tm_hour;
22772310
sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""),
2278-
abs(tm->tm_hour), ((abs(tm->tm_hour) != 1) ? "s" : ""));
2311+
tm->tm_hour, ((tm->tm_hour != 1) ? "s" : ""));
22792312
cp += strlen(cp);
22802313
is_nonzero = TRUE;
22812314
}
22822315

22832316
if (tm->tm_min != 0)
22842317
{
2285-
is_before |= (tm->tm_min < 0);
2318+
if (! is_nonzero)
2319+
is_before = (tm->tm_min < 0);
2320+
if (is_before)
2321+
tm->tm_min = -tm->tm_min;
22862322
sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""),
2287-
abs(tm->tm_min), ((abs(tm->tm_min) != 1) ? "s" : ""));
2323+
tm->tm_min, ((tm->tm_min != 1) ? "s" : ""));
22882324
cp += strlen(cp);
22892325
is_nonzero = TRUE;
22902326
}
@@ -2293,26 +2329,32 @@ EncodeTimeSpan(struct tm * tm, double fsec, int style, char *str)
22932329
if (fsec != 0)
22942330
{
22952331
fsec += tm->tm_sec;
2296-
is_before |= (fsec < 0);
2297-
sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), fabs(fsec));
2332+
if (! is_nonzero)
2333+
is_before = (fsec < 0);
2334+
if (is_before)
2335+
fsec = -fsec;
2336+
sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), fsec);
22982337
cp += strlen(cp);
22992338
is_nonzero = TRUE;
23002339

23012340
/* otherwise, integer seconds only? */
23022341
}
23032342
else if (tm->tm_sec != 0)
23042343
{
2305-
is_before |= (tm->tm_sec < 0);
2344+
if (! is_nonzero)
2345+
is_before = (tm->tm_sec < 0);
2346+
if (is_before)
2347+
tm->tm_sec = -tm->tm_sec;
23062348
sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""),
2307-
abs(tm->tm_sec), ((abs(tm->tm_sec) != 1) ? "s" : ""));
2349+
tm->tm_sec, ((tm->tm_sec != 1) ? "s" : ""));
23082350
cp += strlen(cp);
23092351
is_nonzero = TRUE;
23102352
}
23112353
break;
23122354
}
23132355

23142356
/* identically zero? then put in a unitless zero... */
2315-
if (!is_nonzero)
2357+
if (! is_nonzero)
23162358
{
23172359
strcat(cp, "0");
23182360
cp += strlen(cp);

0 commit comments

Comments
 (0)