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