PostgreSQL Source Code  git master
datetime.c
Go to the documentation of this file.
1 /* src/interfaces/ecpg/pgtypeslib/datetime.c */
2 
3 #include "postgres_fe.h"
4 
5 #include <time.h>
6 #include <ctype.h>
7 #include <limits.h>
8 
9 #include "pgtypeslib_extern.h"
10 #include "dt.h"
11 #include "pgtypes_error.h"
12 #include "pgtypes_date.h"
13 
14 date *
16 {
17  date *result;
18 
19  result = (date *) pgtypes_alloc(sizeof(date));
20  /* result can be NULL if we run out of memory */
21  return result;
22 }
23 
24 void
26 {
27  free(d);
28 }
29 
30 date
32 {
33  date dDate;
34 
35  dDate = 0; /* suppress compiler warning */
36 
37  if (!TIMESTAMP_NOT_FINITE(dt))
38  {
39  /* Microseconds to days */
40  dDate = (dt / USECS_PER_DAY);
41  }
42 
43  return dDate;
44 }
45 
46 date
47 PGTYPESdate_from_asc(char *str, char **endptr)
48 {
49  date dDate;
50  fsec_t fsec;
51  struct tm tt,
52  *tm = &tt;
53  int dtype;
54  int nf;
55  char *field[MAXDATEFIELDS];
56  int ftype[MAXDATEFIELDS];
57  char lowstr[MAXDATELEN + MAXDATEFIELDS];
58  char *realptr;
59  char **ptr = (endptr != NULL) ? endptr : &realptr;
60 
61  bool EuroDates = false;
62 
63  errno = 0;
64  if (strlen(str) > MAXDATELEN)
65  {
66  errno = PGTYPES_DATE_BAD_DATE;
67  return INT_MIN;
68  }
69 
70  if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
71  DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, EuroDates) != 0)
72  {
73  errno = PGTYPES_DATE_BAD_DATE;
74  return INT_MIN;
75  }
76 
77  switch (dtype)
78  {
79  case DTK_DATE:
80  break;
81 
82  case DTK_EPOCH:
83  if (GetEpochTime(tm) < 0)
84  {
85  errno = PGTYPES_DATE_BAD_DATE;
86  return INT_MIN;
87  }
88  break;
89 
90  default:
91  errno = PGTYPES_DATE_BAD_DATE;
92  return INT_MIN;
93  }
94 
95  dDate = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
96 
97  return dDate;
98 }
99 
100 char *
102 {
103  struct tm tt,
104  *tm = &tt;
105  char buf[MAXDATELEN + 1];
106  int DateStyle = 1;
107  bool EuroDates = false;
108 
109  j2date(dDate + date2j(2000, 1, 1), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
110  EncodeDateOnly(tm, DateStyle, buf, EuroDates);
111  return pgtypes_strdup(buf);
112 }
113 
114 void
116 {
117  int y,
118  m,
119  d;
120 
121  j2date((int) (jd + date2j(2000, 1, 1)), &y, &m, &d);
122  mdy[0] = m;
123  mdy[1] = d;
124  mdy[2] = y;
125 }
126 
127 void
128 PGTYPESdate_mdyjul(int *mdy, date * jdate)
129 {
130  /* month is mdy[0] */
131  /* day is mdy[1] */
132  /* year is mdy[2] */
133 
134  *jdate = (date) (date2j(mdy[2], mdy[0], mdy[1]) - date2j(2000, 1, 1));
135 }
136 
137 int
139 {
140  /*
141  * Sunday: 0 Monday: 1 Tuesday: 2 Wednesday: 3 Thursday: 4
142  * Friday: 5 Saturday: 6
143  */
144  return (int) (dDate + date2j(2000, 1, 1) + 1) % 7;
145 }
146 
147 void
149 {
150  struct tm ts;
151 
152  GetCurrentDateTime(&ts);
153  if (errno == 0)
154  *d = date2j(ts.tm_year, ts.tm_mon, ts.tm_mday) - date2j(2000, 1, 1);
155  return;
156 }
157 
158 #define PGTYPES_DATE_NUM_MAX_DIGITS 20 /* should suffice for most
159  * years... */
160 
161 #define PGTYPES_FMTDATE_DAY_DIGITS_LZ 1 /* LZ means "leading zeroes" */
162 #define PGTYPES_FMTDATE_DOW_LITERAL_SHORT 2
163 #define PGTYPES_FMTDATE_MONTH_DIGITS_LZ 3
164 #define PGTYPES_FMTDATE_MONTH_LITERAL_SHORT 4
165 #define PGTYPES_FMTDATE_YEAR_DIGITS_SHORT 5
166 #define PGTYPES_FMTDATE_YEAR_DIGITS_LONG 6
167 
168 int
169 PGTYPESdate_fmt_asc(date dDate, const char *fmtstring, char *outbuf)
170 {
171  static struct
172  {
173  char *format;
174  int component;
175  } mapping[] =
176  {
177  /*
178  * format items have to be sorted according to their length, since the
179  * first pattern that matches gets replaced by its value
180  */
181  {
183  },
184  {
186  },
187  {
189  },
190  {
192  },
193  {
195  },
196  {
198  },
199  {
200  NULL, 0
201  }
202  };
203 
204  union un_fmt_comb replace_val;
205  int replace_type;
206 
207  int i;
208  int dow;
209  char *start_pattern;
210  struct tm tm;
211 
212  /* copy the string over */
213  strcpy(outbuf, fmtstring);
214 
215  /* get the date */
216  j2date(dDate + date2j(2000, 1, 1), &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
217  dow = PGTYPESdate_dayofweek(dDate);
218 
219  for (i = 0; mapping[i].format != NULL; i++)
220  {
221  while ((start_pattern = strstr(outbuf, mapping[i].format)) != NULL)
222  {
223  switch (mapping[i].component)
224  {
226  replace_val.str_val = pgtypes_date_weekdays_short[dow];
227  replace_type = PGTYPES_TYPE_STRING_CONSTANT;
228  break;
230  replace_val.uint_val = tm.tm_mday;
231  replace_type = PGTYPES_TYPE_UINT_2_LZ;
232  break;
234  replace_val.str_val = months[tm.tm_mon - 1];
235  replace_type = PGTYPES_TYPE_STRING_CONSTANT;
236  break;
238  replace_val.uint_val = tm.tm_mon;
239  replace_type = PGTYPES_TYPE_UINT_2_LZ;
240  break;
242  replace_val.uint_val = tm.tm_year;
243  replace_type = PGTYPES_TYPE_UINT_4_LZ;
244  break;
246  replace_val.uint_val = tm.tm_year % 100;
247  replace_type = PGTYPES_TYPE_UINT_2_LZ;
248  break;
249  default:
250 
251  /*
252  * should not happen, set something anyway
253  */
254  replace_val.str_val = " ";
255  replace_type = PGTYPES_TYPE_STRING_CONSTANT;
256  }
257  switch (replace_type)
258  {
261  memcpy(start_pattern, replace_val.str_val,
262  strlen(replace_val.str_val));
263  if (replace_type == PGTYPES_TYPE_STRING_MALLOCED)
264  free(replace_val.str_val);
265  break;
266  case PGTYPES_TYPE_UINT:
267  {
269 
270  if (!t)
271  return -1;
273  "%u", replace_val.uint_val);
274  memcpy(start_pattern, t, strlen(t));
275  free(t);
276  }
277  break;
279  {
281 
282  if (!t)
283  return -1;
285  "%02u", replace_val.uint_val);
286  memcpy(start_pattern, t, strlen(t));
287  free(t);
288  }
289  break;
291  {
293 
294  if (!t)
295  return -1;
297  "%04u", replace_val.uint_val);
298  memcpy(start_pattern, t, strlen(t));
299  free(t);
300  }
301  break;
302  default:
303 
304  /*
305  * doesn't happen (we set replace_type to
306  * PGTYPES_TYPE_STRING_CONSTANT in case of an error above)
307  */
308  break;
309  }
310  }
311  }
312  return 0;
313 }
314 
315 
316 /*
317  * PGTYPESdate_defmt_asc
318  *
319  * function works as follows:
320  * - first we analyze the parameters
321  * - if this is a special case with no delimiters, add delimiters
322  * - find the tokens. First we look for numerical values. If we have found
323  * less than 3 tokens, we check for the months' names and thereafter for
324  * the abbreviations of the months' names.
325  * - then we see which parameter should be the date, the month and the
326  * year and from these values we calculate the date
327  */
328 
329 #define PGTYPES_DATE_MONTH_MAXLENGTH 20 /* probably even less :-) */
330 int
331 PGTYPESdate_defmt_asc(date * d, const char *fmt, const char *str)
332 {
333  /*
334  * token[2] = { 4,6 } means that token 2 starts at position 4 and ends at
335  * (including) position 6
336  */
337  int token[3][2];
338  int token_values[3] = {-1, -1, -1};
339  char *fmt_token_order;
340  char *fmt_ystart,
341  *fmt_mstart,
342  *fmt_dstart;
343  unsigned int i;
344  int reading_digit;
345  int token_count;
346  char *str_copy;
347  struct tm tm;
348 
349  tm.tm_year = tm.tm_mon = tm.tm_mday = 0; /* keep compiler quiet */
350 
351  if (!d || !str || !fmt)
352  {
353  errno = PGTYPES_DATE_ERR_EARGS;
354  return -1;
355  }
356 
357  /* analyze the fmt string */
358  fmt_ystart = strstr(fmt, "yy");
359  fmt_mstart = strstr(fmt, "mm");
360  fmt_dstart = strstr(fmt, "dd");
361 
362  if (!fmt_ystart || !fmt_mstart || !fmt_dstart)
363  {
364  errno = PGTYPES_DATE_ERR_EARGS;
365  return -1;
366  }
367 
368  if (fmt_ystart < fmt_mstart)
369  {
370  /* y m */
371  if (fmt_dstart < fmt_ystart)
372  {
373  /* d y m */
374  fmt_token_order = "dym";
375  }
376  else if (fmt_dstart > fmt_mstart)
377  {
378  /* y m d */
379  fmt_token_order = "ymd";
380  }
381  else
382  {
383  /* y d m */
384  fmt_token_order = "ydm";
385  }
386  }
387  else
388  {
389  /* fmt_ystart > fmt_mstart */
390  /* m y */
391  if (fmt_dstart < fmt_mstart)
392  {
393  /* d m y */
394  fmt_token_order = "dmy";
395  }
396  else if (fmt_dstart > fmt_ystart)
397  {
398  /* m y d */
399  fmt_token_order = "myd";
400  }
401  else
402  {
403  /* m d y */
404  fmt_token_order = "mdy";
405  }
406  }
407 
408  /*
409  * handle the special cases where there is no delimiter between the
410  * digits. If we see this:
411  *
412  * only digits, 6 or 8 bytes then it might be ddmmyy and ddmmyyyy (or
413  * similar)
414  *
415  * we reduce it to a string with delimiters and continue processing
416  */
417 
418  /* check if we have only digits */
419  reading_digit = 1;
420  for (i = 0; str[i]; i++)
421  {
422  if (!isdigit((unsigned char) str[i]))
423  {
424  reading_digit = 0;
425  break;
426  }
427  }
428  if (reading_digit)
429  {
430  int frag_length[3];
431  int target_pos;
432 
433  i = strlen(str);
434  if (i != 8 && i != 6)
435  {
437  return -1;
438  }
439  /* okay, this really is the special case */
440 
441  /*
442  * as long as the string, one additional byte for the terminator and 2
443  * for the delimiters between the 3 fields
444  */
445  str_copy = pgtypes_alloc(strlen(str) + 1 + 2);
446  if (!str_copy)
447  return -1;
448 
449  /* determine length of the fragments */
450  if (i == 6)
451  {
452  frag_length[0] = 2;
453  frag_length[1] = 2;
454  frag_length[2] = 2;
455  }
456  else
457  {
458  if (fmt_token_order[0] == 'y')
459  {
460  frag_length[0] = 4;
461  frag_length[1] = 2;
462  frag_length[2] = 2;
463  }
464  else if (fmt_token_order[1] == 'y')
465  {
466  frag_length[0] = 2;
467  frag_length[1] = 4;
468  frag_length[2] = 2;
469  }
470  else
471  {
472  frag_length[0] = 2;
473  frag_length[1] = 2;
474  frag_length[2] = 4;
475  }
476  }
477  target_pos = 0;
478 
479  /*
480  * XXX: Here we could calculate the positions of the tokens and save
481  * the for loop down there where we again check with isdigit() for
482  * digits.
483  */
484  for (i = 0; i < 3; i++)
485  {
486  int start_pos = 0;
487 
488  if (i >= 1)
489  start_pos += frag_length[0];
490  if (i == 2)
491  start_pos += frag_length[1];
492 
493  strncpy(str_copy + target_pos, str + start_pos,
494  frag_length[i]);
495  target_pos += frag_length[i];
496  if (i != 2)
497  {
498  str_copy[target_pos] = ' ';
499  target_pos++;
500  }
501  }
502  str_copy[target_pos] = '\0';
503  }
504  else
505  {
506  str_copy = pgtypes_strdup(str);
507  if (!str_copy)
508  return -1;
509 
510  /* convert the whole string to lower case */
511  for (i = 0; str_copy[i]; i++)
512  str_copy[i] = (char) pg_tolower((unsigned char) str_copy[i]);
513  }
514 
515  /* look for numerical tokens */
516  reading_digit = 0;
517  token_count = 0;
518  for (i = 0; i < strlen(str_copy); i++)
519  {
520  if (!isdigit((unsigned char) str_copy[i]) && reading_digit)
521  {
522  /* the token is finished */
523  token[token_count][1] = i - 1;
524  reading_digit = 0;
525  token_count++;
526  }
527  else if (isdigit((unsigned char) str_copy[i]) && !reading_digit)
528  {
529  /* we have found a token */
530  token[token_count][0] = i;
531  reading_digit = 1;
532  }
533  }
534 
535  /*
536  * we're at the end of the input string, but maybe we are still reading a
537  * number...
538  */
539  if (reading_digit)
540  {
541  token[token_count][1] = i - 1;
542  token_count++;
543  }
544 
545 
546  if (token_count < 2)
547  {
548  /*
549  * not all tokens found, no way to find 2 missing tokens with string
550  * matches
551  */
552  free(str_copy);
554  return -1;
555  }
556 
557  if (token_count != 3)
558  {
559  /*
560  * not all tokens found but we may find another one with string
561  * matches by testing for the months names and months abbreviations
562  */
563  char *month_lower_tmp = pgtypes_alloc(PGTYPES_DATE_MONTH_MAXLENGTH);
564  char *start_pos;
565  int j;
566  int offset;
567  int found = 0;
568  char **list;
569 
570  if (!month_lower_tmp)
571  {
572  /* free variables we alloc'ed before */
573  free(str_copy);
574  return -1;
575  }
576  list = pgtypes_date_months;
577  for (i = 0; list[i]; i++)
578  {
579  for (j = 0; j < PGTYPES_DATE_MONTH_MAXLENGTH; j++)
580  {
581  month_lower_tmp[j] = (char) pg_tolower((unsigned char) list[i][j]);
582  if (!month_lower_tmp[j])
583  {
584  /* properly terminated */
585  break;
586  }
587  }
588  if ((start_pos = strstr(str_copy, month_lower_tmp)))
589  {
590  offset = start_pos - str_copy;
591 
592  /*
593  * sort the new token into the numeric tokens, shift them if
594  * necessary
595  */
596  if (offset < token[0][0])
597  {
598  token[2][0] = token[1][0];
599  token[2][1] = token[1][1];
600  token[1][0] = token[0][0];
601  token[1][1] = token[0][1];
602  token_count = 0;
603  }
604  else if (offset < token[1][0])
605  {
606  token[2][0] = token[1][0];
607  token[2][1] = token[1][1];
608  token_count = 1;
609  }
610  else
611  token_count = 2;
612  token[token_count][0] = offset;
613  token[token_count][1] = offset + strlen(month_lower_tmp) - 1;
614 
615  /*
616  * the value is the index of the month in the array of months
617  * + 1 (January is month 0)
618  */
619  token_values[token_count] = i + 1;
620  found = 1;
621  break;
622  }
623 
624  /*
625  * evil[tm] hack: if we read the pgtypes_date_months and haven't
626  * found a match, reset list to point to months (abbreviations)
627  * and reset the counter variable i
628  */
629  if (list == pgtypes_date_months)
630  {
631  if (list[i + 1] == NULL)
632  {
633  list = months;
634  i = -1;
635  }
636  }
637  }
638  if (!found)
639  {
640  free(month_lower_tmp);
641  free(str_copy);
642  errno = PGTYPES_DATE_ERR_ENOTDMY;
643  return -1;
644  }
645 
646  /*
647  * here we found a month. token[token_count] and
648  * token_values[token_count] reflect the month's details.
649  *
650  * only the month can be specified with a literal. Here we can do a
651  * quick check if the month is at the right position according to the
652  * format string because we can check if the token that we expect to
653  * be the month is at the position of the only token that already has
654  * a value. If we wouldn't check here we could say "December 4 1990"
655  * with a fmt string of "dd mm yy" for 12 April 1990.
656  */
657  if (fmt_token_order[token_count] != 'm')
658  {
659  /* deal with the error later on */
660  token_values[token_count] = -1;
661  }
662  free(month_lower_tmp);
663  }
664 
665  /* terminate the tokens with ASCII-0 and get their values */
666  for (i = 0; i < 3; i++)
667  {
668  *(str_copy + token[i][1] + 1) = '\0';
669  /* A month already has a value set, check for token_value == -1 */
670  if (token_values[i] == -1)
671  {
672  errno = 0;
673  token_values[i] = strtol(str_copy + token[i][0], (char **) NULL, 10);
674  /* strtol sets errno in case of an error */
675  if (errno)
676  token_values[i] = -1;
677  }
678  if (fmt_token_order[i] == 'd')
679  tm.tm_mday = token_values[i];
680  else if (fmt_token_order[i] == 'm')
681  tm.tm_mon = token_values[i];
682  else if (fmt_token_order[i] == 'y')
683  tm.tm_year = token_values[i];
684  }
685  free(str_copy);
686 
687  if (tm.tm_mday < 1 || tm.tm_mday > 31)
688  {
689  errno = PGTYPES_DATE_BAD_DAY;
690  return -1;
691  }
692 
693  if (tm.tm_mon < 1 || tm.tm_mon > MONTHS_PER_YEAR)
694  {
695  errno = PGTYPES_DATE_BAD_MONTH;
696  return -1;
697  }
698 
699  if (tm.tm_mday == 31 && (tm.tm_mon == 4 || tm.tm_mon == 6 || tm.tm_mon == 9 || tm.tm_mon == 11))
700  {
701  errno = PGTYPES_DATE_BAD_DAY;
702  return -1;
703  }
704 
705  if (tm.tm_mon == 2 && tm.tm_mday > 29)
706  {
707  errno = PGTYPES_DATE_BAD_DAY;
708  return -1;
709  }
710 
711  *d = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - date2j(2000, 1, 1);
712 
713  return 0;
714 }
#define MAXDATELEN
Definition: datetime.h:201
void EncodeDateOnly(struct pg_tm *tm, int style, char *str)
Definition: datetime.c:3858
date PGTYPESdate_from_timestamp(timestamp dt)
Definition: datetime.c:31
#define PGTYPES_DATE_BAD_DATE
Definition: pgtypes_error.h:8
void GetCurrentDateTime(struct pg_tm *tm)
Definition: datetime.c:348
#define PGTYPES_DATE_BAD_DAY
Definition: pgtypes_error.h:12
#define PGTYPES_DATE_ERR_EARGS
Definition: pgtypes_error.h:9
#define PGTYPES_TYPE_UINT_4_LZ
#define PGTYPES_FMTDATE_DAY_DIGITS_LZ
Definition: datetime.c:161
#define PGTYPES_DATE_ERR_ENOTDMY
Definition: pgtypes_error.h:11
int64 timestamp
int PGTYPESdate_fmt_asc(date dDate, const char *fmtstring, char *outbuf)
Definition: datetime.c:169
void PGTYPESdate_today(date *d)
Definition: datetime.c:148
date PGTYPESdate_from_asc(char *str, char **endptr)
Definition: datetime.c:47
char * pgtypes_date_weekdays_short[]
Definition: dt_common.c:497
unsigned char pg_tolower(unsigned char ch)
Definition: pgstrcasecmp.c:122
long date
Definition: pgtypes_date.h:9
#define PGTYPES_DATE_BAD_MONTH
Definition: pgtypes_error.h:13
int PGTYPESdate_defmt_asc(date *d, const char *fmt, const char *str)
Definition: datetime.c:331
#define PGTYPES_TYPE_UINT_2_LZ
#define PGTYPES_TYPE_STRING_CONSTANT
#define PGTYPES_DATE_NUM_MAX_DIGITS
Definition: datetime.c:158
static struct pg_tm tm
Definition: localtime.c:108
#define MONTHS_PER_YEAR
Definition: timestamp.h:69
char * pgtypes_strdup(const char *str)
Definition: common.c:20
#define TIMESTAMP_NOT_FINITE(j)
Definition: timestamp.h:122
void PGTYPESdate_julmdy(date jd, int *mdy)
Definition: datetime.c:115
void PGTYPESdate_mdyjul(int *mdy, date *jdate)
Definition: datetime.c:128
int DecodeDateTime(char **field, int *ftype, int nf, int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp)
Definition: datetime.c:761
const char *const months[]
Definition: datetime.c:66
#define PGTYPES_TYPE_UINT
#define PGTYPES_DATE_ERR_ENOSHORTDATE
Definition: pgtypes_error.h:10
char * pgtypes_alloc(long size)
Definition: common.c:10
static char * buf
Definition: pg_test_fsync.c:68
#define PGTYPES_FMTDATE_YEAR_DIGITS_LONG
Definition: datetime.c:166
#define PGTYPES_DATE_MONTH_MAXLENGTH
Definition: datetime.c:329
date * PGTYPESdate_new(void)
Definition: datetime.c:15
int32 fsec_t
Definition: timestamp.h:41
#define USECS_PER_DAY
Definition: timestamp.h:91
void j2date(int jd, int *year, int *month, int *day)
Definition: datetime.c:295
#define PGTYPES_TYPE_STRING_MALLOCED
#define PGTYPES_FMTDATE_YEAR_DIGITS_SHORT
Definition: datetime.c:165
int date2j(int y, int m, int d)
Definition: datetime.c:270
char * pgtypes_date_months[]
Definition: dt_common.c:499
#define free(a)
Definition: header.h:65
#define PGTYPES_FMTDATE_MONTH_LITERAL_SHORT
Definition: datetime.c:164
#define MAXDATEFIELDS
Definition: datetime.h:203
int DateStyle
Definition: globals.c:115
void PGTYPESdate_free(date *d)
Definition: datetime.c:25
#define DTK_EPOCH
Definition: datetime.h:153
int tm_year
Definition: pgtime.h:32
int i
void GetEpochTime(struct pg_tm *tm)
Definition: timestamp.c:2003
int PGTYPESdate_dayofweek(date dDate)
Definition: datetime.c:138
#define PGTYPES_FMTDATE_DOW_LITERAL_SHORT
Definition: datetime.c:162
static char format
Definition: pg_basebackup.c:89
char * PGTYPESdate_to_asc(date dDate)
Definition: datetime.c:101
int ParseDateTime(const char *timestr, char *workbuf, size_t buflen, char **field, int *ftype, int maxfields, int *numfields)
Definition: datetime.c:540
#define snprintf
Definition: port.h:192
#define PGTYPES_FMTDATE_MONTH_DIGITS_LZ
Definition: datetime.c:163
unsigned int uint_val
#define DTK_DATE
Definition: datetime.h:145