]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/datetime.c
Add conversion from datetime to time data type.
[postgresql] / src / backend / utils / adt / datetime.c
1 /*-------------------------------------------------------------------------
2  *
3  * datetime.c--
4  *        implements DATE and TIME data types specified in SQL-92 standard
5  *
6  * Copyright (c) 1994-5, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.17 1997/10/25 05:16:09 thomas Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>                              /* for sprintf() */
15 #include <string.h>
16 #include <limits.h>
17
18 #include "postgres.h"
19 #ifdef HAVE_FLOAT_H
20 #include <float.h>
21 #endif
22 #include "miscadmin.h"
23 #include "utils/builtins.h"
24 #include "utils/nabstime.h"
25 #include "utils/datetime.h"
26 #include "access/xact.h"
27
28 static int      date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn);
29
30
31 static int      day_tab[2][12] = {
32         {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
33 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
34
35 #define isleap(y) (((y % 4) == 0 && (y % 100) != 0) || (y % 400) == 0)
36
37 #define UTIME_MINYEAR (1901)
38 #define UTIME_MINMONTH (12)
39 #define UTIME_MINDAY (14)
40 #define UTIME_MAXYEAR (2038)
41 #define UTIME_MAXMONTH (01)
42 #define UTIME_MAXDAY (18)
43
44 #define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
45  || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
46   || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
47  && ((y < UTIME_MAXYEAR) \
48  || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
49   || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
50
51 /*****************************************************************************
52  *       Date ADT
53  *****************************************************************************/
54
55
56 /* date_in()
57  * Given date text string, convert to internal date format.
58  */
59 DateADT
60 date_in(char *str)
61 {
62         DateADT         date;
63         double          fsec;
64         struct tm       tt,
65                            *tm = &tt;
66         int                     tzp;
67         int                     dtype;
68         int                     nf;
69         char       *field[MAXDATEFIELDS];
70         int                     ftype[MAXDATEFIELDS];
71         char            lowstr[MAXDATELEN + 1];
72
73         if (!PointerIsValid(str))
74                 elog(WARN, "Bad (null) date external representation", NULL);
75
76 #ifdef DATEDEBUG
77         printf("date_in- input string is %s\n", str);
78 #endif
79         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
80          || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
81                 elog(WARN, "Bad date external representation %s", str);
82
83         switch (dtype)
84         {
85                 case DTK_DATE:
86                         break;
87
88                 case DTK_CURRENT:
89                         GetCurrentTime(tm);
90                         break;
91
92                 case DTK_EPOCH:
93                         tm->tm_year = 1970;
94                         tm->tm_mon = 1;
95                         tm->tm_mday = 1;
96                         break;
97
98                 default:
99                         elog(WARN, "Unrecognized date external representation %s", str);
100         }
101
102         if (tm->tm_year < 0 || tm->tm_year > 32767)
103                 elog(WARN, "date_in: year must be limited to values 0 through 32767 in '%s'", str);
104         if (tm->tm_mon < 1 || tm->tm_mon > 12)
105                 elog(WARN, "date_in: month must be limited to values 1 through 12 in '%s'", str);
106         if (tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
107                 elog(WARN, "date_in: day must be limited to values 1 through %d in '%s'",
108                          day_tab[isleap(tm->tm_year)][tm->tm_mon - 1], str);
109
110         date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
111
112         return (date);
113 } /* date_in() */
114
115 /* date_out()
116  * Given internal format date, convert to text string.
117  */
118 char *
119 date_out(DateADT date)
120 {
121         char       *result;
122         struct tm       tt,
123                            *tm = &tt;
124         char            buf[MAXDATELEN + 1];
125
126         j2date((date + date2j(2000, 1, 1)),
127                    &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
128
129         EncodeDateOnly(tm, DateStyle, buf);
130
131         result = PALLOC(strlen(buf) + 1);
132
133         strcpy(result, buf);
134
135         return (result);
136 } /* date_out() */
137
138 bool
139 date_eq(DateADT dateVal1, DateADT dateVal2)
140 {
141         return (dateVal1 == dateVal2);
142 }
143
144 bool
145 date_ne(DateADT dateVal1, DateADT dateVal2)
146 {
147         return (dateVal1 != dateVal2);
148 }
149
150 bool
151 date_lt(DateADT dateVal1, DateADT dateVal2)
152 {
153         return (dateVal1 < dateVal2);
154 } /* date_lt() */
155
156 bool
157 date_le(DateADT dateVal1, DateADT dateVal2)
158 {
159         return (dateVal1 <= dateVal2);
160 } /* date_le() */
161
162 bool
163 date_gt(DateADT dateVal1, DateADT dateVal2)
164 {
165         return (dateVal1 > dateVal2);
166 } /* date_gt() */
167
168 bool
169 date_ge(DateADT dateVal1, DateADT dateVal2)
170 {
171         return (dateVal1 >= dateVal2);
172 } /* date_ge() */
173
174 int
175 date_cmp(DateADT dateVal1, DateADT dateVal2)
176 {
177         if (dateVal1 < dateVal2)
178         {
179                 return -1;
180         }
181         else if (dateVal1 > dateVal2)
182         {
183                 return 1;
184         }
185         return 0;
186 } /* date_cmp() */
187
188 DateADT
189 date_larger(DateADT dateVal1, DateADT dateVal2)
190 {
191         return (date_gt(dateVal1, dateVal2) ? dateVal1 : dateVal2);
192 } /* date_larger() */
193
194 DateADT
195 date_smaller(DateADT dateVal1, DateADT dateVal2)
196 {
197         return (date_lt(dateVal1, dateVal2) ? dateVal1 : dateVal2);
198 } /* date_smaller() */
199
200 /* Compute difference between two dates in days.
201  */
202 int4
203 date_mi(DateADT dateVal1, DateADT dateVal2)
204 {
205         return (dateVal1 - dateVal2);
206 } /* date_mi() */
207
208 /* Add a number of days to a date, giving a new date.
209  * Must handle both positive and negative numbers of days.
210  */
211 DateADT
212 date_pli(DateADT dateVal, int4 days)
213 {
214         return (dateVal + days);
215 } /* date_pli() */
216
217 /* Subtract a number of days from a date, giving a new date.
218  */
219 DateADT
220 date_mii(DateADT dateVal, int4 days)
221 {
222         return (date_pli(dateVal, -days));
223 } /* date_mii() */
224
225
226 /* date_datetime()
227  * Convert date to datetime data type.
228  */
229 DateTime *
230 date_datetime(DateADT dateVal)
231 {
232         DateTime   *result;
233         struct tm       tt,
234                            *tm = &tt;
235         int                     tz;
236         double          fsec = 0;
237         char       *tzn;
238
239         result = PALLOCTYPE(DateTime);
240
241         if (date2tm(dateVal, &tz, tm, &fsec, &tzn) != 0)
242                 elog(WARN, "Unable to convert date to datetime", NULL);
243
244 #ifdef DATEDEBUG
245         printf("date_datetime- date is %d.%02d.%02d\n", tm->tm_year, tm->tm_mon, tm->tm_mday);
246         printf("date_datetime- time is %02d:%02d:%02d %.7f\n", tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
247 #endif
248
249         if (tm2datetime(tm, fsec, &tz, result) != 0)
250                 elog(WARN, "Datetime out of range", NULL);
251
252         return (result);
253 } /* date_datetime() */
254
255
256 /* datetime_date()
257  * Convert datetime to date data type.
258  */
259 DateADT
260 datetime_date(DateTime *datetime)
261 {
262         DateADT         result;
263         struct tm       tt,
264                            *tm = &tt;
265         int                     tz;
266         double          fsec;
267         char       *tzn;
268
269         if (!PointerIsValid(datetime))
270                 elog(WARN, "Unable to convert null datetime to date", NULL);
271
272         if (DATETIME_NOT_FINITE(*datetime))
273                 elog(WARN, "Unable to convert datetime to date", NULL);
274
275         if (DATETIME_IS_EPOCH(*datetime))
276         {
277                 datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL);
278
279         }
280         else if (DATETIME_IS_CURRENT(*datetime))
281         {
282                 datetime2tm(SetDateTime(*datetime), &tz, tm, &fsec, &tzn);
283
284         }
285         else
286         {
287                 if (datetime2tm(*datetime, &tz, tm, &fsec, &tzn) != 0)
288                         elog(WARN, "Unable to convert datetime to date", NULL);
289         }
290
291         result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
292
293         return (result);
294 } /* datetime_date() */
295
296
297 /* abstime_date()
298  * Convert abstime to date data type.
299  */
300 DateADT
301 abstime_date(AbsoluteTime abstime)
302 {
303         DateADT         result;
304         struct tm       tt,
305                            *tm = &tt;
306         int                     tz;
307
308         switch (abstime)
309         {
310                 case INVALID_ABSTIME:
311                 case NOSTART_ABSTIME:
312                 case NOEND_ABSTIME:
313                         elog(WARN, "Unable to convert reserved abstime value to date", NULL);
314
315                         /*
316                          * pretend to drop through to make compiler think that result
317                          * will be set
318                          */
319
320                 case EPOCH_ABSTIME:
321                         result = date2j(1970, 1, 1) - date2j(2000, 1, 1);
322                         break;
323
324                 case CURRENT_ABSTIME:
325                         GetCurrentTime(tm);
326                         result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
327                         break;
328
329                 default:
330                         abstime2tm(abstime, &tz, tm, NULL);
331                         result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
332                         break;
333         }
334
335         return (result);
336 } /* abstime_date() */
337
338
339 /* date2tm()
340  * Convert date to time structure.
341  * Note that date is an implicit local time, but the system calls assume
342  *      that everything is GMT. So, convert to GMT, rotate to local time,
343  *      and then convert again to try to get the time zones correct.
344  */
345 static int
346 date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn)
347 {
348         struct tm  *tx;
349         time_t          utime;
350
351         *fsec = 0;
352
353         j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
354         tm->tm_hour = 0;
355         tm->tm_min = 0;
356         tm->tm_sec = 0;
357         tm->tm_isdst = -1;
358
359         if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
360         {
361
362                 /* convert to system time */
363                 utime = ((dateVal + (date2j(2000, 1, 1) - date2j(1970, 1, 1))) * 86400);
364                 utime += (12 * 60 * 60);/* rotate to noon to get the right day in
365                                                                  * time zone */
366
367 #ifdef USE_POSIX_TIME
368                 tx = localtime(&utime);
369
370 #ifdef DATEDEBUG
371 #ifdef HAVE_INT_TIMEZONE
372                 printf("date2tm- (localtime) %d.%02d.%02d %02d:%02d:%02.0f %s %s dst=%d\n",
373                            tx->tm_year, tx->tm_mon, tx->tm_mday, tx->tm_hour, tx->tm_min, (double) tm->tm_sec,
374                            tzname[0], tzname[1], tx->tm_isdst);
375 #endif
376 #endif
377                 tm->tm_year = tx->tm_year + 1900;
378                 tm->tm_mon = tx->tm_mon + 1;
379                 tm->tm_mday = tx->tm_mday;
380                 tm->tm_isdst = tx->tm_isdst;
381
382 #ifdef HAVE_INT_TIMEZONE
383                 *tzp = (tm->tm_isdst ? (timezone - 3600) : timezone);
384                 if (tzn != NULL)
385                         *tzn = tzname[(tm->tm_isdst > 0)];
386
387 #else                                                   /* !HAVE_INT_TIMEZONE */
388                 tm->tm_gmtoff = tx->tm_gmtoff;
389                 tm->tm_zone = tx->tm_zone;
390
391                 *tzp = (tm->tm_isdst ? (tm->tm_gmtoff - 3600) : tm->tm_gmtoff); /* tm_gmtoff is
392                                                                                                                                                  * Sun/DEC-ism */
393                 if (tzn != NULL)
394                         *tzn = tm->tm_zone;
395 #endif
396
397 #else                                                   /* !USE_POSIX_TIME */
398                 *tzp = CTimeZone;               /* V7 conventions; don't know timezone? */
399                 if (tzn != NULL)
400                         *tzn = CTZName;
401 #endif
402
403                 /* otherwise, outside of timezone range so convert to GMT... */
404         }
405         else
406         {
407 #ifdef DATEDEBUG
408                 printf("date2tm- convert %d-%d-%d %d:%d%d to datetime\n",
409                            tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
410 #endif
411
412                 *tzp = 0;
413                 tm->tm_isdst = 0;
414                 if (tzn != NULL)
415                         *tzn = NULL;
416         }
417
418         return 0;
419 } /* date2tm() */
420
421
422 /*****************************************************************************
423  *       Time ADT
424  *****************************************************************************/
425
426
427 TimeADT *
428 time_in(char *str)
429 {
430         TimeADT    *time;
431
432         double          fsec;
433         struct tm       tt,
434                            *tm = &tt;
435
436         int                     nf;
437         char            lowstr[MAXDATELEN + 1];
438         char       *field[MAXDATEFIELDS];
439         int                     dtype;
440         int                     ftype[MAXDATEFIELDS];
441
442         if (!PointerIsValid(str))
443                 elog(WARN, "Bad (null) time external representation", NULL);
444
445         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
446                 || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec) != 0))
447                 elog(WARN, "Bad time external representation '%s'", str);
448
449         if ((tm->tm_hour < 0) || (tm->tm_hour > 23))
450                 elog(WARN, "Hour must be limited to values 0 through 23 in '%s'", str);
451         if ((tm->tm_min < 0) || (tm->tm_min > 59))
452                 elog(WARN, "Minute must be limited to values 0 through 59 in '%s'", str);
453         if ((tm->tm_sec < 0) || ((tm->tm_sec + fsec) >= 60))
454                 elog(WARN, "Second must be limited to values 0 through < 60 in '%s'", str);
455
456         time = PALLOCTYPE(TimeADT);
457
458         *time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
459
460         return (time);
461 } /* time_in() */
462
463
464 char *
465 time_out(TimeADT *time)
466 {
467         char       *result;
468         struct tm       tt,
469                            *tm = &tt;
470
471         double          fsec;
472         char            buf[MAXDATELEN + 1];
473
474         if (!PointerIsValid(time))
475                 return NULL;
476
477         tm->tm_hour = (*time / (60 * 60));
478         tm->tm_min = (((int) (*time / 60)) % 60);
479         tm->tm_sec = (((int) *time) % 60);
480
481         fsec = 0;
482
483         EncodeTimeOnly(tm, fsec, DateStyle, buf);
484
485         result = PALLOC(strlen(buf) + 1);
486
487         strcpy(result, buf);
488
489         return (result);
490 } /* time_out() */
491
492
493 bool
494 time_eq(TimeADT *time1, TimeADT *time2)
495 {
496         if (!PointerIsValid(time1) || !PointerIsValid(time2))
497                 return (FALSE);
498
499         return (*time1 == *time2);
500 } /* time_eq() */
501
502 bool
503 time_ne(TimeADT *time1, TimeADT *time2)
504 {
505         if (!PointerIsValid(time1) || !PointerIsValid(time2))
506                 return (FALSE);
507
508         return (*time1 != *time2);
509 } /* time_eq() */
510
511 bool
512 time_lt(TimeADT *time1, TimeADT *time2)
513 {
514         if (!PointerIsValid(time1) || !PointerIsValid(time2))
515                 return (FALSE);
516
517         return (*time1 < *time2);
518 } /* time_eq() */
519
520 bool
521 time_le(TimeADT *time1, TimeADT *time2)
522 {
523         if (!PointerIsValid(time1) || !PointerIsValid(time2))
524                 return (FALSE);
525
526         return (*time1 <= *time2);
527 } /* time_eq() */
528
529 bool
530 time_gt(TimeADT *time1, TimeADT *time2)
531 {
532         if (!PointerIsValid(time1) || !PointerIsValid(time2))
533                 return (FALSE);
534
535         return (*time1 > *time2);
536 } /* time_eq() */
537
538 bool
539 time_ge(TimeADT *time1, TimeADT *time2)
540 {
541         if (!PointerIsValid(time1) || !PointerIsValid(time2))
542                 return (FALSE);
543
544         return (*time1 >= *time2);
545 } /* time_eq() */
546
547 int
548 time_cmp(TimeADT *time1, TimeADT *time2)
549 {
550         return ((*time1 < *time2) ? -1 : (((*time1 > *time2) ? 1 : 0)));
551 } /* time_cmp() */
552
553
554 /* datetime_time()
555  * Convert datetime to time data type.
556  */
557 TimeADT *
558 datetime_time(DateTime *datetime)
559 {
560         TimeADT         *result;
561         struct tm       tt,
562                            *tm = &tt;
563         int                     tz;
564         double          fsec;
565         char       *tzn;
566
567         if (!PointerIsValid(datetime))
568                 elog(WARN, "Unable to convert null datetime to date", NULL);
569
570         if (DATETIME_NOT_FINITE(*datetime))
571                 elog(WARN, "Unable to convert datetime to date", NULL);
572
573         if (DATETIME_IS_EPOCH(*datetime))
574         {
575                 datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL);
576
577         }
578         else if (DATETIME_IS_CURRENT(*datetime))
579         {
580                 datetime2tm(SetDateTime(*datetime), &tz, tm, &fsec, &tzn);
581
582         }
583         else
584         {
585                 if (datetime2tm(*datetime, &tz, tm, &fsec, &tzn) != 0)
586                         elog(WARN, "Unable to convert datetime to date", NULL);
587         }
588
589         result = PALLOCTYPE(TimeADT);
590
591         *result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
592
593         return (result);
594 } /* datetime_time() */
595
596
597 /* datet_datetime()
598  * Convert date and time to datetime data type.
599  * Should be called datetime_datetime()
600  *  but need <= 16 characters for function names.
601  */
602 DateTime *
603 datet_datetime(DateADT date, TimeADT *time)
604 {
605         DateTime   *result;
606
607         if (!PointerIsValid(time))
608         {
609                 result = PALLOCTYPE(DateTime);
610                 DATETIME_INVALID(*result);
611         } else {
612                 result = date_datetime(date);
613                 *result += *time;
614         }
615
616         return (result);
617 } /* datet_datetime() */
618
619
620 int32                                                   /* RelativeTime */
621 int42reltime(int32 timevalue)
622 {
623         return (timevalue);
624 }