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