Age Owner TLA Line data Source code
1 : /*
2 : * src/interfaces/ecpg/pgtypeslib/timestamp.c
3 : */
4 : #include "postgres_fe.h"
5 :
6 : #include <time.h>
7 : #include <limits.h>
8 : #include <math.h>
9 :
10 : #ifdef __FAST_MATH__
11 : #error -ffast-math is known to break this code
12 : #endif
13 :
14 : #include "dt.h"
15 : #include "pgtypes_date.h"
16 : #include "pgtypes_timestamp.h"
17 : #include "pgtypeslib_extern.h"
18 :
19 : static int64
7325 meskes 20 CBC 147 : time2t(const int hour, const int min, const int sec, const fsec_t fsec)
21 : {
6471 bruce 22 147 : return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
23 : } /* time2t() */
24 :
25 : static timestamp
7152 meskes 26 21 : dt2local(timestamp dt, int tz)
27 : {
6530 bruce 28 21 : dt -= (tz * USECS_PER_SEC);
7313 meskes 29 21 : return dt;
30 : } /* dt2local() */
31 :
32 : /* tm2timestamp()
33 : * Convert a tm structure to a timestamp data type.
34 : * Note that year is _not_ 1900-based, but is an explicit full value.
35 : * Also, month is one-based, _not_ zero-based.
36 : *
37 : * Returns -1 on failure (overflow).
38 : */
39 : int
2118 tgl 40 147 : tm2timestamp(struct tm *tm, fsec_t fsec, int *tzp, timestamp * result)
41 : {
42 : int dDate;
43 : int64 time;
44 :
45 : /* Prevent overflow in Julian-day routines */
7325 meskes 46 147 : if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
7325 meskes 47 UBC 0 : return -1;
48 :
7152 meskes 49 CBC 147 : dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
7325 50 147 : time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
6530 bruce 51 147 : *result = (dDate * USECS_PER_DAY) + time;
52 : /* check for major overflow */
53 147 : if ((*result - time) / USECS_PER_DAY != dDate)
7219 tgl 54 UBC 0 : return -1;
55 : /* check for just-barely overflow (okay except time-of-day wraps) */
56 : /* caution: we want to allow 1999-12-31 24:00:00 */
3688 tgl 57 CBC 147 : if ((*result < 0 && dDate > 0) ||
58 147 : (*result > 0 && dDate < -1))
7219 tgl 59 UBC 0 : return -1;
7325 meskes 60 CBC 147 : if (tzp != NULL)
61 21 : *result = dt2local(*result, -(*tzp));
62 :
63 : /* final range check catches just-out-of-range timestamps */
2580 tgl 64 147 : if (!IS_VALID_TIMESTAMP(*result))
2580 tgl 65 UBC 0 : return -1;
66 :
7325 meskes 67 CBC 147 : return 0;
68 : } /* tm2timestamp() */
69 :
70 : static timestamp
7325 meskes 71 UBC 0 : SetEpochTimestamp(void)
72 : {
5177 73 0 : int64 noresult = 0;
74 : timestamp dt;
75 : struct tm tt,
7188 bruce 76 0 : *tm = &tt;
77 :
5177 meskes 78 0 : if (GetEpochTime(tm) < 0)
79 0 : return noresult;
80 :
7325 81 0 : tm2timestamp(tm, 0, NULL, &dt);
7313 82 0 : return dt;
83 : } /* SetEpochTimestamp() */
84 :
85 : /* timestamp2tm()
86 : * Convert timestamp data type to POSIX time structure.
87 : * Note that year is _not_ 1900-based, but is an explicit full value.
88 : * Also, month is one-based, _not_ zero-based.
89 : * Returns:
90 : * 0 on success
91 : * -1 on out of range
92 : *
93 : * For dates within the system-supported time_t range, convert to the
94 : * local time zone. If out of this range, leave as GMT. - tgl 97/05/27
95 : */
96 : static int
2118 tgl 97 CBC 163 : timestamp2tm(timestamp dt, int *tzp, struct tm *tm, fsec_t *fsec, const char **tzn)
98 : {
99 : int64 dDate,
100 : date0;
101 : int64 time;
102 : #if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
103 : time_t utime;
104 : struct tm *tx;
105 : #endif
106 :
7325 meskes 107 163 : date0 = date2j(2000, 1, 1);
108 :
6391 tgl 109 163 : time = dt;
6530 bruce 110 163 : TMODULO(time, dDate, USECS_PER_DAY);
111 :
7325 meskes 112 163 : if (time < INT64CONST(0))
113 : {
6530 bruce 114 92 : time += USECS_PER_DAY;
7152 meskes 115 92 : dDate -= 1;
116 : }
117 :
118 : /* add offset to go from J2000 back to standard Julian date */
6391 tgl 119 163 : dDate += date0;
120 :
121 : /* Julian day routine does not work for negative Julian days */
122 163 : if (dDate < 0 || dDate > (timestamp) INT_MAX)
6391 tgl 123 UBC 0 : return -1;
124 :
6391 tgl 125 CBC 163 : j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
126 163 : dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
127 :
7325 meskes 128 163 : if (tzp != NULL)
129 : {
130 : /*
131 : * Does this fall within the capabilities of the localtime()
132 : * interface? Then use this to rotate to the local time zone.
133 : */
7325 meskes 134 UBC 0 : if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
135 : {
136 : #if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
137 :
6529 bruce 138 0 : utime = dt / USECS_PER_SEC +
139 0 : ((date0 - date2j(1970, 1, 1)) * INT64CONST(86400));
140 :
7325 meskes 141 0 : tx = localtime(&utime);
142 0 : tm->tm_year = tx->tm_year + 1900;
143 0 : tm->tm_mon = tx->tm_mon + 1;
144 0 : tm->tm_mday = tx->tm_mday;
145 0 : tm->tm_hour = tx->tm_hour;
146 0 : tm->tm_min = tx->tm_min;
147 0 : tm->tm_isdst = tx->tm_isdst;
148 :
149 : #if defined(HAVE_STRUCT_TM_TM_ZONE)
150 0 : tm->tm_gmtoff = tx->tm_gmtoff;
151 0 : tm->tm_zone = tx->tm_zone;
152 :
2118 tgl 153 0 : *tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */
7325 meskes 154 0 : if (tzn != NULL)
4042 peter_e 155 0 : *tzn = tm->tm_zone;
156 : #elif defined(HAVE_INT_TIMEZONE)
157 : *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
158 : if (tzn != NULL)
159 : *tzn = TZNAME_GLOBAL[(tm->tm_isdst > 0)];
160 : #endif
161 : #else /* not (HAVE_STRUCT_TM_TM_ZONE ||
162 : * HAVE_INT_TIMEZONE) */
163 : *tzp = 0;
164 : /* Mark this as *no* time zone available */
165 : tm->tm_isdst = -1;
166 : if (tzn != NULL)
167 : *tzn = NULL;
168 : #endif
169 : }
170 : else
171 : {
7325 meskes 172 0 : *tzp = 0;
173 : /* Mark this as *no* time zone available */
174 0 : tm->tm_isdst = -1;
175 0 : if (tzn != NULL)
176 0 : *tzn = NULL;
177 : }
178 : }
179 : else
180 : {
7325 meskes 181 CBC 163 : tm->tm_isdst = -1;
182 163 : if (tzn != NULL)
7325 meskes 183 UBC 0 : *tzn = NULL;
184 : }
185 :
3551 meskes 186 CBC 163 : tm->tm_yday = dDate - date2j(tm->tm_year, 1, 1) + 1;
187 :
7325 188 163 : return 0;
189 : } /* timestamp2tm() */
190 :
191 : /* EncodeSpecialTimestamp()
192 : * * Convert reserved timestamp data type to string.
193 : * */
194 : static void
7152 meskes 195 UBC 0 : EncodeSpecialTimestamp(timestamp dt, char *str)
196 : {
7313 197 0 : if (TIMESTAMP_IS_NOBEGIN(dt))
198 0 : strcpy(str, EARLY);
199 0 : else if (TIMESTAMP_IS_NOEND(dt))
200 0 : strcpy(str, LATE);
201 : else
2036 peter_e 202 0 : abort(); /* shouldn't happen */
203 0 : }
204 :
205 : timestamp
7313 meskes 206 CBC 125 : PGTYPEStimestamp_from_asc(char *str, char **endptr)
207 : {
208 : timestamp result;
7325 209 125 : int64 noresult = 0;
210 : fsec_t fsec;
211 : struct tm tt,
7188 bruce 212 125 : *tm = &tt;
213 : int dtype;
214 : int nf;
215 : char *field[MAXDATEFIELDS];
216 : int ftype[MAXDATEFIELDS];
217 : char lowstr[MAXDATELEN + MAXDATEFIELDS];
218 : char *realptr;
219 125 : char **ptr = (endptr != NULL) ? endptr : &realptr;
220 :
3338 noah 221 125 : if (strlen(str) > MAXDATELEN)
222 : {
7315 meskes 223 UBC 0 : errno = PGTYPES_TS_BAD_TIMESTAMP;
2061 peter_e 224 0 : return noresult;
225 : }
226 :
5072 meskes 227 CBC 250 : if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
5717 228 125 : DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, 0) != 0)
229 : {
7188 bruce 230 1 : errno = PGTYPES_TS_BAD_TIMESTAMP;
2061 peter_e 231 1 : return noresult;
232 : }
233 :
7325 meskes 234 124 : switch (dtype)
235 : {
236 124 : case DTK_DATE:
237 124 : if (tm2timestamp(tm, fsec, NULL, &result) != 0)
238 : {
7315 meskes 239 UBC 0 : errno = PGTYPES_TS_BAD_TIMESTAMP;
2061 peter_e 240 0 : return noresult;
241 : }
7325 meskes 242 CBC 124 : break;
243 :
7325 meskes 244 UBC 0 : case DTK_EPOCH:
245 0 : result = SetEpochTimestamp();
246 0 : break;
247 :
248 0 : case DTK_LATE:
249 0 : TIMESTAMP_NOEND(result);
250 0 : break;
251 :
252 0 : case DTK_EARLY:
253 0 : TIMESTAMP_NOBEGIN(result);
254 0 : break;
255 :
256 0 : default:
7315 257 0 : errno = PGTYPES_TS_BAD_TIMESTAMP;
2061 peter_e 258 0 : return noresult;
259 : }
260 :
261 : /* AdjustTimestampForTypmod(&result, typmod); */
262 :
263 : /*
264 : * Since it's difficult to test for noresult, make sure errno is 0 if no
265 : * error occurred.
266 : */
6984 meskes 267 CBC 124 : errno = 0;
7325 268 124 : return result;
269 : }
270 :
271 : char *
7152 272 158 : PGTYPEStimestamp_to_asc(timestamp tstamp)
273 : {
274 : struct tm tt,
7188 bruce 275 158 : *tm = &tt;
276 : char buf[MAXDATELEN + 1];
277 : fsec_t fsec;
1357 michael 278 158 : int DateStyle = 1; /* this defaults to USE_ISO_DATES, shall we
279 : * make it an option? */
280 :
7325 meskes 281 158 : if (TIMESTAMP_NOT_FINITE(tstamp))
7313 meskes 282 UBC 0 : EncodeSpecialTimestamp(tstamp, buf);
7313 meskes 283 CBC 158 : else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
4043 peter_e 284 158 : EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf, 0);
285 : else
286 : {
7315 meskes 287 UBC 0 : errno = PGTYPES_TS_BAD_TIMESTAMP;
7325 288 0 : return NULL;
289 : }
7313 meskes 290 CBC 158 : return pgtypes_strdup(buf);
291 : }
292 :
293 : void
6385 bruce 294 1 : PGTYPEStimestamp_current(timestamp * ts)
295 : {
296 : struct tm tm;
297 :
7313 meskes 298 1 : GetCurrentDateTime(&tm);
5177 299 1 : if (errno == 0)
300 1 : tm2timestamp(&tm, 0, NULL, ts);
7313 301 1 : }
302 :
303 : static int
2118 tgl 304 4 : dttofmtasc_replace(timestamp * ts, date dDate, int dow, struct tm *tm,
305 : char *output, int *pstr_len, const char *fmtstr)
306 : {
307 : union un_fmt_comb replace_val;
308 : int replace_type;
309 : int i;
4130 meskes 310 4 : const char *p = fmtstr;
7188 bruce 311 4 : char *q = output;
312 :
313 73 : while (*p)
314 : {
315 69 : if (*p == '%')
316 : {
7313 meskes 317 19 : p++;
318 : /* fix compiler warning */
7191 319 19 : replace_type = PGTYPES_TYPE_NOTHING;
7188 bruce 320 19 : switch (*p)
321 : {
322 : /* the abbreviated name of the day in the week */
323 : /* XXX should be locale aware */
7313 meskes 324 2 : case 'a':
7191 325 2 : replace_val.str_val = pgtypes_date_weekdays_short[dow];
326 2 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
7313 327 2 : break;
328 : /* the full name of the day in the week */
329 : /* XXX should be locale aware */
7313 meskes 330 UBC 0 : case 'A':
7191 331 0 : replace_val.str_val = days[dow];
332 0 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
7313 333 0 : break;
334 : /* the abbreviated name of the month */
335 : /* XXX should be locale aware */
7313 meskes 336 CBC 2 : case 'b':
337 : case 'h':
1226 tomas.vondra 338 2 : replace_val.str_val = months[tm->tm_mon - 1];
7191 meskes 339 2 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
7313 340 2 : break;
341 : /* the full name of the month */
342 : /* XXX should be locale aware */
7313 meskes 343 UBC 0 : case 'B':
1226 tomas.vondra 344 0 : replace_val.str_val = pgtypes_date_months[tm->tm_mon - 1];
7191 meskes 345 0 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
7313 346 0 : break;
347 :
348 : /*
349 : * The preferred date and time representation for
350 : * the current locale.
351 : */
352 0 : case 'c':
353 : /* XXX */
354 0 : break;
355 : /* the century number with leading zeroes */
356 0 : case 'C':
7160 357 0 : replace_val.uint_val = tm->tm_year / 100;
7191 358 0 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7313 359 0 : break;
360 : /* day with leading zeroes (01 - 31) */
7313 meskes 361 CBC 2 : case 'd':
7191 362 2 : replace_val.uint_val = tm->tm_mday;
363 2 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7313 364 2 : break;
365 : /* the date in the format mm/dd/yy */
7313 meskes 366 UBC 0 : case 'D':
367 :
368 : /*
369 : * ts, dDate, dow, tm is information about the timestamp
370 : *
371 : * q is the start of the current output buffer
372 : *
373 : * pstr_len is a pointer to the remaining size of output,
374 : * i.e. the size of q
375 : */
376 0 : i = dttofmtasc_replace(ts, dDate, dow, tm,
377 : q, pstr_len,
378 : "%m/%d/%y");
7188 bruce 379 0 : if (i)
380 0 : return i;
7313 meskes 381 0 : break;
382 : /* day with leading spaces (01 - 31) */
383 0 : case 'e':
7191 384 0 : replace_val.uint_val = tm->tm_mday;
385 0 : replace_type = PGTYPES_TYPE_UINT_2_LS;
7313 386 0 : break;
387 :
388 : /*
389 : * alternative format modifier
390 : */
391 0 : case 'E':
392 : {
7188 bruce 393 0 : char tmp[4] = "%Ex";
394 :
7313 meskes 395 0 : p++;
7188 bruce 396 0 : if (*p == '\0')
7313 meskes 397 0 : return -1;
398 0 : tmp[2] = *p;
399 :
400 : /*
401 : * strftime's month is 0 based, ours is 1 based
402 : */
403 0 : tm->tm_mon -= 1;
404 0 : i = strftime(q, *pstr_len, tmp, tm);
7188 bruce 405 0 : if (i == 0)
406 0 : return -1;
407 0 : while (*q)
408 : {
7313 meskes 409 0 : q++;
410 0 : (*pstr_len)--;
411 : }
412 0 : tm->tm_mon += 1;
7191 413 0 : replace_type = PGTYPES_TYPE_NOTHING;
7313 414 0 : break;
415 : }
416 :
417 : /*
418 : * The ISO 8601 year with century as a decimal number. The
419 : * 4-digit year corresponding to the ISO week number.
420 : */
421 0 : case 'G':
422 : {
423 : /* Keep compiler quiet - Don't use a literal format */
4322 bruce 424 0 : const char *fmt = "%G";
425 :
4364 andrew 426 0 : tm->tm_mon -= 1;
427 0 : i = strftime(q, *pstr_len, fmt, tm);
428 0 : if (i == 0)
429 0 : return -1;
430 0 : while (*q)
431 : {
432 0 : q++;
433 0 : (*pstr_len)--;
434 : }
435 0 : tm->tm_mon += 1;
436 0 : replace_type = PGTYPES_TYPE_NOTHING;
437 : }
7313 meskes 438 0 : break;
439 :
440 : /*
441 : * Like %G, but without century, i.e., with a 2-digit year
442 : * (00-99).
443 : */
444 0 : case 'g':
445 : {
5867 446 0 : const char *fmt = "%g"; /* Keep compiler quiet about
447 : * 2-digit year */
448 :
7228 449 0 : tm->tm_mon -= 1;
450 0 : i = strftime(q, *pstr_len, fmt, tm);
7188 bruce 451 0 : if (i == 0)
452 0 : return -1;
453 0 : while (*q)
454 : {
7228 meskes 455 0 : q++;
456 0 : (*pstr_len)--;
457 : }
458 0 : tm->tm_mon += 1;
7191 459 0 : replace_type = PGTYPES_TYPE_NOTHING;
460 : }
7313 461 0 : break;
462 : /* hour (24 hour clock) with leading zeroes */
7313 meskes 463 CBC 2 : case 'H':
7191 464 2 : replace_val.uint_val = tm->tm_hour;
465 2 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7313 466 2 : break;
467 : /* hour (12 hour clock) with leading zeroes */
7313 meskes 468 UBC 0 : case 'I':
7191 469 0 : replace_val.uint_val = tm->tm_hour % 12;
470 0 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7313 471 0 : break;
472 :
473 : /*
474 : * The day of the year as a decimal number with leading
475 : * zeroes. It ranges from 001 to 366.
476 : */
7313 meskes 477 CBC 1 : case 'j':
7191 478 1 : replace_val.uint_val = tm->tm_yday;
479 1 : replace_type = PGTYPES_TYPE_UINT_3_LZ;
7313 480 1 : break;
481 :
482 : /*
483 : * The hour (24 hour clock). Leading zeroes will be turned
484 : * into spaces.
485 : */
7313 meskes 486 UBC 0 : case 'k':
7191 487 0 : replace_val.uint_val = tm->tm_hour;
488 0 : replace_type = PGTYPES_TYPE_UINT_2_LS;
7313 489 0 : break;
490 :
491 : /*
492 : * The hour (12 hour clock). Leading zeroes will be turned
493 : * into spaces.
494 : */
495 0 : case 'l':
7191 496 0 : replace_val.uint_val = tm->tm_hour % 12;
497 0 : replace_type = PGTYPES_TYPE_UINT_2_LS;
7313 498 0 : break;
499 : /* The month as a decimal number with a leading zero */
500 0 : case 'm':
7191 501 0 : replace_val.uint_val = tm->tm_mon;
502 0 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7313 503 0 : break;
504 : /* The minute as a decimal number with a leading zero */
7313 meskes 505 CBC 2 : case 'M':
7191 506 2 : replace_val.uint_val = tm->tm_min;
507 2 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7313 508 2 : break;
509 : /* A newline character */
7313 meskes 510 UBC 0 : case 'n':
7191 511 0 : replace_val.char_val = '\n';
512 0 : replace_type = PGTYPES_TYPE_CHAR;
7313 513 0 : break;
514 : /* the AM/PM specifier (uppercase) */
515 : /* XXX should be locale aware */
516 0 : case 'p':
7188 bruce 517 0 : if (tm->tm_hour < 12)
7191 meskes 518 0 : replace_val.str_val = "AM";
519 : else
520 0 : replace_val.str_val = "PM";
521 0 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
7313 522 0 : break;
523 : /* the AM/PM specifier (lowercase) */
524 : /* XXX should be locale aware */
525 0 : case 'P':
7188 bruce 526 0 : if (tm->tm_hour < 12)
7191 meskes 527 0 : replace_val.str_val = "am";
528 : else
529 0 : replace_val.str_val = "pm";
530 0 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
7313 531 0 : break;
532 : /* the time in the format %I:%M:%S %p */
533 : /* XXX should be locale aware */
534 0 : case 'r':
535 0 : i = dttofmtasc_replace(ts, dDate, dow, tm,
536 : q, pstr_len,
537 : "%I:%M:%S %p");
7188 bruce 538 0 : if (i)
539 0 : return i;
7313 meskes 540 0 : break;
541 : /* The time in 24 hour notation (%H:%M) */
542 0 : case 'R':
543 0 : i = dttofmtasc_replace(ts, dDate, dow, tm,
544 : q, pstr_len,
545 : "%H:%M");
7188 bruce 546 0 : if (i)
547 0 : return i;
7313 meskes 548 0 : break;
549 : /* The number of seconds since the Epoch (1970-01-01) */
550 0 : case 's':
6480 bruce 551 0 : replace_val.int64_val = (*ts - SetEpochTimestamp()) / 1000000.0;
7191 meskes 552 0 : replace_type = PGTYPES_TYPE_INT64;
7313 553 0 : break;
554 : /* seconds as a decimal number with leading zeroes */
7313 meskes 555 CBC 2 : case 'S':
7191 556 2 : replace_val.uint_val = tm->tm_sec;
7141 557 2 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7313 558 2 : break;
559 : /* A tabulator */
7313 meskes 560 UBC 0 : case 't':
7191 561 0 : replace_val.char_val = '\t';
562 0 : replace_type = PGTYPES_TYPE_CHAR;
7313 563 0 : break;
564 : /* The time in 24 hour notation (%H:%M:%S) */
565 0 : case 'T':
566 0 : i = dttofmtasc_replace(ts, dDate, dow, tm,
567 : q, pstr_len,
568 : "%H:%M:%S");
7188 bruce 569 0 : if (i)
570 0 : return i;
7313 meskes 571 0 : break;
572 :
573 : /*
574 : * The day of the week as a decimal, Monday = 1, Sunday =
575 : * 7
576 : */
577 0 : case 'u':
7191 578 0 : replace_val.uint_val = dow;
6081 579 0 : if (replace_val.uint_val == 0)
580 0 : replace_val.uint_val = 7;
7191 581 0 : replace_type = PGTYPES_TYPE_UINT;
7313 582 0 : break;
583 : /* The week number of the year as a decimal number */
584 0 : case 'U':
585 0 : tm->tm_mon -= 1;
586 0 : i = strftime(q, *pstr_len, "%U", tm);
7188 bruce 587 0 : if (i == 0)
588 0 : return -1;
589 0 : while (*q)
590 : {
7313 meskes 591 0 : q++;
592 0 : (*pstr_len)--;
593 : }
594 0 : tm->tm_mon += 1;
7191 595 0 : replace_type = PGTYPES_TYPE_NOTHING;
7313 596 0 : break;
597 :
598 : /*
599 : * The ISO 8601:1988 week number of the current year as a
600 : * decimal number.
601 : */
602 0 : case 'V':
603 : {
604 : /* Keep compiler quiet - Don't use a literal format */
4322 bruce 605 0 : const char *fmt = "%V";
606 :
4364 andrew 607 0 : i = strftime(q, *pstr_len, fmt, tm);
608 0 : if (i == 0)
609 0 : return -1;
610 0 : while (*q)
611 : {
612 0 : q++;
613 0 : (*pstr_len)--;
614 : }
615 0 : replace_type = PGTYPES_TYPE_NOTHING;
616 : }
7313 meskes 617 0 : break;
618 :
619 : /*
620 : * The day of the week as a decimal, Sunday being 0 and
621 : * Monday 1.
622 : */
623 0 : case 'w':
7191 624 0 : replace_val.uint_val = dow;
625 0 : replace_type = PGTYPES_TYPE_UINT;
7313 626 0 : break;
627 : /* The week number of the year (another definition) */
628 0 : case 'W':
629 0 : tm->tm_mon -= 1;
630 0 : i = strftime(q, *pstr_len, "%U", tm);
7188 bruce 631 0 : if (i == 0)
632 0 : return -1;
633 0 : while (*q)
634 : {
7313 meskes 635 0 : q++;
636 0 : (*pstr_len)--;
637 : }
638 0 : tm->tm_mon += 1;
7191 639 0 : replace_type = PGTYPES_TYPE_NOTHING;
7313 640 0 : break;
641 :
642 : /*
643 : * The preferred date representation for the current
644 : * locale without the time.
645 : */
7313 meskes 646 CBC 1 : case 'x':
647 : {
5867 648 1 : const char *fmt = "%x"; /* Keep compiler quiet about
649 : * 2-digit year */
650 :
7228 651 1 : tm->tm_mon -= 1;
652 1 : i = strftime(q, *pstr_len, fmt, tm);
7188 bruce 653 1 : if (i == 0)
7188 bruce 654 UBC 0 : return -1;
7188 bruce 655 CBC 9 : while (*q)
656 : {
7228 meskes 657 8 : q++;
658 8 : (*pstr_len)--;
659 : }
660 1 : tm->tm_mon += 1;
7191 661 1 : replace_type = PGTYPES_TYPE_NOTHING;
662 : }
7313 663 1 : break;
664 :
665 : /*
666 : * The preferred time representation for the current
667 : * locale without the date.
668 : */
669 1 : case 'X':
670 1 : tm->tm_mon -= 1;
671 1 : i = strftime(q, *pstr_len, "%X", tm);
7188 bruce 672 1 : if (i == 0)
7188 bruce 673 UBC 0 : return -1;
7188 bruce 674 CBC 9 : while (*q)
675 : {
7313 meskes 676 8 : q++;
677 8 : (*pstr_len)--;
678 : }
679 1 : tm->tm_mon += 1;
7191 680 1 : replace_type = PGTYPES_TYPE_NOTHING;
7313 681 1 : break;
682 : /* The year without the century (2 digits, leading zeroes) */
7313 meskes 683 UBC 0 : case 'y':
7191 684 0 : replace_val.uint_val = tm->tm_year % 100;
685 0 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7313 686 0 : break;
687 : /* The year with the century (4 digits) */
7313 meskes 688 CBC 3 : case 'Y':
7160 689 3 : replace_val.uint_val = tm->tm_year;
7191 690 3 : replace_type = PGTYPES_TYPE_UINT;
7313 691 3 : break;
692 : /* The time zone offset from GMT */
7313 meskes 693 UBC 0 : case 'z':
694 0 : tm->tm_mon -= 1;
695 0 : i = strftime(q, *pstr_len, "%z", tm);
7188 bruce 696 0 : if (i == 0)
697 0 : return -1;
698 0 : while (*q)
699 : {
7313 meskes 700 0 : q++;
701 0 : (*pstr_len)--;
702 : }
703 0 : tm->tm_mon += 1;
7191 704 0 : replace_type = PGTYPES_TYPE_NOTHING;
7313 705 0 : break;
706 : /* The name or abbreviation of the time zone */
707 0 : case 'Z':
708 0 : tm->tm_mon -= 1;
709 0 : i = strftime(q, *pstr_len, "%Z", tm);
7188 bruce 710 0 : if (i == 0)
711 0 : return -1;
712 0 : while (*q)
713 : {
7313 meskes 714 0 : q++;
715 0 : (*pstr_len)--;
716 : }
717 0 : tm->tm_mon += 1;
7191 718 0 : replace_type = PGTYPES_TYPE_NOTHING;
7313 719 0 : break;
720 : /* A % sign */
7313 meskes 721 CBC 1 : case '%':
7191 722 1 : replace_val.char_val = '%';
723 1 : replace_type = PGTYPES_TYPE_CHAR;
7313 724 1 : break;
7313 meskes 725 UBC 0 : case '\0':
726 : /* fmtstr: foo%' - The string ends with a % sign */
727 :
728 : /*
729 : * this is not compliant to the specification
730 : */
731 0 : return -1;
732 0 : default:
733 :
734 : /*
735 : * if we don't know the pattern, we just copy it
736 : */
7188 bruce 737 0 : if (*pstr_len > 1)
738 : {
7313 meskes 739 0 : *q = '%';
7188 bruce 740 0 : q++;
741 0 : (*pstr_len)--;
742 0 : if (*pstr_len > 1)
743 : {
7313 meskes 744 0 : *q = *p;
7188 bruce 745 0 : q++;
746 0 : (*pstr_len)--;
747 : }
748 : else
749 : {
7313 meskes 750 0 : *q = '\0';
751 0 : return -1;
752 : }
753 0 : *q = '\0';
754 : }
755 : else
7188 bruce 756 0 : return -1;
7313 meskes 757 0 : break;
758 : }
7313 meskes 759 CBC 19 : i = pgtypes_fmt_replace(replace_val, replace_type, &q, pstr_len);
7188 bruce 760 19 : if (i)
7313 meskes 761 UBC 0 : return i;
762 : }
763 : else
764 : {
7188 bruce 765 CBC 50 : if (*pstr_len > 1)
766 : {
7313 meskes 767 50 : *q = *p;
768 50 : (*pstr_len)--;
769 50 : q++;
770 50 : *q = '\0';
771 : }
772 : else
7188 bruce 773 UBC 0 : return -1;
774 : }
7313 meskes 775 CBC 69 : p++;
776 : }
777 4 : return 0;
778 : }
779 :
780 :
781 : int
4130 782 4 : PGTYPEStimestamp_fmt_asc(timestamp * ts, char *output, int str_len, const char *fmtstr)
783 : {
784 : struct tm tm;
785 : fsec_t fsec;
786 : date dDate;
787 : int dow;
788 :
7313 789 4 : dDate = PGTYPESdate_from_timestamp(*ts);
790 4 : dow = PGTYPESdate_dayofweek(dDate);
791 4 : timestamp2tm(*ts, NULL, &tm, &fsec, NULL);
792 :
793 4 : return dttofmtasc_replace(ts, dDate, dow, &tm, output, &str_len, fmtstr);
794 : }
795 :
796 : int
6385 bruce 797 UBC 0 : PGTYPEStimestamp_sub(timestamp * ts1, timestamp * ts2, interval * iv)
798 : {
7313 meskes 799 0 : if (TIMESTAMP_NOT_FINITE(*ts1) || TIMESTAMP_NOT_FINITE(*ts2))
800 0 : return PGTYPES_TS_ERR_EINFTIME;
801 : else
5477 802 0 : iv->time = (*ts1 - *ts2);
803 :
7313 804 0 : iv->month = 0;
805 :
806 0 : return 0;
807 : }
808 :
809 : int
1986 peter_e 810 CBC 25 : PGTYPEStimestamp_defmt_asc(const char *str, const char *fmt, timestamp * d)
811 : {
812 : int year,
813 : month,
814 : day;
815 : int hour,
816 : minute,
817 : second;
818 : int tz;
819 :
820 : int i;
821 : char *mstr;
822 : char *mfmt;
823 :
7188 bruce 824 25 : if (!fmt)
7191 meskes 825 1 : fmt = "%Y-%m-%d %H:%M:%S";
7188 bruce 826 25 : if (!fmt[0])
7191 meskes 827 1 : return 1;
828 :
829 24 : mstr = pgtypes_strdup(str);
830 24 : mfmt = pgtypes_strdup(fmt);
831 :
832 : /*
833 : * initialize with impossible values so that we can see if the fields
834 : * where specified at all
835 : */
836 : /* XXX ambiguity with 1 BC for year? */
7188 bruce 837 24 : year = -1;
838 24 : month = -1;
839 24 : day = -1;
840 24 : hour = 0;
841 24 : minute = -1;
842 24 : second = -1;
7191 meskes 843 24 : tz = 0;
844 :
845 24 : i = PGTYPEStimestamp_defmt_scan(&mstr, mfmt, d, &year, &month, &day, &hour, &minute, &second, &tz);
846 24 : free(mstr);
847 24 : free(mfmt);
848 24 : return i;
849 : }
850 :
851 : /*
852 : * add an interval to a time stamp
853 : *
854 : * *tout = tin + span
855 : *
856 : * returns 0 if successful
857 : * returns -1 if it fails
858 : *
859 : */
860 :
861 : int
6385 bruce 862 7 : PGTYPEStimestamp_add_interval(timestamp * tin, interval * span, timestamp * tout)
863 : {
864 7 : if (TIMESTAMP_NOT_FINITE(*tin))
6385 bruce 865 UBC 0 : *tout = *tin;
866 : else
867 : {
6385 bruce 868 CBC 7 : if (span->month != 0)
869 : {
870 : struct tm tt,
871 1 : *tm = &tt;
872 : fsec_t fsec;
873 :
874 1 : if (timestamp2tm(*tin, NULL, tm, &fsec, NULL) != 0)
6385 bruce 875 UBC 0 : return -1;
6385 bruce 876 CBC 1 : tm->tm_mon += span->month;
877 1 : if (tm->tm_mon > MONTHS_PER_YEAR)
878 : {
879 1 : tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
880 1 : tm->tm_mon = (tm->tm_mon - 1) % MONTHS_PER_YEAR + 1;
881 : }
6385 bruce 882 UBC 0 : else if (tm->tm_mon < 1)
883 : {
884 0 : tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
885 0 : tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
886 : }
887 :
888 :
889 : /* adjust for end of month boundary problems... */
6385 bruce 890 CBC 1 : if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
6385 bruce 891 UBC 0 : tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
892 :
893 :
6385 bruce 894 CBC 1 : if (tm2timestamp(tm, fsec, NULL, tin) != 0)
6385 bruce 895 UBC 0 : return -1;
896 : }
897 :
6385 bruce 898 CBC 7 : *tin += span->time;
899 7 : *tout = *tin;
900 : }
901 :
361 alvherre 902 7 : return 0;
903 : }
904 :
905 :
906 : /*
907 : * subtract an interval from a time stamp
908 : *
909 : * *tout = tin - span
910 : *
911 : * returns 0 if successful
912 : * returns -1 if it fails
913 : *
914 : */
915 :
916 : int
6385 bruce 917 UBC 0 : PGTYPEStimestamp_sub_interval(timestamp * tin, interval * span, timestamp * tout)
918 : {
919 : interval tspan;
920 :
921 0 : tspan.month = -span->month;
922 0 : tspan.time = -span->time;
923 :
924 0 : return PGTYPEStimestamp_add_interval(tin, &tspan, tout);
925 : }
|