Coverage Report

Created: 2025-08-12 06:43

/src/postgres/src/backend/utils/adt/datetime.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * datetime.c
4
 *    Support functions for date/time types.
5
 *
6
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/utils/adt/datetime.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include <ctype.h>
18
#include <limits.h>
19
#include <math.h>
20
21
#include "access/htup_details.h"
22
#include "access/xact.h"
23
#include "common/int.h"
24
#include "common/string.h"
25
#include "funcapi.h"
26
#include "miscadmin.h"
27
#include "nodes/nodeFuncs.h"
28
#include "parser/scansup.h"
29
#include "utils/builtins.h"
30
#include "utils/date.h"
31
#include "utils/datetime.h"
32
#include "utils/guc.h"
33
#include "utils/tzparser.h"
34
35
static int  DecodeNumber(int flen, char *str, bool haveTextMonth,
36
             int fmask, int *tmask,
37
             struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
38
static int  DecodeNumberField(int len, char *str,
39
                int fmask, int *tmask,
40
                struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
41
static int  DecodeTimeCommon(char *str, int fmask, int range,
42
               int *tmask, struct pg_itm *itm);
43
static int  DecodeTime(char *str, int fmask, int range,
44
             int *tmask, struct pg_tm *tm, fsec_t *fsec);
45
static int  DecodeTimeForInterval(char *str, int fmask, int range,
46
                  int *tmask, struct pg_itm_in *itm_in);
47
static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
48
static int  DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
49
             struct pg_tm *tm);
50
static char *AppendSeconds(char *cp, int sec, fsec_t fsec,
51
               int precision, bool fillzeros);
52
static bool int64_multiply_add(int64 val, int64 multiplier, int64 *sum);
53
static bool AdjustFractMicroseconds(double frac, int64 scale,
54
                  struct pg_itm_in *itm_in);
55
static bool AdjustFractDays(double frac, int scale,
56
              struct pg_itm_in *itm_in);
57
static bool AdjustFractYears(double frac, int scale,
58
               struct pg_itm_in *itm_in);
59
static bool AdjustMicroseconds(int64 val, double fval, int64 scale,
60
                 struct pg_itm_in *itm_in);
61
static bool AdjustDays(int64 val, int scale,
62
             struct pg_itm_in *itm_in);
63
static bool AdjustMonths(int64 val, struct pg_itm_in *itm_in);
64
static bool AdjustYears(int64 val, int scale,
65
            struct pg_itm_in *itm_in);
66
static int  DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp,
67
                      pg_time_t *tp);
68
static bool DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t,
69
                          const char *abbr, pg_tz *tzp,
70
                          int *offset, int *isdst);
71
static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
72
                   DateTimeErrorExtra *extra);
73
74
75
const int day_tab[2][13] =
76
{
77
  {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
78
  {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}
79
};
80
81
const char *const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
82
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
83
84
const char *const days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
85
"Thursday", "Friday", "Saturday", NULL};
86
87
88
/*****************************************************************************
89
 *   PRIVATE ROUTINES                            *
90
 *****************************************************************************/
91
92
/*
93
 * datetktbl holds date/time keywords.
94
 *
95
 * Note that this table must be strictly alphabetically ordered to allow an
96
 * O(ln(N)) search algorithm to be used.
97
 *
98
 * The token field must be NUL-terminated; we truncate entries to TOKMAXLEN
99
 * characters to fit.
100
 *
101
 * The static table contains no TZ, DTZ, or DYNTZ entries; rather those
102
 * are loaded from configuration files and stored in zoneabbrevtbl, whose
103
 * abbrevs[] field has the same format as the static datetktbl.
104
 */
105
static const datetkn datetktbl[] = {
106
  /* token, type, value */
107
  {"+infinity", RESERV, DTK_LATE},  /* same as "infinity" */
108
  {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
109
  {DA_D, ADBC, AD},     /* "ad" for years > 0 */
110
  {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
111
  {"am", AMPM, AM},
112
  {"apr", MONTH, 4},
113
  {"april", MONTH, 4},
114
  {"at", IGNORE_DTF, 0},    /* "at" (throwaway) */
115
  {"aug", MONTH, 8},
116
  {"august", MONTH, 8},
117
  {DB_C, ADBC, BC},     /* "bc" for years <= 0 */
118
  {"d", UNITS, DTK_DAY},    /* "day of month" for ISO input */
119
  {"dec", MONTH, 12},
120
  {"december", MONTH, 12},
121
  {"dow", UNITS, DTK_DOW},  /* day of week */
122
  {"doy", UNITS, DTK_DOY},  /* day of year */
123
  {"dst", DTZMOD, SECS_PER_HOUR},
124
  {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
125
  {"feb", MONTH, 2},
126
  {"february", MONTH, 2},
127
  {"fri", DOW, 5},
128
  {"friday", DOW, 5},
129
  {"h", UNITS, DTK_HOUR},   /* "hour" */
130
  {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
131
  {"isodow", UNITS, DTK_ISODOW},  /* ISO day of week, Sunday == 7 */
132
  {"isoyear", UNITS, DTK_ISOYEAR},  /* year in terms of the ISO week date */
133
  {"j", UNITS, DTK_JULIAN},
134
  {"jan", MONTH, 1},
135
  {"january", MONTH, 1},
136
  {"jd", UNITS, DTK_JULIAN},
137
  {"jul", MONTH, 7},
138
  {"julian", UNITS, DTK_JULIAN},
139
  {"july", MONTH, 7},
140
  {"jun", MONTH, 6},
141
  {"june", MONTH, 6},
142
  {"m", UNITS, DTK_MONTH},  /* "month" for ISO input */
143
  {"mar", MONTH, 3},
144
  {"march", MONTH, 3},
145
  {"may", MONTH, 5},
146
  {"mm", UNITS, DTK_MINUTE},  /* "minute" for ISO input */
147
  {"mon", DOW, 1},
148
  {"monday", DOW, 1},
149
  {"nov", MONTH, 11},
150
  {"november", MONTH, 11},
151
  {NOW, RESERV, DTK_NOW},   /* current transaction time */
152
  {"oct", MONTH, 10},
153
  {"october", MONTH, 10},
154
  {"on", IGNORE_DTF, 0},    /* "on" (throwaway) */
155
  {"pm", AMPM, PM},
156
  {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
157
  {"sat", DOW, 6},
158
  {"saturday", DOW, 6},
159
  {"sep", MONTH, 9},
160
  {"sept", MONTH, 9},
161
  {"september", MONTH, 9},
162
  {"sun", DOW, 0},
163
  {"sunday", DOW, 0},
164
  {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
165
  {"thu", DOW, 4},
166
  {"thur", DOW, 4},
167
  {"thurs", DOW, 4},
168
  {"thursday", DOW, 4},
169
  {TODAY, RESERV, DTK_TODAY}, /* midnight */
170
  {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
171
  {"tue", DOW, 2},
172
  {"tues", DOW, 2},
173
  {"tuesday", DOW, 2},
174
  {"wed", DOW, 3},
175
  {"wednesday", DOW, 3},
176
  {"weds", DOW, 3},
177
  {"y", UNITS, DTK_YEAR},   /* "year" for ISO input */
178
  {YESTERDAY, RESERV, DTK_YESTERDAY}  /* yesterday midnight */
179
};
180
181
static const int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
182
183
/*
184
 * deltatktbl: same format as datetktbl, but holds keywords used to represent
185
 * time units (eg, for intervals, and for EXTRACT).
186
 */
187
static const datetkn deltatktbl[] = {
188
  /* token, type, value */
189
  {"@", IGNORE_DTF, 0},   /* postgres relative prefix */
190
  {DAGO, AGO, 0},       /* "ago" indicates negative time offset */
191
  {"c", UNITS, DTK_CENTURY},  /* "century" relative */
192
  {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
193
  {"centuries", UNITS, DTK_CENTURY},  /* "centuries" relative */
194
  {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
195
  {"d", UNITS, DTK_DAY},    /* "day" relative */
196
  {DDAY, UNITS, DTK_DAY},   /* "day" relative */
197
  {"days", UNITS, DTK_DAY}, /* "days" relative */
198
  {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
199
  {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
200
  {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
201
  {"decs", UNITS, DTK_DECADE},  /* "decades" relative */
202
  {"h", UNITS, DTK_HOUR},   /* "hour" relative */
203
  {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
204
  {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
205
  {"hr", UNITS, DTK_HOUR},  /* "hour" relative */
206
  {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
207
  {"m", UNITS, DTK_MINUTE}, /* "minute" relative */
208
  {"microsecon", UNITS, DTK_MICROSEC},  /* "microsecond" relative */
209
  {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
210
  {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
211
  {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
212
  {"millisecon", UNITS, DTK_MILLISEC},  /* relative */
213
  {"mils", UNITS, DTK_MILLENNIUM},  /* "millennia" relative */
214
  {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
215
  {"mins", UNITS, DTK_MINUTE},  /* "minutes" relative */
216
  {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
217
  {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
218
  {"mon", UNITS, DTK_MONTH},  /* "months" relative */
219
  {"mons", UNITS, DTK_MONTH}, /* "months" relative */
220
  {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
221
  {"months", UNITS, DTK_MONTH},
222
  {"ms", UNITS, DTK_MILLISEC},
223
  {"msec", UNITS, DTK_MILLISEC},
224
  {DMILLISEC, UNITS, DTK_MILLISEC},
225
  {"mseconds", UNITS, DTK_MILLISEC},
226
  {"msecs", UNITS, DTK_MILLISEC},
227
  {"qtr", UNITS, DTK_QUARTER},  /* "quarter" relative */
228
  {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
229
  {"s", UNITS, DTK_SECOND},
230
  {"sec", UNITS, DTK_SECOND},
231
  {DSECOND, UNITS, DTK_SECOND},
232
  {"seconds", UNITS, DTK_SECOND},
233
  {"secs", UNITS, DTK_SECOND},
234
  {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
235
  {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
236
  {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
237
  {"us", UNITS, DTK_MICROSEC},  /* "microsecond" relative */
238
  {"usec", UNITS, DTK_MICROSEC},  /* "microsecond" relative */
239
  {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
240
  {"useconds", UNITS, DTK_MICROSEC},  /* "microseconds" relative */
241
  {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
242
  {"w", UNITS, DTK_WEEK},   /* "week" relative */
243
  {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
244
  {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
245
  {"y", UNITS, DTK_YEAR},   /* "year" relative */
246
  {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */
247
  {"years", UNITS, DTK_YEAR}, /* "years" relative */
248
  {"yr", UNITS, DTK_YEAR},  /* "year" relative */
249
  {"yrs", UNITS, DTK_YEAR}  /* "years" relative */
250
};
251
252
static const int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
253
254
static TimeZoneAbbrevTable *zoneabbrevtbl = NULL;
255
256
/* Caches of recent lookup results in the above tables */
257
258
static const datetkn *datecache[MAXDATEFIELDS] = {NULL};
259
260
static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};
261
262
/* Cache for results of timezone abbreviation lookups */
263
264
typedef struct TzAbbrevCache
265
{
266
  char    abbrev[TOKMAXLEN + 1];  /* always NUL-terminated */
267
  char    ftype;      /* TZ, DTZ, or DYNTZ */
268
  int     offset;     /* GMT offset, if fixed-offset */
269
  pg_tz    *tz;       /* relevant zone, if variable-offset */
270
} TzAbbrevCache;
271
272
static TzAbbrevCache tzabbrevcache[MAXDATEFIELDS];
273
274
275
/*
276
 * Calendar time to Julian date conversions.
277
 * Julian date is commonly used in astronomical applications,
278
 *  since it is numerically accurate and computationally simple.
279
 * The algorithms here will accurately convert between Julian day
280
 *  and calendar date for all non-negative Julian days
281
 *  (i.e. from Nov 24, -4713 on).
282
 *
283
 * Rewritten to eliminate overflow problems. This now allows the
284
 * routines to work correctly for all Julian day counts from
285
 * 0 to 2147483647  (Nov 24, -4713 to Jun 3, 5874898) assuming
286
 * a 32-bit integer. Longer types should also work to the limits
287
 * of their precision.
288
 *
289
 * Actually, date2j() will work sanely, in the sense of producing
290
 * valid negative Julian dates, significantly before Nov 24, -4713.
291
 * We rely on it to do so back to Nov 1, -4713; see IS_VALID_JULIAN()
292
 * and associated commentary in timestamp.h.
293
 */
294
295
int
296
date2j(int year, int month, int day)
297
0
{
298
0
  int     julian;
299
0
  int     century;
300
301
0
  if (month > 2)
302
0
  {
303
0
    month += 1;
304
0
    year += 4800;
305
0
  }
306
0
  else
307
0
  {
308
0
    month += 13;
309
0
    year += 4799;
310
0
  }
311
312
0
  century = year / 100;
313
0
  julian = year * 365 - 32167;
314
0
  julian += year / 4 - century + century / 4;
315
0
  julian += 7834 * month / 256 + day;
316
317
0
  return julian;
318
0
}                /* date2j() */
319
320
void
321
j2date(int jd, int *year, int *month, int *day)
322
0
{
323
0
  unsigned int julian;
324
0
  unsigned int quad;
325
0
  unsigned int extra;
326
0
  int     y;
327
328
0
  julian = jd;
329
0
  julian += 32044;
330
0
  quad = julian / 146097;
331
0
  extra = (julian - quad * 146097) * 4 + 3;
332
0
  julian += 60 + quad * 3 + extra / 146097;
333
0
  quad = julian / 1461;
334
0
  julian -= quad * 1461;
335
0
  y = julian * 4 / 1461;
336
0
  julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
337
0
    + 123;
338
0
  y += quad * 4;
339
0
  *year = y - 4800;
340
0
  quad = julian * 2141 / 65536;
341
0
  *day = julian - 7834 * quad / 256;
342
0
  *month = (quad + 10) % MONTHS_PER_YEAR + 1;
343
0
}                /* j2date() */
344
345
346
/*
347
 * j2day - convert Julian date to day-of-week (0..6 == Sun..Sat)
348
 *
349
 * Note: various places use the locution j2day(date - 1) to produce a
350
 * result according to the convention 0..6 = Mon..Sun.  This is a bit of
351
 * a crock, but will work as long as the computation here is just a modulo.
352
 */
353
int
354
j2day(int date)
355
0
{
356
0
  date += 1;
357
0
  date %= 7;
358
  /* Cope if division truncates towards zero, as it probably does */
359
0
  if (date < 0)
360
0
    date += 7;
361
362
0
  return date;
363
0
}                /* j2day() */
364
365
366
/*
367
 * GetCurrentDateTime()
368
 *
369
 * Get the transaction start time ("now()") broken down as a struct pg_tm,
370
 * converted according to the session timezone setting.
371
 *
372
 * This is just a convenience wrapper for GetCurrentTimeUsec, to cover the
373
 * case where caller doesn't need either fractional seconds or tz offset.
374
 */
375
void
376
GetCurrentDateTime(struct pg_tm *tm)
377
0
{
378
0
  fsec_t    fsec;
379
380
0
  GetCurrentTimeUsec(tm, &fsec, NULL);
381
0
}
382
383
/*
384
 * GetCurrentTimeUsec()
385
 *
386
 * Get the transaction start time ("now()") broken down as a struct pg_tm,
387
 * including fractional seconds and timezone offset.  The time is converted
388
 * according to the session timezone setting.
389
 *
390
 * Callers may pass tzp = NULL if they don't need the offset, but this does
391
 * not affect the conversion behavior (unlike timestamp2tm()).
392
 *
393
 * Internally, we cache the result, since this could be called many times
394
 * in a transaction, within which now() doesn't change.
395
 */
396
void
397
GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
398
0
{
399
0
  TimestampTz cur_ts = GetCurrentTransactionStartTimestamp();
400
401
  /*
402
   * The cache key must include both current time and current timezone.  By
403
   * representing the timezone by just a pointer, we're assuming that
404
   * distinct timezone settings could never have the same pointer value.
405
   * This is true by virtue of the hashtable used inside pg_tzset();
406
   * however, it might need another look if we ever allow entries in that
407
   * hash to be recycled.
408
   */
409
0
  static TimestampTz cache_ts = 0;
410
0
  static pg_tz *cache_timezone = NULL;
411
0
  static struct pg_tm cache_tm;
412
0
  static fsec_t cache_fsec;
413
0
  static int  cache_tz;
414
415
0
  if (cur_ts != cache_ts || session_timezone != cache_timezone)
416
0
  {
417
    /*
418
     * Make sure cache is marked invalid in case of error after partial
419
     * update within timestamp2tm.
420
     */
421
0
    cache_timezone = NULL;
422
423
    /*
424
     * Perform the computation, storing results into cache.  We do not
425
     * really expect any error here, since current time surely ought to be
426
     * within range, but check just for sanity's sake.
427
     */
428
0
    if (timestamp2tm(cur_ts, &cache_tz, &cache_tm, &cache_fsec,
429
0
             NULL, session_timezone) != 0)
430
0
      ereport(ERROR,
431
0
          (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
432
0
           errmsg("timestamp out of range")));
433
434
    /* OK, so mark the cache valid. */
435
0
    cache_ts = cur_ts;
436
0
    cache_timezone = session_timezone;
437
0
  }
438
439
0
  *tm = cache_tm;
440
0
  *fsec = cache_fsec;
441
0
  if (tzp != NULL)
442
0
    *tzp = cache_tz;
443
0
}
444
445
446
/*
447
 * Append seconds and fractional seconds (if any) at *cp.
448
 *
449
 * precision is the max number of fraction digits, fillzeros says to
450
 * pad to two integral-seconds digits.
451
 *
452
 * Returns a pointer to the new end of string.  No NUL terminator is put
453
 * there; callers are responsible for NUL terminating str themselves.
454
 *
455
 * Note that any sign is stripped from the input sec and fsec values.
456
 */
457
static char *
458
AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
459
0
{
460
0
  Assert(precision >= 0);
461
462
0
  if (fillzeros)
463
0
    cp = pg_ultostr_zeropad(cp, abs(sec), 2);
464
0
  else
465
0
    cp = pg_ultostr(cp, abs(sec));
466
467
  /* fsec_t is just an int32 */
468
0
  if (fsec != 0)
469
0
  {
470
0
    int32   value = abs(fsec);
471
0
    char     *end = &cp[precision + 1];
472
0
    bool    gotnonzero = false;
473
474
0
    *cp++ = '.';
475
476
    /*
477
     * Append the fractional seconds part.  Note that we don't want any
478
     * trailing zeros here, so since we're building the number in reverse
479
     * we'll skip appending zeros until we've output a non-zero digit.
480
     */
481
0
    while (precision--)
482
0
    {
483
0
      int32   oldval = value;
484
0
      int32   remainder;
485
486
0
      value /= 10;
487
0
      remainder = oldval - value * 10;
488
489
      /* check if we got a non-zero */
490
0
      if (remainder)
491
0
        gotnonzero = true;
492
493
0
      if (gotnonzero)
494
0
        cp[precision] = '0' + remainder;
495
0
      else
496
0
        end = &cp[precision];
497
0
    }
498
499
    /*
500
     * If we still have a non-zero value then precision must have not been
501
     * enough to print the number.  We punt the problem to pg_ultostr(),
502
     * which will generate a correct answer in the minimum valid width.
503
     */
504
0
    if (value)
505
0
      return pg_ultostr(cp, abs(fsec));
506
507
0
    return end;
508
0
  }
509
0
  else
510
0
    return cp;
511
0
}
512
513
514
/*
515
 * Variant of above that's specialized to timestamp case.
516
 *
517
 * Returns a pointer to the new end of string.  No NUL terminator is put
518
 * there; callers are responsible for NUL terminating str themselves.
519
 */
520
static char *
521
AppendTimestampSeconds(char *cp, struct pg_tm *tm, fsec_t fsec)
522
0
{
523
0
  return AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
524
0
}
525
526
527
/*
528
 * Add val * multiplier to *sum.
529
 * Returns true if successful, false on overflow.
530
 */
531
static bool
532
int64_multiply_add(int64 val, int64 multiplier, int64 *sum)
533
0
{
534
0
  int64   product;
535
536
0
  if (pg_mul_s64_overflow(val, multiplier, &product) ||
537
0
    pg_add_s64_overflow(*sum, product, sum))
538
0
    return false;
539
0
  return true;
540
0
}
541
542
/*
543
 * Multiply frac by scale (to produce microseconds) and add to itm_in->tm_usec.
544
 * Returns true if successful, false if itm_in overflows.
545
 */
546
static bool
547
AdjustFractMicroseconds(double frac, int64 scale,
548
            struct pg_itm_in *itm_in)
549
0
{
550
0
  int64   usec;
551
552
  /* Fast path for common case */
553
0
  if (frac == 0)
554
0
    return true;
555
556
  /*
557
   * We assume the input frac has abs value less than 1, so overflow of frac
558
   * or usec is not an issue for interesting values of scale.
559
   */
560
0
  frac *= scale;
561
0
  usec = (int64) frac;
562
563
  /* Round off any fractional microsecond */
564
0
  frac -= usec;
565
0
  if (frac > 0.5)
566
0
    usec++;
567
0
  else if (frac < -0.5)
568
0
    usec--;
569
570
0
  return !pg_add_s64_overflow(itm_in->tm_usec, usec, &itm_in->tm_usec);
571
0
}
572
573
/*
574
 * Multiply frac by scale (to produce days).  Add the integral part of the
575
 * result to itm_in->tm_mday, the fractional part to itm_in->tm_usec.
576
 * Returns true if successful, false if itm_in overflows.
577
 */
578
static bool
579
AdjustFractDays(double frac, int scale,
580
        struct pg_itm_in *itm_in)
581
0
{
582
0
  int     extra_days;
583
584
  /* Fast path for common case */
585
0
  if (frac == 0)
586
0
    return true;
587
588
  /*
589
   * We assume the input frac has abs value less than 1, so overflow of frac
590
   * or extra_days is not an issue.
591
   */
592
0
  frac *= scale;
593
0
  extra_days = (int) frac;
594
595
  /* ... but this could overflow, if tm_mday is already nonzero */
596
0
  if (pg_add_s32_overflow(itm_in->tm_mday, extra_days, &itm_in->tm_mday))
597
0
    return false;
598
599
  /* Handle any fractional day */
600
0
  frac -= extra_days;
601
0
  return AdjustFractMicroseconds(frac, USECS_PER_DAY, itm_in);
602
0
}
603
604
/*
605
 * Multiply frac by scale (to produce years), then further scale up to months.
606
 * Add the integral part of the result to itm_in->tm_mon, discarding any
607
 * fractional part.
608
 * Returns true if successful, false if itm_in overflows.
609
 */
610
static bool
611
AdjustFractYears(double frac, int scale,
612
         struct pg_itm_in *itm_in)
613
0
{
614
  /*
615
   * As above, we assume abs(frac) < 1, so this can't overflow for any
616
   * interesting value of scale.
617
   */
618
0
  int     extra_months = (int) rint(frac * scale * MONTHS_PER_YEAR);
619
620
0
  return !pg_add_s32_overflow(itm_in->tm_mon, extra_months, &itm_in->tm_mon);
621
0
}
622
623
/*
624
 * Add (val + fval) * scale to itm_in->tm_usec.
625
 * Returns true if successful, false if itm_in overflows.
626
 */
627
static bool
628
AdjustMicroseconds(int64 val, double fval, int64 scale,
629
           struct pg_itm_in *itm_in)
630
0
{
631
  /* Handle the integer part */
632
0
  if (!int64_multiply_add(val, scale, &itm_in->tm_usec))
633
0
    return false;
634
  /* Handle the float part */
635
0
  return AdjustFractMicroseconds(fval, scale, itm_in);
636
0
}
637
638
/*
639
 * Multiply val by scale (to produce days) and add to itm_in->tm_mday.
640
 * Returns true if successful, false if itm_in overflows.
641
 */
642
static bool
643
AdjustDays(int64 val, int scale, struct pg_itm_in *itm_in)
644
0
{
645
0
  int     days;
646
647
0
  if (val < INT_MIN || val > INT_MAX)
648
0
    return false;
649
0
  return !pg_mul_s32_overflow((int32) val, scale, &days) &&
650
0
    !pg_add_s32_overflow(itm_in->tm_mday, days, &itm_in->tm_mday);
651
0
}
652
653
/*
654
 * Add val to itm_in->tm_mon (no need for scale here, as val is always
655
 * in months already).
656
 * Returns true if successful, false if itm_in overflows.
657
 */
658
static bool
659
AdjustMonths(int64 val, struct pg_itm_in *itm_in)
660
0
{
661
0
  if (val < INT_MIN || val > INT_MAX)
662
0
    return false;
663
0
  return !pg_add_s32_overflow(itm_in->tm_mon, (int32) val, &itm_in->tm_mon);
664
0
}
665
666
/*
667
 * Multiply val by scale (to produce years) and add to itm_in->tm_year.
668
 * Returns true if successful, false if itm_in overflows.
669
 */
670
static bool
671
AdjustYears(int64 val, int scale,
672
      struct pg_itm_in *itm_in)
673
0
{
674
0
  int     years;
675
676
0
  if (val < INT_MIN || val > INT_MAX)
677
0
    return false;
678
0
  return !pg_mul_s32_overflow((int32) val, scale, &years) &&
679
0
    !pg_add_s32_overflow(itm_in->tm_year, years, &itm_in->tm_year);
680
0
}
681
682
683
/*
684
 * Parse the fractional part of a number (decimal point and optional digits,
685
 * followed by end of string).  Returns the fractional value into *frac.
686
 *
687
 * Returns 0 if successful, DTERR code if bogus input detected.
688
 */
689
static int
690
ParseFraction(char *cp, double *frac)
691
0
{
692
  /* Caller should always pass the start of the fraction part */
693
0
  Assert(*cp == '.');
694
695
  /*
696
   * We want to allow just "." with no digits, but some versions of strtod
697
   * will report EINVAL for that, so special-case it.
698
   */
699
0
  if (cp[1] == '\0')
700
0
  {
701
0
    *frac = 0;
702
0
  }
703
0
  else
704
0
  {
705
    /*
706
     * On the other hand, let's reject anything that's not digits after
707
     * the ".".  strtod is happy with input like ".123e9", but that'd
708
     * break callers' expectation that the result is in 0..1.  (It's quite
709
     * difficult to get here with such input, but not impossible.)
710
     */
711
0
    if (strspn(cp + 1, "0123456789") != strlen(cp + 1))
712
0
      return DTERR_BAD_FORMAT;
713
714
0
    errno = 0;
715
0
    *frac = strtod(cp, &cp);
716
    /* check for parse failure (probably redundant given prior check) */
717
0
    if (*cp != '\0' || errno != 0)
718
0
      return DTERR_BAD_FORMAT;
719
0
  }
720
0
  return 0;
721
0
}
722
723
/*
724
 * Fetch a fractional-second value with suitable error checking.
725
 * Same as ParseFraction except we convert the result to integer microseconds.
726
 */
727
static int
728
ParseFractionalSecond(char *cp, fsec_t *fsec)
729
0
{
730
0
  double    frac;
731
0
  int     dterr;
732
733
0
  dterr = ParseFraction(cp, &frac);
734
0
  if (dterr)
735
0
    return dterr;
736
0
  *fsec = rint(frac * 1000000);
737
0
  return 0;
738
0
}
739
740
741
/* ParseDateTime()
742
 *  Break string into tokens based on a date/time context.
743
 *  Returns 0 if successful, DTERR code if bogus input detected.
744
 *
745
 * timestr - the input string
746
 * workbuf - workspace for field string storage. This must be
747
 *   larger than the largest legal input for this datetime type --
748
 *   some additional space will be needed to NUL terminate fields.
749
 * buflen - the size of workbuf
750
 * field[] - pointers to field strings are returned in this array
751
 * ftype[] - field type indicators are returned in this array
752
 * maxfields - dimensions of the above two arrays
753
 * *numfields - set to the actual number of fields detected
754
 *
755
 * The fields extracted from the input are stored as separate,
756
 * null-terminated strings in the workspace at workbuf. Any text is
757
 * converted to lower case.
758
 *
759
 * Several field types are assigned:
760
 *  DTK_NUMBER - digits and (possibly) a decimal point
761
 *  DTK_DATE - digits and two delimiters, or digits and text
762
 *  DTK_TIME - digits, colon delimiters, and possibly a decimal point
763
 *  DTK_STRING - text (no digits or punctuation)
764
 *  DTK_SPECIAL - leading "+" or "-" followed by text
765
 *  DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-')
766
 *
767
 * Note that some field types can hold unexpected items:
768
 *  DTK_NUMBER can hold date fields (yy.ddd)
769
 *  DTK_STRING can hold months (January) and time zones (PST)
770
 *  DTK_DATE can hold time zone names (America/New_York, GMT-8)
771
 */
772
int
773
ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
774
        char **field, int *ftype, int maxfields, int *numfields)
775
0
{
776
0
  int     nf = 0;
777
0
  const char *cp = timestr;
778
0
  char     *bufp = workbuf;
779
0
  const char *bufend = workbuf + buflen;
780
781
  /*
782
   * Set the character pointed-to by "bufptr" to "newchar", and increment
783
   * "bufptr". "end" gives the end of the buffer -- we return an error if
784
   * there is no space left to append a character to the buffer. Note that
785
   * "bufptr" is evaluated twice.
786
   */
787
0
#define APPEND_CHAR(bufptr, end, newchar)   \
788
0
  do                      \
789
0
  {                     \
790
0
    if (((bufptr) + 1) >= (end))     \
791
0
      return DTERR_BAD_FORMAT;     \
792
0
    *(bufptr)++ = newchar;          \
793
0
  } while (0)
794
795
  /* outer loop through fields */
796
0
  while (*cp != '\0')
797
0
  {
798
    /* Ignore spaces between fields */
799
0
    if (isspace((unsigned char) *cp))
800
0
    {
801
0
      cp++;
802
0
      continue;
803
0
    }
804
805
    /* Record start of current field */
806
0
    if (nf >= maxfields)
807
0
      return DTERR_BAD_FORMAT;
808
0
    field[nf] = bufp;
809
810
    /* leading digit? then date or time */
811
0
    if (isdigit((unsigned char) *cp))
812
0
    {
813
0
      APPEND_CHAR(bufp, bufend, *cp++);
814
0
      while (isdigit((unsigned char) *cp))
815
0
        APPEND_CHAR(bufp, bufend, *cp++);
816
817
      /* time field? */
818
0
      if (*cp == ':')
819
0
      {
820
0
        ftype[nf] = DTK_TIME;
821
0
        APPEND_CHAR(bufp, bufend, *cp++);
822
0
        while (isdigit((unsigned char) *cp) ||
823
0
             (*cp == ':') || (*cp == '.'))
824
0
          APPEND_CHAR(bufp, bufend, *cp++);
825
0
      }
826
      /* date field? allow embedded text month */
827
0
      else if (*cp == '-' || *cp == '/' || *cp == '.')
828
0
      {
829
        /* save delimiting character to use later */
830
0
        char    delim = *cp;
831
832
0
        APPEND_CHAR(bufp, bufend, *cp++);
833
        /* second field is all digits? then no embedded text month */
834
0
        if (isdigit((unsigned char) *cp))
835
0
        {
836
0
          ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
837
0
          while (isdigit((unsigned char) *cp))
838
0
            APPEND_CHAR(bufp, bufend, *cp++);
839
840
          /*
841
           * insist that the delimiters match to get a three-field
842
           * date.
843
           */
844
0
          if (*cp == delim)
845
0
          {
846
0
            ftype[nf] = DTK_DATE;
847
0
            APPEND_CHAR(bufp, bufend, *cp++);
848
0
            while (isdigit((unsigned char) *cp) || *cp == delim)
849
0
              APPEND_CHAR(bufp, bufend, *cp++);
850
0
          }
851
0
        }
852
0
        else
853
0
        {
854
0
          ftype[nf] = DTK_DATE;
855
0
          while (isalnum((unsigned char) *cp) || *cp == delim)
856
0
            APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
857
0
        }
858
0
      }
859
860
      /*
861
       * otherwise, number only and will determine year, month, day, or
862
       * concatenated fields later...
863
       */
864
0
      else
865
0
        ftype[nf] = DTK_NUMBER;
866
0
    }
867
    /* Leading decimal point? Then fractional seconds... */
868
0
    else if (*cp == '.')
869
0
    {
870
0
      APPEND_CHAR(bufp, bufend, *cp++);
871
0
      while (isdigit((unsigned char) *cp))
872
0
        APPEND_CHAR(bufp, bufend, *cp++);
873
874
0
      ftype[nf] = DTK_NUMBER;
875
0
    }
876
877
    /*
878
     * text? then date string, month, day of week, special, or timezone
879
     */
880
0
    else if (isalpha((unsigned char) *cp))
881
0
    {
882
0
      bool    is_date;
883
884
0
      ftype[nf] = DTK_STRING;
885
0
      APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
886
0
      while (isalpha((unsigned char) *cp))
887
0
        APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
888
889
      /*
890
       * Dates can have embedded '-', '/', or '.' separators.  It could
891
       * also be a timezone name containing embedded '/', '+', '-', '_',
892
       * or ':' (but '_' or ':' can't be the first punctuation). If the
893
       * next character is a digit or '+', we need to check whether what
894
       * we have so far is a recognized non-timezone keyword --- if so,
895
       * don't believe that this is the start of a timezone.
896
       */
897
0
      is_date = false;
898
0
      if (*cp == '-' || *cp == '/' || *cp == '.')
899
0
        is_date = true;
900
0
      else if (*cp == '+' || isdigit((unsigned char) *cp))
901
0
      {
902
0
        *bufp = '\0'; /* null-terminate current field value */
903
        /* we need search only the core token table, not TZ names */
904
0
        if (datebsearch(field[nf], datetktbl, szdatetktbl) == NULL)
905
0
          is_date = true;
906
0
      }
907
0
      if (is_date)
908
0
      {
909
0
        ftype[nf] = DTK_DATE;
910
0
        do
911
0
        {
912
0
          APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
913
0
        } while (*cp == '+' || *cp == '-' ||
914
0
             *cp == '/' || *cp == '_' ||
915
0
             *cp == '.' || *cp == ':' ||
916
0
             isalnum((unsigned char) *cp));
917
0
      }
918
0
    }
919
    /* sign? then special or numeric timezone */
920
0
    else if (*cp == '+' || *cp == '-')
921
0
    {
922
0
      APPEND_CHAR(bufp, bufend, *cp++);
923
      /* soak up leading whitespace */
924
0
      while (isspace((unsigned char) *cp))
925
0
        cp++;
926
      /* numeric timezone? */
927
      /* note that "DTK_TZ" could also be a signed float or yyyy-mm */
928
0
      if (isdigit((unsigned char) *cp))
929
0
      {
930
0
        ftype[nf] = DTK_TZ;
931
0
        APPEND_CHAR(bufp, bufend, *cp++);
932
0
        while (isdigit((unsigned char) *cp) ||
933
0
             *cp == ':' || *cp == '.' || *cp == '-')
934
0
          APPEND_CHAR(bufp, bufend, *cp++);
935
0
      }
936
      /* special? */
937
0
      else if (isalpha((unsigned char) *cp))
938
0
      {
939
0
        ftype[nf] = DTK_SPECIAL;
940
0
        APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
941
0
        while (isalpha((unsigned char) *cp))
942
0
          APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
943
0
      }
944
      /* otherwise something wrong... */
945
0
      else
946
0
        return DTERR_BAD_FORMAT;
947
0
    }
948
    /* ignore other punctuation but use as delimiter */
949
0
    else if (ispunct((unsigned char) *cp))
950
0
    {
951
0
      cp++;
952
0
      continue;
953
0
    }
954
    /* otherwise, something is not right... */
955
0
    else
956
0
      return DTERR_BAD_FORMAT;
957
958
    /* force in a delimiter after each field */
959
0
    *bufp++ = '\0';
960
0
    nf++;
961
0
  }
962
963
0
  *numfields = nf;
964
965
0
  return 0;
966
0
}
967
968
969
/* DecodeDateTime()
970
 * Interpret previously parsed fields for general date and time.
971
 * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
972
 * (Currently, all callers treat 1 as an error return too.)
973
 *
974
 * Inputs are field[] and ftype[] arrays, of length nf.
975
 * Other arguments are outputs.
976
 *
977
 *    External format(s):
978
 *        "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
979
 *        "Fri Feb-7-1997 15:23:27"
980
 *        "Feb-7-1997 15:23:27"
981
 *        "2-7-1997 15:23:27"
982
 *        "1997-2-7 15:23:27"
983
 *        "1997.038 15:23:27"   (day of year 1-366)
984
 *    Also supports input in compact time:
985
 *        "970207 152327"
986
 *        "97038 152327"
987
 *        "20011225T040506.789-07"
988
 *
989
 * Use the system-provided functions to get the current time zone
990
 * if not specified in the input string.
991
 *
992
 * If the date is outside the range of pg_time_t (in practice that could only
993
 * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas
994
 * 1997-05-27
995
 */
996
int
997
DecodeDateTime(char **field, int *ftype, int nf,
998
         int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
999
         DateTimeErrorExtra *extra)
1000
0
{
1001
0
  int     fmask = 0,
1002
0
        tmask,
1003
0
        type;
1004
0
  int     ptype = 0;    /* "prefix type" for ISO and Julian formats */
1005
0
  int     i;
1006
0
  int     val;
1007
0
  int     dterr;
1008
0
  int     mer = HR24;
1009
0
  bool    haveTextMonth = false;
1010
0
  bool    isjulian = false;
1011
0
  bool    is2digits = false;
1012
0
  bool    bc = false;
1013
0
  pg_tz    *namedTz = NULL;
1014
0
  pg_tz    *abbrevTz = NULL;
1015
0
  pg_tz    *valtz;
1016
0
  char     *abbrev = NULL;
1017
0
  struct pg_tm cur_tm;
1018
1019
  /*
1020
   * We'll insist on at least all of the date fields, but initialize the
1021
   * remaining fields in case they are not set later...
1022
   */
1023
0
  *dtype = DTK_DATE;
1024
0
  tm->tm_hour = 0;
1025
0
  tm->tm_min = 0;
1026
0
  tm->tm_sec = 0;
1027
0
  *fsec = 0;
1028
  /* don't know daylight savings time status apriori */
1029
0
  tm->tm_isdst = -1;
1030
0
  if (tzp != NULL)
1031
0
    *tzp = 0;
1032
1033
0
  for (i = 0; i < nf; i++)
1034
0
  {
1035
0
    switch (ftype[i])
1036
0
    {
1037
0
      case DTK_DATE:
1038
1039
        /*
1040
         * Integral julian day with attached time zone? All other
1041
         * forms with JD will be separated into distinct fields, so we
1042
         * handle just this case here.
1043
         */
1044
0
        if (ptype == DTK_JULIAN)
1045
0
        {
1046
0
          char     *cp;
1047
0
          int     jday;
1048
1049
0
          if (tzp == NULL)
1050
0
            return DTERR_BAD_FORMAT;
1051
1052
0
          errno = 0;
1053
0
          jday = strtoint(field[i], &cp, 10);
1054
0
          if (errno == ERANGE || jday < 0)
1055
0
            return DTERR_FIELD_OVERFLOW;
1056
1057
0
          j2date(jday, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1058
0
          isjulian = true;
1059
1060
          /* Get the time zone from the end of the string */
1061
0
          dterr = DecodeTimezone(cp, tzp);
1062
0
          if (dterr)
1063
0
            return dterr;
1064
1065
0
          tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
1066
0
          ptype = 0;
1067
0
          break;
1068
0
        }
1069
1070
        /*
1071
         * Already have a date? Then this might be a time zone name
1072
         * with embedded punctuation (e.g. "America/New_York") or a
1073
         * run-together time with trailing time zone (e.g. hhmmss-zz).
1074
         * - thomas 2001-12-25
1075
         *
1076
         * We consider it a time zone if we already have month & day.
1077
         * This is to allow the form "mmm dd hhmmss tz year", which
1078
         * we've historically accepted.
1079
         */
1080
0
        else if (ptype != 0 ||
1081
0
             ((fmask & (DTK_M(MONTH) | DTK_M(DAY))) ==
1082
0
              (DTK_M(MONTH) | DTK_M(DAY))))
1083
0
        {
1084
          /* No time zone accepted? Then quit... */
1085
0
          if (tzp == NULL)
1086
0
            return DTERR_BAD_FORMAT;
1087
1088
0
          if (isdigit((unsigned char) *field[i]) || ptype != 0)
1089
0
          {
1090
0
            char     *cp;
1091
1092
            /*
1093
             * Allow a preceding "t" field, but no other units.
1094
             */
1095
0
            if (ptype != 0)
1096
0
            {
1097
              /* Sanity check; should not fail this test */
1098
0
              if (ptype != DTK_TIME)
1099
0
                return DTERR_BAD_FORMAT;
1100
0
              ptype = 0;
1101
0
            }
1102
1103
            /*
1104
             * Starts with a digit but we already have a time
1105
             * field? Then we are in trouble with a date and time
1106
             * already...
1107
             */
1108
0
            if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1109
0
              return DTERR_BAD_FORMAT;
1110
1111
0
            if ((cp = strchr(field[i], '-')) == NULL)
1112
0
              return DTERR_BAD_FORMAT;
1113
1114
            /* Get the time zone from the end of the string */
1115
0
            dterr = DecodeTimezone(cp, tzp);
1116
0
            if (dterr)
1117
0
              return dterr;
1118
0
            *cp = '\0';
1119
1120
            /*
1121
             * Then read the rest of the field as a concatenated
1122
             * time
1123
             */
1124
0
            dterr = DecodeNumberField(strlen(field[i]), field[i],
1125
0
                          fmask,
1126
0
                          &tmask, tm,
1127
0
                          fsec, &is2digits);
1128
0
            if (dterr < 0)
1129
0
              return dterr;
1130
1131
            /*
1132
             * modify tmask after returning from
1133
             * DecodeNumberField()
1134
             */
1135
0
            tmask |= DTK_M(TZ);
1136
0
          }
1137
0
          else
1138
0
          {
1139
0
            namedTz = pg_tzset(field[i]);
1140
0
            if (!namedTz)
1141
0
            {
1142
0
              extra->dtee_timezone = field[i];
1143
0
              return DTERR_BAD_TIMEZONE;
1144
0
            }
1145
            /* we'll apply the zone setting below */
1146
0
            tmask = DTK_M(TZ);
1147
0
          }
1148
0
        }
1149
0
        else
1150
0
        {
1151
0
          dterr = DecodeDate(field[i], fmask,
1152
0
                     &tmask, &is2digits, tm);
1153
0
          if (dterr)
1154
0
            return dterr;
1155
0
        }
1156
0
        break;
1157
1158
0
      case DTK_TIME:
1159
1160
        /*
1161
         * This might be an ISO time following a "t" field.
1162
         */
1163
0
        if (ptype != 0)
1164
0
        {
1165
          /* Sanity check; should not fail this test */
1166
0
          if (ptype != DTK_TIME)
1167
0
            return DTERR_BAD_FORMAT;
1168
0
          ptype = 0;
1169
0
        }
1170
0
        dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE,
1171
0
                   &tmask, tm, fsec);
1172
0
        if (dterr)
1173
0
          return dterr;
1174
1175
        /* check for time overflow */
1176
0
        if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec,
1177
0
                   *fsec))
1178
0
          return DTERR_FIELD_OVERFLOW;
1179
0
        break;
1180
1181
0
      case DTK_TZ:
1182
0
        {
1183
0
          int     tz;
1184
1185
0
          if (tzp == NULL)
1186
0
            return DTERR_BAD_FORMAT;
1187
1188
0
          dterr = DecodeTimezone(field[i], &tz);
1189
0
          if (dterr)
1190
0
            return dterr;
1191
0
          *tzp = tz;
1192
0
          tmask = DTK_M(TZ);
1193
0
        }
1194
0
        break;
1195
1196
0
      case DTK_NUMBER:
1197
1198
        /*
1199
         * Deal with cases where previous field labeled this one
1200
         */
1201
0
        if (ptype != 0)
1202
0
        {
1203
0
          char     *cp;
1204
0
          int     value;
1205
1206
0
          errno = 0;
1207
0
          value = strtoint(field[i], &cp, 10);
1208
0
          if (errno == ERANGE)
1209
0
            return DTERR_FIELD_OVERFLOW;
1210
0
          if (*cp != '.' && *cp != '\0')
1211
0
            return DTERR_BAD_FORMAT;
1212
1213
0
          switch (ptype)
1214
0
          {
1215
0
            case DTK_JULIAN:
1216
              /* previous field was a label for "julian date" */
1217
0
              if (value < 0)
1218
0
                return DTERR_FIELD_OVERFLOW;
1219
0
              tmask = DTK_DATE_M;
1220
0
              j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1221
0
              isjulian = true;
1222
1223
              /* fractional Julian Day? */
1224
0
              if (*cp == '.')
1225
0
              {
1226
0
                double    time;
1227
1228
0
                dterr = ParseFraction(cp, &time);
1229
0
                if (dterr)
1230
0
                  return dterr;
1231
0
                time *= USECS_PER_DAY;
1232
0
                dt2time(time,
1233
0
                    &tm->tm_hour, &tm->tm_min,
1234
0
                    &tm->tm_sec, fsec);
1235
0
                tmask |= DTK_TIME_M;
1236
0
              }
1237
0
              break;
1238
1239
0
            case DTK_TIME:
1240
              /* previous field was "t" for ISO time */
1241
0
              dterr = DecodeNumberField(strlen(field[i]), field[i],
1242
0
                            (fmask | DTK_DATE_M),
1243
0
                            &tmask, tm,
1244
0
                            fsec, &is2digits);
1245
0
              if (dterr < 0)
1246
0
                return dterr;
1247
0
              if (tmask != DTK_TIME_M)
1248
0
                return DTERR_BAD_FORMAT;
1249
0
              break;
1250
1251
0
            default:
1252
0
              return DTERR_BAD_FORMAT;
1253
0
              break;
1254
0
          }
1255
1256
0
          ptype = 0;
1257
0
          *dtype = DTK_DATE;
1258
0
        }
1259
0
        else
1260
0
        {
1261
0
          char     *cp;
1262
0
          int     flen;
1263
1264
0
          flen = strlen(field[i]);
1265
0
          cp = strchr(field[i], '.');
1266
1267
          /* Embedded decimal and no date yet? */
1268
0
          if (cp != NULL && !(fmask & DTK_DATE_M))
1269
0
          {
1270
0
            dterr = DecodeDate(field[i], fmask,
1271
0
                       &tmask, &is2digits, tm);
1272
0
            if (dterr)
1273
0
              return dterr;
1274
0
          }
1275
          /* embedded decimal and several digits before? */
1276
0
          else if (cp != NULL && flen - strlen(cp) > 2)
1277
0
          {
1278
            /*
1279
             * Interpret as a concatenated date or time Set the
1280
             * type field to allow decoding other fields later.
1281
             * Example: 20011223 or 040506
1282
             */
1283
0
            dterr = DecodeNumberField(flen, field[i], fmask,
1284
0
                          &tmask, tm,
1285
0
                          fsec, &is2digits);
1286
0
            if (dterr < 0)
1287
0
              return dterr;
1288
0
          }
1289
1290
          /*
1291
           * Is this a YMD or HMS specification, or a year number?
1292
           * YMD and HMS are required to be six digits or more, so
1293
           * if it is 5 digits, it is a year.  If it is six or more
1294
           * digits, we assume it is YMD or HMS unless no date and
1295
           * no time values have been specified.  This forces 6+
1296
           * digit years to be at the end of the string, or to use
1297
           * the ISO date specification.
1298
           */
1299
0
          else if (flen >= 6 && (!(fmask & DTK_DATE_M) ||
1300
0
                       !(fmask & DTK_TIME_M)))
1301
0
          {
1302
0
            dterr = DecodeNumberField(flen, field[i], fmask,
1303
0
                          &tmask, tm,
1304
0
                          fsec, &is2digits);
1305
0
            if (dterr < 0)
1306
0
              return dterr;
1307
0
          }
1308
          /* otherwise it is a single date/time field... */
1309
0
          else
1310
0
          {
1311
0
            dterr = DecodeNumber(flen, field[i],
1312
0
                       haveTextMonth, fmask,
1313
0
                       &tmask, tm,
1314
0
                       fsec, &is2digits);
1315
0
            if (dterr)
1316
0
              return dterr;
1317
0
          }
1318
0
        }
1319
0
        break;
1320
1321
0
      case DTK_STRING:
1322
0
      case DTK_SPECIAL:
1323
        /* timezone abbrevs take precedence over built-in tokens */
1324
0
        dterr = DecodeTimezoneAbbrev(i, field[i],
1325
0
                       &type, &val, &valtz, extra);
1326
0
        if (dterr)
1327
0
          return dterr;
1328
0
        if (type == UNKNOWN_FIELD)
1329
0
          type = DecodeSpecial(i, field[i], &val);
1330
0
        if (type == IGNORE_DTF)
1331
0
          continue;
1332
1333
0
        tmask = DTK_M(type);
1334
0
        switch (type)
1335
0
        {
1336
0
          case RESERV:
1337
0
            switch (val)
1338
0
            {
1339
0
              case DTK_NOW:
1340
0
                tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1341
0
                *dtype = DTK_DATE;
1342
0
                GetCurrentTimeUsec(tm, fsec, tzp);
1343
0
                break;
1344
1345
0
              case DTK_YESTERDAY:
1346
0
                tmask = DTK_DATE_M;
1347
0
                *dtype = DTK_DATE;
1348
0
                GetCurrentDateTime(&cur_tm);
1349
0
                j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) - 1,
1350
0
                     &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1351
0
                break;
1352
1353
0
              case DTK_TODAY:
1354
0
                tmask = DTK_DATE_M;
1355
0
                *dtype = DTK_DATE;
1356
0
                GetCurrentDateTime(&cur_tm);
1357
0
                tm->tm_year = cur_tm.tm_year;
1358
0
                tm->tm_mon = cur_tm.tm_mon;
1359
0
                tm->tm_mday = cur_tm.tm_mday;
1360
0
                break;
1361
1362
0
              case DTK_TOMORROW:
1363
0
                tmask = DTK_DATE_M;
1364
0
                *dtype = DTK_DATE;
1365
0
                GetCurrentDateTime(&cur_tm);
1366
0
                j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) + 1,
1367
0
                     &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1368
0
                break;
1369
1370
0
              case DTK_ZULU:
1371
0
                tmask = (DTK_TIME_M | DTK_M(TZ));
1372
0
                *dtype = DTK_DATE;
1373
0
                tm->tm_hour = 0;
1374
0
                tm->tm_min = 0;
1375
0
                tm->tm_sec = 0;
1376
0
                if (tzp != NULL)
1377
0
                  *tzp = 0;
1378
0
                break;
1379
1380
0
              case DTK_EPOCH:
1381
0
              case DTK_LATE:
1382
0
              case DTK_EARLY:
1383
0
                tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1384
0
                *dtype = val;
1385
                /* caller ignores tm for these dtype codes */
1386
0
                break;
1387
1388
0
              default:
1389
0
                elog(ERROR, "unrecognized RESERV datetime token: %d",
1390
0
                   val);
1391
0
            }
1392
1393
0
            break;
1394
1395
0
          case MONTH:
1396
1397
            /*
1398
             * already have a (numeric) month? then see if we can
1399
             * substitute...
1400
             */
1401
0
            if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
1402
0
              !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 &&
1403
0
              tm->tm_mon <= 31)
1404
0
            {
1405
0
              tm->tm_mday = tm->tm_mon;
1406
0
              tmask = DTK_M(DAY);
1407
0
            }
1408
0
            haveTextMonth = true;
1409
0
            tm->tm_mon = val;
1410
0
            break;
1411
1412
0
          case DTZMOD:
1413
1414
            /*
1415
             * daylight savings time modifier (solves "MET DST"
1416
             * syntax)
1417
             */
1418
0
            tmask |= DTK_M(DTZ);
1419
0
            tm->tm_isdst = 1;
1420
0
            if (tzp == NULL)
1421
0
              return DTERR_BAD_FORMAT;
1422
0
            *tzp -= val;
1423
0
            break;
1424
1425
0
          case DTZ:
1426
1427
            /*
1428
             * set mask for TZ here _or_ check for DTZ later when
1429
             * getting default timezone
1430
             */
1431
0
            tmask |= DTK_M(TZ);
1432
0
            tm->tm_isdst = 1;
1433
0
            if (tzp == NULL)
1434
0
              return DTERR_BAD_FORMAT;
1435
0
            *tzp = -val;
1436
0
            break;
1437
1438
0
          case TZ:
1439
0
            tm->tm_isdst = 0;
1440
0
            if (tzp == NULL)
1441
0
              return DTERR_BAD_FORMAT;
1442
0
            *tzp = -val;
1443
0
            break;
1444
1445
0
          case DYNTZ:
1446
0
            tmask |= DTK_M(TZ);
1447
0
            if (tzp == NULL)
1448
0
              return DTERR_BAD_FORMAT;
1449
            /* we'll determine the actual offset later */
1450
0
            abbrevTz = valtz;
1451
0
            abbrev = field[i];
1452
0
            break;
1453
1454
0
          case AMPM:
1455
0
            mer = val;
1456
0
            break;
1457
1458
0
          case ADBC:
1459
0
            bc = (val == BC);
1460
0
            break;
1461
1462
0
          case DOW:
1463
0
            tm->tm_wday = val;
1464
0
            break;
1465
1466
0
          case UNITS:
1467
0
            tmask = 0;
1468
            /* reject consecutive unhandled units */
1469
0
            if (ptype != 0)
1470
0
              return DTERR_BAD_FORMAT;
1471
0
            ptype = val;
1472
0
            break;
1473
1474
0
          case ISOTIME:
1475
1476
            /*
1477
             * This is a filler field "t" indicating that the next
1478
             * field is time. Try to verify that this is sensible.
1479
             */
1480
0
            tmask = 0;
1481
1482
            /* No preceding date? Then quit... */
1483
0
            if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1484
0
              return DTERR_BAD_FORMAT;
1485
1486
            /* reject consecutive unhandled units */
1487
0
            if (ptype != 0)
1488
0
              return DTERR_BAD_FORMAT;
1489
0
            ptype = val;
1490
0
            break;
1491
1492
0
          case UNKNOWN_FIELD:
1493
1494
            /*
1495
             * Before giving up and declaring error, check to see
1496
             * if it is an all-alpha timezone name.
1497
             */
1498
0
            namedTz = pg_tzset(field[i]);
1499
0
            if (!namedTz)
1500
0
              return DTERR_BAD_FORMAT;
1501
            /* we'll apply the zone setting below */
1502
0
            tmask = DTK_M(TZ);
1503
0
            break;
1504
1505
0
          default:
1506
0
            return DTERR_BAD_FORMAT;
1507
0
        }
1508
0
        break;
1509
1510
0
      default:
1511
0
        return DTERR_BAD_FORMAT;
1512
0
    }
1513
1514
0
    if (tmask & fmask)
1515
0
      return DTERR_BAD_FORMAT;
1516
0
    fmask |= tmask;
1517
0
  }              /* end loop over fields */
1518
1519
  /* reject if prefix type appeared and was never handled */
1520
0
  if (ptype != 0)
1521
0
    return DTERR_BAD_FORMAT;
1522
1523
  /* do additional checking for normal date specs (but not "infinity" etc) */
1524
0
  if (*dtype == DTK_DATE)
1525
0
  {
1526
    /* do final checking/adjustment of Y/M/D fields */
1527
0
    dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
1528
0
    if (dterr)
1529
0
      return dterr;
1530
1531
    /* handle AM/PM */
1532
0
    if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
1533
0
      return DTERR_FIELD_OVERFLOW;
1534
0
    if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
1535
0
      tm->tm_hour = 0;
1536
0
    else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
1537
0
      tm->tm_hour += HOURS_PER_DAY / 2;
1538
1539
    /* check for incomplete input */
1540
0
    if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1541
0
    {
1542
0
      if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1543
0
        return 1;
1544
0
      return DTERR_BAD_FORMAT;
1545
0
    }
1546
1547
    /*
1548
     * If we had a full timezone spec, compute the offset (we could not do
1549
     * it before, because we need the date to resolve DST status).
1550
     */
1551
0
    if (namedTz != NULL)
1552
0
    {
1553
      /* daylight savings time modifier disallowed with full TZ */
1554
0
      if (fmask & DTK_M(DTZMOD))
1555
0
        return DTERR_BAD_FORMAT;
1556
1557
0
      *tzp = DetermineTimeZoneOffset(tm, namedTz);
1558
0
    }
1559
1560
    /*
1561
     * Likewise, if we had a dynamic timezone abbreviation, resolve it
1562
     * now.
1563
     */
1564
0
    if (abbrevTz != NULL)
1565
0
    {
1566
      /* daylight savings time modifier disallowed with dynamic TZ */
1567
0
      if (fmask & DTK_M(DTZMOD))
1568
0
        return DTERR_BAD_FORMAT;
1569
1570
0
      *tzp = DetermineTimeZoneAbbrevOffset(tm, abbrev, abbrevTz);
1571
0
    }
1572
1573
    /* timezone not specified? then use session timezone */
1574
0
    if (tzp != NULL && !(fmask & DTK_M(TZ)))
1575
0
    {
1576
      /*
1577
       * daylight savings time modifier but no standard timezone? then
1578
       * error
1579
       */
1580
0
      if (fmask & DTK_M(DTZMOD))
1581
0
        return DTERR_BAD_FORMAT;
1582
1583
0
      *tzp = DetermineTimeZoneOffset(tm, session_timezone);
1584
0
    }
1585
0
  }
1586
1587
0
  return 0;
1588
0
}
1589
1590
1591
/* DetermineTimeZoneOffset()
1592
 *
1593
 * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min,
1594
 * and tm_sec fields are set, and a zic-style time zone definition, determine
1595
 * the applicable GMT offset and daylight-savings status at that time.
1596
 * Set the struct pg_tm's tm_isdst field accordingly, and return the GMT
1597
 * offset as the function result.
1598
 *
1599
 * Note: if the date is out of the range we can deal with, we return zero
1600
 * as the GMT offset and set tm_isdst = 0.  We don't throw an error here,
1601
 * though probably some higher-level code will.
1602
 */
1603
int
1604
DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
1605
0
{
1606
0
  pg_time_t t;
1607
1608
0
  return DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1609
0
}
1610
1611
1612
/* DetermineTimeZoneOffsetInternal()
1613
 *
1614
 * As above, but also return the actual UTC time imputed to the date/time
1615
 * into *tp.
1616
 *
1617
 * In event of an out-of-range date, we punt by returning zero into *tp.
1618
 * This is okay for the immediate callers but is a good reason for not
1619
 * exposing this worker function globally.
1620
 *
1621
 * Note: it might seem that we should use mktime() for this, but bitter
1622
 * experience teaches otherwise.  This code is much faster than most versions
1623
 * of mktime(), anyway.
1624
 */
1625
static int
1626
DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp, pg_time_t *tp)
1627
0
{
1628
0
  int     date,
1629
0
        sec;
1630
0
  pg_time_t day,
1631
0
        mytime,
1632
0
        prevtime,
1633
0
        boundary,
1634
0
        beforetime,
1635
0
        aftertime;
1636
0
  long int  before_gmtoff,
1637
0
        after_gmtoff;
1638
0
  int     before_isdst,
1639
0
        after_isdst;
1640
0
  int     res;
1641
1642
  /*
1643
   * First, generate the pg_time_t value corresponding to the given
1644
   * y/m/d/h/m/s taken as GMT time.  If this overflows, punt and decide the
1645
   * timezone is GMT.  (For a valid Julian date, integer overflow should be
1646
   * impossible with 64-bit pg_time_t, but let's check for safety.)
1647
   */
1648
0
  if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
1649
0
    goto overflow;
1650
0
  date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE;
1651
1652
0
  day = ((pg_time_t) date) * SECS_PER_DAY;
1653
0
  if (day / SECS_PER_DAY != date)
1654
0
    goto overflow;
1655
0
  sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * MINS_PER_HOUR) * SECS_PER_MINUTE;
1656
0
  mytime = day + sec;
1657
  /* since sec >= 0, overflow could only be from +day to -mytime */
1658
0
  if (mytime < 0 && day > 0)
1659
0
    goto overflow;
1660
1661
  /*
1662
   * Find the DST time boundary just before or following the target time. We
1663
   * assume that all zones have GMT offsets less than 24 hours, and that DST
1664
   * boundaries can't be closer together than 48 hours, so backing up 24
1665
   * hours and finding the "next" boundary will work.
1666
   */
1667
0
  prevtime = mytime - SECS_PER_DAY;
1668
0
  if (mytime < 0 && prevtime > 0)
1669
0
    goto overflow;
1670
1671
0
  res = pg_next_dst_boundary(&prevtime,
1672
0
                 &before_gmtoff, &before_isdst,
1673
0
                 &boundary,
1674
0
                 &after_gmtoff, &after_isdst,
1675
0
                 tzp);
1676
0
  if (res < 0)
1677
0
    goto overflow;     /* failure? */
1678
1679
0
  if (res == 0)
1680
0
  {
1681
    /* Non-DST zone, life is simple */
1682
0
    tm->tm_isdst = before_isdst;
1683
0
    *tp = mytime - before_gmtoff;
1684
0
    return -(int) before_gmtoff;
1685
0
  }
1686
1687
  /*
1688
   * Form the candidate pg_time_t values with local-time adjustment
1689
   */
1690
0
  beforetime = mytime - before_gmtoff;
1691
0
  if ((before_gmtoff > 0 &&
1692
0
     mytime < 0 && beforetime > 0) ||
1693
0
    (before_gmtoff <= 0 &&
1694
0
     mytime > 0 && beforetime < 0))
1695
0
    goto overflow;
1696
0
  aftertime = mytime - after_gmtoff;
1697
0
  if ((after_gmtoff > 0 &&
1698
0
     mytime < 0 && aftertime > 0) ||
1699
0
    (after_gmtoff <= 0 &&
1700
0
     mytime > 0 && aftertime < 0))
1701
0
    goto overflow;
1702
1703
  /*
1704
   * If both before or both after the boundary time, we know what to do. The
1705
   * boundary time itself is considered to be after the transition, which
1706
   * means we can accept aftertime == boundary in the second case.
1707
   */
1708
0
  if (beforetime < boundary && aftertime < boundary)
1709
0
  {
1710
0
    tm->tm_isdst = before_isdst;
1711
0
    *tp = beforetime;
1712
0
    return -(int) before_gmtoff;
1713
0
  }
1714
0
  if (beforetime > boundary && aftertime >= boundary)
1715
0
  {
1716
0
    tm->tm_isdst = after_isdst;
1717
0
    *tp = aftertime;
1718
0
    return -(int) after_gmtoff;
1719
0
  }
1720
1721
  /*
1722
   * It's an invalid or ambiguous time due to timezone transition.  In a
1723
   * spring-forward transition, prefer the "before" interpretation; in a
1724
   * fall-back transition, prefer "after".  (We used to define and implement
1725
   * this test as "prefer the standard-time interpretation", but that rule
1726
   * does not help to resolve the behavior when both times are reported as
1727
   * standard time; which does happen, eg Europe/Moscow in Oct 2014.  Also,
1728
   * in some zones such as Europe/Dublin, there is widespread confusion
1729
   * about which time offset is "standard" time, so it's fortunate that our
1730
   * behavior doesn't depend on that.)
1731
   */
1732
0
  if (beforetime > aftertime)
1733
0
  {
1734
0
    tm->tm_isdst = before_isdst;
1735
0
    *tp = beforetime;
1736
0
    return -(int) before_gmtoff;
1737
0
  }
1738
0
  tm->tm_isdst = after_isdst;
1739
0
  *tp = aftertime;
1740
0
  return -(int) after_gmtoff;
1741
1742
0
overflow:
1743
  /* Given date is out of range, so assume UTC */
1744
0
  tm->tm_isdst = 0;
1745
0
  *tp = 0;
1746
0
  return 0;
1747
0
}
1748
1749
1750
/* DetermineTimeZoneAbbrevOffset()
1751
 *
1752
 * Determine the GMT offset and DST flag to be attributed to a dynamic
1753
 * time zone abbreviation, that is one whose meaning has changed over time.
1754
 * *tm contains the local time at which the meaning should be determined,
1755
 * and tm->tm_isdst receives the DST flag.
1756
 *
1757
 * This differs from the behavior of DetermineTimeZoneOffset() in that a
1758
 * standard-time or daylight-time abbreviation forces use of the corresponding
1759
 * GMT offset even when the zone was then in DS or standard time respectively.
1760
 * (However, that happens only if we can match the given abbreviation to some
1761
 * abbreviation that appears in the IANA timezone data.  Otherwise, we fall
1762
 * back to doing DetermineTimeZoneOffset().)
1763
 */
1764
int
1765
DetermineTimeZoneAbbrevOffset(struct pg_tm *tm, const char *abbr, pg_tz *tzp)
1766
0
{
1767
0
  pg_time_t t;
1768
0
  int     zone_offset;
1769
0
  int     abbr_offset;
1770
0
  int     abbr_isdst;
1771
1772
  /*
1773
   * Compute the UTC time we want to probe at.  (In event of overflow, we'll
1774
   * probe at the epoch, which is a bit random but probably doesn't matter.)
1775
   */
1776
0
  zone_offset = DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1777
1778
  /*
1779
   * Try to match the abbreviation to something in the zone definition.
1780
   */
1781
0
  if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp,
1782
0
                        &abbr_offset, &abbr_isdst))
1783
0
  {
1784
    /* Success, so use the abbrev-specific answers. */
1785
0
    tm->tm_isdst = abbr_isdst;
1786
0
    return abbr_offset;
1787
0
  }
1788
1789
  /*
1790
   * No match, so use the answers we already got from
1791
   * DetermineTimeZoneOffsetInternal.
1792
   */
1793
0
  return zone_offset;
1794
0
}
1795
1796
1797
/* DetermineTimeZoneAbbrevOffsetTS()
1798
 *
1799
 * As above but the probe time is specified as a TimestampTz (hence, UTC time),
1800
 * and DST status is returned into *isdst rather than into tm->tm_isdst.
1801
 */
1802
int
1803
DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr,
1804
                pg_tz *tzp, int *isdst)
1805
0
{
1806
0
  pg_time_t t = timestamptz_to_time_t(ts);
1807
0
  int     zone_offset;
1808
0
  int     abbr_offset;
1809
0
  int     tz;
1810
0
  struct pg_tm tm;
1811
0
  fsec_t    fsec;
1812
1813
  /*
1814
   * If the abbrev matches anything in the zone data, this is pretty easy.
1815
   */
1816
0
  if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp,
1817
0
                        &abbr_offset, isdst))
1818
0
    return abbr_offset;
1819
1820
  /*
1821
   * Else, break down the timestamp so we can use DetermineTimeZoneOffset.
1822
   */
1823
0
  if (timestamp2tm(ts, &tz, &tm, &fsec, NULL, tzp) != 0)
1824
0
    ereport(ERROR,
1825
0
        (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1826
0
         errmsg("timestamp out of range")));
1827
1828
0
  zone_offset = DetermineTimeZoneOffset(&tm, tzp);
1829
0
  *isdst = tm.tm_isdst;
1830
0
  return zone_offset;
1831
0
}
1832
1833
1834
/* DetermineTimeZoneAbbrevOffsetInternal()
1835
 *
1836
 * Workhorse for above two functions: work from a pg_time_t probe instant.
1837
 * On success, return GMT offset and DST status into *offset and *isdst.
1838
 */
1839
static bool
1840
DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp,
1841
                    int *offset, int *isdst)
1842
0
{
1843
0
  char    upabbr[TZ_STRLEN_MAX + 1];
1844
0
  unsigned char *p;
1845
0
  long int  gmtoff;
1846
1847
  /* We need to force the abbrev to upper case */
1848
0
  strlcpy(upabbr, abbr, sizeof(upabbr));
1849
0
  for (p = (unsigned char *) upabbr; *p; p++)
1850
0
    *p = pg_toupper(*p);
1851
1852
  /* Look up the abbrev's meaning at this time in this zone */
1853
0
  if (pg_interpret_timezone_abbrev(upabbr,
1854
0
                   &t,
1855
0
                   &gmtoff,
1856
0
                   isdst,
1857
0
                   tzp))
1858
0
  {
1859
    /* Change sign to agree with DetermineTimeZoneOffset() */
1860
0
    *offset = (int) -gmtoff;
1861
0
    return true;
1862
0
  }
1863
0
  return false;
1864
0
}
1865
1866
1867
/* TimeZoneAbbrevIsKnown()
1868
 *
1869
 * Detect whether the given string is a time zone abbreviation that's known
1870
 * in the specified TZDB timezone, and if so whether it's fixed or varying
1871
 * meaning.  The match is not case-sensitive.
1872
 */
1873
static bool
1874
TimeZoneAbbrevIsKnown(const char *abbr, pg_tz *tzp,
1875
            bool *isfixed, int *offset, int *isdst)
1876
0
{
1877
0
  char    upabbr[TZ_STRLEN_MAX + 1];
1878
0
  unsigned char *p;
1879
0
  long int  gmtoff;
1880
1881
  /* We need to force the abbrev to upper case */
1882
0
  strlcpy(upabbr, abbr, sizeof(upabbr));
1883
0
  for (p = (unsigned char *) upabbr; *p; p++)
1884
0
    *p = pg_toupper(*p);
1885
1886
  /* Look up the abbrev's meaning in this zone */
1887
0
  if (pg_timezone_abbrev_is_known(upabbr,
1888
0
                  isfixed,
1889
0
                  &gmtoff,
1890
0
                  isdst,
1891
0
                  tzp))
1892
0
  {
1893
    /* Change sign to agree with DetermineTimeZoneOffset() */
1894
0
    *offset = (int) -gmtoff;
1895
0
    return true;
1896
0
  }
1897
0
  return false;
1898
0
}
1899
1900
1901
/* DecodeTimeOnly()
1902
 * Interpret parsed string as time fields only.
1903
 * Returns 0 if successful, DTERR code if bogus input detected.
1904
 *
1905
 * Inputs are field[] and ftype[] arrays, of length nf.
1906
 * Other arguments are outputs.
1907
 *
1908
 * Note that support for time zone is here for
1909
 * SQL TIME WITH TIME ZONE, but it reveals
1910
 * bogosity with SQL date/time standards, since
1911
 * we must infer a time zone from current time.
1912
 * - thomas 2000-03-10
1913
 * Allow specifying date to get a better time zone,
1914
 * if time zones are allowed. - thomas 2001-12-26
1915
 */
1916
int
1917
DecodeTimeOnly(char **field, int *ftype, int nf,
1918
         int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
1919
         DateTimeErrorExtra *extra)
1920
0
{
1921
0
  int     fmask = 0,
1922
0
        tmask,
1923
0
        type;
1924
0
  int     ptype = 0;    /* "prefix type" for ISO and Julian formats */
1925
0
  int     i;
1926
0
  int     val;
1927
0
  int     dterr;
1928
0
  bool    isjulian = false;
1929
0
  bool    is2digits = false;
1930
0
  bool    bc = false;
1931
0
  int     mer = HR24;
1932
0
  pg_tz    *namedTz = NULL;
1933
0
  pg_tz    *abbrevTz = NULL;
1934
0
  char     *abbrev = NULL;
1935
0
  pg_tz    *valtz;
1936
1937
0
  *dtype = DTK_TIME;
1938
0
  tm->tm_hour = 0;
1939
0
  tm->tm_min = 0;
1940
0
  tm->tm_sec = 0;
1941
0
  *fsec = 0;
1942
  /* don't know daylight savings time status apriori */
1943
0
  tm->tm_isdst = -1;
1944
1945
0
  if (tzp != NULL)
1946
0
    *tzp = 0;
1947
1948
0
  for (i = 0; i < nf; i++)
1949
0
  {
1950
0
    switch (ftype[i])
1951
0
    {
1952
0
      case DTK_DATE:
1953
1954
        /*
1955
         * Time zone not allowed? Then should not accept dates or time
1956
         * zones no matter what else!
1957
         */
1958
0
        if (tzp == NULL)
1959
0
          return DTERR_BAD_FORMAT;
1960
1961
        /* Under limited circumstances, we will accept a date... */
1962
0
        if (i == 0 && nf >= 2 &&
1963
0
          (ftype[nf - 1] == DTK_DATE || ftype[1] == DTK_TIME))
1964
0
        {
1965
0
          dterr = DecodeDate(field[i], fmask,
1966
0
                     &tmask, &is2digits, tm);
1967
0
          if (dterr)
1968
0
            return dterr;
1969
0
        }
1970
        /* otherwise, this is a time and/or time zone */
1971
0
        else
1972
0
        {
1973
0
          if (isdigit((unsigned char) *field[i]))
1974
0
          {
1975
0
            char     *cp;
1976
1977
            /*
1978
             * Starts with a digit but we already have a time
1979
             * field? Then we are in trouble with time already...
1980
             */
1981
0
            if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1982
0
              return DTERR_BAD_FORMAT;
1983
1984
            /*
1985
             * Should not get here and fail. Sanity check only...
1986
             */
1987
0
            if ((cp = strchr(field[i], '-')) == NULL)
1988
0
              return DTERR_BAD_FORMAT;
1989
1990
            /* Get the time zone from the end of the string */
1991
0
            dterr = DecodeTimezone(cp, tzp);
1992
0
            if (dterr)
1993
0
              return dterr;
1994
0
            *cp = '\0';
1995
1996
            /*
1997
             * Then read the rest of the field as a concatenated
1998
             * time
1999
             */
2000
0
            dterr = DecodeNumberField(strlen(field[i]), field[i],
2001
0
                          (fmask | DTK_DATE_M),
2002
0
                          &tmask, tm,
2003
0
                          fsec, &is2digits);
2004
0
            if (dterr < 0)
2005
0
              return dterr;
2006
0
            ftype[i] = dterr;
2007
2008
0
            tmask |= DTK_M(TZ);
2009
0
          }
2010
0
          else
2011
0
          {
2012
0
            namedTz = pg_tzset(field[i]);
2013
0
            if (!namedTz)
2014
0
            {
2015
0
              extra->dtee_timezone = field[i];
2016
0
              return DTERR_BAD_TIMEZONE;
2017
0
            }
2018
            /* we'll apply the zone setting below */
2019
0
            ftype[i] = DTK_TZ;
2020
0
            tmask = DTK_M(TZ);
2021
0
          }
2022
0
        }
2023
0
        break;
2024
2025
0
      case DTK_TIME:
2026
2027
        /*
2028
         * This might be an ISO time following a "t" field.
2029
         */
2030
0
        if (ptype != 0)
2031
0
        {
2032
0
          if (ptype != DTK_TIME)
2033
0
            return DTERR_BAD_FORMAT;
2034
0
          ptype = 0;
2035
0
        }
2036
2037
0
        dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
2038
0
                   INTERVAL_FULL_RANGE,
2039
0
                   &tmask, tm, fsec);
2040
0
        if (dterr)
2041
0
          return dterr;
2042
0
        break;
2043
2044
0
      case DTK_TZ:
2045
0
        {
2046
0
          int     tz;
2047
2048
0
          if (tzp == NULL)
2049
0
            return DTERR_BAD_FORMAT;
2050
2051
0
          dterr = DecodeTimezone(field[i], &tz);
2052
0
          if (dterr)
2053
0
            return dterr;
2054
0
          *tzp = tz;
2055
0
          tmask = DTK_M(TZ);
2056
0
        }
2057
0
        break;
2058
2059
0
      case DTK_NUMBER:
2060
2061
        /*
2062
         * Deal with cases where previous field labeled this one
2063
         */
2064
0
        if (ptype != 0)
2065
0
        {
2066
0
          char     *cp;
2067
0
          int     value;
2068
2069
0
          errno = 0;
2070
0
          value = strtoint(field[i], &cp, 10);
2071
0
          if (errno == ERANGE)
2072
0
            return DTERR_FIELD_OVERFLOW;
2073
0
          if (*cp != '.' && *cp != '\0')
2074
0
            return DTERR_BAD_FORMAT;
2075
2076
0
          switch (ptype)
2077
0
          {
2078
0
            case DTK_JULIAN:
2079
              /* previous field was a label for "julian date" */
2080
0
              if (tzp == NULL)
2081
0
                return DTERR_BAD_FORMAT;
2082
0
              if (value < 0)
2083
0
                return DTERR_FIELD_OVERFLOW;
2084
0
              tmask = DTK_DATE_M;
2085
0
              j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2086
0
              isjulian = true;
2087
2088
0
              if (*cp == '.')
2089
0
              {
2090
0
                double    time;
2091
2092
0
                dterr = ParseFraction(cp, &time);
2093
0
                if (dterr)
2094
0
                  return dterr;
2095
0
                time *= USECS_PER_DAY;
2096
0
                dt2time(time,
2097
0
                    &tm->tm_hour, &tm->tm_min,
2098
0
                    &tm->tm_sec, fsec);
2099
0
                tmask |= DTK_TIME_M;
2100
0
              }
2101
0
              break;
2102
2103
0
            case DTK_TIME:
2104
              /* previous field was "t" for ISO time */
2105
0
              dterr = DecodeNumberField(strlen(field[i]), field[i],
2106
0
                            (fmask | DTK_DATE_M),
2107
0
                            &tmask, tm,
2108
0
                            fsec, &is2digits);
2109
0
              if (dterr < 0)
2110
0
                return dterr;
2111
0
              ftype[i] = dterr;
2112
2113
0
              if (tmask != DTK_TIME_M)
2114
0
                return DTERR_BAD_FORMAT;
2115
0
              break;
2116
2117
0
            default:
2118
0
              return DTERR_BAD_FORMAT;
2119
0
              break;
2120
0
          }
2121
2122
0
          ptype = 0;
2123
0
          *dtype = DTK_DATE;
2124
0
        }
2125
0
        else
2126
0
        {
2127
0
          char     *cp;
2128
0
          int     flen;
2129
2130
0
          flen = strlen(field[i]);
2131
0
          cp = strchr(field[i], '.');
2132
2133
          /* Embedded decimal? */
2134
0
          if (cp != NULL)
2135
0
          {
2136
            /*
2137
             * Under limited circumstances, we will accept a
2138
             * date...
2139
             */
2140
0
            if (i == 0 && nf >= 2 && ftype[nf - 1] == DTK_DATE)
2141
0
            {
2142
0
              dterr = DecodeDate(field[i], fmask,
2143
0
                         &tmask, &is2digits, tm);
2144
0
              if (dterr)
2145
0
                return dterr;
2146
0
            }
2147
            /* embedded decimal and several digits before? */
2148
0
            else if (flen - strlen(cp) > 2)
2149
0
            {
2150
              /*
2151
               * Interpret as a concatenated date or time Set
2152
               * the type field to allow decoding other fields
2153
               * later. Example: 20011223 or 040506
2154
               */
2155
0
              dterr = DecodeNumberField(flen, field[i],
2156
0
                            (fmask | DTK_DATE_M),
2157
0
                            &tmask, tm,
2158
0
                            fsec, &is2digits);
2159
0
              if (dterr < 0)
2160
0
                return dterr;
2161
0
              ftype[i] = dterr;
2162
0
            }
2163
0
            else
2164
0
              return DTERR_BAD_FORMAT;
2165
0
          }
2166
0
          else if (flen > 4)
2167
0
          {
2168
0
            dterr = DecodeNumberField(flen, field[i],
2169
0
                          (fmask | DTK_DATE_M),
2170
0
                          &tmask, tm,
2171
0
                          fsec, &is2digits);
2172
0
            if (dterr < 0)
2173
0
              return dterr;
2174
0
            ftype[i] = dterr;
2175
0
          }
2176
          /* otherwise it is a single date/time field... */
2177
0
          else
2178
0
          {
2179
0
            dterr = DecodeNumber(flen, field[i],
2180
0
                       false,
2181
0
                       (fmask | DTK_DATE_M),
2182
0
                       &tmask, tm,
2183
0
                       fsec, &is2digits);
2184
0
            if (dterr)
2185
0
              return dterr;
2186
0
          }
2187
0
        }
2188
0
        break;
2189
2190
0
      case DTK_STRING:
2191
0
      case DTK_SPECIAL:
2192
        /* timezone abbrevs take precedence over built-in tokens */
2193
0
        dterr = DecodeTimezoneAbbrev(i, field[i],
2194
0
                       &type, &val, &valtz, extra);
2195
0
        if (dterr)
2196
0
          return dterr;
2197
0
        if (type == UNKNOWN_FIELD)
2198
0
          type = DecodeSpecial(i, field[i], &val);
2199
0
        if (type == IGNORE_DTF)
2200
0
          continue;
2201
2202
0
        tmask = DTK_M(type);
2203
0
        switch (type)
2204
0
        {
2205
0
          case RESERV:
2206
0
            switch (val)
2207
0
            {
2208
0
              case DTK_NOW:
2209
0
                tmask = DTK_TIME_M;
2210
0
                *dtype = DTK_TIME;
2211
0
                GetCurrentTimeUsec(tm, fsec, NULL);
2212
0
                break;
2213
2214
0
              case DTK_ZULU:
2215
0
                tmask = (DTK_TIME_M | DTK_M(TZ));
2216
0
                *dtype = DTK_TIME;
2217
0
                tm->tm_hour = 0;
2218
0
                tm->tm_min = 0;
2219
0
                tm->tm_sec = 0;
2220
0
                tm->tm_isdst = 0;
2221
0
                break;
2222
2223
0
              default:
2224
0
                return DTERR_BAD_FORMAT;
2225
0
            }
2226
2227
0
            break;
2228
2229
0
          case DTZMOD:
2230
2231
            /*
2232
             * daylight savings time modifier (solves "MET DST"
2233
             * syntax)
2234
             */
2235
0
            tmask |= DTK_M(DTZ);
2236
0
            tm->tm_isdst = 1;
2237
0
            if (tzp == NULL)
2238
0
              return DTERR_BAD_FORMAT;
2239
0
            *tzp -= val;
2240
0
            break;
2241
2242
0
          case DTZ:
2243
2244
            /*
2245
             * set mask for TZ here _or_ check for DTZ later when
2246
             * getting default timezone
2247
             */
2248
0
            tmask |= DTK_M(TZ);
2249
0
            tm->tm_isdst = 1;
2250
0
            if (tzp == NULL)
2251
0
              return DTERR_BAD_FORMAT;
2252
0
            *tzp = -val;
2253
0
            ftype[i] = DTK_TZ;
2254
0
            break;
2255
2256
0
          case TZ:
2257
0
            tm->tm_isdst = 0;
2258
0
            if (tzp == NULL)
2259
0
              return DTERR_BAD_FORMAT;
2260
0
            *tzp = -val;
2261
0
            ftype[i] = DTK_TZ;
2262
0
            break;
2263
2264
0
          case DYNTZ:
2265
0
            tmask |= DTK_M(TZ);
2266
0
            if (tzp == NULL)
2267
0
              return DTERR_BAD_FORMAT;
2268
            /* we'll determine the actual offset later */
2269
0
            abbrevTz = valtz;
2270
0
            abbrev = field[i];
2271
0
            ftype[i] = DTK_TZ;
2272
0
            break;
2273
2274
0
          case AMPM:
2275
0
            mer = val;
2276
0
            break;
2277
2278
0
          case ADBC:
2279
0
            bc = (val == BC);
2280
0
            break;
2281
2282
0
          case UNITS:
2283
0
            tmask = 0;
2284
            /* reject consecutive unhandled units */
2285
0
            if (ptype != 0)
2286
0
              return DTERR_BAD_FORMAT;
2287
0
            ptype = val;
2288
0
            break;
2289
2290
0
          case ISOTIME:
2291
0
            tmask = 0;
2292
            /* reject consecutive unhandled units */
2293
0
            if (ptype != 0)
2294
0
              return DTERR_BAD_FORMAT;
2295
0
            ptype = val;
2296
0
            break;
2297
2298
0
          case UNKNOWN_FIELD:
2299
2300
            /*
2301
             * Before giving up and declaring error, check to see
2302
             * if it is an all-alpha timezone name.
2303
             */
2304
0
            namedTz = pg_tzset(field[i]);
2305
0
            if (!namedTz)
2306
0
              return DTERR_BAD_FORMAT;
2307
            /* we'll apply the zone setting below */
2308
0
            tmask = DTK_M(TZ);
2309
0
            break;
2310
2311
0
          default:
2312
0
            return DTERR_BAD_FORMAT;
2313
0
        }
2314
0
        break;
2315
2316
0
      default:
2317
0
        return DTERR_BAD_FORMAT;
2318
0
    }
2319
2320
0
    if (tmask & fmask)
2321
0
      return DTERR_BAD_FORMAT;
2322
0
    fmask |= tmask;
2323
0
  }              /* end loop over fields */
2324
2325
  /* reject if prefix type appeared and was never handled */
2326
0
  if (ptype != 0)
2327
0
    return DTERR_BAD_FORMAT;
2328
2329
  /* do final checking/adjustment of Y/M/D fields */
2330
0
  dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
2331
0
  if (dterr)
2332
0
    return dterr;
2333
2334
  /* handle AM/PM */
2335
0
  if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
2336
0
    return DTERR_FIELD_OVERFLOW;
2337
0
  if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
2338
0
    tm->tm_hour = 0;
2339
0
  else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
2340
0
    tm->tm_hour += HOURS_PER_DAY / 2;
2341
2342
  /* check for time overflow */
2343
0
  if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec))
2344
0
    return DTERR_FIELD_OVERFLOW;
2345
2346
0
  if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2347
0
    return DTERR_BAD_FORMAT;
2348
2349
  /*
2350
   * If we had a full timezone spec, compute the offset (we could not do it
2351
   * before, because we may need the date to resolve DST status).
2352
   */
2353
0
  if (namedTz != NULL)
2354
0
  {
2355
0
    long int  gmtoff;
2356
2357
    /* daylight savings time modifier disallowed with full TZ */
2358
0
    if (fmask & DTK_M(DTZMOD))
2359
0
      return DTERR_BAD_FORMAT;
2360
2361
    /* if non-DST zone, we do not need to know the date */
2362
0
    if (pg_get_timezone_offset(namedTz, &gmtoff))
2363
0
    {
2364
0
      *tzp = -(int) gmtoff;
2365
0
    }
2366
0
    else
2367
0
    {
2368
      /* a date has to be specified */
2369
0
      if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2370
0
        return DTERR_BAD_FORMAT;
2371
0
      *tzp = DetermineTimeZoneOffset(tm, namedTz);
2372
0
    }
2373
0
  }
2374
2375
  /*
2376
   * Likewise, if we had a dynamic timezone abbreviation, resolve it now.
2377
   */
2378
0
  if (abbrevTz != NULL)
2379
0
  {
2380
0
    struct pg_tm tt,
2381
0
           *tmp = &tt;
2382
2383
    /*
2384
     * daylight savings time modifier but no standard timezone? then error
2385
     */
2386
0
    if (fmask & DTK_M(DTZMOD))
2387
0
      return DTERR_BAD_FORMAT;
2388
2389
0
    if ((fmask & DTK_DATE_M) == 0)
2390
0
      GetCurrentDateTime(tmp);
2391
0
    else
2392
0
    {
2393
      /* a date has to be specified */
2394
0
      if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2395
0
        return DTERR_BAD_FORMAT;
2396
0
      tmp->tm_year = tm->tm_year;
2397
0
      tmp->tm_mon = tm->tm_mon;
2398
0
      tmp->tm_mday = tm->tm_mday;
2399
0
    }
2400
0
    tmp->tm_hour = tm->tm_hour;
2401
0
    tmp->tm_min = tm->tm_min;
2402
0
    tmp->tm_sec = tm->tm_sec;
2403
0
    *tzp = DetermineTimeZoneAbbrevOffset(tmp, abbrev, abbrevTz);
2404
0
    tm->tm_isdst = tmp->tm_isdst;
2405
0
  }
2406
2407
  /* timezone not specified? then use session timezone */
2408
0
  if (tzp != NULL && !(fmask & DTK_M(TZ)))
2409
0
  {
2410
0
    struct pg_tm tt,
2411
0
           *tmp = &tt;
2412
2413
    /*
2414
     * daylight savings time modifier but no standard timezone? then error
2415
     */
2416
0
    if (fmask & DTK_M(DTZMOD))
2417
0
      return DTERR_BAD_FORMAT;
2418
2419
0
    if ((fmask & DTK_DATE_M) == 0)
2420
0
      GetCurrentDateTime(tmp);
2421
0
    else
2422
0
    {
2423
      /* a date has to be specified */
2424
0
      if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2425
0
        return DTERR_BAD_FORMAT;
2426
0
      tmp->tm_year = tm->tm_year;
2427
0
      tmp->tm_mon = tm->tm_mon;
2428
0
      tmp->tm_mday = tm->tm_mday;
2429
0
    }
2430
0
    tmp->tm_hour = tm->tm_hour;
2431
0
    tmp->tm_min = tm->tm_min;
2432
0
    tmp->tm_sec = tm->tm_sec;
2433
0
    *tzp = DetermineTimeZoneOffset(tmp, session_timezone);
2434
0
    tm->tm_isdst = tmp->tm_isdst;
2435
0
  }
2436
2437
0
  return 0;
2438
0
}
2439
2440
/* DecodeDate()
2441
 * Decode date string which includes delimiters.
2442
 * Return 0 if okay, a DTERR code if not.
2443
 *
2444
 *  str: field to be parsed
2445
 *  fmask: bitmask for field types already seen
2446
 *  *tmask: receives bitmask for fields found here
2447
 *  *is2digits: set to true if we find 2-digit year
2448
 *  *tm: field values are stored into appropriate members of this struct
2449
 */
2450
static int
2451
DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
2452
       struct pg_tm *tm)
2453
0
{
2454
0
  fsec_t    fsec;
2455
0
  int     nf = 0;
2456
0
  int     i,
2457
0
        len;
2458
0
  int     dterr;
2459
0
  bool    haveTextMonth = false;
2460
0
  int     type,
2461
0
        val,
2462
0
        dmask = 0;
2463
0
  char     *field[MAXDATEFIELDS];
2464
2465
0
  *tmask = 0;
2466
2467
  /* parse this string... */
2468
0
  while (*str != '\0' && nf < MAXDATEFIELDS)
2469
0
  {
2470
    /* skip field separators */
2471
0
    while (*str != '\0' && !isalnum((unsigned char) *str))
2472
0
      str++;
2473
2474
0
    if (*str == '\0')
2475
0
      return DTERR_BAD_FORMAT; /* end of string after separator */
2476
2477
0
    field[nf] = str;
2478
0
    if (isdigit((unsigned char) *str))
2479
0
    {
2480
0
      while (isdigit((unsigned char) *str))
2481
0
        str++;
2482
0
    }
2483
0
    else if (isalpha((unsigned char) *str))
2484
0
    {
2485
0
      while (isalpha((unsigned char) *str))
2486
0
        str++;
2487
0
    }
2488
2489
    /* Just get rid of any non-digit, non-alpha characters... */
2490
0
    if (*str != '\0')
2491
0
      *str++ = '\0';
2492
0
    nf++;
2493
0
  }
2494
2495
  /* look first for text fields, since that will be unambiguous month */
2496
0
  for (i = 0; i < nf; i++)
2497
0
  {
2498
0
    if (isalpha((unsigned char) *field[i]))
2499
0
    {
2500
0
      type = DecodeSpecial(i, field[i], &val);
2501
0
      if (type == IGNORE_DTF)
2502
0
        continue;
2503
2504
0
      dmask = DTK_M(type);
2505
0
      switch (type)
2506
0
      {
2507
0
        case MONTH:
2508
0
          tm->tm_mon = val;
2509
0
          haveTextMonth = true;
2510
0
          break;
2511
2512
0
        default:
2513
0
          return DTERR_BAD_FORMAT;
2514
0
      }
2515
0
      if (fmask & dmask)
2516
0
        return DTERR_BAD_FORMAT;
2517
2518
0
      fmask |= dmask;
2519
0
      *tmask |= dmask;
2520
2521
      /* mark this field as being completed */
2522
0
      field[i] = NULL;
2523
0
    }
2524
0
  }
2525
2526
  /* now pick up remaining numeric fields */
2527
0
  for (i = 0; i < nf; i++)
2528
0
  {
2529
0
    if (field[i] == NULL)
2530
0
      continue;
2531
2532
0
    if ((len = strlen(field[i])) <= 0)
2533
0
      return DTERR_BAD_FORMAT;
2534
2535
0
    dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
2536
0
               &dmask, tm,
2537
0
               &fsec, is2digits);
2538
0
    if (dterr)
2539
0
      return dterr;
2540
2541
0
    if (fmask & dmask)
2542
0
      return DTERR_BAD_FORMAT;
2543
2544
0
    fmask |= dmask;
2545
0
    *tmask |= dmask;
2546
0
  }
2547
2548
0
  if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
2549
0
    return DTERR_BAD_FORMAT;
2550
2551
  /* validation of the field values must wait until ValidateDate() */
2552
2553
0
  return 0;
2554
0
}
2555
2556
/* ValidateDate()
2557
 * Check valid year/month/day values, handle BC and DOY cases
2558
 * Return 0 if okay, a DTERR code if not.
2559
 */
2560
int
2561
ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
2562
       struct pg_tm *tm)
2563
0
{
2564
0
  if (fmask & DTK_M(YEAR))
2565
0
  {
2566
0
    if (isjulian)
2567
0
    {
2568
      /* tm_year is correct and should not be touched */
2569
0
    }
2570
0
    else if (bc)
2571
0
    {
2572
      /* there is no year zero in AD/BC notation */
2573
0
      if (tm->tm_year <= 0)
2574
0
        return DTERR_FIELD_OVERFLOW;
2575
      /* internally, we represent 1 BC as year zero, 2 BC as -1, etc */
2576
0
      tm->tm_year = -(tm->tm_year - 1);
2577
0
    }
2578
0
    else if (is2digits)
2579
0
    {
2580
      /* process 1 or 2-digit input as 1970-2069 AD, allow '0' and '00' */
2581
0
      if (tm->tm_year < 0) /* just paranoia */
2582
0
        return DTERR_FIELD_OVERFLOW;
2583
0
      if (tm->tm_year < 70)
2584
0
        tm->tm_year += 2000;
2585
0
      else if (tm->tm_year < 100)
2586
0
        tm->tm_year += 1900;
2587
0
    }
2588
0
    else
2589
0
    {
2590
      /* there is no year zero in AD/BC notation */
2591
0
      if (tm->tm_year <= 0)
2592
0
        return DTERR_FIELD_OVERFLOW;
2593
0
    }
2594
0
  }
2595
2596
  /* now that we have correct year, decode DOY */
2597
0
  if (fmask & DTK_M(DOY))
2598
0
  {
2599
0
    j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
2600
0
         &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2601
0
  }
2602
2603
  /* check for valid month */
2604
0
  if (fmask & DTK_M(MONTH))
2605
0
  {
2606
0
    if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
2607
0
      return DTERR_MD_FIELD_OVERFLOW;
2608
0
  }
2609
2610
  /* minimal check for valid day */
2611
0
  if (fmask & DTK_M(DAY))
2612
0
  {
2613
0
    if (tm->tm_mday < 1 || tm->tm_mday > 31)
2614
0
      return DTERR_MD_FIELD_OVERFLOW;
2615
0
  }
2616
2617
0
  if ((fmask & DTK_DATE_M) == DTK_DATE_M)
2618
0
  {
2619
    /*
2620
     * Check for valid day of month, now that we know for sure the month
2621
     * and year.  Note we don't use MD_FIELD_OVERFLOW here, since it seems
2622
     * unlikely that "Feb 29" is a YMD-order error.
2623
     */
2624
0
    if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2625
0
      return DTERR_FIELD_OVERFLOW;
2626
0
  }
2627
2628
0
  return 0;
2629
0
}
2630
2631
2632
/* DecodeTimeCommon()
2633
 * Decode time string which includes delimiters.
2634
 * Return 0 if okay, a DTERR code if not.
2635
 * tmask and itm are output parameters.
2636
 *
2637
 * This code is shared between the timestamp and interval cases.
2638
 * We return a struct pg_itm (of which only the tm_usec, tm_sec, tm_min,
2639
 * and tm_hour fields are used) and let the wrapper functions below
2640
 * convert and range-check as necessary.
2641
 */
2642
static int
2643
DecodeTimeCommon(char *str, int fmask, int range,
2644
         int *tmask, struct pg_itm *itm)
2645
0
{
2646
0
  char     *cp;
2647
0
  int     dterr;
2648
0
  fsec_t    fsec = 0;
2649
2650
0
  *tmask = DTK_TIME_M;
2651
2652
0
  errno = 0;
2653
0
  itm->tm_hour = strtoi64(str, &cp, 10);
2654
0
  if (errno == ERANGE)
2655
0
    return DTERR_FIELD_OVERFLOW;
2656
0
  if (*cp != ':')
2657
0
    return DTERR_BAD_FORMAT;
2658
0
  errno = 0;
2659
0
  itm->tm_min = strtoint(cp + 1, &cp, 10);
2660
0
  if (errno == ERANGE)
2661
0
    return DTERR_FIELD_OVERFLOW;
2662
0
  if (*cp == '\0')
2663
0
  {
2664
0
    itm->tm_sec = 0;
2665
    /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */
2666
0
    if (range == (INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND)))
2667
0
    {
2668
0
      if (itm->tm_hour > INT_MAX || itm->tm_hour < INT_MIN)
2669
0
        return DTERR_FIELD_OVERFLOW;
2670
0
      itm->tm_sec = itm->tm_min;
2671
0
      itm->tm_min = (int) itm->tm_hour;
2672
0
      itm->tm_hour = 0;
2673
0
    }
2674
0
  }
2675
0
  else if (*cp == '.')
2676
0
  {
2677
    /* always assume mm:ss.sss is MINUTE TO SECOND */
2678
0
    dterr = ParseFractionalSecond(cp, &fsec);
2679
0
    if (dterr)
2680
0
      return dterr;
2681
0
    if (itm->tm_hour > INT_MAX || itm->tm_hour < INT_MIN)
2682
0
      return DTERR_FIELD_OVERFLOW;
2683
0
    itm->tm_sec = itm->tm_min;
2684
0
    itm->tm_min = (int) itm->tm_hour;
2685
0
    itm->tm_hour = 0;
2686
0
  }
2687
0
  else if (*cp == ':')
2688
0
  {
2689
0
    errno = 0;
2690
0
    itm->tm_sec = strtoint(cp + 1, &cp, 10);
2691
0
    if (errno == ERANGE)
2692
0
      return DTERR_FIELD_OVERFLOW;
2693
0
    if (*cp == '.')
2694
0
    {
2695
0
      dterr = ParseFractionalSecond(cp, &fsec);
2696
0
      if (dterr)
2697
0
        return dterr;
2698
0
    }
2699
0
    else if (*cp != '\0')
2700
0
      return DTERR_BAD_FORMAT;
2701
0
  }
2702
0
  else
2703
0
    return DTERR_BAD_FORMAT;
2704
2705
  /* do a sanity check; but caller must check the range of tm_hour */
2706
0
  if (itm->tm_hour < 0 ||
2707
0
    itm->tm_min < 0 || itm->tm_min > MINS_PER_HOUR - 1 ||
2708
0
    itm->tm_sec < 0 || itm->tm_sec > SECS_PER_MINUTE ||
2709
0
    fsec < 0 || fsec > USECS_PER_SEC)
2710
0
    return DTERR_FIELD_OVERFLOW;
2711
2712
0
  itm->tm_usec = (int) fsec;
2713
2714
0
  return 0;
2715
0
}
2716
2717
/* DecodeTime()
2718
 * Decode time string which includes delimiters.
2719
 * Return 0 if okay, a DTERR code if not.
2720
 *
2721
 * This version is used for timestamps.  The results are returned into
2722
 * the tm_hour/tm_min/tm_sec fields of *tm, and microseconds into *fsec.
2723
 */
2724
static int
2725
DecodeTime(char *str, int fmask, int range,
2726
       int *tmask, struct pg_tm *tm, fsec_t *fsec)
2727
0
{
2728
0
  struct pg_itm itm;
2729
0
  int     dterr;
2730
2731
0
  dterr = DecodeTimeCommon(str, fmask, range,
2732
0
               tmask, &itm);
2733
0
  if (dterr)
2734
0
    return dterr;
2735
2736
0
  if (itm.tm_hour > INT_MAX)
2737
0
    return DTERR_FIELD_OVERFLOW;
2738
0
  tm->tm_hour = (int) itm.tm_hour;
2739
0
  tm->tm_min = itm.tm_min;
2740
0
  tm->tm_sec = itm.tm_sec;
2741
0
  *fsec = itm.tm_usec;
2742
2743
0
  return 0;
2744
0
}
2745
2746
/* DecodeTimeForInterval()
2747
 * Decode time string which includes delimiters.
2748
 * Return 0 if okay, a DTERR code if not.
2749
 *
2750
 * This version is used for intervals.  The results are returned into
2751
 * itm_in->tm_usec.
2752
 */
2753
static int
2754
DecodeTimeForInterval(char *str, int fmask, int range,
2755
            int *tmask, struct pg_itm_in *itm_in)
2756
0
{
2757
0
  struct pg_itm itm;
2758
0
  int     dterr;
2759
2760
0
  dterr = DecodeTimeCommon(str, fmask, range,
2761
0
               tmask, &itm);
2762
0
  if (dterr)
2763
0
    return dterr;
2764
2765
0
  itm_in->tm_usec = itm.tm_usec;
2766
0
  if (!int64_multiply_add(itm.tm_hour, USECS_PER_HOUR, &itm_in->tm_usec) ||
2767
0
    !int64_multiply_add(itm.tm_min, USECS_PER_MINUTE, &itm_in->tm_usec) ||
2768
0
    !int64_multiply_add(itm.tm_sec, USECS_PER_SEC, &itm_in->tm_usec))
2769
0
    return DTERR_FIELD_OVERFLOW;
2770
2771
0
  return 0;
2772
0
}
2773
2774
2775
/* DecodeNumber()
2776
 * Interpret plain numeric field as a date value in context.
2777
 * Return 0 if okay, a DTERR code if not.
2778
 */
2779
static int
2780
DecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,
2781
       int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
2782
0
{
2783
0
  int     val;
2784
0
  char     *cp;
2785
0
  int     dterr;
2786
2787
0
  *tmask = 0;
2788
2789
0
  errno = 0;
2790
0
  val = strtoint(str, &cp, 10);
2791
0
  if (errno == ERANGE)
2792
0
    return DTERR_FIELD_OVERFLOW;
2793
0
  if (cp == str)
2794
0
    return DTERR_BAD_FORMAT;
2795
2796
0
  if (*cp == '.')
2797
0
  {
2798
    /*
2799
     * More than two digits before decimal point? Then could be a date or
2800
     * a run-together time: 2001.360 20011225 040506.789
2801
     */
2802
0
    if (cp - str > 2)
2803
0
    {
2804
0
      dterr = DecodeNumberField(flen, str,
2805
0
                    (fmask | DTK_DATE_M),
2806
0
                    tmask, tm,
2807
0
                    fsec, is2digits);
2808
0
      if (dterr < 0)
2809
0
        return dterr;
2810
0
      return 0;
2811
0
    }
2812
2813
0
    dterr = ParseFractionalSecond(cp, fsec);
2814
0
    if (dterr)
2815
0
      return dterr;
2816
0
  }
2817
0
  else if (*cp != '\0')
2818
0
    return DTERR_BAD_FORMAT;
2819
2820
  /* Special case for day of year */
2821
0
  if (flen == 3 && (fmask & DTK_DATE_M) == DTK_M(YEAR) && val >= 1 &&
2822
0
    val <= 366)
2823
0
  {
2824
0
    *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
2825
0
    tm->tm_yday = val;
2826
    /* tm_mon and tm_mday can't actually be set yet ... */
2827
0
    return 0;
2828
0
  }
2829
2830
  /* Switch based on what we have so far */
2831
0
  switch (fmask & DTK_DATE_M)
2832
0
  {
2833
0
    case 0:
2834
2835
      /*
2836
       * Nothing so far; make a decision about what we think the input
2837
       * is.  There used to be lots of heuristics here, but the
2838
       * consensus now is to be paranoid.  It *must* be either
2839
       * YYYY-MM-DD (with a more-than-two-digit year field), or the
2840
       * field order defined by DateOrder.
2841
       */
2842
0
      if (flen >= 3 || DateOrder == DATEORDER_YMD)
2843
0
      {
2844
0
        *tmask = DTK_M(YEAR);
2845
0
        tm->tm_year = val;
2846
0
      }
2847
0
      else if (DateOrder == DATEORDER_DMY)
2848
0
      {
2849
0
        *tmask = DTK_M(DAY);
2850
0
        tm->tm_mday = val;
2851
0
      }
2852
0
      else
2853
0
      {
2854
0
        *tmask = DTK_M(MONTH);
2855
0
        tm->tm_mon = val;
2856
0
      }
2857
0
      break;
2858
2859
0
    case (DTK_M(YEAR)):
2860
      /* Must be at second field of YY-MM-DD */
2861
0
      *tmask = DTK_M(MONTH);
2862
0
      tm->tm_mon = val;
2863
0
      break;
2864
2865
0
    case (DTK_M(MONTH)):
2866
0
      if (haveTextMonth)
2867
0
      {
2868
        /*
2869
         * We are at the first numeric field of a date that included a
2870
         * textual month name.  We want to support the variants
2871
         * MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous
2872
         * inputs.  We will also accept MON-DD-YY or DD-MON-YY in
2873
         * either DMY or MDY modes, as well as YY-MON-DD in YMD mode.
2874
         */
2875
0
        if (flen >= 3 || DateOrder == DATEORDER_YMD)
2876
0
        {
2877
0
          *tmask = DTK_M(YEAR);
2878
0
          tm->tm_year = val;
2879
0
        }
2880
0
        else
2881
0
        {
2882
0
          *tmask = DTK_M(DAY);
2883
0
          tm->tm_mday = val;
2884
0
        }
2885
0
      }
2886
0
      else
2887
0
      {
2888
        /* Must be at second field of MM-DD-YY */
2889
0
        *tmask = DTK_M(DAY);
2890
0
        tm->tm_mday = val;
2891
0
      }
2892
0
      break;
2893
2894
0
    case (DTK_M(YEAR) | DTK_M(MONTH)):
2895
0
      if (haveTextMonth)
2896
0
      {
2897
        /* Need to accept DD-MON-YYYY even in YMD mode */
2898
0
        if (flen >= 3 && *is2digits)
2899
0
        {
2900
          /* Guess that first numeric field is day was wrong */
2901
0
          *tmask = DTK_M(DAY);  /* YEAR is already set */
2902
0
          tm->tm_mday = tm->tm_year;
2903
0
          tm->tm_year = val;
2904
0
          *is2digits = false;
2905
0
        }
2906
0
        else
2907
0
        {
2908
0
          *tmask = DTK_M(DAY);
2909
0
          tm->tm_mday = val;
2910
0
        }
2911
0
      }
2912
0
      else
2913
0
      {
2914
        /* Must be at third field of YY-MM-DD */
2915
0
        *tmask = DTK_M(DAY);
2916
0
        tm->tm_mday = val;
2917
0
      }
2918
0
      break;
2919
2920
0
    case (DTK_M(DAY)):
2921
      /* Must be at second field of DD-MM-YY */
2922
0
      *tmask = DTK_M(MONTH);
2923
0
      tm->tm_mon = val;
2924
0
      break;
2925
2926
0
    case (DTK_M(MONTH) | DTK_M(DAY)):
2927
      /* Must be at third field of DD-MM-YY or MM-DD-YY */
2928
0
      *tmask = DTK_M(YEAR);
2929
0
      tm->tm_year = val;
2930
0
      break;
2931
2932
0
    case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
2933
      /* we have all the date, so it must be a time field */
2934
0
      dterr = DecodeNumberField(flen, str, fmask,
2935
0
                    tmask, tm,
2936
0
                    fsec, is2digits);
2937
0
      if (dterr < 0)
2938
0
        return dterr;
2939
0
      return 0;
2940
2941
0
    default:
2942
      /* Anything else is bogus input */
2943
0
      return DTERR_BAD_FORMAT;
2944
0
  }
2945
2946
  /*
2947
   * When processing a year field, mark it for adjustment if it's only one
2948
   * or two digits.
2949
   */
2950
0
  if (*tmask == DTK_M(YEAR))
2951
0
    *is2digits = (flen <= 2);
2952
2953
0
  return 0;
2954
0
}
2955
2956
2957
/* DecodeNumberField()
2958
 * Interpret numeric string as a concatenated date or time field.
2959
 * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
2960
 *
2961
 * Use the context of previously decoded fields to help with
2962
 * the interpretation.
2963
 */
2964
static int
2965
DecodeNumberField(int len, char *str, int fmask,
2966
          int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
2967
0
{
2968
0
  char     *cp;
2969
2970
  /*
2971
   * This function was originally meant to cope only with DTK_NUMBER fields,
2972
   * but we now sometimes abuse it to parse (parts of) DTK_DATE fields,
2973
   * which can contain letters and other punctuation.  Reject if it's not a
2974
   * valid DTK_NUMBER, that is digits and decimal point(s).  (ParseFraction
2975
   * will reject if there's more than one decimal point.)
2976
   */
2977
0
  if (strspn(str, "0123456789.") != len)
2978
0
    return DTERR_BAD_FORMAT;
2979
2980
  /*
2981
   * Have a decimal point? Then this is a date or something with a seconds
2982
   * field...
2983
   */
2984
0
  if ((cp = strchr(str, '.')) != NULL)
2985
0
  {
2986
0
    int     dterr;
2987
2988
    /* Convert the fraction and store at *fsec */
2989
0
    dterr = ParseFractionalSecond(cp, fsec);
2990
0
    if (dterr)
2991
0
      return dterr;
2992
    /* Now truncate off the fraction for further processing */
2993
0
    *cp = '\0';
2994
0
    len = strlen(str);
2995
0
  }
2996
  /* No decimal point and no complete date yet? */
2997
0
  else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2998
0
  {
2999
0
    if (len >= 6)
3000
0
    {
3001
0
      *tmask = DTK_DATE_M;
3002
3003
      /*
3004
       * Start from end and consider first 2 as Day, next 2 as Month,
3005
       * and the rest as Year.
3006
       */
3007
0
      tm->tm_mday = atoi(str + (len - 2));
3008
0
      *(str + (len - 2)) = '\0';
3009
0
      tm->tm_mon = atoi(str + (len - 4));
3010
0
      *(str + (len - 4)) = '\0';
3011
0
      tm->tm_year = atoi(str);
3012
0
      if ((len - 4) == 2)
3013
0
        *is2digits = true;
3014
3015
0
      return DTK_DATE;
3016
0
    }
3017
0
  }
3018
3019
  /* not all time fields are specified? */
3020
0
  if ((fmask & DTK_TIME_M) != DTK_TIME_M)
3021
0
  {
3022
    /* hhmmss */
3023
0
    if (len == 6)
3024
0
    {
3025
0
      *tmask = DTK_TIME_M;
3026
0
      tm->tm_sec = atoi(str + 4);
3027
0
      *(str + 4) = '\0';
3028
0
      tm->tm_min = atoi(str + 2);
3029
0
      *(str + 2) = '\0';
3030
0
      tm->tm_hour = atoi(str);
3031
3032
0
      return DTK_TIME;
3033
0
    }
3034
    /* hhmm? */
3035
0
    else if (len == 4)
3036
0
    {
3037
0
      *tmask = DTK_TIME_M;
3038
0
      tm->tm_sec = 0;
3039
0
      tm->tm_min = atoi(str + 2);
3040
0
      *(str + 2) = '\0';
3041
0
      tm->tm_hour = atoi(str);
3042
3043
0
      return DTK_TIME;
3044
0
    }
3045
0
  }
3046
3047
0
  return DTERR_BAD_FORMAT;
3048
0
}
3049
3050
3051
/* DecodeTimezone()
3052
 * Interpret string as a numeric timezone.
3053
 *
3054
 * Return 0 if okay (and set *tzp), a DTERR code if not okay.
3055
 */
3056
int
3057
DecodeTimezone(const char *str, int *tzp)
3058
0
{
3059
0
  int     tz;
3060
0
  int     hr,
3061
0
        min,
3062
0
        sec = 0;
3063
0
  char     *cp;
3064
3065
  /* leading character must be "+" or "-" */
3066
0
  if (*str != '+' && *str != '-')
3067
0
    return DTERR_BAD_FORMAT;
3068
3069
0
  errno = 0;
3070
0
  hr = strtoint(str + 1, &cp, 10);
3071
0
  if (errno == ERANGE)
3072
0
    return DTERR_TZDISP_OVERFLOW;
3073
3074
  /* explicit delimiter? */
3075
0
  if (*cp == ':')
3076
0
  {
3077
0
    errno = 0;
3078
0
    min = strtoint(cp + 1, &cp, 10);
3079
0
    if (errno == ERANGE)
3080
0
      return DTERR_TZDISP_OVERFLOW;
3081
0
    if (*cp == ':')
3082
0
    {
3083
0
      errno = 0;
3084
0
      sec = strtoint(cp + 1, &cp, 10);
3085
0
      if (errno == ERANGE)
3086
0
        return DTERR_TZDISP_OVERFLOW;
3087
0
    }
3088
0
  }
3089
  /* otherwise, might have run things together... */
3090
0
  else if (*cp == '\0' && strlen(str) > 3)
3091
0
  {
3092
0
    min = hr % 100;
3093
0
    hr = hr / 100;
3094
    /* we could, but don't, support a run-together hhmmss format */
3095
0
  }
3096
0
  else
3097
0
    min = 0;
3098
3099
  /* Range-check the values; see notes in datatype/timestamp.h */
3100
0
  if (hr < 0 || hr > MAX_TZDISP_HOUR)
3101
0
    return DTERR_TZDISP_OVERFLOW;
3102
0
  if (min < 0 || min >= MINS_PER_HOUR)
3103
0
    return DTERR_TZDISP_OVERFLOW;
3104
0
  if (sec < 0 || sec >= SECS_PER_MINUTE)
3105
0
    return DTERR_TZDISP_OVERFLOW;
3106
3107
0
  tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec;
3108
0
  if (*str == '-')
3109
0
    tz = -tz;
3110
3111
0
  *tzp = -tz;
3112
3113
0
  if (*cp != '\0')
3114
0
    return DTERR_BAD_FORMAT;
3115
3116
0
  return 0;
3117
0
}
3118
3119
3120
/* DecodeTimezoneAbbrev()
3121
 * Interpret string as a timezone abbreviation, if possible.
3122
 *
3123
 * Sets *ftype to an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if
3124
 * string is not any known abbreviation.  On success, set *offset and *tz to
3125
 * represent the UTC offset (for TZ or DTZ) or underlying zone (for DYNTZ).
3126
 * Note that full timezone names (such as America/New_York) are not handled
3127
 * here, mostly for historical reasons.
3128
 *
3129
 * The function result is 0 or a DTERR code; in the latter case, *extra
3130
 * is filled as needed.  Note that unknown-abbreviation is not considered
3131
 * an error case.  Also note that many callers assume that the DTERR code
3132
 * is one that DateTimeParseError does not require "str" or "datatype"
3133
 * strings for.
3134
 *
3135
 * Given string must be lowercased already.
3136
 *
3137
 * Implement a cache lookup since it is likely that dates
3138
 *  will be related in format.
3139
 */
3140
int
3141
DecodeTimezoneAbbrev(int field, const char *lowtoken,
3142
           int *ftype, int *offset, pg_tz **tz,
3143
           DateTimeErrorExtra *extra)
3144
0
{
3145
0
  TzAbbrevCache *tzc = &tzabbrevcache[field];
3146
0
  bool    isfixed;
3147
0
  int     isdst;
3148
0
  const datetkn *tp;
3149
3150
  /*
3151
   * Do we have a cached result?  Use strncmp so that we match truncated
3152
   * names, although we shouldn't really see that happen with normal
3153
   * abbreviations.
3154
   */
3155
0
  if (strncmp(lowtoken, tzc->abbrev, TOKMAXLEN) == 0)
3156
0
  {
3157
0
    *ftype = tzc->ftype;
3158
0
    *offset = tzc->offset;
3159
0
    *tz = tzc->tz;
3160
0
    return 0;
3161
0
  }
3162
3163
  /*
3164
   * See if the current session_timezone recognizes it.  Checking this
3165
   * before zoneabbrevtbl allows us to correctly handle abbreviations whose
3166
   * meaning varies across zones, such as "LMT".
3167
   */
3168
0
  if (session_timezone &&
3169
0
    TimeZoneAbbrevIsKnown(lowtoken, session_timezone,
3170
0
                &isfixed, offset, &isdst))
3171
0
  {
3172
0
    *ftype = (isfixed ? (isdst ? DTZ : TZ) : DYNTZ);
3173
0
    *tz = (isfixed ? NULL : session_timezone);
3174
    /* flip sign to agree with the convention used in zoneabbrevtbl */
3175
0
    *offset = -(*offset);
3176
    /* cache result; use strlcpy to truncate name if necessary */
3177
0
    strlcpy(tzc->abbrev, lowtoken, TOKMAXLEN + 1);
3178
0
    tzc->ftype = *ftype;
3179
0
    tzc->offset = *offset;
3180
0
    tzc->tz = *tz;
3181
0
    return 0;
3182
0
  }
3183
3184
  /* Nope, so look in zoneabbrevtbl */
3185
0
  if (zoneabbrevtbl)
3186
0
    tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
3187
0
             zoneabbrevtbl->numabbrevs);
3188
0
  else
3189
0
    tp = NULL;
3190
0
  if (tp == NULL)
3191
0
  {
3192
0
    *ftype = UNKNOWN_FIELD;
3193
0
    *offset = 0;
3194
0
    *tz = NULL;
3195
    /* failure results are not cached */
3196
0
  }
3197
0
  else
3198
0
  {
3199
0
    *ftype = tp->type;
3200
0
    if (tp->type == DYNTZ)
3201
0
    {
3202
0
      *offset = 0;
3203
0
      *tz = FetchDynamicTimeZone(zoneabbrevtbl, tp, extra);
3204
0
      if (*tz == NULL)
3205
0
        return DTERR_BAD_ZONE_ABBREV;
3206
0
    }
3207
0
    else
3208
0
    {
3209
0
      *offset = tp->value;
3210
0
      *tz = NULL;
3211
0
    }
3212
3213
    /* cache result; use strlcpy to truncate name if necessary */
3214
0
    strlcpy(tzc->abbrev, lowtoken, TOKMAXLEN + 1);
3215
0
    tzc->ftype = *ftype;
3216
0
    tzc->offset = *offset;
3217
0
    tzc->tz = *tz;
3218
0
  }
3219
3220
0
  return 0;
3221
0
}
3222
3223
/*
3224
 * Reset tzabbrevcache after a change in session_timezone.
3225
 */
3226
void
3227
ClearTimeZoneAbbrevCache(void)
3228
2
{
3229
2
  memset(tzabbrevcache, 0, sizeof(tzabbrevcache));
3230
2
}
3231
3232
3233
/* DecodeSpecial()
3234
 * Decode text string using lookup table.
3235
 *
3236
 * Recognizes the keywords listed in datetktbl.
3237
 * Note: at one time this would also recognize timezone abbreviations,
3238
 * but no more; use DecodeTimezoneAbbrev for that.
3239
 *
3240
 * Given string must be lowercased already.
3241
 *
3242
 * Implement a cache lookup since it is likely that dates
3243
 *  will be related in format.
3244
 */
3245
int
3246
DecodeSpecial(int field, const char *lowtoken, int *val)
3247
0
{
3248
0
  int     type;
3249
0
  const datetkn *tp;
3250
3251
0
  tp = datecache[field];
3252
  /* use strncmp so that we match truncated tokens */
3253
0
  if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3254
0
  {
3255
0
    tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
3256
0
  }
3257
0
  if (tp == NULL)
3258
0
  {
3259
0
    type = UNKNOWN_FIELD;
3260
0
    *val = 0;
3261
0
  }
3262
0
  else
3263
0
  {
3264
0
    datecache[field] = tp;
3265
0
    type = tp->type;
3266
0
    *val = tp->value;
3267
0
  }
3268
3269
0
  return type;
3270
0
}
3271
3272
3273
/* DecodeTimezoneName()
3274
 * Interpret string as a timezone abbreviation or name.
3275
 * Throw error if the name is not recognized.
3276
 *
3277
 * The return value indicates what kind of zone identifier it is:
3278
 *  TZNAME_FIXED_OFFSET: fixed offset from UTC
3279
 *  TZNAME_DYNTZ: dynamic timezone abbreviation
3280
 *  TZNAME_ZONE: full tzdb zone name
3281
 *
3282
 * For TZNAME_FIXED_OFFSET, *offset receives the UTC offset (in seconds,
3283
 * with ISO sign convention: positive is east of Greenwich).
3284
 * For the other two cases, *tz receives the timezone struct representing
3285
 * the zone name or the abbreviation's underlying zone.
3286
 */
3287
int
3288
DecodeTimezoneName(const char *tzname, int *offset, pg_tz **tz)
3289
0
{
3290
0
  char     *lowzone;
3291
0
  int     dterr,
3292
0
        type;
3293
0
  DateTimeErrorExtra extra;
3294
3295
  /*
3296
   * First we look in the timezone abbreviation table (to handle cases like
3297
   * "EST"), and if that fails, we look in the timezone database (to handle
3298
   * cases like "America/New_York").  This matches the order in which
3299
   * timestamp input checks the cases; it's important because the timezone
3300
   * database unwisely uses a few zone names that are identical to offset
3301
   * abbreviations.
3302
   */
3303
3304
  /* DecodeTimezoneAbbrev requires lowercase input */
3305
0
  lowzone = downcase_truncate_identifier(tzname,
3306
0
                       strlen(tzname),
3307
0
                       false);
3308
3309
0
  dterr = DecodeTimezoneAbbrev(0, lowzone, &type, offset, tz, &extra);
3310
0
  if (dterr)
3311
0
    DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
3312
3313
0
  if (type == TZ || type == DTZ)
3314
0
  {
3315
    /* fixed-offset abbreviation, return the offset */
3316
0
    return TZNAME_FIXED_OFFSET;
3317
0
  }
3318
0
  else if (type == DYNTZ)
3319
0
  {
3320
    /* dynamic-offset abbreviation, return its referenced timezone */
3321
0
    return TZNAME_DYNTZ;
3322
0
  }
3323
0
  else
3324
0
  {
3325
    /* try it as a full zone name */
3326
0
    *tz = pg_tzset(tzname);
3327
0
    if (*tz == NULL)
3328
0
      ereport(ERROR,
3329
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3330
0
           errmsg("time zone \"%s\" not recognized", tzname)));
3331
0
    return TZNAME_ZONE;
3332
0
  }
3333
0
}
3334
3335
/* DecodeTimezoneNameToTz()
3336
 * Interpret string as a timezone abbreviation or name.
3337
 * Throw error if the name is not recognized.
3338
 *
3339
 * This is a simple wrapper for DecodeTimezoneName that produces a pg_tz *
3340
 * result in all cases.
3341
 */
3342
pg_tz *
3343
DecodeTimezoneNameToTz(const char *tzname)
3344
0
{
3345
0
  pg_tz    *result;
3346
0
  int     offset;
3347
3348
0
  if (DecodeTimezoneName(tzname, &offset, &result) == TZNAME_FIXED_OFFSET)
3349
0
  {
3350
    /* fixed-offset abbreviation, get a pg_tz descriptor for that */
3351
0
    result = pg_tzset_offset(-offset);  /* flip to POSIX sign convention */
3352
0
  }
3353
0
  return result;
3354
0
}
3355
3356
/* DecodeTimezoneAbbrevPrefix()
3357
 * Interpret prefix of string as a timezone abbreviation, if possible.
3358
 *
3359
 * This has roughly the same functionality as DecodeTimezoneAbbrev(),
3360
 * but the API is adapted to the needs of formatting.c.  Notably,
3361
 * we will match the longest possible prefix of the given string
3362
 * rather than insisting on a complete match, and downcasing is applied
3363
 * here rather than in the caller.
3364
 *
3365
 * Returns the length of the timezone abbreviation, or -1 if not recognized.
3366
 * On success, sets *offset to the GMT offset for the abbreviation if it
3367
 * is a fixed-offset abbreviation, or sets *tz to the pg_tz struct for
3368
 * a dynamic abbreviation.
3369
 */
3370
int
3371
DecodeTimezoneAbbrevPrefix(const char *str, int *offset, pg_tz **tz)
3372
0
{
3373
0
  char    lowtoken[TOKMAXLEN + 1];
3374
0
  int     len;
3375
3376
0
  *offset = 0;        /* avoid uninitialized vars on failure */
3377
0
  *tz = NULL;
3378
3379
  /* Downcase as much of the string as we could need */
3380
0
  for (len = 0; len < TOKMAXLEN; len++)
3381
0
  {
3382
0
    if (*str == '\0' || !isalpha((unsigned char) *str))
3383
0
      break;
3384
0
    lowtoken[len] = pg_tolower((unsigned char) *str++);
3385
0
  }
3386
0
  lowtoken[len] = '\0';
3387
3388
  /*
3389
   * We could avoid doing repeated binary searches if we cared to duplicate
3390
   * datebsearch here, but it's not clear that such an optimization would be
3391
   * worth the trouble.  In common cases there's probably not anything after
3392
   * the zone abbrev anyway.  So just search with successively truncated
3393
   * strings.
3394
   */
3395
0
  while (len > 0)
3396
0
  {
3397
0
    bool    isfixed;
3398
0
    int     isdst;
3399
0
    const datetkn *tp;
3400
3401
    /* See if the current session_timezone recognizes it. */
3402
0
    if (session_timezone &&
3403
0
      TimeZoneAbbrevIsKnown(lowtoken, session_timezone,
3404
0
                  &isfixed, offset, &isdst))
3405
0
    {
3406
0
      if (isfixed)
3407
0
      {
3408
        /* flip sign to agree with the convention in zoneabbrevtbl */
3409
0
        *offset = -(*offset);
3410
0
      }
3411
0
      else
3412
0
      {
3413
        /* Caller must resolve the abbrev's current meaning */
3414
0
        *tz = session_timezone;
3415
0
      }
3416
0
      return len;
3417
0
    }
3418
3419
    /* Known in zoneabbrevtbl? */
3420
0
    if (zoneabbrevtbl)
3421
0
      tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
3422
0
               zoneabbrevtbl->numabbrevs);
3423
0
    else
3424
0
      tp = NULL;
3425
0
    if (tp != NULL)
3426
0
    {
3427
0
      if (tp->type == DYNTZ)
3428
0
      {
3429
0
        DateTimeErrorExtra extra;
3430
0
        pg_tz    *tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp,
3431
0
                             &extra);
3432
3433
0
        if (tzp != NULL)
3434
0
        {
3435
          /* Caller must resolve the abbrev's current meaning */
3436
0
          *tz = tzp;
3437
0
          return len;
3438
0
        }
3439
0
      }
3440
0
      else
3441
0
      {
3442
        /* Fixed-offset zone abbrev, so it's easy */
3443
0
        *offset = tp->value;
3444
0
        return len;
3445
0
      }
3446
0
    }
3447
3448
    /* Nope, try the next shorter string. */
3449
0
    lowtoken[--len] = '\0';
3450
0
  }
3451
3452
  /* Did not find a match */
3453
0
  return -1;
3454
0
}
3455
3456
3457
/* ClearPgItmIn
3458
 *
3459
 * Zero out a pg_itm_in
3460
 */
3461
static inline void
3462
ClearPgItmIn(struct pg_itm_in *itm_in)
3463
0
{
3464
0
  itm_in->tm_usec = 0;
3465
0
  itm_in->tm_mday = 0;
3466
0
  itm_in->tm_mon = 0;
3467
0
  itm_in->tm_year = 0;
3468
0
}
3469
3470
3471
/* DecodeInterval()
3472
 * Interpret previously parsed fields for general time interval.
3473
 * Returns 0 if successful, DTERR code if bogus input detected.
3474
 * dtype and itm_in are output parameters.
3475
 *
3476
 * Allow "date" field DTK_DATE since this could be just
3477
 *  an unsigned floating point number. - thomas 1997-11-16
3478
 *
3479
 * Allow ISO-style time span, with implicit units on number of days
3480
 *  preceding an hh:mm:ss field. - thomas 1998-04-30
3481
 *
3482
 * itm_in remains undefined for infinite interval values for which dtype alone
3483
 * suffices.
3484
 */
3485
int
3486
DecodeInterval(char **field, int *ftype, int nf, int range,
3487
         int *dtype, struct pg_itm_in *itm_in)
3488
0
{
3489
0
  bool    force_negative = false;
3490
0
  bool    is_before = false;
3491
0
  bool    parsing_unit_val = false;
3492
0
  char     *cp;
3493
0
  int     fmask = 0,
3494
0
        tmask,
3495
0
        type,
3496
0
        uval;
3497
0
  int     i;
3498
0
  int     dterr;
3499
0
  int64   val;
3500
0
  double    fval;
3501
3502
0
  *dtype = DTK_DELTA;
3503
0
  type = IGNORE_DTF;
3504
0
  ClearPgItmIn(itm_in);
3505
3506
  /*----------
3507
   * The SQL standard defines the interval literal
3508
   *   '-1 1:00:00'
3509
   * to mean "negative 1 days and negative 1 hours", while Postgres
3510
   * traditionally treats this as meaning "negative 1 days and positive
3511
   * 1 hours".  In SQL_STANDARD intervalstyle, we apply the leading sign
3512
   * to all fields if there are no other explicit signs.
3513
   *
3514
   * We leave the signs alone if there are additional explicit signs.
3515
   * This protects us against misinterpreting postgres-style dump output,
3516
   * since the postgres-style output code has always put an explicit sign on
3517
   * all fields following a negative field.  But note that SQL-spec output
3518
   * is ambiguous and can be misinterpreted on load!  (So it's best practice
3519
   * to dump in postgres style, not SQL style.)
3520
   *----------
3521
   */
3522
0
  if (IntervalStyle == INTSTYLE_SQL_STANDARD && nf > 0 && *field[0] == '-')
3523
0
  {
3524
0
    force_negative = true;
3525
    /* Check for additional explicit signs */
3526
0
    for (i = 1; i < nf; i++)
3527
0
    {
3528
0
      if (*field[i] == '-' || *field[i] == '+')
3529
0
      {
3530
0
        force_negative = false;
3531
0
        break;
3532
0
      }
3533
0
    }
3534
0
  }
3535
3536
  /* read through list backwards to pick up units before values */
3537
0
  for (i = nf - 1; i >= 0; i--)
3538
0
  {
3539
0
    switch (ftype[i])
3540
0
    {
3541
0
      case DTK_TIME:
3542
0
        dterr = DecodeTimeForInterval(field[i], fmask, range,
3543
0
                        &tmask, itm_in);
3544
0
        if (dterr)
3545
0
          return dterr;
3546
0
        if (force_negative &&
3547
0
          itm_in->tm_usec > 0)
3548
0
          itm_in->tm_usec = -itm_in->tm_usec;
3549
0
        type = DTK_DAY;
3550
0
        parsing_unit_val = false;
3551
0
        break;
3552
3553
0
      case DTK_TZ:
3554
3555
        /*
3556
         * Timezone means a token with a leading sign character and at
3557
         * least one digit; there could be ':', '.', '-' embedded in
3558
         * it as well.
3559
         */
3560
0
        Assert(*field[i] == '-' || *field[i] == '+');
3561
3562
        /*
3563
         * Check for signed hh:mm or hh:mm:ss.  If so, process exactly
3564
         * like DTK_TIME case above, plus handling the sign.
3565
         */
3566
0
        if (strchr(field[i] + 1, ':') != NULL &&
3567
0
          DecodeTimeForInterval(field[i] + 1, fmask, range,
3568
0
                      &tmask, itm_in) == 0)
3569
0
        {
3570
0
          if (*field[i] == '-')
3571
0
          {
3572
            /* flip the sign on time field */
3573
0
            if (itm_in->tm_usec == PG_INT64_MIN)
3574
0
              return DTERR_FIELD_OVERFLOW;
3575
0
            itm_in->tm_usec = -itm_in->tm_usec;
3576
0
          }
3577
3578
0
          if (force_negative &&
3579
0
            itm_in->tm_usec > 0)
3580
0
            itm_in->tm_usec = -itm_in->tm_usec;
3581
3582
          /*
3583
           * Set the next type to be a day, if units are not
3584
           * specified. This handles the case of '1 +02:03' since we
3585
           * are reading right to left.
3586
           */
3587
0
          type = DTK_DAY;
3588
0
          parsing_unit_val = false;
3589
0
          break;
3590
0
        }
3591
3592
        /*
3593
         * Otherwise, fall through to DTK_NUMBER case, which can
3594
         * handle signed float numbers and signed year-month values.
3595
         */
3596
3597
        /* FALLTHROUGH */
3598
3599
0
      case DTK_DATE:
3600
0
      case DTK_NUMBER:
3601
0
        if (type == IGNORE_DTF)
3602
0
        {
3603
          /* use typmod to decide what rightmost field is */
3604
0
          switch (range)
3605
0
          {
3606
0
            case INTERVAL_MASK(YEAR):
3607
0
              type = DTK_YEAR;
3608
0
              break;
3609
0
            case INTERVAL_MASK(MONTH):
3610
0
            case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
3611
0
              type = DTK_MONTH;
3612
0
              break;
3613
0
            case INTERVAL_MASK(DAY):
3614
0
              type = DTK_DAY;
3615
0
              break;
3616
0
            case INTERVAL_MASK(HOUR):
3617
0
            case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
3618
0
              type = DTK_HOUR;
3619
0
              break;
3620
0
            case INTERVAL_MASK(MINUTE):
3621
0
            case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3622
0
            case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3623
0
              type = DTK_MINUTE;
3624
0
              break;
3625
0
            case INTERVAL_MASK(SECOND):
3626
0
            case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3627
0
            case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3628
0
            case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3629
0
              type = DTK_SECOND;
3630
0
              break;
3631
0
            default:
3632
0
              type = DTK_SECOND;
3633
0
              break;
3634
0
          }
3635
0
        }
3636
3637
0
        errno = 0;
3638
0
        val = strtoi64(field[i], &cp, 10);
3639
0
        if (errno == ERANGE)
3640
0
          return DTERR_FIELD_OVERFLOW;
3641
3642
0
        if (*cp == '-')
3643
0
        {
3644
          /* SQL "years-months" syntax */
3645
0
          int     val2;
3646
3647
0
          val2 = strtoint(cp + 1, &cp, 10);
3648
0
          if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
3649
0
            return DTERR_FIELD_OVERFLOW;
3650
0
          if (*cp != '\0')
3651
0
            return DTERR_BAD_FORMAT;
3652
0
          type = DTK_MONTH;
3653
0
          if (*field[i] == '-')
3654
0
            val2 = -val2;
3655
0
          if (pg_mul_s64_overflow(val, MONTHS_PER_YEAR, &val))
3656
0
            return DTERR_FIELD_OVERFLOW;
3657
0
          if (pg_add_s64_overflow(val, val2, &val))
3658
0
            return DTERR_FIELD_OVERFLOW;
3659
0
          fval = 0;
3660
0
        }
3661
0
        else if (*cp == '.')
3662
0
        {
3663
0
          dterr = ParseFraction(cp, &fval);
3664
0
          if (dterr)
3665
0
            return dterr;
3666
0
          if (*field[i] == '-')
3667
0
            fval = -fval;
3668
0
        }
3669
0
        else if (*cp == '\0')
3670
0
          fval = 0;
3671
0
        else
3672
0
          return DTERR_BAD_FORMAT;
3673
3674
0
        tmask = 0;    /* DTK_M(type); */
3675
3676
0
        if (force_negative)
3677
0
        {
3678
          /* val and fval should be of same sign, but test anyway */
3679
0
          if (val > 0)
3680
0
            val = -val;
3681
0
          if (fval > 0)
3682
0
            fval = -fval;
3683
0
        }
3684
3685
0
        switch (type)
3686
0
        {
3687
0
          case DTK_MICROSEC:
3688
0
            if (!AdjustMicroseconds(val, fval, 1, itm_in))
3689
0
              return DTERR_FIELD_OVERFLOW;
3690
0
            tmask = DTK_M(MICROSECOND);
3691
0
            break;
3692
3693
0
          case DTK_MILLISEC:
3694
0
            if (!AdjustMicroseconds(val, fval, 1000, itm_in))
3695
0
              return DTERR_FIELD_OVERFLOW;
3696
0
            tmask = DTK_M(MILLISECOND);
3697
0
            break;
3698
3699
0
          case DTK_SECOND:
3700
0
            if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3701
0
              return DTERR_FIELD_OVERFLOW;
3702
3703
            /*
3704
             * If any subseconds were specified, consider this
3705
             * microsecond and millisecond input as well.
3706
             */
3707
0
            if (fval == 0)
3708
0
              tmask = DTK_M(SECOND);
3709
0
            else
3710
0
              tmask = DTK_ALL_SECS_M;
3711
0
            break;
3712
3713
0
          case DTK_MINUTE:
3714
0
            if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3715
0
              return DTERR_FIELD_OVERFLOW;
3716
0
            tmask = DTK_M(MINUTE);
3717
0
            break;
3718
3719
0
          case DTK_HOUR:
3720
0
            if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3721
0
              return DTERR_FIELD_OVERFLOW;
3722
0
            tmask = DTK_M(HOUR);
3723
0
            type = DTK_DAY; /* set for next field */
3724
0
            break;
3725
3726
0
          case DTK_DAY:
3727
0
            if (!AdjustDays(val, 1, itm_in) ||
3728
0
              !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3729
0
              return DTERR_FIELD_OVERFLOW;
3730
0
            tmask = DTK_M(DAY);
3731
0
            break;
3732
3733
0
          case DTK_WEEK:
3734
0
            if (!AdjustDays(val, 7, itm_in) ||
3735
0
              !AdjustFractDays(fval, 7, itm_in))
3736
0
              return DTERR_FIELD_OVERFLOW;
3737
0
            tmask = DTK_M(WEEK);
3738
0
            break;
3739
3740
0
          case DTK_MONTH:
3741
0
            if (!AdjustMonths(val, itm_in) ||
3742
0
              !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3743
0
              return DTERR_FIELD_OVERFLOW;
3744
0
            tmask = DTK_M(MONTH);
3745
0
            break;
3746
3747
0
          case DTK_YEAR:
3748
0
            if (!AdjustYears(val, 1, itm_in) ||
3749
0
              !AdjustFractYears(fval, 1, itm_in))
3750
0
              return DTERR_FIELD_OVERFLOW;
3751
0
            tmask = DTK_M(YEAR);
3752
0
            break;
3753
3754
0
          case DTK_DECADE:
3755
0
            if (!AdjustYears(val, 10, itm_in) ||
3756
0
              !AdjustFractYears(fval, 10, itm_in))
3757
0
              return DTERR_FIELD_OVERFLOW;
3758
0
            tmask = DTK_M(DECADE);
3759
0
            break;
3760
3761
0
          case DTK_CENTURY:
3762
0
            if (!AdjustYears(val, 100, itm_in) ||
3763
0
              !AdjustFractYears(fval, 100, itm_in))
3764
0
              return DTERR_FIELD_OVERFLOW;
3765
0
            tmask = DTK_M(CENTURY);
3766
0
            break;
3767
3768
0
          case DTK_MILLENNIUM:
3769
0
            if (!AdjustYears(val, 1000, itm_in) ||
3770
0
              !AdjustFractYears(fval, 1000, itm_in))
3771
0
              return DTERR_FIELD_OVERFLOW;
3772
0
            tmask = DTK_M(MILLENNIUM);
3773
0
            break;
3774
3775
0
          default:
3776
0
            return DTERR_BAD_FORMAT;
3777
0
        }
3778
0
        parsing_unit_val = false;
3779
0
        break;
3780
3781
0
      case DTK_STRING:
3782
0
      case DTK_SPECIAL:
3783
        /* reject consecutive unhandled units */
3784
0
        if (parsing_unit_val)
3785
0
          return DTERR_BAD_FORMAT;
3786
0
        type = DecodeUnits(i, field[i], &uval);
3787
0
        if (type == UNKNOWN_FIELD)
3788
0
          type = DecodeSpecial(i, field[i], &uval);
3789
0
        if (type == IGNORE_DTF)
3790
0
          continue;
3791
3792
0
        tmask = 0;    /* DTK_M(type); */
3793
0
        switch (type)
3794
0
        {
3795
0
          case UNITS:
3796
0
            type = uval;
3797
0
            parsing_unit_val = true;
3798
0
            break;
3799
3800
0
          case AGO:
3801
3802
            /*
3803
             * "ago" is only allowed to appear at the end of the
3804
             * interval.
3805
             */
3806
0
            if (i != nf - 1)
3807
0
              return DTERR_BAD_FORMAT;
3808
0
            is_before = true;
3809
0
            type = uval;
3810
0
            break;
3811
3812
0
          case RESERV:
3813
0
            tmask = (DTK_DATE_M | DTK_TIME_M);
3814
3815
            /*
3816
             * Only reserved words corresponding to infinite
3817
             * intervals are accepted.
3818
             */
3819
0
            if (uval != DTK_LATE && uval != DTK_EARLY)
3820
0
              return DTERR_BAD_FORMAT;
3821
3822
            /*
3823
             * Infinity cannot be followed by anything else. We
3824
             * could allow "ago" to reverse the sign of infinity
3825
             * but using signed infinity is more intuitive.
3826
             */
3827
0
            if (i != nf - 1)
3828
0
              return DTERR_BAD_FORMAT;
3829
3830
0
            *dtype = uval;
3831
0
            break;
3832
3833
0
          default:
3834
0
            return DTERR_BAD_FORMAT;
3835
0
        }
3836
0
        break;
3837
3838
0
      default:
3839
0
        return DTERR_BAD_FORMAT;
3840
0
    }
3841
3842
0
    if (tmask & fmask)
3843
0
      return DTERR_BAD_FORMAT;
3844
0
    fmask |= tmask;
3845
0
  }
3846
3847
  /* ensure that at least one time field has been found */
3848
0
  if (fmask == 0)
3849
0
    return DTERR_BAD_FORMAT;
3850
3851
  /* reject if unit appeared and was never handled */
3852
0
  if (parsing_unit_val)
3853
0
    return DTERR_BAD_FORMAT;
3854
3855
  /* finally, AGO negates everything */
3856
0
  if (is_before)
3857
0
  {
3858
0
    if (itm_in->tm_usec == PG_INT64_MIN ||
3859
0
      itm_in->tm_mday == INT_MIN ||
3860
0
      itm_in->tm_mon == INT_MIN ||
3861
0
      itm_in->tm_year == INT_MIN)
3862
0
      return DTERR_FIELD_OVERFLOW;
3863
3864
0
    itm_in->tm_usec = -itm_in->tm_usec;
3865
0
    itm_in->tm_mday = -itm_in->tm_mday;
3866
0
    itm_in->tm_mon = -itm_in->tm_mon;
3867
0
    itm_in->tm_year = -itm_in->tm_year;
3868
0
  }
3869
3870
0
  return 0;
3871
0
}
3872
3873
3874
/*
3875
 * Helper functions to avoid duplicated code in DecodeISO8601Interval.
3876
 *
3877
 * Parse a decimal value and break it into integer and fractional parts.
3878
 * Set *endptr to end+1 of the parsed substring.
3879
 * Returns 0 or DTERR code.
3880
 */
3881
static int
3882
ParseISO8601Number(char *str, char **endptr, int64 *ipart, double *fpart)
3883
0
{
3884
0
  double    val;
3885
3886
  /*
3887
   * Historically this has accepted anything that strtod() would take,
3888
   * notably including "e" notation, so continue doing that.  This is
3889
   * slightly annoying because the precision of double is less than that of
3890
   * int64, so we would lose accuracy for inputs larger than 2^53 or so.
3891
   * However, historically we rejected inputs outside the int32 range,
3892
   * making that concern moot.  What we do now is reject abs(val) above
3893
   * 1.0e15 (a round number a bit less than 2^50), so that any accepted
3894
   * value will have an exact integer part, and thereby a fraction part with
3895
   * abs(*fpart) less than 1.  In the absence of field complaints it doesn't
3896
   * seem worth working harder.
3897
   */
3898
0
  if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
3899
0
    return DTERR_BAD_FORMAT;
3900
0
  errno = 0;
3901
0
  val = strtod(str, endptr);
3902
  /* did we not see anything that looks like a double? */
3903
0
  if (*endptr == str || errno != 0)
3904
0
    return DTERR_BAD_FORMAT;
3905
  /* watch out for overflow, including infinities; reject NaN too */
3906
0
  if (isnan(val) || val < -1.0e15 || val > 1.0e15)
3907
0
    return DTERR_FIELD_OVERFLOW;
3908
  /* be very sure we truncate towards zero (cf dtrunc()) */
3909
0
  if (val >= 0)
3910
0
    *ipart = (int64) floor(val);
3911
0
  else
3912
0
    *ipart = (int64) -floor(-val);
3913
0
  *fpart = val - *ipart;
3914
  /* Callers expect this to hold */
3915
0
  Assert(*fpart > -1.0 && *fpart < 1.0);
3916
0
  return 0;
3917
0
}
3918
3919
/*
3920
 * Determine number of integral digits in a valid ISO 8601 number field
3921
 * (we should ignore sign and any fraction part)
3922
 */
3923
static int
3924
ISO8601IntegerWidth(char *fieldstart)
3925
0
{
3926
  /* We might have had a leading '-' */
3927
0
  if (*fieldstart == '-')
3928
0
    fieldstart++;
3929
0
  return strspn(fieldstart, "0123456789");
3930
0
}
3931
3932
3933
/* DecodeISO8601Interval()
3934
 *  Decode an ISO 8601 time interval of the "format with designators"
3935
 *  (section 4.4.3.2) or "alternative format" (section 4.4.3.3)
3936
 *  Examples:  P1D  for 1 day
3937
 *         PT1H for 1 hour
3938
 *         P2Y6M7DT1H30M for 2 years, 6 months, 7 days 1 hour 30 min
3939
 *         P0002-06-07T01:30:00 the same value in alternative format
3940
 *
3941
 * Returns 0 if successful, DTERR code if bogus input detected.
3942
 * Note: error code should be DTERR_BAD_FORMAT if input doesn't look like
3943
 * ISO8601, otherwise this could cause unexpected error messages.
3944
 * dtype and itm_in are output parameters.
3945
 *
3946
 *  A couple exceptions from the spec:
3947
 *   - a week field ('W') may coexist with other units
3948
 *   - allows decimals in fields other than the least significant unit.
3949
 */
3950
int
3951
DecodeISO8601Interval(char *str,
3952
            int *dtype, struct pg_itm_in *itm_in)
3953
0
{
3954
0
  bool    datepart = true;
3955
0
  bool    havefield = false;
3956
3957
0
  *dtype = DTK_DELTA;
3958
0
  ClearPgItmIn(itm_in);
3959
3960
0
  if (strlen(str) < 2 || str[0] != 'P')
3961
0
    return DTERR_BAD_FORMAT;
3962
3963
0
  str++;
3964
0
  while (*str)
3965
0
  {
3966
0
    char     *fieldstart;
3967
0
    int64   val;
3968
0
    double    fval;
3969
0
    char    unit;
3970
0
    int     dterr;
3971
3972
0
    if (*str == 'T')   /* T indicates the beginning of the time part */
3973
0
    {
3974
0
      datepart = false;
3975
0
      havefield = false;
3976
0
      str++;
3977
0
      continue;
3978
0
    }
3979
3980
0
    fieldstart = str;
3981
0
    dterr = ParseISO8601Number(str, &str, &val, &fval);
3982
0
    if (dterr)
3983
0
      return dterr;
3984
3985
    /*
3986
     * Note: we could step off the end of the string here.  Code below
3987
     * *must* exit the loop if unit == '\0'.
3988
     */
3989
0
    unit = *str++;
3990
3991
0
    if (datepart)
3992
0
    {
3993
0
      switch (unit)   /* before T: Y M W D */
3994
0
      {
3995
0
        case 'Y':
3996
0
          if (!AdjustYears(val, 1, itm_in) ||
3997
0
            !AdjustFractYears(fval, 1, itm_in))
3998
0
            return DTERR_FIELD_OVERFLOW;
3999
0
          break;
4000
0
        case 'M':
4001
0
          if (!AdjustMonths(val, itm_in) ||
4002
0
            !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
4003
0
            return DTERR_FIELD_OVERFLOW;
4004
0
          break;
4005
0
        case 'W':
4006
0
          if (!AdjustDays(val, 7, itm_in) ||
4007
0
            !AdjustFractDays(fval, 7, itm_in))
4008
0
            return DTERR_FIELD_OVERFLOW;
4009
0
          break;
4010
0
        case 'D':
4011
0
          if (!AdjustDays(val, 1, itm_in) ||
4012
0
            !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
4013
0
            return DTERR_FIELD_OVERFLOW;
4014
0
          break;
4015
0
        case 'T':   /* ISO 8601 4.4.3.3 Alternative Format / Basic */
4016
0
        case '\0':
4017
0
          if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
4018
0
          {
4019
0
            if (!AdjustYears(val / 10000, 1, itm_in) ||
4020
0
              !AdjustMonths((val / 100) % 100, itm_in) ||
4021
0
              !AdjustDays(val % 100, 1, itm_in) ||
4022
0
              !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
4023
0
              return DTERR_FIELD_OVERFLOW;
4024
0
            if (unit == '\0')
4025
0
              return 0;
4026
0
            datepart = false;
4027
0
            havefield = false;
4028
0
            continue;
4029
0
          }
4030
          /* Else fall through to extended alternative format */
4031
          /* FALLTHROUGH */
4032
0
        case '-':   /* ISO 8601 4.4.3.3 Alternative Format,
4033
                 * Extended */
4034
0
          if (havefield)
4035
0
            return DTERR_BAD_FORMAT;
4036
4037
0
          if (!AdjustYears(val, 1, itm_in) ||
4038
0
            !AdjustFractYears(fval, 1, itm_in))
4039
0
            return DTERR_FIELD_OVERFLOW;
4040
0
          if (unit == '\0')
4041
0
            return 0;
4042
0
          if (unit == 'T')
4043
0
          {
4044
0
            datepart = false;
4045
0
            havefield = false;
4046
0
            continue;
4047
0
          }
4048
4049
0
          dterr = ParseISO8601Number(str, &str, &val, &fval);
4050
0
          if (dterr)
4051
0
            return dterr;
4052
0
          if (!AdjustMonths(val, itm_in) ||
4053
0
            !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
4054
0
            return DTERR_FIELD_OVERFLOW;
4055
0
          if (*str == '\0')
4056
0
            return 0;
4057
0
          if (*str == 'T')
4058
0
          {
4059
0
            datepart = false;
4060
0
            havefield = false;
4061
0
            continue;
4062
0
          }
4063
0
          if (*str != '-')
4064
0
            return DTERR_BAD_FORMAT;
4065
0
          str++;
4066
4067
0
          dterr = ParseISO8601Number(str, &str, &val, &fval);
4068
0
          if (dterr)
4069
0
            return dterr;
4070
0
          if (!AdjustDays(val, 1, itm_in) ||
4071
0
            !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
4072
0
            return DTERR_FIELD_OVERFLOW;
4073
0
          if (*str == '\0')
4074
0
            return 0;
4075
0
          if (*str == 'T')
4076
0
          {
4077
0
            datepart = false;
4078
0
            havefield = false;
4079
0
            continue;
4080
0
          }
4081
0
          return DTERR_BAD_FORMAT;
4082
0
        default:
4083
          /* not a valid date unit suffix */
4084
0
          return DTERR_BAD_FORMAT;
4085
0
      }
4086
0
    }
4087
0
    else
4088
0
    {
4089
0
      switch (unit)   /* after T: H M S */
4090
0
      {
4091
0
        case 'H':
4092
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
4093
0
            return DTERR_FIELD_OVERFLOW;
4094
0
          break;
4095
0
        case 'M':
4096
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
4097
0
            return DTERR_FIELD_OVERFLOW;
4098
0
          break;
4099
0
        case 'S':
4100
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
4101
0
            return DTERR_FIELD_OVERFLOW;
4102
0
          break;
4103
0
        case '\0':    /* ISO 8601 4.4.3.3 Alternative Format */
4104
0
          if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
4105
0
          {
4106
0
            if (!AdjustMicroseconds(val / 10000, 0, USECS_PER_HOUR, itm_in) ||
4107
0
              !AdjustMicroseconds((val / 100) % 100, 0, USECS_PER_MINUTE, itm_in) ||
4108
0
              !AdjustMicroseconds(val % 100, 0, USECS_PER_SEC, itm_in) ||
4109
0
              !AdjustFractMicroseconds(fval, 1, itm_in))
4110
0
              return DTERR_FIELD_OVERFLOW;
4111
0
            return 0;
4112
0
          }
4113
          /* Else fall through to extended alternative format */
4114
          /* FALLTHROUGH */
4115
0
        case ':':   /* ISO 8601 4.4.3.3 Alternative Format,
4116
                 * Extended */
4117
0
          if (havefield)
4118
0
            return DTERR_BAD_FORMAT;
4119
4120
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
4121
0
            return DTERR_FIELD_OVERFLOW;
4122
0
          if (unit == '\0')
4123
0
            return 0;
4124
4125
0
          dterr = ParseISO8601Number(str, &str, &val, &fval);
4126
0
          if (dterr)
4127
0
            return dterr;
4128
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
4129
0
            return DTERR_FIELD_OVERFLOW;
4130
0
          if (*str == '\0')
4131
0
            return 0;
4132
0
          if (*str != ':')
4133
0
            return DTERR_BAD_FORMAT;
4134
0
          str++;
4135
4136
0
          dterr = ParseISO8601Number(str, &str, &val, &fval);
4137
0
          if (dterr)
4138
0
            return dterr;
4139
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
4140
0
            return DTERR_FIELD_OVERFLOW;
4141
0
          if (*str == '\0')
4142
0
            return 0;
4143
0
          return DTERR_BAD_FORMAT;
4144
4145
0
        default:
4146
          /* not a valid time unit suffix */
4147
0
          return DTERR_BAD_FORMAT;
4148
0
      }
4149
0
    }
4150
4151
0
    havefield = true;
4152
0
  }
4153
4154
0
  return 0;
4155
0
}
4156
4157
4158
/* DecodeUnits()
4159
 * Decode text string using lookup table.
4160
 *
4161
 * This routine recognizes keywords associated with time interval units.
4162
 *
4163
 * Given string must be lowercased already.
4164
 *
4165
 * Implement a cache lookup since it is likely that dates
4166
 *  will be related in format.
4167
 */
4168
int
4169
DecodeUnits(int field, const char *lowtoken, int *val)
4170
0
{
4171
0
  int     type;
4172
0
  const datetkn *tp;
4173
4174
0
  tp = deltacache[field];
4175
  /* use strncmp so that we match truncated tokens */
4176
0
  if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
4177
0
  {
4178
0
    tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
4179
0
  }
4180
0
  if (tp == NULL)
4181
0
  {
4182
0
    type = UNKNOWN_FIELD;
4183
0
    *val = 0;
4184
0
  }
4185
0
  else
4186
0
  {
4187
0
    deltacache[field] = tp;
4188
0
    type = tp->type;
4189
0
    *val = tp->value;
4190
0
  }
4191
4192
0
  return type;
4193
0
}                /* DecodeUnits() */
4194
4195
/*
4196
 * Report an error detected by one of the datetime input processing routines.
4197
 *
4198
 * dterr is the error code, and *extra contains any auxiliary info we need
4199
 * for the error report.  extra can be NULL if not needed for the particular
4200
 * dterr value.
4201
 *
4202
 * str is the original input string, and datatype is the name of the datatype
4203
 * we were trying to accept.  (For some DTERR codes, these are not used and
4204
 * can be NULL.)
4205
 *
4206
 * If escontext points to an ErrorSaveContext node, that is filled instead
4207
 * of throwing an error.
4208
 *
4209
 * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
4210
 * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
4211
 * separate SQLSTATE codes, so ...
4212
 */
4213
void
4214
DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
4215
           const char *str, const char *datatype,
4216
           Node *escontext)
4217
0
{
4218
0
  switch (dterr)
4219
0
  {
4220
0
    case DTERR_FIELD_OVERFLOW:
4221
0
      errsave(escontext,
4222
0
          (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
4223
0
           errmsg("date/time field value out of range: \"%s\"",
4224
0
              str)));
4225
0
      break;
4226
0
    case DTERR_MD_FIELD_OVERFLOW:
4227
      /* <nanny>same as above, but add hint about DateStyle</nanny> */
4228
0
      errsave(escontext,
4229
0
          (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
4230
0
           errmsg("date/time field value out of range: \"%s\"",
4231
0
              str),
4232
0
           errhint("Perhaps you need a different \"DateStyle\" setting.")));
4233
0
      break;
4234
0
    case DTERR_INTERVAL_OVERFLOW:
4235
0
      errsave(escontext,
4236
0
          (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
4237
0
           errmsg("interval field value out of range: \"%s\"",
4238
0
              str)));
4239
0
      break;
4240
0
    case DTERR_TZDISP_OVERFLOW:
4241
0
      errsave(escontext,
4242
0
          (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
4243
0
           errmsg("time zone displacement out of range: \"%s\"",
4244
0
              str)));
4245
0
      break;
4246
0
    case DTERR_BAD_TIMEZONE:
4247
0
      errsave(escontext,
4248
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4249
0
           errmsg("time zone \"%s\" not recognized",
4250
0
              extra->dtee_timezone)));
4251
0
      break;
4252
0
    case DTERR_BAD_ZONE_ABBREV:
4253
0
      errsave(escontext,
4254
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
4255
0
           errmsg("time zone \"%s\" not recognized",
4256
0
              extra->dtee_timezone),
4257
0
           errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".",
4258
0
                 extra->dtee_abbrev)));
4259
0
      break;
4260
0
    case DTERR_BAD_FORMAT:
4261
0
    default:
4262
0
      errsave(escontext,
4263
0
          (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
4264
0
           errmsg("invalid input syntax for type %s: \"%s\"",
4265
0
              datatype, str)));
4266
0
      break;
4267
0
  }
4268
0
}
4269
4270
/* datebsearch()
4271
 * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
4272
 * is WAY faster than the generic bsearch().
4273
 */
4274
static const datetkn *
4275
datebsearch(const char *key, const datetkn *base, int nel)
4276
0
{
4277
0
  if (nel > 0)
4278
0
  {
4279
0
    const datetkn *last = base + nel - 1,
4280
0
           *position;
4281
0
    int     result;
4282
4283
0
    while (last >= base)
4284
0
    {
4285
0
      position = base + ((last - base) >> 1);
4286
      /* precheck the first character for a bit of extra speed */
4287
0
      result = (int) key[0] - (int) position->token[0];
4288
0
      if (result == 0)
4289
0
      {
4290
        /* use strncmp so that we match truncated tokens */
4291
0
        result = strncmp(key, position->token, TOKMAXLEN);
4292
0
        if (result == 0)
4293
0
          return position;
4294
0
      }
4295
0
      if (result < 0)
4296
0
        last = position - 1;
4297
0
      else
4298
0
        base = position + 1;
4299
0
    }
4300
0
  }
4301
0
  return NULL;
4302
0
}
4303
4304
/* EncodeTimezone()
4305
 *    Copies representation of a numeric timezone offset to str.
4306
 *
4307
 * Returns a pointer to the new end of string.  No NUL terminator is put
4308
 * there; callers are responsible for NUL terminating str themselves.
4309
 */
4310
static char *
4311
EncodeTimezone(char *str, int tz, int style)
4312
0
{
4313
0
  int     hour,
4314
0
        min,
4315
0
        sec;
4316
4317
0
  sec = abs(tz);
4318
0
  min = sec / SECS_PER_MINUTE;
4319
0
  sec -= min * SECS_PER_MINUTE;
4320
0
  hour = min / MINS_PER_HOUR;
4321
0
  min -= hour * MINS_PER_HOUR;
4322
4323
  /* TZ is negated compared to sign we wish to display ... */
4324
0
  *str++ = (tz <= 0 ? '+' : '-');
4325
4326
0
  if (sec != 0)
4327
0
  {
4328
0
    str = pg_ultostr_zeropad(str, hour, 2);
4329
0
    *str++ = ':';
4330
0
    str = pg_ultostr_zeropad(str, min, 2);
4331
0
    *str++ = ':';
4332
0
    str = pg_ultostr_zeropad(str, sec, 2);
4333
0
  }
4334
0
  else if (min != 0 || style == USE_XSD_DATES)
4335
0
  {
4336
0
    str = pg_ultostr_zeropad(str, hour, 2);
4337
0
    *str++ = ':';
4338
0
    str = pg_ultostr_zeropad(str, min, 2);
4339
0
  }
4340
0
  else
4341
0
    str = pg_ultostr_zeropad(str, hour, 2);
4342
0
  return str;
4343
0
}
4344
4345
/* EncodeDateOnly()
4346
 * Encode date as local time.
4347
 */
4348
void
4349
EncodeDateOnly(struct pg_tm *tm, int style, char *str)
4350
0
{
4351
0
  Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
4352
4353
0
  switch (style)
4354
0
  {
4355
0
    case USE_ISO_DATES:
4356
0
    case USE_XSD_DATES:
4357
      /* compatible with ISO date formats */
4358
0
      str = pg_ultostr_zeropad(str,
4359
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4360
0
      *str++ = '-';
4361
0
      str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4362
0
      *str++ = '-';
4363
0
      str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4364
0
      break;
4365
4366
0
    case USE_SQL_DATES:
4367
      /* compatible with Oracle/Ingres date formats */
4368
0
      if (DateOrder == DATEORDER_DMY)
4369
0
      {
4370
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4371
0
        *str++ = '/';
4372
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4373
0
      }
4374
0
      else
4375
0
      {
4376
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4377
0
        *str++ = '/';
4378
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4379
0
      }
4380
0
      *str++ = '/';
4381
0
      str = pg_ultostr_zeropad(str,
4382
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4383
0
      break;
4384
4385
0
    case USE_GERMAN_DATES:
4386
      /* German-style date format */
4387
0
      str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4388
0
      *str++ = '.';
4389
0
      str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4390
0
      *str++ = '.';
4391
0
      str = pg_ultostr_zeropad(str,
4392
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4393
0
      break;
4394
4395
0
    case USE_POSTGRES_DATES:
4396
0
    default:
4397
      /* traditional date-only style for Postgres */
4398
0
      if (DateOrder == DATEORDER_DMY)
4399
0
      {
4400
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4401
0
        *str++ = '-';
4402
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4403
0
      }
4404
0
      else
4405
0
      {
4406
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4407
0
        *str++ = '-';
4408
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4409
0
      }
4410
0
      *str++ = '-';
4411
0
      str = pg_ultostr_zeropad(str,
4412
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4413
0
      break;
4414
0
  }
4415
4416
0
  if (tm->tm_year <= 0)
4417
0
  {
4418
0
    memcpy(str, " BC", 3);  /* Don't copy NUL */
4419
0
    str += 3;
4420
0
  }
4421
0
  *str = '\0';
4422
0
}
4423
4424
4425
/* EncodeTimeOnly()
4426
 * Encode time fields only.
4427
 *
4428
 * tm and fsec are the value to encode, print_tz determines whether to include
4429
 * a time zone (the difference between time and timetz types), tz is the
4430
 * numeric time zone offset, style is the date style, str is where to write the
4431
 * output.
4432
 */
4433
void
4434
EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
4435
0
{
4436
0
  str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4437
0
  *str++ = ':';
4438
0
  str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4439
0
  *str++ = ':';
4440
0
  str = AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
4441
0
  if (print_tz)
4442
0
    str = EncodeTimezone(str, tz, style);
4443
0
  *str = '\0';
4444
0
}
4445
4446
4447
/* EncodeDateTime()
4448
 * Encode date and time interpreted as local time.
4449
 *
4450
 * tm and fsec are the value to encode, print_tz determines whether to include
4451
 * a time zone (the difference between timestamp and timestamptz types), tz is
4452
 * the numeric time zone offset, tzn is the textual time zone, which if
4453
 * specified will be used instead of tz by some styles, style is the date
4454
 * style, str is where to write the output.
4455
 *
4456
 * Supported date styles:
4457
 *  Postgres - day mon hh:mm:ss yyyy tz
4458
 *  SQL - mm/dd/yyyy hh:mm:ss.ss tz
4459
 *  ISO - yyyy-mm-dd hh:mm:ss+/-tz
4460
 *  German - dd.mm.yyyy hh:mm:ss tz
4461
 *  XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
4462
 */
4463
void
4464
EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str)
4465
0
{
4466
0
  int     day;
4467
4468
0
  Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
4469
4470
  /*
4471
   * Negative tm_isdst means we have no valid time zone translation.
4472
   */
4473
0
  if (tm->tm_isdst < 0)
4474
0
    print_tz = false;
4475
4476
0
  switch (style)
4477
0
  {
4478
0
    case USE_ISO_DATES:
4479
0
    case USE_XSD_DATES:
4480
      /* Compatible with ISO-8601 date formats */
4481
0
      str = pg_ultostr_zeropad(str,
4482
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4483
0
      *str++ = '-';
4484
0
      str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4485
0
      *str++ = '-';
4486
0
      str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4487
0
      *str++ = (style == USE_ISO_DATES) ? ' ' : 'T';
4488
0
      str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4489
0
      *str++ = ':';
4490
0
      str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4491
0
      *str++ = ':';
4492
0
      str = AppendTimestampSeconds(str, tm, fsec);
4493
0
      if (print_tz)
4494
0
        str = EncodeTimezone(str, tz, style);
4495
0
      break;
4496
4497
0
    case USE_SQL_DATES:
4498
      /* Compatible with Oracle/Ingres date formats */
4499
0
      if (DateOrder == DATEORDER_DMY)
4500
0
      {
4501
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4502
0
        *str++ = '/';
4503
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4504
0
      }
4505
0
      else
4506
0
      {
4507
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4508
0
        *str++ = '/';
4509
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4510
0
      }
4511
0
      *str++ = '/';
4512
0
      str = pg_ultostr_zeropad(str,
4513
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4514
0
      *str++ = ' ';
4515
0
      str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4516
0
      *str++ = ':';
4517
0
      str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4518
0
      *str++ = ':';
4519
0
      str = AppendTimestampSeconds(str, tm, fsec);
4520
4521
      /*
4522
       * Note: the uses of %.*s in this function would be risky if the
4523
       * timezone names ever contain non-ASCII characters, since we are
4524
       * not being careful to do encoding-aware clipping.  However, all
4525
       * TZ abbreviations in the IANA database are plain ASCII.
4526
       */
4527
0
      if (print_tz)
4528
0
      {
4529
0
        if (tzn)
4530
0
        {
4531
0
          sprintf(str, " %.*s", MAXTZLEN, tzn);
4532
0
          str += strlen(str);
4533
0
        }
4534
0
        else
4535
0
          str = EncodeTimezone(str, tz, style);
4536
0
      }
4537
0
      break;
4538
4539
0
    case USE_GERMAN_DATES:
4540
      /* German variant on European style */
4541
0
      str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4542
0
      *str++ = '.';
4543
0
      str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4544
0
      *str++ = '.';
4545
0
      str = pg_ultostr_zeropad(str,
4546
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4547
0
      *str++ = ' ';
4548
0
      str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4549
0
      *str++ = ':';
4550
0
      str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4551
0
      *str++ = ':';
4552
0
      str = AppendTimestampSeconds(str, tm, fsec);
4553
4554
0
      if (print_tz)
4555
0
      {
4556
0
        if (tzn)
4557
0
        {
4558
0
          sprintf(str, " %.*s", MAXTZLEN, tzn);
4559
0
          str += strlen(str);
4560
0
        }
4561
0
        else
4562
0
          str = EncodeTimezone(str, tz, style);
4563
0
      }
4564
0
      break;
4565
4566
0
    case USE_POSTGRES_DATES:
4567
0
    default:
4568
      /* Backward-compatible with traditional Postgres abstime dates */
4569
0
      day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
4570
0
      tm->tm_wday = j2day(day);
4571
0
      memcpy(str, days[tm->tm_wday], 3);
4572
0
      str += 3;
4573
0
      *str++ = ' ';
4574
0
      if (DateOrder == DATEORDER_DMY)
4575
0
      {
4576
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4577
0
        *str++ = ' ';
4578
0
        memcpy(str, months[tm->tm_mon - 1], 3);
4579
0
        str += 3;
4580
0
      }
4581
0
      else
4582
0
      {
4583
0
        memcpy(str, months[tm->tm_mon - 1], 3);
4584
0
        str += 3;
4585
0
        *str++ = ' ';
4586
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4587
0
      }
4588
0
      *str++ = ' ';
4589
0
      str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4590
0
      *str++ = ':';
4591
0
      str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4592
0
      *str++ = ':';
4593
0
      str = AppendTimestampSeconds(str, tm, fsec);
4594
0
      *str++ = ' ';
4595
0
      str = pg_ultostr_zeropad(str,
4596
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4597
4598
0
      if (print_tz)
4599
0
      {
4600
0
        if (tzn)
4601
0
        {
4602
0
          sprintf(str, " %.*s", MAXTZLEN, tzn);
4603
0
          str += strlen(str);
4604
0
        }
4605
0
        else
4606
0
        {
4607
          /*
4608
           * We have a time zone, but no string version. Use the
4609
           * numeric form, but be sure to include a leading space to
4610
           * avoid formatting something which would be rejected by
4611
           * the date/time parser later. - thomas 2001-10-19
4612
           */
4613
0
          *str++ = ' ';
4614
0
          str = EncodeTimezone(str, tz, style);
4615
0
        }
4616
0
      }
4617
0
      break;
4618
0
  }
4619
4620
0
  if (tm->tm_year <= 0)
4621
0
  {
4622
0
    memcpy(str, " BC", 3);  /* Don't copy NUL */
4623
0
    str += 3;
4624
0
  }
4625
0
  *str = '\0';
4626
0
}
4627
4628
4629
/*
4630
 * Helper functions to avoid duplicated code in EncodeInterval.
4631
 */
4632
4633
/* Append an ISO-8601-style interval field, but only if value isn't zero */
4634
static char *
4635
AddISO8601IntPart(char *cp, int64 value, char units)
4636
0
{
4637
0
  if (value == 0)
4638
0
    return cp;
4639
0
  sprintf(cp, "%" PRId64 "%c", value, units);
4640
0
  return cp + strlen(cp);
4641
0
}
4642
4643
/* Append a postgres-style interval field, but only if value isn't zero */
4644
static char *
4645
AddPostgresIntPart(char *cp, int64 value, const char *units,
4646
           bool *is_zero, bool *is_before)
4647
0
{
4648
0
  if (value == 0)
4649
0
    return cp;
4650
0
  sprintf(cp, "%s%s%" PRId64 " %s%s",
4651
0
      (!*is_zero) ? " " : "",
4652
0
      (*is_before && value > 0) ? "+" : "",
4653
0
      value,
4654
0
      units,
4655
0
      (value != 1) ? "s" : "");
4656
4657
  /*
4658
   * Each nonzero field sets is_before for (only) the next one.  This is a
4659
   * tad bizarre but it's how it worked before...
4660
   */
4661
0
  *is_before = (value < 0);
4662
0
  *is_zero = false;
4663
0
  return cp + strlen(cp);
4664
0
}
4665
4666
/* Append a verbose-style interval field, but only if value isn't zero */
4667
static char *
4668
AddVerboseIntPart(char *cp, int64 value, const char *units,
4669
          bool *is_zero, bool *is_before)
4670
0
{
4671
0
  if (value == 0)
4672
0
    return cp;
4673
  /* first nonzero value sets is_before */
4674
0
  if (*is_zero)
4675
0
  {
4676
0
    *is_before = (value < 0);
4677
0
    value = i64abs(value);
4678
0
  }
4679
0
  else if (*is_before)
4680
0
    value = -value;
4681
0
  sprintf(cp, " %" PRId64 " %s%s", value, units, (value == 1) ? "" : "s");
4682
0
  *is_zero = false;
4683
0
  return cp + strlen(cp);
4684
0
}
4685
4686
4687
/* EncodeInterval()
4688
 * Interpret time structure as a delta time and convert to string.
4689
 *
4690
 * Support "traditional Postgres" and ISO-8601 styles.
4691
 * Actually, afaik ISO does not address time interval formatting,
4692
 *  but this looks similar to the spec for absolute date/time.
4693
 * - thomas 1998-04-30
4694
 *
4695
 * Actually, afaik, ISO 8601 does specify formats for "time
4696
 * intervals...[of the]...format with time-unit designators", which
4697
 * are pretty ugly.  The format looks something like
4698
 *     P1Y1M1DT1H1M1.12345S
4699
 * but useful for exchanging data with computers instead of humans.
4700
 * - ron 2003-07-14
4701
 *
4702
 * And ISO's SQL 2008 standard specifies standards for
4703
 * "year-month literal"s (that look like '2-3') and
4704
 * "day-time literal"s (that look like ('4 5:6:7')
4705
 */
4706
void
4707
EncodeInterval(struct pg_itm *itm, int style, char *str)
4708
0
{
4709
0
  char     *cp = str;
4710
0
  int     year = itm->tm_year;
4711
0
  int     mon = itm->tm_mon;
4712
0
  int64   mday = itm->tm_mday;  /* tm_mday could be INT_MIN */
4713
0
  int64   hour = itm->tm_hour;
4714
0
  int     min = itm->tm_min;
4715
0
  int     sec = itm->tm_sec;
4716
0
  int     fsec = itm->tm_usec;
4717
0
  bool    is_before = false;
4718
0
  bool    is_zero = true;
4719
4720
  /*
4721
   * The sign of year and month are guaranteed to match, since they are
4722
   * stored internally as "month". But we'll need to check for is_before and
4723
   * is_zero when determining the signs of day and hour/minute/seconds
4724
   * fields.
4725
   */
4726
0
  switch (style)
4727
0
  {
4728
      /* SQL Standard interval format */
4729
0
    case INTSTYLE_SQL_STANDARD:
4730
0
      {
4731
0
        bool    has_negative = year < 0 || mon < 0 ||
4732
0
          mday < 0 || hour < 0 ||
4733
0
          min < 0 || sec < 0 || fsec < 0;
4734
0
        bool    has_positive = year > 0 || mon > 0 ||
4735
0
          mday > 0 || hour > 0 ||
4736
0
          min > 0 || sec > 0 || fsec > 0;
4737
0
        bool    has_year_month = year != 0 || mon != 0;
4738
0
        bool    has_day_time = mday != 0 || hour != 0 ||
4739
0
          min != 0 || sec != 0 || fsec != 0;
4740
0
        bool    has_day = mday != 0;
4741
0
        bool    sql_standard_value = !(has_negative && has_positive) &&
4742
0
          !(has_year_month && has_day_time);
4743
4744
        /*
4745
         * SQL Standard wants only 1 "<sign>" preceding the whole
4746
         * interval ... but can't do that if mixed signs.
4747
         */
4748
0
        if (has_negative && sql_standard_value)
4749
0
        {
4750
0
          *cp++ = '-';
4751
0
          year = -year;
4752
0
          mon = -mon;
4753
0
          mday = -mday;
4754
0
          hour = -hour;
4755
0
          min = -min;
4756
0
          sec = -sec;
4757
0
          fsec = -fsec;
4758
0
        }
4759
4760
0
        if (!has_negative && !has_positive)
4761
0
        {
4762
0
          sprintf(cp, "0");
4763
0
        }
4764
0
        else if (!sql_standard_value)
4765
0
        {
4766
          /*
4767
           * For non sql-standard interval values, force outputting
4768
           * the signs to avoid ambiguities with intervals with
4769
           * mixed sign components.
4770
           */
4771
0
          char    year_sign = (year < 0 || mon < 0) ? '-' : '+';
4772
0
          char    day_sign = (mday < 0) ? '-' : '+';
4773
0
          char    sec_sign = (hour < 0 || min < 0 ||
4774
0
                      sec < 0 || fsec < 0) ? '-' : '+';
4775
4776
0
          sprintf(cp, "%c%d-%d %c%" PRId64 " %c%" PRId64 ":%02d:",
4777
0
              year_sign, abs(year), abs(mon),
4778
0
              day_sign, i64abs(mday),
4779
0
              sec_sign, i64abs(hour), abs(min));
4780
0
          cp += strlen(cp);
4781
0
          cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4782
0
          *cp = '\0';
4783
0
        }
4784
0
        else if (has_year_month)
4785
0
        {
4786
0
          sprintf(cp, "%d-%d", year, mon);
4787
0
        }
4788
0
        else if (has_day)
4789
0
        {
4790
0
          sprintf(cp, "%" PRId64 " %" PRId64 ":%02d:",
4791
0
              mday, hour, min);
4792
0
          cp += strlen(cp);
4793
0
          cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4794
0
          *cp = '\0';
4795
0
        }
4796
0
        else
4797
0
        {
4798
0
          sprintf(cp, "%" PRId64 ":%02d:", hour, min);
4799
0
          cp += strlen(cp);
4800
0
          cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4801
0
          *cp = '\0';
4802
0
        }
4803
0
      }
4804
0
      break;
4805
4806
      /* ISO 8601 "time-intervals by duration only" */
4807
0
    case INTSTYLE_ISO_8601:
4808
      /* special-case zero to avoid printing nothing */
4809
0
      if (year == 0 && mon == 0 && mday == 0 &&
4810
0
        hour == 0 && min == 0 && sec == 0 && fsec == 0)
4811
0
      {
4812
0
        sprintf(cp, "PT0S");
4813
0
        break;
4814
0
      }
4815
0
      *cp++ = 'P';
4816
0
      cp = AddISO8601IntPart(cp, year, 'Y');
4817
0
      cp = AddISO8601IntPart(cp, mon, 'M');
4818
0
      cp = AddISO8601IntPart(cp, mday, 'D');
4819
0
      if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
4820
0
        *cp++ = 'T';
4821
0
      cp = AddISO8601IntPart(cp, hour, 'H');
4822
0
      cp = AddISO8601IntPart(cp, min, 'M');
4823
0
      if (sec != 0 || fsec != 0)
4824
0
      {
4825
0
        if (sec < 0 || fsec < 0)
4826
0
          *cp++ = '-';
4827
0
        cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4828
0
        *cp++ = 'S';
4829
0
        *cp++ = '\0';
4830
0
      }
4831
0
      break;
4832
4833
      /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
4834
0
    case INTSTYLE_POSTGRES:
4835
0
      cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
4836
4837
      /*
4838
       * Ideally we should spell out "month" like we do for "year" and
4839
       * "day".  However, for backward compatibility, we can't easily
4840
       * fix this.  bjm 2011-05-24
4841
       */
4842
0
      cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
4843
0
      cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
4844
0
      if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
4845
0
      {
4846
0
        bool    minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
4847
4848
0
        sprintf(cp, "%s%s%02" PRId64 ":%02d:",
4849
0
            is_zero ? "" : " ",
4850
0
            (minus ? "-" : (is_before ? "+" : "")),
4851
0
            i64abs(hour), abs(min));
4852
0
        cp += strlen(cp);
4853
0
        cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4854
0
        *cp = '\0';
4855
0
      }
4856
0
      break;
4857
4858
      /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
4859
0
    case INTSTYLE_POSTGRES_VERBOSE:
4860
0
    default:
4861
0
      strcpy(cp, "@");
4862
0
      cp++;
4863
0
      cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
4864
0
      cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
4865
0
      cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
4866
0
      cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
4867
0
      cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
4868
0
      if (sec != 0 || fsec != 0)
4869
0
      {
4870
0
        *cp++ = ' ';
4871
0
        if (sec < 0 || (sec == 0 && fsec < 0))
4872
0
        {
4873
0
          if (is_zero)
4874
0
            is_before = true;
4875
0
          else if (!is_before)
4876
0
            *cp++ = '-';
4877
0
        }
4878
0
        else if (is_before)
4879
0
          *cp++ = '-';
4880
0
        cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4881
        /* We output "ago", not negatives, so use abs(). */
4882
0
        sprintf(cp, " sec%s",
4883
0
            (abs(sec) != 1 || fsec != 0) ? "s" : "");
4884
0
        is_zero = false;
4885
0
      }
4886
      /* identically zero? then put in a unitless zero... */
4887
0
      if (is_zero)
4888
0
        strcat(cp, " 0");
4889
0
      if (is_before)
4890
0
        strcat(cp, " ago");
4891
0
      break;
4892
0
  }
4893
0
}
4894
4895
4896
/*
4897
 * We've been burnt by stupid errors in the ordering of the datetkn tables
4898
 * once too often.  Arrange to check them during postmaster start.
4899
 */
4900
static bool
4901
CheckDateTokenTable(const char *tablename, const datetkn *base, int nel)
4902
0
{
4903
0
  bool    ok = true;
4904
0
  int     i;
4905
4906
0
  for (i = 0; i < nel; i++)
4907
0
  {
4908
    /* check for token strings that don't fit */
4909
0
    if (strlen(base[i].token) > TOKMAXLEN)
4910
0
    {
4911
      /* %.*s is safe since all our tokens are ASCII */
4912
0
      elog(LOG, "token too long in %s table: \"%.*s\"",
4913
0
         tablename,
4914
0
         TOKMAXLEN + 1, base[i].token);
4915
0
      ok = false;
4916
0
      break;       /* don't risk applying strcmp */
4917
0
    }
4918
    /* check for out of order */
4919
0
    if (i > 0 &&
4920
0
      strcmp(base[i - 1].token, base[i].token) >= 0)
4921
0
    {
4922
0
      elog(LOG, "ordering error in %s table: \"%s\" >= \"%s\"",
4923
0
         tablename,
4924
0
         base[i - 1].token,
4925
0
         base[i].token);
4926
0
      ok = false;
4927
0
    }
4928
0
  }
4929
0
  return ok;
4930
0
}
4931
4932
bool
4933
CheckDateTokenTables(void)
4934
0
{
4935
0
  bool    ok = true;
4936
4937
0
  Assert(UNIX_EPOCH_JDATE == date2j(1970, 1, 1));
4938
0
  Assert(POSTGRES_EPOCH_JDATE == date2j(2000, 1, 1));
4939
4940
0
  ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
4941
0
  ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
4942
0
  return ok;
4943
0
}
4944
4945
/*
4946
 * Common code for temporal prosupport functions: simplify, if possible,
4947
 * a call to a temporal type's length-coercion function.
4948
 *
4949
 * Types time, timetz, timestamp and timestamptz each have a range of allowed
4950
 * precisions.  An unspecified precision is rigorously equivalent to the
4951
 * highest specifiable precision.  We can replace the function call with a
4952
 * no-op RelabelType if it is coercing to the same or higher precision as the
4953
 * input is known to have.
4954
 *
4955
 * The input Node is always a FuncExpr, but to reduce the #include footprint
4956
 * of datetime.h, we declare it as Node *.
4957
 *
4958
 * Note: timestamp_scale throws an error when the typmod is out of range, but
4959
 * we can't get there from a cast: our typmodin will have caught it already.
4960
 */
4961
Node *
4962
TemporalSimplify(int32 max_precis, Node *node)
4963
0
{
4964
0
  FuncExpr   *expr = castNode(FuncExpr, node);
4965
0
  Node     *ret = NULL;
4966
0
  Node     *typmod;
4967
4968
0
  Assert(list_length(expr->args) >= 2);
4969
4970
0
  typmod = (Node *) lsecond(expr->args);
4971
4972
0
  if (IsA(typmod, Const) && !((Const *) typmod)->constisnull)
4973
0
  {
4974
0
    Node     *source = (Node *) linitial(expr->args);
4975
0
    int32   old_precis = exprTypmod(source);
4976
0
    int32   new_precis = DatumGetInt32(((Const *) typmod)->constvalue);
4977
4978
0
    if (new_precis < 0 || new_precis == max_precis ||
4979
0
      (old_precis >= 0 && new_precis >= old_precis))
4980
0
      ret = relabel_to_typmod(source, new_precis);
4981
0
  }
4982
4983
0
  return ret;
4984
0
}
4985
4986
/*
4987
 * This function gets called during timezone config file load or reload
4988
 * to create the final array of timezone tokens.  The argument array
4989
 * is already sorted in name order.
4990
 *
4991
 * The result is a TimeZoneAbbrevTable (which must be a single guc_malloc'd
4992
 * chunk) or NULL on alloc failure.  No other error conditions are defined.
4993
 */
4994
TimeZoneAbbrevTable *
4995
ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
4996
0
{
4997
0
  TimeZoneAbbrevTable *tbl;
4998
0
  Size    tbl_size;
4999
0
  int     i;
5000
5001
  /* Space for fixed fields and datetkn array */
5002
0
  tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
5003
0
    n * sizeof(datetkn);
5004
0
  tbl_size = MAXALIGN(tbl_size);
5005
  /* Count up space for dynamic abbreviations */
5006
0
  for (i = 0; i < n; i++)
5007
0
  {
5008
0
    struct tzEntry *abbr = abbrevs + i;
5009
5010
0
    if (abbr->zone != NULL)
5011
0
    {
5012
0
      Size    dsize;
5013
5014
0
      dsize = offsetof(DynamicZoneAbbrev, zone) +
5015
0
        strlen(abbr->zone) + 1;
5016
0
      tbl_size += MAXALIGN(dsize);
5017
0
    }
5018
0
  }
5019
5020
  /* Alloc the result ... */
5021
0
  tbl = guc_malloc(LOG, tbl_size);
5022
0
  if (!tbl)
5023
0
    return NULL;
5024
5025
  /* ... and fill it in */
5026
0
  tbl->tblsize = tbl_size;
5027
0
  tbl->numabbrevs = n;
5028
  /* in this loop, tbl_size reprises the space calculation above */
5029
0
  tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
5030
0
    n * sizeof(datetkn);
5031
0
  tbl_size = MAXALIGN(tbl_size);
5032
0
  for (i = 0; i < n; i++)
5033
0
  {
5034
0
    struct tzEntry *abbr = abbrevs + i;
5035
0
    datetkn    *dtoken = tbl->abbrevs + i;
5036
5037
    /* use strlcpy to truncate name if necessary */
5038
0
    strlcpy(dtoken->token, abbr->abbrev, TOKMAXLEN + 1);
5039
0
    if (abbr->zone != NULL)
5040
0
    {
5041
      /* Allocate a DynamicZoneAbbrev for this abbreviation */
5042
0
      DynamicZoneAbbrev *dtza;
5043
0
      Size    dsize;
5044
5045
0
      dtza = (DynamicZoneAbbrev *) ((char *) tbl + tbl_size);
5046
0
      dtza->tz = NULL;
5047
0
      strcpy(dtza->zone, abbr->zone);
5048
5049
0
      dtoken->type = DYNTZ;
5050
      /* value is offset from table start to DynamicZoneAbbrev */
5051
0
      dtoken->value = (int32) tbl_size;
5052
5053
0
      dsize = offsetof(DynamicZoneAbbrev, zone) +
5054
0
        strlen(abbr->zone) + 1;
5055
0
      tbl_size += MAXALIGN(dsize);
5056
0
    }
5057
0
    else
5058
0
    {
5059
0
      dtoken->type = abbr->is_dst ? DTZ : TZ;
5060
0
      dtoken->value = abbr->offset;
5061
0
    }
5062
0
  }
5063
5064
  /* Assert the two loops above agreed on size calculations */
5065
0
  Assert(tbl->tblsize == tbl_size);
5066
5067
  /* Check the ordering, if testing */
5068
0
  Assert(CheckDateTokenTable("timezone abbreviations", tbl->abbrevs, n));
5069
5070
0
  return tbl;
5071
0
}
5072
5073
/*
5074
 * Install a TimeZoneAbbrevTable as the active table.
5075
 *
5076
 * Caller is responsible that the passed table doesn't go away while in use.
5077
 */
5078
void
5079
InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
5080
0
{
5081
0
  zoneabbrevtbl = tbl;
5082
  /* reset tzabbrevcache, which may contain results from old table */
5083
0
  memset(tzabbrevcache, 0, sizeof(tzabbrevcache));
5084
0
}
5085
5086
/*
5087
 * Helper subroutine to locate pg_tz timezone for a dynamic abbreviation.
5088
 *
5089
 * On failure, returns NULL and fills *extra for a DTERR_BAD_ZONE_ABBREV error.
5090
 */
5091
static pg_tz *
5092
FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
5093
           DateTimeErrorExtra *extra)
5094
0
{
5095
0
  DynamicZoneAbbrev *dtza;
5096
5097
  /* Just some sanity checks to prevent indexing off into nowhere */
5098
0
  Assert(tp->type == DYNTZ);
5099
0
  Assert(tp->value > 0 && tp->value < tbl->tblsize);
5100
5101
0
  dtza = (DynamicZoneAbbrev *) ((char *) tbl + tp->value);
5102
5103
  /* Look up the underlying zone if we haven't already */
5104
0
  if (dtza->tz == NULL)
5105
0
  {
5106
0
    dtza->tz = pg_tzset(dtza->zone);
5107
0
    if (dtza->tz == NULL)
5108
0
    {
5109
      /* Ooops, bogus zone name in config file entry */
5110
0
      extra->dtee_timezone = dtza->zone;
5111
0
      extra->dtee_abbrev = tp->token;
5112
0
    }
5113
0
  }
5114
0
  return dtza->tz;
5115
0
}
5116
5117
5118
/*
5119
 * This set-returning function reads all the time zone abbreviations
5120
 * defined by the IANA data for the current timezone setting,
5121
 * and returns a set of (abbrev, utc_offset, is_dst).
5122
 */
5123
Datum
5124
pg_timezone_abbrevs_zone(PG_FUNCTION_ARGS)
5125
0
{
5126
0
  FuncCallContext *funcctx;
5127
0
  int      *pindex;
5128
0
  Datum   result;
5129
0
  HeapTuple tuple;
5130
0
  Datum   values[3];
5131
0
  bool    nulls[3] = {0};
5132
0
  TimestampTz now = GetCurrentTransactionStartTimestamp();
5133
0
  pg_time_t t = timestamptz_to_time_t(now);
5134
0
  const char *abbrev;
5135
0
  long int  gmtoff;
5136
0
  int     isdst;
5137
0
  struct pg_itm_in itm_in;
5138
0
  Interval   *resInterval;
5139
5140
  /* stuff done only on the first call of the function */
5141
0
  if (SRF_IS_FIRSTCALL())
5142
0
  {
5143
0
    TupleDesc tupdesc;
5144
0
    MemoryContext oldcontext;
5145
5146
    /* create a function context for cross-call persistence */
5147
0
    funcctx = SRF_FIRSTCALL_INIT();
5148
5149
    /*
5150
     * switch to memory context appropriate for multiple function calls
5151
     */
5152
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5153
5154
    /* allocate memory for user context */
5155
0
    pindex = (int *) palloc(sizeof(int));
5156
0
    *pindex = 0;
5157
0
    funcctx->user_fctx = pindex;
5158
5159
0
    if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
5160
0
      elog(ERROR, "return type must be a row type");
5161
0
    funcctx->tuple_desc = tupdesc;
5162
5163
0
    MemoryContextSwitchTo(oldcontext);
5164
0
  }
5165
5166
  /* stuff done on every call of the function */
5167
0
  funcctx = SRF_PERCALL_SETUP();
5168
0
  pindex = (int *) funcctx->user_fctx;
5169
5170
0
  while ((abbrev = pg_get_next_timezone_abbrev(pindex,
5171
0
                         session_timezone)) != NULL)
5172
0
  {
5173
    /* Ignore abbreviations that aren't all-alphabetic */
5174
0
    if (strspn(abbrev, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != strlen(abbrev))
5175
0
      continue;
5176
5177
    /* Determine the current meaning of the abbrev */
5178
0
    if (!pg_interpret_timezone_abbrev(abbrev,
5179
0
                      &t,
5180
0
                      &gmtoff,
5181
0
                      &isdst,
5182
0
                      session_timezone))
5183
0
      continue;     /* hm, not actually used in this zone? */
5184
5185
0
    values[0] = CStringGetTextDatum(abbrev);
5186
5187
    /* Convert offset (in seconds) to an interval; can't overflow */
5188
0
    MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5189
0
    itm_in.tm_usec = (int64) gmtoff * USECS_PER_SEC;
5190
0
    resInterval = (Interval *) palloc(sizeof(Interval));
5191
0
    (void) itmin2interval(&itm_in, resInterval);
5192
0
    values[1] = IntervalPGetDatum(resInterval);
5193
5194
0
    values[2] = BoolGetDatum(isdst);
5195
5196
0
    tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
5197
0
    result = HeapTupleGetDatum(tuple);
5198
5199
0
    SRF_RETURN_NEXT(funcctx, result);
5200
0
  }
5201
5202
0
  SRF_RETURN_DONE(funcctx);
5203
0
}
5204
5205
/*
5206
 * This set-returning function reads all the time zone abbreviations
5207
 * defined by the timezone_abbreviations setting,
5208
 * and returns a set of (abbrev, utc_offset, is_dst).
5209
 */
5210
Datum
5211
pg_timezone_abbrevs_abbrevs(PG_FUNCTION_ARGS)
5212
0
{
5213
0
  FuncCallContext *funcctx;
5214
0
  int      *pindex;
5215
0
  Datum   result;
5216
0
  HeapTuple tuple;
5217
0
  Datum   values[3];
5218
0
  bool    nulls[3] = {0};
5219
0
  const datetkn *tp;
5220
0
  char    buffer[TOKMAXLEN + 1];
5221
0
  int     gmtoffset;
5222
0
  bool    is_dst;
5223
0
  unsigned char *p;
5224
0
  struct pg_itm_in itm_in;
5225
0
  Interval   *resInterval;
5226
5227
  /* stuff done only on the first call of the function */
5228
0
  if (SRF_IS_FIRSTCALL())
5229
0
  {
5230
0
    TupleDesc tupdesc;
5231
0
    MemoryContext oldcontext;
5232
5233
    /* create a function context for cross-call persistence */
5234
0
    funcctx = SRF_FIRSTCALL_INIT();
5235
5236
    /*
5237
     * switch to memory context appropriate for multiple function calls
5238
     */
5239
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5240
5241
    /* allocate memory for user context */
5242
0
    pindex = (int *) palloc(sizeof(int));
5243
0
    *pindex = 0;
5244
0
    funcctx->user_fctx = pindex;
5245
5246
0
    if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
5247
0
      elog(ERROR, "return type must be a row type");
5248
0
    funcctx->tuple_desc = tupdesc;
5249
5250
0
    MemoryContextSwitchTo(oldcontext);
5251
0
  }
5252
5253
  /* stuff done on every call of the function */
5254
0
  funcctx = SRF_PERCALL_SETUP();
5255
0
  pindex = (int *) funcctx->user_fctx;
5256
5257
0
  if (zoneabbrevtbl == NULL ||
5258
0
    *pindex >= zoneabbrevtbl->numabbrevs)
5259
0
    SRF_RETURN_DONE(funcctx);
5260
5261
0
  tp = zoneabbrevtbl->abbrevs + *pindex;
5262
5263
0
  switch (tp->type)
5264
0
  {
5265
0
    case TZ:
5266
0
      gmtoffset = tp->value;
5267
0
      is_dst = false;
5268
0
      break;
5269
0
    case DTZ:
5270
0
      gmtoffset = tp->value;
5271
0
      is_dst = true;
5272
0
      break;
5273
0
    case DYNTZ:
5274
0
      {
5275
        /* Determine the current meaning of the abbrev */
5276
0
        pg_tz    *tzp;
5277
0
        DateTimeErrorExtra extra;
5278
0
        TimestampTz now;
5279
0
        int     isdst;
5280
5281
0
        tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra);
5282
0
        if (tzp == NULL)
5283
0
          DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra,
5284
0
                     NULL, NULL, NULL);
5285
0
        now = GetCurrentTransactionStartTimestamp();
5286
0
        gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now,
5287
0
                               tp->token,
5288
0
                               tzp,
5289
0
                               &isdst);
5290
0
        is_dst = (bool) isdst;
5291
0
        break;
5292
0
      }
5293
0
    default:
5294
0
      elog(ERROR, "unrecognized timezone type %d", (int) tp->type);
5295
0
      gmtoffset = 0;    /* keep compiler quiet */
5296
0
      is_dst = false;
5297
0
      break;
5298
0
  }
5299
5300
  /*
5301
   * Convert name to text, using upcasing conversion that is the inverse of
5302
   * what ParseDateTime() uses.
5303
   */
5304
0
  strlcpy(buffer, tp->token, sizeof(buffer));
5305
0
  for (p = (unsigned char *) buffer; *p; p++)
5306
0
    *p = pg_toupper(*p);
5307
5308
0
  values[0] = CStringGetTextDatum(buffer);
5309
5310
  /* Convert offset (in seconds) to an interval; can't overflow */
5311
0
  MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5312
0
  itm_in.tm_usec = (int64) gmtoffset * USECS_PER_SEC;
5313
0
  resInterval = (Interval *) palloc(sizeof(Interval));
5314
0
  (void) itmin2interval(&itm_in, resInterval);
5315
0
  values[1] = IntervalPGetDatum(resInterval);
5316
5317
0
  values[2] = BoolGetDatum(is_dst);
5318
5319
0
  (*pindex)++;
5320
5321
0
  tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
5322
0
  result = HeapTupleGetDatum(tuple);
5323
5324
0
  SRF_RETURN_NEXT(funcctx, result);
5325
0
}
5326
5327
/*
5328
 * This set-returning function reads all the available full time zones
5329
 * and returns a set of (name, abbrev, utc_offset, is_dst).
5330
 */
5331
Datum
5332
pg_timezone_names(PG_FUNCTION_ARGS)
5333
0
{
5334
0
  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
5335
0
  pg_tzenum  *tzenum;
5336
0
  pg_tz    *tz;
5337
0
  Datum   values[4];
5338
0
  bool    nulls[4] = {0};
5339
0
  int     tzoff;
5340
0
  struct pg_tm tm;
5341
0
  fsec_t    fsec;
5342
0
  const char *tzn;
5343
0
  Interval   *resInterval;
5344
0
  struct pg_itm_in itm_in;
5345
5346
0
  InitMaterializedSRF(fcinfo, 0);
5347
5348
  /* initialize timezone scanning code */
5349
0
  tzenum = pg_tzenumerate_start();
5350
5351
  /* search for another zone to display */
5352
0
  for (;;)
5353
0
  {
5354
0
    tz = pg_tzenumerate_next(tzenum);
5355
0
    if (!tz)
5356
0
      break;
5357
5358
    /* Convert now() to local time in this zone */
5359
0
    if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
5360
0
             &tzoff, &tm, &fsec, &tzn, tz) != 0)
5361
0
      continue;     /* ignore if conversion fails */
5362
5363
    /*
5364
     * IANA's rather silly "Factory" time zone used to emit ridiculously
5365
     * long "abbreviations" such as "Local time zone must be set--see zic
5366
     * manual page" or "Local time zone must be set--use tzsetup".  While
5367
     * modern versions of tzdb emit the much saner "-00", it seems some
5368
     * benighted packagers are hacking the IANA data so that it continues
5369
     * to produce these strings.  To prevent producing a weirdly wide
5370
     * abbrev column, reject ridiculously long abbreviations.
5371
     */
5372
0
    if (tzn && strlen(tzn) > 31)
5373
0
      continue;
5374
5375
0
    values[0] = CStringGetTextDatum(pg_get_timezone_name(tz));
5376
0
    values[1] = CStringGetTextDatum(tzn ? tzn : "");
5377
5378
    /* Convert tzoff to an interval; can't overflow */
5379
0
    MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5380
0
    itm_in.tm_usec = (int64) -tzoff * USECS_PER_SEC;
5381
0
    resInterval = (Interval *) palloc(sizeof(Interval));
5382
0
    (void) itmin2interval(&itm_in, resInterval);
5383
0
    values[2] = IntervalPGetDatum(resInterval);
5384
5385
0
    values[3] = BoolGetDatum(tm.tm_isdst > 0);
5386
5387
0
    tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
5388
0
  }
5389
5390
0
  pg_tzenumerate_end(tzenum);
5391
0
  return (Datum) 0;
5392
0
}