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