Age Owner Branch data TLA Line data Source code
1 : : /* src/interfaces/ecpg/pgtypeslib/interval.c */
2 : :
3 : : #include "postgres_fe.h"
4 : :
5 : : #include <time.h>
6 : : #include <math.h>
7 : : #include <limits.h>
8 : :
9 : : #ifdef __FAST_MATH__
10 : : #error -ffast-math is known to break this code
11 : : #endif
12 : :
13 : : #include "common/string.h"
14 : : #include "dt.h"
15 : : #include "pgtypes_error.h"
16 : : #include "pgtypes_interval.h"
17 : : #include "pgtypeslib_extern.h"
18 : :
19 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c
20 : : * and changed struct pg_tm to struct tm
21 : : */
22 : : static void
2489 tgl@sss.pgh.pa.us 23 :CBC 38 : AdjustFractSeconds(double frac, struct /* pg_ */ tm *tm, fsec_t *fsec, int scale)
24 : : {
25 : : int sec;
26 : :
5618 meskes@postgresql.or 27 [ + - ]: 38 : if (frac == 0)
28 : 38 : return;
5421 bruce@momjian.us 29 :UBC 0 : frac *= scale;
30 : 0 : sec = (int) frac;
5618 meskes@postgresql.or 31 : 0 : tm->tm_sec += sec;
5421 bruce@momjian.us 32 : 0 : frac -= sec;
33 : 0 : *fsec += rint(frac * 1000000);
34 : : }
35 : :
36 : :
37 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c
38 : : * and changed struct pg_tm to struct tm
39 : : */
40 : : static void
2489 tgl@sss.pgh.pa.us 41 : 0 : AdjustFractDays(double frac, struct /* pg_ */ tm *tm, fsec_t *fsec, int scale)
42 : : {
43 : : int extra_days;
44 : :
5618 meskes@postgresql.or 45 [ # # ]: 0 : if (frac == 0)
46 : 0 : return;
5421 bruce@momjian.us 47 : 0 : frac *= scale;
48 : 0 : extra_days = (int) frac;
5618 meskes@postgresql.or 49 : 0 : tm->tm_mday += extra_days;
5421 bruce@momjian.us 50 : 0 : frac -= extra_days;
5618 meskes@postgresql.or 51 : 0 : AdjustFractSeconds(frac, tm, fsec, SECS_PER_DAY);
52 : : }
53 : :
54 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
55 : : static int
2357 peter_e@gmx.net 56 : 0 : ParseISO8601Number(const char *str, char **endptr, int *ipart, double *fpart)
57 : : {
58 : : double val;
59 : :
5618 meskes@postgresql.or 60 [ # # # # : 0 : if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
# # ]
61 : 0 : return DTERR_BAD_FORMAT;
62 : 0 : errno = 0;
63 : 0 : val = strtod(str, endptr);
64 : : /* did we not see anything that looks like a double? */
65 [ # # # # ]: 0 : if (*endptr == str || errno != 0)
66 : 0 : return DTERR_BAD_FORMAT;
67 : : /* watch out for overflow */
68 [ # # # # ]: 0 : if (val < INT_MIN || val > INT_MAX)
69 : 0 : return DTERR_FIELD_OVERFLOW;
70 : : /* be very sure we truncate towards zero (cf dtrunc()) */
71 [ # # ]: 0 : if (val >= 0)
72 : 0 : *ipart = (int) floor(val);
73 : : else
74 : 0 : *ipart = (int) -floor(-val);
75 : 0 : *fpart = val - *ipart;
76 : 0 : return 0;
77 : : }
78 : :
79 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
80 : : static int
2357 peter_e@gmx.net 81 : 0 : ISO8601IntegerWidth(const char *fieldstart)
82 : : {
83 : : /* We might have had a leading '-' */
5618 meskes@postgresql.or 84 [ # # ]: 0 : if (*fieldstart == '-')
85 : 0 : fieldstart++;
86 : 0 : return strspn(fieldstart, "0123456789");
87 : : }
88 : :
89 : :
90 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c
91 : : * and changed struct pg_tm to struct tm
92 : : */
93 : : static inline void
2489 tgl@sss.pgh.pa.us 94 :CBC 30 : ClearPgTm(struct /* pg_ */ tm *tm, fsec_t *fsec)
95 : : {
5618 meskes@postgresql.or 96 : 30 : tm->tm_year = 0;
5421 bruce@momjian.us 97 : 30 : tm->tm_mon = 0;
5618 meskes@postgresql.or 98 : 30 : tm->tm_mday = 0;
99 : 30 : tm->tm_hour = 0;
5421 bruce@momjian.us 100 : 30 : tm->tm_min = 0;
101 : 30 : tm->tm_sec = 0;
102 : 30 : *fsec = 0;
5618 meskes@postgresql.or 103 : 30 : }
104 : :
105 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c
106 : : *
107 : : * * changed struct pg_tm to struct tm
108 : : *
109 : : * * Made the function static
110 : : */
111 : : static int
112 : 1 : DecodeISO8601Interval(char *str,
113 : : int *dtype, struct /* pg_ */ tm *tm, fsec_t *fsec)
114 : : {
5421 bruce@momjian.us 115 : 1 : bool datepart = true;
116 : 1 : bool havefield = false;
117 : :
5618 meskes@postgresql.or 118 : 1 : *dtype = DTK_DELTA;
119 : 1 : ClearPgTm(tm, fsec);
120 : :
121 [ + - + - ]: 1 : if (strlen(str) < 2 || str[0] != 'P')
122 : 1 : return DTERR_BAD_FORMAT;
123 : :
5618 meskes@postgresql.or 124 :UBC 0 : str++;
125 [ # # ]: 0 : while (*str)
126 : : {
127 : : char *fieldstart;
128 : : int val;
129 : : double fval;
130 : : char unit;
131 : : int dterr;
132 : :
5421 bruce@momjian.us 133 [ # # ]: 0 : if (*str == 'T') /* T indicates the beginning of the time part */
134 : : {
5618 meskes@postgresql.or 135 : 0 : datepart = false;
136 : 0 : havefield = false;
137 : 0 : str++;
138 : 0 : continue;
139 : : }
140 : :
141 : 0 : fieldstart = str;
142 : 0 : dterr = ParseISO8601Number(str, &str, &val, &fval);
143 [ # # ]: 0 : if (dterr)
144 : 0 : return dterr;
145 : :
146 : : /*
147 : : * Note: we could step off the end of the string here. Code below
148 : : * *must* exit the loop if unit == '\0'.
149 : : */
150 : 0 : unit = *str++;
151 : :
152 [ # # ]: 0 : if (datepart)
153 : : {
5421 bruce@momjian.us 154 [ # # # # : 0 : switch (unit) /* before T: Y M W D */
# # # ]
155 : : {
5618 meskes@postgresql.or 156 : 0 : case 'Y':
157 : 0 : tm->tm_year += val;
985 bruce@momjian.us 158 : 0 : tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
5618 meskes@postgresql.or 159 : 0 : break;
160 : 0 : case 'M':
161 : 0 : tm->tm_mon += val;
162 : 0 : AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
163 : 0 : break;
164 : 0 : case 'W':
165 : 0 : tm->tm_mday += val * 7;
166 : 0 : AdjustFractDays(fval, tm, fsec, 7);
167 : 0 : break;
168 : 0 : case 'D':
169 : 0 : tm->tm_mday += val;
170 : 0 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
171 : 0 : break;
5421 bruce@momjian.us 172 : 0 : case 'T': /* ISO 8601 4.4.3.3 Alternative Format / Basic */
173 : : case '\0':
5618 meskes@postgresql.or 174 [ # # # # ]: 0 : if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
175 : : {
176 : 0 : tm->tm_year += val / 10000;
5421 bruce@momjian.us 177 : 0 : tm->tm_mon += (val / 100) % 100;
5618 meskes@postgresql.or 178 : 0 : tm->tm_mday += val % 100;
179 : 0 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
180 [ # # ]: 0 : if (unit == '\0')
181 : 0 : return 0;
182 : 0 : datepart = false;
183 : 0 : havefield = false;
184 : 0 : continue;
185 : : }
186 : : /* Else fall through to extended alternative format */
187 : : /* FALLTHROUGH */
188 : : case '-': /* ISO 8601 4.4.3.3 Alternative Format,
189 : : * Extended */
190 [ # # ]: 0 : if (havefield)
191 : 0 : return DTERR_BAD_FORMAT;
192 : :
193 : 0 : tm->tm_year += val;
985 bruce@momjian.us 194 : 0 : tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
5618 meskes@postgresql.or 195 [ # # ]: 0 : if (unit == '\0')
196 : 0 : return 0;
197 [ # # ]: 0 : if (unit == 'T')
198 : : {
199 : 0 : datepart = false;
200 : 0 : havefield = false;
201 : 0 : continue;
202 : : }
203 : :
204 : 0 : dterr = ParseISO8601Number(str, &str, &val, &fval);
205 [ # # ]: 0 : if (dterr)
206 : 0 : return dterr;
5421 bruce@momjian.us 207 : 0 : tm->tm_mon += val;
5618 meskes@postgresql.or 208 : 0 : AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
209 [ # # ]: 0 : if (*str == '\0')
210 : 0 : return 0;
211 [ # # ]: 0 : if (*str == 'T')
212 : : {
213 : 0 : datepart = false;
214 : 0 : havefield = false;
215 : 0 : continue;
216 : : }
217 [ # # ]: 0 : if (*str != '-')
218 : 0 : return DTERR_BAD_FORMAT;
219 : 0 : str++;
220 : :
221 : 0 : dterr = ParseISO8601Number(str, &str, &val, &fval);
222 [ # # ]: 0 : if (dterr)
223 : 0 : return dterr;
224 : 0 : tm->tm_mday += val;
225 : 0 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
226 [ # # ]: 0 : if (*str == '\0')
227 : 0 : return 0;
228 [ # # ]: 0 : if (*str == 'T')
229 : : {
230 : 0 : datepart = false;
231 : 0 : havefield = false;
232 : 0 : continue;
233 : : }
234 : 0 : return DTERR_BAD_FORMAT;
235 : 0 : default:
236 : : /* not a valid date unit suffix */
237 : 0 : return DTERR_BAD_FORMAT;
238 : : }
239 : : }
240 : : else
241 : : {
5421 bruce@momjian.us 242 [ # # # # : 0 : switch (unit) /* after T: H M S */
# # ]
243 : : {
5618 meskes@postgresql.or 244 : 0 : case 'H':
245 : 0 : tm->tm_hour += val;
246 : 0 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
247 : 0 : break;
248 : 0 : case 'M':
249 : 0 : tm->tm_min += val;
250 : 0 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
251 : 0 : break;
252 : 0 : case 'S':
253 : 0 : tm->tm_sec += val;
254 : 0 : AdjustFractSeconds(fval, tm, fsec, 1);
255 : 0 : break;
5421 bruce@momjian.us 256 : 0 : case '\0': /* ISO 8601 4.4.3.3 Alternative Format */
257 [ # # # # ]: 0 : if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
258 : : {
5618 meskes@postgresql.or 259 : 0 : tm->tm_hour += val / 10000;
5421 bruce@momjian.us 260 : 0 : tm->tm_min += (val / 100) % 100;
261 : 0 : tm->tm_sec += val % 100;
5618 meskes@postgresql.or 262 : 0 : AdjustFractSeconds(fval, tm, fsec, 1);
263 : 0 : return 0;
264 : : }
265 : : /* Else fall through to extended alternative format */
266 : : /* FALLTHROUGH */
267 : : case ':': /* ISO 8601 4.4.3.3 Alternative Format,
268 : : * Extended */
269 [ # # ]: 0 : if (havefield)
270 : 0 : return DTERR_BAD_FORMAT;
271 : :
272 : 0 : tm->tm_hour += val;
273 : 0 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
274 [ # # ]: 0 : if (unit == '\0')
275 : 0 : return 0;
276 : :
277 : 0 : dterr = ParseISO8601Number(str, &str, &val, &fval);
278 [ # # ]: 0 : if (dterr)
279 : 0 : return dterr;
5421 bruce@momjian.us 280 : 0 : tm->tm_min += val;
5618 meskes@postgresql.or 281 : 0 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
282 [ # # ]: 0 : if (*str == '\0')
283 : 0 : return 0;
284 [ # # ]: 0 : if (*str != ':')
285 : 0 : return DTERR_BAD_FORMAT;
286 : 0 : str++;
287 : :
288 : 0 : dterr = ParseISO8601Number(str, &str, &val, &fval);
289 [ # # ]: 0 : if (dterr)
290 : 0 : return dterr;
5421 bruce@momjian.us 291 : 0 : tm->tm_sec += val;
5618 meskes@postgresql.or 292 : 0 : AdjustFractSeconds(fval, tm, fsec, 1);
293 [ # # ]: 0 : if (*str == '\0')
294 : 0 : return 0;
295 : 0 : return DTERR_BAD_FORMAT;
296 : :
297 : 0 : default:
298 : : /* not a valid time unit suffix */
299 : 0 : return DTERR_BAD_FORMAT;
300 : : }
301 : : }
302 : :
303 : 0 : havefield = true;
304 : : }
305 : :
306 : 0 : return 0;
307 : : }
308 : :
309 : :
310 : :
311 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c
312 : : * with 3 exceptions
313 : : *
314 : : * * changed struct pg_tm to struct tm
315 : : *
316 : : * * ECPG code called this without a 'range' parameter
317 : : * removed 'int range' from the argument list and
318 : : * places where DecodeTime is called; and added
319 : : * int range = INTERVAL_FULL_RANGE;
320 : : *
321 : : * * ECPG seems not to have a global IntervalStyle
322 : : * so added
323 : : * int IntervalStyle = INTSTYLE_POSTGRES;
324 : : */
325 : : int
2489 tgl@sss.pgh.pa.us 326 :CBC 29 : DecodeInterval(char **field, int *ftype, int nf, /* int range, */
327 : : int *dtype, struct /* pg_ */ tm *tm, fsec_t *fsec)
328 : : {
5421 bruce@momjian.us 329 : 29 : int IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
330 : 29 : int range = INTERVAL_FULL_RANGE;
2433 peter_e@gmx.net 331 : 29 : bool is_before = false;
332 : : char *cp;
7686 meskes@postgresql.or 333 : 29 : int fmask = 0,
334 : : tmask,
335 : : type;
336 : : int i;
337 : : int dterr;
338 : : int val;
339 : : double fval;
340 : :
341 : 29 : *dtype = DTK_DELTA;
342 : 29 : type = IGNORE_DTF;
5421 bruce@momjian.us 343 : 29 : ClearPgTm(tm, fsec);
344 : :
345 : : /* read through list backwards to pick up units before values */
7686 meskes@postgresql.or 346 [ + + ]: 117 : for (i = nf - 1; i >= 0; i--)
347 : : {
348 [ + - + + : 89 : switch (ftype[i])
- ]
349 : : {
350 : 1 : case DTK_TIME:
5421 bruce@momjian.us 351 : 1 : dterr = DecodeTime(field[i], /* range, */
352 : : &tmask, tm, fsec);
5618 meskes@postgresql.or 353 [ - + ]: 1 : if (dterr)
5618 meskes@postgresql.or 354 :UBC 0 : return dterr;
7686 meskes@postgresql.or 355 :CBC 1 : type = DTK_DAY;
356 : 1 : break;
357 : :
7686 meskes@postgresql.or 358 :UBC 0 : case DTK_TZ:
359 : :
360 : : /*
361 : : * Timezone is a token with a leading sign character and at
362 : : * least one digit; there could be ':', '.', '-' embedded in
363 : : * it as well.
364 : : */
2407 peter_e@gmx.net 365 [ # # # # ]: 0 : Assert(*field[i] == '-' || *field[i] == '+');
366 : :
367 : : /*
368 : : * Try for hh:mm or hh:mm:ss. If not, fall through to
369 : : * DTK_NUMBER case, which can handle signed float numbers and
370 : : * signed year-month values.
371 : : */
5618 meskes@postgresql.or 372 [ # # # # ]: 0 : if (strchr(field[i] + 1, ':') != NULL &&
5421 bruce@momjian.us 373 : 0 : DecodeTime(field[i] + 1, /* INTERVAL_FULL_RANGE, */
374 : : &tmask, tm, fsec) == 0)
375 : : {
7686 meskes@postgresql.or 376 [ # # ]: 0 : if (*field[i] == '-')
377 : : {
378 : : /* flip the sign on all fields */
379 : 0 : tm->tm_hour = -tm->tm_hour;
380 : 0 : tm->tm_min = -tm->tm_min;
381 : 0 : tm->tm_sec = -tm->tm_sec;
382 : 0 : *fsec = -(*fsec);
383 : : }
384 : :
385 : : /*
386 : : * Set the next type to be a day, if units are not
387 : : * specified. This handles the case of '1 +02:03' since we
388 : : * are reading right to left.
389 : : */
390 : 0 : type = DTK_DAY;
391 : 0 : tmask = DTK_M(TZ);
392 : 0 : break;
393 : : }
394 : : /* FALL THROUGH */
395 : :
396 : : case DTK_DATE:
397 : : case DTK_NUMBER:
5618 meskes@postgresql.or 398 [ - + ]:CBC 44 : if (type == IGNORE_DTF)
399 : : {
400 : : /* use typmod to decide what rightmost field is */
401 : : switch (range)
402 : : {
5618 meskes@postgresql.or 403 :UBC 0 : case INTERVAL_MASK(YEAR):
404 : 0 : type = DTK_YEAR;
405 : 0 : break;
406 : 0 : case INTERVAL_MASK(MONTH):
407 : : case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
408 : 0 : type = DTK_MONTH;
409 : 0 : break;
410 : 0 : case INTERVAL_MASK(DAY):
411 : 0 : type = DTK_DAY;
412 : 0 : break;
413 : 0 : case INTERVAL_MASK(HOUR):
414 : : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
415 : : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
416 : : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
417 : 0 : type = DTK_HOUR;
418 : 0 : break;
419 : 0 : case INTERVAL_MASK(MINUTE):
420 : : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
421 : 0 : type = DTK_MINUTE;
422 : 0 : break;
423 : 0 : case INTERVAL_MASK(SECOND):
424 : : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
425 : : case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
426 : 0 : type = DTK_SECOND;
427 : 0 : break;
428 : 0 : default:
429 : 0 : type = DTK_SECOND;
430 : 0 : break;
431 : : }
432 : : }
433 : :
5618 meskes@postgresql.or 434 :CBC 44 : errno = 0;
2913 tgl@sss.pgh.pa.us 435 : 44 : val = strtoint(field[i], &cp, 10);
5618 meskes@postgresql.or 436 [ - + ]: 44 : if (errno == ERANGE)
5618 meskes@postgresql.or 437 :UBC 0 : return DTERR_FIELD_OVERFLOW;
438 : :
5618 meskes@postgresql.or 439 [ - + ]:CBC 44 : if (*cp == '-')
440 : : {
441 : : /* SQL "years-months" syntax */
442 : : int val2;
443 : :
2913 tgl@sss.pgh.pa.us 444 :UBC 0 : val2 = strtoint(cp + 1, &cp, 10);
5618 meskes@postgresql.or 445 [ # # # # : 0 : if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
# # ]
446 : 0 : return DTERR_FIELD_OVERFLOW;
447 [ # # ]: 0 : if (*cp != '\0')
448 : 0 : return DTERR_BAD_FORMAT;
449 : 0 : type = DTK_MONTH;
450 [ # # ]: 0 : if (*field[i] == '-')
451 : 0 : val2 = -val2;
452 : 0 : val = val * MONTHS_PER_YEAR + val2;
453 : 0 : fval = 0;
454 : : }
5618 meskes@postgresql.or 455 [ - + ]:CBC 44 : else if (*cp == '.')
456 : : {
5618 meskes@postgresql.or 457 :UBC 0 : errno = 0;
7686 458 : 0 : fval = strtod(cp, &cp);
5618 459 [ # # # # ]: 0 : if (*cp != '\0' || errno != 0)
460 : 0 : return DTERR_BAD_FORMAT;
461 : :
462 [ # # ]: 0 : if (*field[i] == '-')
6900 bruce@momjian.us 463 : 0 : fval = -fval;
464 : : }
7686 meskes@postgresql.or 465 [ + - ]:CBC 44 : else if (*cp == '\0')
466 : 44 : fval = 0;
467 : : else
5618 meskes@postgresql.or 468 :UBC 0 : return DTERR_BAD_FORMAT;
469 : :
7686 meskes@postgresql.or 470 [ - - + + :CBC 44 : tmask = 0; /* DTK_M(type); */
+ + - - +
- - - - ]
471 : :
472 : : switch (type)
473 : : {
7686 meskes@postgresql.or 474 :UBC 0 : case DTK_MICROSEC:
5618 475 : 0 : *fsec += rint(val + fval);
476 : 0 : tmask = DTK_M(MICROSECOND);
7686 477 : 0 : break;
478 : :
479 : 0 : case DTK_MILLISEC:
5618 480 : 0 : *fsec += rint((val + fval) * 1000);
481 : 0 : tmask = DTK_M(MILLISECOND);
7686 482 : 0 : break;
483 : :
7686 meskes@postgresql.or 484 :CBC 5 : case DTK_SECOND:
485 : 5 : tm->tm_sec += val;
5618 486 : 5 : *fsec += rint(fval * 1000000);
487 : :
488 : : /*
489 : : * If any subseconds were specified, consider this
490 : : * microsecond and millisecond input as well.
491 : : */
492 [ + - ]: 5 : if (fval == 0)
493 : 5 : tmask = DTK_M(SECOND);
494 : : else
5618 meskes@postgresql.or 495 :UBC 0 : tmask = DTK_ALL_SECS_M;
7686 meskes@postgresql.or 496 :CBC 5 : break;
497 : :
498 : 7 : case DTK_MINUTE:
499 : 7 : tm->tm_min += val;
5618 500 : 7 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
7686 501 : 7 : tmask = DTK_M(MINUTE);
502 : 7 : break;
503 : :
504 : 25 : case DTK_HOUR:
505 : 25 : tm->tm_hour += val;
5618 506 : 25 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
7686 507 : 25 : tmask = DTK_M(HOUR);
5618 508 : 25 : type = DTK_DAY;
7686 509 : 25 : break;
510 : :
511 : 6 : case DTK_DAY:
512 : 6 : tm->tm_mday += val;
5618 513 : 6 : AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
6900 bruce@momjian.us 514 [ - + ]: 6 : tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
7686 meskes@postgresql.or 515 : 6 : break;
516 : :
7686 meskes@postgresql.or 517 :UBC 0 : case DTK_WEEK:
518 : 0 : tm->tm_mday += val * 7;
5618 519 : 0 : AdjustFractDays(fval, tm, fsec, 7);
6900 bruce@momjian.us 520 [ # # ]: 0 : tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
7686 meskes@postgresql.or 521 : 0 : break;
522 : :
523 : 0 : case DTK_MONTH:
524 : 0 : tm->tm_mon += val;
5618 525 : 0 : AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
7686 526 : 0 : tmask = DTK_M(MONTH);
527 : 0 : break;
528 : :
7686 meskes@postgresql.or 529 :CBC 1 : case DTK_YEAR:
530 : 1 : tm->tm_year += val;
985 bruce@momjian.us 531 : 1 : tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
6900 532 [ - + ]: 1 : tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
7686 meskes@postgresql.or 533 : 1 : break;
534 : :
7686 meskes@postgresql.or 535 :UBC 0 : case DTK_DECADE:
536 : 0 : tm->tm_year += val * 10;
985 bruce@momjian.us 537 : 0 : tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 10);
6900 538 [ # # ]: 0 : tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
7686 meskes@postgresql.or 539 : 0 : break;
540 : :
541 : 0 : case DTK_CENTURY:
542 : 0 : tm->tm_year += val * 100;
985 bruce@momjian.us 543 : 0 : tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 100);
6900 544 [ # # ]: 0 : tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
7686 meskes@postgresql.or 545 : 0 : break;
546 : :
547 : 0 : case DTK_MILLENNIUM:
548 : 0 : tm->tm_year += val * 1000;
985 bruce@momjian.us 549 : 0 : tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 1000);
6900 550 [ # # ]: 0 : tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
7686 meskes@postgresql.or 551 : 0 : break;
552 : :
553 : 0 : default:
5618 554 : 0 : return DTERR_BAD_FORMAT;
555 : : }
7686 meskes@postgresql.or 556 :CBC 44 : break;
557 : :
558 : 44 : case DTK_STRING:
559 : : case DTK_SPECIAL:
560 : 44 : type = DecodeUnits(i, field[i], &val);
561 [ - + ]: 44 : if (type == IGNORE_DTF)
7686 meskes@postgresql.or 562 :UBC 0 : continue;
563 : :
7686 meskes@postgresql.or 564 [ + - - + ]:CBC 44 : tmask = 0; /* DTK_M(type); */
565 : : switch (type)
566 : : {
567 : 43 : case UNITS:
568 : 43 : type = val;
569 : 43 : break;
570 : :
7686 meskes@postgresql.or 571 :UBC 0 : case AGO:
2433 peter_e@gmx.net 572 : 0 : is_before = true;
7686 meskes@postgresql.or 573 : 0 : type = val;
574 : 0 : break;
575 : :
576 : 0 : case RESERV:
5004 tgl@sss.pgh.pa.us 577 : 0 : tmask = (DTK_DATE_M | DTK_TIME_M);
7686 meskes@postgresql.or 578 : 0 : *dtype = val;
579 : 0 : break;
580 : :
7686 meskes@postgresql.or 581 :CBC 1 : default:
5618 582 : 1 : return DTERR_BAD_FORMAT;
583 : : }
7686 584 : 43 : break;
585 : :
7686 meskes@postgresql.or 586 :UBC 0 : default:
5618 587 : 0 : return DTERR_BAD_FORMAT;
588 : : }
589 : :
7686 meskes@postgresql.or 590 [ - + ]:CBC 88 : if (tmask & fmask)
5618 meskes@postgresql.or 591 :UBC 0 : return DTERR_BAD_FORMAT;
7686 meskes@postgresql.or 592 :CBC 88 : fmask |= tmask;
593 : : }
594 : :
595 : : /* ensure that at least one time field has been found */
5618 596 [ - + ]: 28 : if (fmask == 0)
5618 meskes@postgresql.or 597 :UBC 0 : return DTERR_BAD_FORMAT;
598 : :
599 : : /* ensure fractional seconds are fractional */
7686 meskes@postgresql.or 600 [ - + ]:CBC 28 : if (*fsec != 0)
601 : : {
602 : : int sec;
603 : :
6764 bruce@momjian.us 604 :UBC 0 : sec = *fsec / USECS_PER_SEC;
605 : 0 : *fsec -= sec * USECS_PER_SEC;
7686 meskes@postgresql.or 606 : 0 : tm->tm_sec += sec;
607 : : }
608 : :
609 : : /*----------
610 : : * The SQL standard defines the interval literal
611 : : * '-1 1:00:00'
612 : : * to mean "negative 1 days and negative 1 hours", while Postgres
613 : : * traditionally treats this as meaning "negative 1 days and positive
614 : : * 1 hours". In SQL_STANDARD intervalstyle, we apply the leading sign
615 : : * to all fields if there are no other explicit signs.
616 : : *
617 : : * We leave the signs alone if there are additional explicit signs.
618 : : * This protects us against misinterpreting postgres-style dump output,
619 : : * since the postgres-style output code has always put an explicit sign on
620 : : * all fields following a negative field. But note that SQL-spec output
621 : : * is ambiguous and can be misinterpreted on load! (So it's best practice
622 : : * to dump in postgres style, not SQL style.)
623 : : *----------
624 : : */
5618 meskes@postgresql.or 625 [ - + - - ]:CBC 28 : if (IntervalStyle == INTSTYLE_SQL_STANDARD && *field[0] == '-')
626 : : {
627 : : /* Check for additional explicit signs */
5421 bruce@momjian.us 628 :UBC 0 : bool more_signs = false;
629 : :
5618 meskes@postgresql.or 630 [ # # ]: 0 : for (i = 1; i < nf; i++)
631 : : {
632 [ # # # # ]: 0 : if (*field[i] == '-' || *field[i] == '+')
633 : : {
634 : 0 : more_signs = true;
635 : 0 : break;
636 : : }
637 : : }
638 : :
639 [ # # ]: 0 : if (!more_signs)
640 : : {
641 : : /*
642 : : * Rather than re-determining which field was field[0], just force
643 : : * 'em all negative.
644 : : */
645 [ # # ]: 0 : if (*fsec > 0)
646 : 0 : *fsec = -(*fsec);
647 [ # # ]: 0 : if (tm->tm_sec > 0)
648 : 0 : tm->tm_sec = -tm->tm_sec;
649 [ # # ]: 0 : if (tm->tm_min > 0)
650 : 0 : tm->tm_min = -tm->tm_min;
651 [ # # ]: 0 : if (tm->tm_hour > 0)
652 : 0 : tm->tm_hour = -tm->tm_hour;
653 [ # # ]: 0 : if (tm->tm_mday > 0)
654 : 0 : tm->tm_mday = -tm->tm_mday;
655 [ # # ]: 0 : if (tm->tm_mon > 0)
656 : 0 : tm->tm_mon = -tm->tm_mon;
657 [ # # ]: 0 : if (tm->tm_year > 0)
658 : 0 : tm->tm_year = -tm->tm_year;
659 : : }
660 : : }
661 : :
662 : : /* finally, AGO negates everything */
7686 meskes@postgresql.or 663 [ - + ]:CBC 28 : if (is_before)
664 : : {
7686 meskes@postgresql.or 665 :UBC 0 : *fsec = -(*fsec);
5618 666 : 0 : tm->tm_sec = -tm->tm_sec;
667 : 0 : tm->tm_min = -tm->tm_min;
668 : 0 : tm->tm_hour = -tm->tm_hour;
669 : 0 : tm->tm_mday = -tm->tm_mday;
670 : 0 : tm->tm_mon = -tm->tm_mon;
671 : 0 : tm->tm_year = -tm->tm_year;
672 : : }
673 : :
5618 meskes@postgresql.or 674 :CBC 28 : return 0;
675 : : }
676 : :
677 : :
678 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
679 : : static char *
680 : 270 : AddVerboseIntPart(char *cp, int value, const char *units,
681 : : bool *is_zero, bool *is_before)
682 : : {
683 [ + + ]: 270 : if (value == 0)
684 : 195 : return cp;
685 : : /* first nonzero value sets is_before */
686 [ + + ]: 75 : if (*is_zero)
687 : : {
688 : 54 : *is_before = (value < 0);
689 : 54 : value = abs(value);
690 : : }
691 [ - + ]: 21 : else if (*is_before)
5618 meskes@postgresql.or 692 :UBC 0 : value = -value;
1079 bruce@momjian.us 693 [ + + ]:CBC 75 : sprintf(cp, " %d %s%s", value, units, (value == 1) ? "" : "s");
2433 peter_e@gmx.net 694 : 75 : *is_zero = false;
5618 meskes@postgresql.or 695 : 75 : return cp + strlen(cp);
696 : : }
697 : :
698 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
699 : : static char *
5618 meskes@postgresql.or 700 :UBC 0 : AddPostgresIntPart(char *cp, int value, const char *units,
701 : : bool *is_zero, bool *is_before)
702 : : {
703 [ # # ]: 0 : if (value == 0)
704 : 0 : return cp;
705 [ # # ]: 0 : sprintf(cp, "%s%s%d %s%s",
706 [ # # ]: 0 : (!*is_zero) ? " " : "",
707 [ # # # # ]: 0 : (*is_before && value > 0) ? "+" : "",
708 : : value,
709 : : units,
710 : : (value != 1) ? "s" : "");
711 : :
712 : : /*
713 : : * Each nonzero field sets is_before for (only) the next one. This is a
714 : : * tad bizarre but it's how it worked before...
715 : : */
716 : 0 : *is_before = (value < 0);
2433 peter_e@gmx.net 717 : 0 : *is_zero = false;
5618 meskes@postgresql.or 718 : 0 : return cp + strlen(cp);
719 : : }
720 : :
721 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
722 : : static char *
723 : 0 : AddISO8601IntPart(char *cp, int value, char units)
724 : : {
725 [ # # ]: 0 : if (value == 0)
726 : 0 : return cp;
727 : 0 : sprintf(cp, "%d%c", value, units);
728 : 0 : return cp + strlen(cp);
729 : : }
730 : :
731 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
732 : : static void
5618 meskes@postgresql.or 733 :CBC 10 : AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
734 : : {
735 [ + - ]: 10 : if (fsec == 0)
736 : : {
737 [ - + ]: 10 : if (fillzeros)
5618 meskes@postgresql.or 738 :UBC 0 : sprintf(cp, "%02d", abs(sec));
739 : : else
5618 meskes@postgresql.or 740 :CBC 10 : sprintf(cp, "%d", abs(sec));
741 : : }
742 : : else
743 : : {
5618 meskes@postgresql.or 744 [ # # ]:UBC 0 : if (fillzeros)
555 peter@eisentraut.org 745 : 0 : sprintf(cp, "%02d.%0*d", abs(sec), precision, abs(fsec));
746 : : else
747 : 0 : sprintf(cp, "%d.%0*d", abs(sec), precision, abs(fsec));
5618 meskes@postgresql.or 748 : 0 : TrimTrailingZeros(cp);
749 : : }
5618 meskes@postgresql.or 750 :CBC 10 : }
751 : :
752 : :
753 : : /* copy&pasted from .../src/backend/utils/adt/datetime.c
754 : : *
755 : : * Change pg_tm to tm
756 : : */
757 : :
758 : : void
2489 tgl@sss.pgh.pa.us 759 : 54 : EncodeInterval(struct /* pg_ */ tm *tm, fsec_t fsec, int style, char *str)
760 : : {
7686 meskes@postgresql.or 761 : 54 : char *cp = str;
5618 762 : 54 : int year = tm->tm_year;
5421 bruce@momjian.us 763 : 54 : int mon = tm->tm_mon;
5618 meskes@postgresql.or 764 : 54 : int mday = tm->tm_mday;
765 : 54 : int hour = tm->tm_hour;
5421 bruce@momjian.us 766 : 54 : int min = tm->tm_min;
767 : 54 : int sec = tm->tm_sec;
2433 peter_e@gmx.net 768 : 54 : bool is_before = false;
769 : 54 : bool is_zero = true;
770 : :
771 : : /*
772 : : * The sign of year and month are guaranteed to match, since they are
773 : : * stored internally as "month". But we'll need to check for is_before and
774 : : * is_zero when determining the signs of day and hour/minute/seconds
775 : : * fields.
776 : : */
7686 meskes@postgresql.or 777 [ - - - + ]: 54 : switch (style)
778 : : {
779 : : /* SQL Standard interval format */
5618 meskes@postgresql.or 780 :UBC 0 : case INTSTYLE_SQL_STANDARD:
781 : : {
5421 bruce@momjian.us 782 [ # # # # ]: 0 : bool has_negative = year < 0 || mon < 0 ||
331 tgl@sss.pgh.pa.us 783 [ # # # # ]: 0 : mday < 0 || hour < 0 ||
784 [ # # # # : 0 : min < 0 || sec < 0 || fsec < 0;
# # ]
5421 bruce@momjian.us 785 [ # # # # ]: 0 : bool has_positive = year > 0 || mon > 0 ||
331 tgl@sss.pgh.pa.us 786 [ # # # # ]: 0 : mday > 0 || hour > 0 ||
787 [ # # # # : 0 : min > 0 || sec > 0 || fsec > 0;
# # ]
5421 bruce@momjian.us 788 [ # # # # ]: 0 : bool has_year_month = year != 0 || mon != 0;
789 [ # # # # ]: 0 : bool has_day_time = mday != 0 || hour != 0 ||
331 tgl@sss.pgh.pa.us 790 [ # # # # : 0 : min != 0 || sec != 0 || fsec != 0;
# # ]
5421 bruce@momjian.us 791 : 0 : bool has_day = mday != 0;
792 [ # # # # ]: 0 : bool sql_standard_value = !(has_negative && has_positive) &&
331 tgl@sss.pgh.pa.us 793 [ # # # # ]: 0 : !(has_year_month && has_day_time);
794 : :
795 : : /*
796 : : * SQL Standard wants only 1 "<sign>" preceding the whole
797 : : * interval ... but can't do that if mixed signs.
798 : : */
5618 meskes@postgresql.or 799 [ # # # # ]: 0 : if (has_negative && sql_standard_value)
800 : : {
801 : 0 : *cp++ = '-';
802 : 0 : year = -year;
5421 bruce@momjian.us 803 : 0 : mon = -mon;
5618 meskes@postgresql.or 804 : 0 : mday = -mday;
805 : 0 : hour = -hour;
5421 bruce@momjian.us 806 : 0 : min = -min;
807 : 0 : sec = -sec;
5618 meskes@postgresql.or 808 : 0 : fsec = -fsec;
809 : : }
810 : :
811 [ # # # # ]: 0 : if (!has_negative && !has_positive)
812 : : {
813 : 0 : sprintf(cp, "0");
814 : : }
815 [ # # ]: 0 : else if (!sql_standard_value)
816 : : {
817 : : /*
818 : : * For non sql-standard interval values, force outputting
819 : : * the signs to avoid ambiguities with intervals with
820 : : * mixed sign components.
821 : : */
5421 bruce@momjian.us 822 [ # # # # ]: 0 : char year_sign = (year < 0 || mon < 0) ? '-' : '+';
823 [ # # ]: 0 : char day_sign = (mday < 0) ? '-' : '+';
824 [ # # # # : 0 : char sec_sign = (hour < 0 || min < 0 ||
# # ]
825 [ # # ]: 0 : sec < 0 || fsec < 0) ? '-' : '+';
826 : :
5618 meskes@postgresql.or 827 : 0 : sprintf(cp, "%c%d-%d %c%d %c%d:%02d:",
828 : : year_sign, abs(year), abs(mon),
829 : : day_sign, abs(mday),
830 : : sec_sign, abs(hour), abs(min));
7686 831 : 0 : cp += strlen(cp);
5618 832 : 0 : AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
833 : : }
834 [ # # ]: 0 : else if (has_year_month)
835 : : {
836 : 0 : sprintf(cp, "%d-%d", year, mon);
837 : : }
838 [ # # ]: 0 : else if (has_day)
839 : : {
840 : 0 : sprintf(cp, "%d %d:%02d:", mday, hour, min);
7686 841 : 0 : cp += strlen(cp);
5618 842 : 0 : AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
843 : : }
844 : : else
845 : : {
846 : 0 : sprintf(cp, "%d:%02d:", hour, min);
7686 847 : 0 : cp += strlen(cp);
5618 848 : 0 : AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
849 : : }
850 : : }
7686 851 : 0 : break;
852 : :
853 : : /* ISO 8601 "time-intervals by duration only" */
5618 854 : 0 : case INTSTYLE_ISO_8601:
855 : : /* special-case zero to avoid printing nothing */
856 [ # # # # : 0 : if (year == 0 && mon == 0 && mday == 0 &&
# # # # ]
5421 bruce@momjian.us 857 [ # # # # : 0 : hour == 0 && min == 0 && sec == 0 && fsec == 0)
# # ]
858 : : {
5618 meskes@postgresql.or 859 : 0 : sprintf(cp, "PT0S");
860 : 0 : break;
861 : : }
862 : 0 : *cp++ = 'P';
863 : 0 : cp = AddISO8601IntPart(cp, year, 'Y');
5421 bruce@momjian.us 864 : 0 : cp = AddISO8601IntPart(cp, mon, 'M');
5618 meskes@postgresql.or 865 : 0 : cp = AddISO8601IntPart(cp, mday, 'D');
866 [ # # # # : 0 : if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
# # # # ]
867 : 0 : *cp++ = 'T';
868 : 0 : cp = AddISO8601IntPart(cp, hour, 'H');
5421 bruce@momjian.us 869 : 0 : cp = AddISO8601IntPart(cp, min, 'M');
5618 meskes@postgresql.or 870 [ # # # # ]: 0 : if (sec != 0 || fsec != 0)
871 : : {
872 [ # # # # ]: 0 : if (sec < 0 || fsec < 0)
873 : 0 : *cp++ = '-';
874 : 0 : AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
7686 875 : 0 : cp += strlen(cp);
5618 876 : 0 : *cp++ = 'S';
5337 877 : 0 : *cp = '\0';
878 : : }
5618 879 : 0 : break;
880 : :
881 : : /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
882 : 0 : case INTSTYLE_POSTGRES:
883 : 0 : cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
884 : 0 : cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
885 : 0 : cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
886 [ # # # # : 0 : if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
# # # # #
# ]
887 : : {
5421 bruce@momjian.us 888 [ # # # # : 0 : bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
# # # # ]
889 : :
5618 meskes@postgresql.or 890 [ # # ]: 0 : sprintf(cp, "%s%s%02d:%02d:",
891 [ # # ]: 0 : is_zero ? "" : " ",
892 [ # # ]: 0 : (minus ? "-" : (is_before ? "+" : "")),
893 : : abs(hour), abs(min));
7686 894 : 0 : cp += strlen(cp);
5618 895 : 0 : AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
896 : : }
897 : 0 : break;
898 : :
899 : : /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
5618 meskes@postgresql.or 900 :CBC 54 : case INTSTYLE_POSTGRES_VERBOSE:
901 : : default:
902 : 54 : strcpy(cp, "@");
903 : 54 : cp++;
904 : 54 : cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
905 : 54 : cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
906 : 54 : cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
907 : 54 : cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
908 : 54 : cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
909 [ + + - + ]: 54 : if (sec != 0 || fsec != 0)
910 : : {
911 : 10 : *cp++ = ' ';
912 [ + - - + : 10 : if (sec < 0 || (sec == 0 && fsec < 0))
- - ]
913 : : {
5618 meskes@postgresql.or 914 [ # # ]:UBC 0 : if (is_zero)
2433 peter_e@gmx.net 915 : 0 : is_before = true;
5618 meskes@postgresql.or 916 [ # # ]: 0 : else if (!is_before)
917 : 0 : *cp++ = '-';
918 : : }
5618 meskes@postgresql.or 919 [ - + ]:CBC 10 : else if (is_before)
5618 meskes@postgresql.or 920 :UBC 0 : *cp++ = '-';
5618 meskes@postgresql.or 921 :CBC 10 : AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
7686 922 : 10 : cp += strlen(cp);
923 : : /* We output "ago", not negatives, so use abs(). */
5618 924 : 10 : sprintf(cp, " sec%s",
925 [ + + - + ]: 10 : (abs(sec) != 1 || fsec != 0) ? "s" : "");
2433 peter_e@gmx.net 926 : 10 : is_zero = false;
927 : : }
928 : : /* identically zero? then put in a unitless zero... */
5618 meskes@postgresql.or 929 [ - + ]: 54 : if (is_zero)
5618 meskes@postgresql.or 930 :UBC 0 : strcat(cp, " 0");
5618 meskes@postgresql.or 931 [ - + ]:CBC 54 : if (is_before)
5618 meskes@postgresql.or 932 :UBC 0 : strcat(cp, " ago");
7686 meskes@postgresql.or 933 :CBC 54 : break;
934 : : }
2407 peter_e@gmx.net 935 : 54 : }
936 : :
937 : :
938 : : /* interval2tm()
939 : : * Convert an interval data type to a tm structure.
940 : : */
941 : : static int
2489 tgl@sss.pgh.pa.us 942 : 54 : interval2tm(interval span, struct tm *tm, fsec_t *fsec)
943 : : {
944 : : int64 time;
945 : :
7686 meskes@postgresql.or 946 [ + + ]: 54 : if (span.month != 0)
947 : : {
6842 bruce@momjian.us 948 : 3 : tm->tm_year = span.month / MONTHS_PER_YEAR;
949 : 3 : tm->tm_mon = span.month % MONTHS_PER_YEAR;
950 : : }
951 : : else
952 : : {
7686 meskes@postgresql.or 953 : 51 : tm->tm_year = 0;
954 : 51 : tm->tm_mon = 0;
955 : : }
956 : :
957 : 54 : time = span.time;
958 : :
6764 bruce@momjian.us 959 : 54 : tm->tm_mday = time / USECS_PER_DAY;
960 : 54 : time -= tm->tm_mday * USECS_PER_DAY;
961 : 54 : tm->tm_hour = time / USECS_PER_HOUR;
962 : 54 : time -= tm->tm_hour * USECS_PER_HOUR;
963 : 54 : tm->tm_min = time / USECS_PER_MINUTE;
964 : 54 : time -= tm->tm_min * USECS_PER_MINUTE;
965 : 54 : tm->tm_sec = time / USECS_PER_SEC;
966 : 54 : *fsec = time - (tm->tm_sec * USECS_PER_SEC);
967 : :
7686 meskes@postgresql.or 968 : 54 : return 0;
969 : : } /* interval2tm() */
970 : :
971 : : static int
2489 tgl@sss.pgh.pa.us 972 : 28 : tm2interval(struct tm *tm, fsec_t fsec, interval * span)
973 : : {
3631 bruce@momjian.us 974 [ + - ]: 28 : if ((double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon > INT_MAX ||
975 [ - + ]: 28 : (double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon < INT_MIN)
3727 bruce@momjian.us 976 :UBC 0 : return -1;
6842 bruce@momjian.us 977 :CBC 28 : span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
978 : 28 : span->time = (((((((tm->tm_mday * INT64CONST(24)) +
6756 979 : 28 : tm->tm_hour) * INT64CONST(60)) +
980 : 28 : tm->tm_min) * INT64CONST(60)) +
981 : 28 : tm->tm_sec) * USECS_PER_SEC) + fsec;
982 : :
7686 meskes@postgresql.or 983 : 28 : return 0;
984 : : } /* tm2interval() */
985 : :
986 : : interval *
6422 987 : 16 : PGTYPESinterval_new(void)
988 : : {
989 : : interval *result;
990 : :
991 : 16 : result = (interval *) pgtypes_alloc(sizeof(interval));
992 : : /* result can be NULL if we run out of memory */
993 : 16 : return result;
994 : : }
995 : :
996 : : void
6402 bruce@momjian.us 997 : 13 : PGTYPESinterval_free(interval * intvl)
998 : : {
6422 meskes@postgresql.or 999 : 13 : free(intvl);
1000 : 13 : }
1001 : :
1002 : : interval *
7684 1003 : 29 : PGTYPESinterval_from_asc(char *str, char **endptr)
1004 : : {
7523 1005 : 29 : interval *result = NULL;
1006 : : fsec_t fsec;
1007 : : struct tm tt,
7686 1008 : 29 : *tm = &tt;
1009 : : int dtype;
1010 : : int nf;
1011 : : char *field[MAXDATEFIELDS];
1012 : : int ftype[MAXDATEFIELDS];
1013 : : char lowstr[MAXDATELEN + MAXDATEFIELDS];
1014 : : char *realptr;
7559 bruce@momjian.us 1015 [ + + ]: 29 : char **ptr = (endptr != NULL) ? endptr : &realptr;
1016 : :
7686 meskes@postgresql.or 1017 : 29 : tm->tm_year = 0;
1018 : 29 : tm->tm_mon = 0;
1019 : 29 : tm->tm_mday = 0;
1020 : 29 : tm->tm_hour = 0;
1021 : 29 : tm->tm_min = 0;
1022 : 29 : tm->tm_sec = 0;
1023 : 29 : fsec = 0;
1024 : :
3709 noah@leadboat.com 1025 [ - + ]: 29 : if (strlen(str) > MAXDATELEN)
1026 : : {
7686 meskes@postgresql.or 1027 :UBC 0 : errno = PGTYPES_INTVL_BAD_INTERVAL;
1028 : 0 : return NULL;
1029 : : }
1030 : :
5443 meskes@postgresql.or 1031 [ + - + + ]:CBC 58 : if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
5618 1032 [ + - ]: 30 : (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0 &&
1033 : 1 : DecodeISO8601Interval(str, &dtype, tm, &fsec) != 0))
1034 : : {
7686 1035 : 1 : errno = PGTYPES_INTVL_BAD_INTERVAL;
1036 : 1 : return NULL;
1037 : : }
1038 : :
7523 1039 : 28 : result = (interval *) pgtypes_alloc(sizeof(interval));
7686 1040 [ - + ]: 28 : if (!result)
7686 meskes@postgresql.or 1041 :UBC 0 : return NULL;
1042 : :
7686 meskes@postgresql.or 1043 [ - + ]:CBC 28 : if (dtype != DTK_DELTA)
1044 : : {
7686 meskes@postgresql.or 1045 :UBC 0 : errno = PGTYPES_INTVL_BAD_INTERVAL;
7223 1046 : 0 : free(result);
7686 1047 : 0 : return NULL;
1048 : : }
1049 : :
7686 meskes@postgresql.or 1050 [ - + ]:CBC 28 : if (tm2interval(tm, fsec, result) != 0)
1051 : : {
7686 meskes@postgresql.or 1052 :UBC 0 : errno = PGTYPES_INTVL_BAD_INTERVAL;
7223 1053 : 0 : free(result);
7686 1054 : 0 : return NULL;
1055 : : }
1056 : :
6522 meskes@postgresql.or 1057 :CBC 28 : errno = 0;
7686 1058 : 28 : return result;
1059 : : }
1060 : :
1061 : : char *
6756 bruce@momjian.us 1062 : 54 : PGTYPESinterval_to_asc(interval * span)
1063 : : {
1064 : : struct tm tt,
7686 meskes@postgresql.or 1065 : 54 : *tm = &tt;
1066 : : fsec_t fsec;
1067 : : char buf[MAXDATELEN + 1];
5421 bruce@momjian.us 1068 : 54 : int IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
1069 : :
7686 meskes@postgresql.or 1070 [ - + ]: 54 : if (interval2tm(*span, tm, &fsec) != 0)
1071 : : {
7686 meskes@postgresql.or 1072 :UBC 0 : errno = PGTYPES_INTVL_BAD_INTERVAL;
1073 : 0 : return NULL;
1074 : : }
1075 : :
2407 peter_e@gmx.net 1076 :CBC 54 : EncodeInterval(tm, fsec, IntervalStyle, buf);
1077 : :
7559 bruce@momjian.us 1078 : 54 : return pgtypes_strdup(buf);
1079 : : }
1080 : :
1081 : : int
6431 meskes@postgresql.or 1082 : 17 : PGTYPESinterval_copy(interval * intvlsrc, interval * intvldest)
1083 : : {
1084 : 17 : intvldest->time = intvlsrc->time;
1085 : 17 : intvldest->month = intvlsrc->month;
1086 : :
7686 1087 : 17 : return 0;
1088 : : }
|