1
#include "postgres_fe.h"
10
#include "pgtypes_error.h"
11
#include "pgtypes_date.h"
14
PGTYPESdate_from_timestamp(timestamp dt)
18
dDate = 0; /* suppress compiler warning */
20
if (TIMESTAMP_NOT_FINITE(dt))
23
#ifdef HAVE_INT64_TIMESTAMP
24
/* Microseconds to days */
25
dDate = (dt / INT64CONST(86400000000));
28
dDate = (dt / 86400.0);
35
PGTYPESdate_from_asc(char *str, char **endptr)
45
char *field[MAXDATEFIELDS];
46
int ftype[MAXDATEFIELDS];
47
char lowstr[MAXDATELEN + 1];
49
char **ptr = (endptr != NULL) ? endptr : &realptr;
51
bool EuroDates = FALSE;
54
if (strlen(str) >= sizeof(lowstr))
56
errno = PGTYPES_DATE_BAD_DATE;
60
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0)
61
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp, EuroDates) != 0))
63
errno = PGTYPES_DATE_BAD_DATE;
77
errno = PGTYPES_DATE_BAD_DATE;
81
dDate = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
87
PGTYPESdate_to_asc(date dDate)
91
char buf[MAXDATELEN + 1];
93
bool EuroDates = FALSE;
95
j2date((dDate + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
96
EncodeDateOnly(tm, DateStyle, buf, EuroDates);
97
return pgtypes_strdup(buf);
101
PGTYPESdate_julmdy(date jd, int *mdy)
107
j2date((int) (jd + date2j(2000, 1, 1)), &y, &m, &d);
114
PGTYPESdate_mdyjul(int *mdy, date *jdate)
116
/* month is mdy[0] */
120
*jdate = (date) (date2j(mdy[2], mdy[0], mdy[1]) - date2j(2000, 1, 1));
124
PGTYPESdate_dayofweek(date dDate)
127
* Sunday: 0 Monday: 1 Tuesday: 2 Wednesday: 3 Thursday: 4
128
* Friday: 5 Saturday: 6
130
return (int) (dDate + date2j(2000, 1, 1) + 1) % 7;
134
PGTYPESdate_today(date *d)
138
GetCurrentDateTime(&ts);
139
*d = date2j(ts.tm_year, ts.tm_mon, ts.tm_mday) - date2j(2000, 1, 1);
143
#define PGTYPES_DATE_NUM_MAX_DIGITS 20 /* should suffice for most
146
#define PGTYPES_FMTDATE_DAY_DIGITS_LZ 1 /* LZ means "leading
148
#define PGTYPES_FMTDATE_DOW_LITERAL_SHORT 2
149
#define PGTYPES_FMTDATE_MONTH_DIGITS_LZ 3
150
#define PGTYPES_FMTDATE_MONTH_LITERAL_SHORT 4
151
#define PGTYPES_FMTDATE_YEAR_DIGITS_SHORT 5
152
#define PGTYPES_FMTDATE_YEAR_DIGITS_LONG 6
155
PGTYPESdate_fmt_asc(date dDate, char *fmtstring, char *outbuf)
164
* format items have to be sorted according to their length, since
165
* the first pattern that matches gets replaced by its value
168
"ddd", PGTYPES_FMTDATE_DOW_LITERAL_SHORT
171
"dd", PGTYPES_FMTDATE_DAY_DIGITS_LZ
174
"mmm", PGTYPES_FMTDATE_MONTH_LITERAL_SHORT
177
"mm", PGTYPES_FMTDATE_MONTH_DIGITS_LZ
180
"yyyy", PGTYPES_FMTDATE_YEAR_DIGITS_LONG
183
"yy", PGTYPES_FMTDATE_YEAR_DIGITS_SHORT
190
union un_fmt_comb replace_val;
198
/* XXX error handling ? */
199
/* copy the string over */
200
strcpy(outbuf, fmtstring);
203
j2date((dDate + date2j(2000, 1, 1)), &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
204
dow = PGTYPESdate_dayofweek(dDate);
206
for (i = 0; mapping[i].format != NULL; i++)
208
while ((start_pattern = strstr(outbuf, mapping[i].format)) != NULL)
210
switch (mapping[i].component)
212
case PGTYPES_FMTDATE_DOW_LITERAL_SHORT:
213
replace_val.str_val = pgtypes_date_weekdays_short[dow];
214
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
216
case PGTYPES_FMTDATE_DAY_DIGITS_LZ:
217
replace_val.uint_val = tm.tm_mday;
218
replace_type = PGTYPES_TYPE_UINT_2_LZ;
220
case PGTYPES_FMTDATE_MONTH_LITERAL_SHORT:
221
replace_val.str_val = months[tm.tm_mon - 1];
222
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
224
case PGTYPES_FMTDATE_MONTH_DIGITS_LZ:
225
replace_val.uint_val = tm.tm_mon;
226
replace_type = PGTYPES_TYPE_UINT_2_LZ;
228
case PGTYPES_FMTDATE_YEAR_DIGITS_LONG:
229
replace_val.uint_val = tm.tm_year;
230
replace_type = PGTYPES_TYPE_UINT_4_LZ;
232
case PGTYPES_FMTDATE_YEAR_DIGITS_SHORT:
233
replace_val.uint_val = tm.tm_year % 1000;
234
replace_type = PGTYPES_TYPE_UINT_2_LZ;
239
* should not happen, set something anyway
241
replace_val.str_val = " ";
242
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
244
switch (replace_type)
246
case PGTYPES_TYPE_STRING_MALLOCED:
247
case PGTYPES_TYPE_STRING_CONSTANT:
248
strncpy(start_pattern, replace_val.str_val,
249
strlen(replace_val.str_val));
250
if (replace_type == PGTYPES_TYPE_STRING_MALLOCED)
251
free(replace_val.str_val);
253
case PGTYPES_TYPE_UINT:
255
char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
259
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
260
"%u", replace_val.uint_val);
261
strncpy(start_pattern, t, strlen(t));
265
case PGTYPES_TYPE_UINT_2_LZ:
267
char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
271
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
272
"%02u", replace_val.uint_val);
273
strncpy(start_pattern, t, strlen(t));
277
case PGTYPES_TYPE_UINT_4_LZ:
279
char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
283
snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
284
"%04u", replace_val.uint_val);
285
strncpy(start_pattern, t, strlen(t));
292
* doesn't happen (we set replace_type to
293
* PGTYPES_TYPE_STRING_CONSTANT in case of an error
305
* PGTYPESdate_defmt_asc
307
* function works as follows:
308
* - first we analyze the paramters
309
* - if this is a special case with no delimiters, add delimters
310
* - find the tokens. First we look for numerical values. If we have found
311
* less than 3 tokens, we check for the months' names and thereafter for
312
* the abbreviations of the months' names.
313
* - then we see which parameter should be the date, the month and the
314
* year and from these values we calculate the date
317
#define PGTYPES_DATE_MONTH_MAXLENGTH 20 /* probably even less :-) */
319
PGTYPESdate_defmt_asc(date *d, char *fmt, char *str)
322
* token[2] = { 4,6 } means that token 2 starts at position 4 and ends
323
* at (including) position 6
326
int token_values[3] = {-1, -1, -1};
327
char *fmt_token_order;
337
if (!d || !str || !fmt)
339
errno = PGTYPES_DATE_ERR_EARGS;
343
/* analyze the fmt string */
344
fmt_ystart = strstr(fmt, "yy");
345
fmt_mstart = strstr(fmt, "mm");
346
fmt_dstart = strstr(fmt, "dd");
348
if (!fmt_ystart || !fmt_mstart || !fmt_dstart)
350
errno = PGTYPES_DATE_ERR_EARGS;
354
if (fmt_ystart < fmt_mstart)
357
if (fmt_dstart < fmt_ystart)
360
fmt_token_order = "dym";
362
else if (fmt_dstart > fmt_mstart)
365
fmt_token_order = "ymd";
370
fmt_token_order = "ydm";
375
/* fmt_ystart > fmt_mstart */
377
if (fmt_dstart < fmt_mstart)
380
fmt_token_order = "dmy";
382
else if (fmt_dstart > fmt_ystart)
385
fmt_token_order = "myd";
390
fmt_token_order = "mdy";
395
* handle the special cases where there is no delimiter between the
396
* digits. If we see this:
398
* only digits, 6 or 8 bytes then it might be ddmmyy and ddmmyyyy (or
401
* we reduce it to a string with delimiters and continue processing
404
/* check if we have only digits */
406
for (i = 0; str[i]; i++)
408
if (!isdigit((unsigned char) str[i]))
420
if (i != 8 && i != 6)
422
errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
425
/* okay, this really is the special case */
428
* as long as the string, one additional byte for the terminator
429
* and 2 for the delimiters between the 3 fiedls
431
str_copy = pgtypes_alloc(strlen(str) + 1 + 2);
435
/* determine length of the fragments */
444
if (fmt_token_order[0] == 'y')
450
else if (fmt_token_order[1] == 'y')
466
* XXX: Here we could calculate the positions of the tokens and
467
* save the for loop down there where we again check with
468
* isdigit() for digits.
470
for (i = 0; i < 3; i++)
475
start_pos += frag_length[0];
477
start_pos += frag_length[1];
479
strncpy(str_copy + target_pos, str + start_pos,
481
target_pos += frag_length[i];
484
str_copy[target_pos] = ' ';
488
str_copy[target_pos] = '\0';
492
str_copy = pgtypes_strdup(str);
496
/* convert the whole string to lower case */
497
for (i = 0; str_copy[i]; i++)
498
str_copy[i] = (char) pg_tolower((unsigned char) str_copy[i]);
501
/* look for numerical tokens */
504
for (i = 0; i < strlen(str_copy); i++)
506
if (!isdigit((unsigned char) str_copy[i]) && reading_digit)
508
/* the token is finished */
509
token[token_count][1] = i - 1;
513
else if (isdigit((unsigned char) str_copy[i]) && !reading_digit)
515
/* we have found a token */
516
token[token_count][0] = i;
522
* we're at the end of the input string, but maybe we are still
523
* reading a number...
527
token[token_count][1] = i - 1;
535
* not all tokens found, no way to find 2 missing tokens with
539
errno = PGTYPES_DATE_ERR_ENOTDMY;
543
if (token_count != 3)
546
* not all tokens found but we may find another one with string
547
* matches by testing for the months names and months
550
char *month_lower_tmp = pgtypes_alloc(PGTYPES_DATE_MONTH_MAXLENGTH);
557
if (!month_lower_tmp)
559
/* free variables we alloc'ed before */
563
list = pgtypes_date_months;
564
for (i = 0; list[i]; i++)
566
for (j = 0; j < PGTYPES_DATE_MONTH_MAXLENGTH; j++)
568
month_lower_tmp[j] = (char) pg_tolower((unsigned char) list[i][j]);
569
if (!month_lower_tmp[j])
571
/* properly terminated */
575
if ((start_pos = strstr(str_copy, month_lower_tmp)))
577
offset = start_pos - str_copy;
580
* sort the new token into the numeric tokens, shift them
583
if (offset < token[0][0])
585
token[2][0] = token[1][0];
586
token[2][1] = token[1][1];
587
token[1][0] = token[0][0];
588
token[1][1] = token[0][1];
591
else if (offset < token[1][0])
593
token[2][0] = token[1][0];
594
token[2][1] = token[1][1];
599
token[token_count][0] = offset;
600
token[token_count][1] = offset + strlen(month_lower_tmp) - 1;
603
* the value is the index of the month in the array of
604
* months + 1 (January is month 0)
606
token_values[token_count] = i + 1;
612
* evil[tm] hack: if we read the pgtypes_date_months and
613
* haven't found a match, reset list to point to
614
* pgtypes_date_months_short and reset the counter variable i
616
if (list == pgtypes_date_months)
618
if (list[i + 1] == NULL)
627
free(month_lower_tmp);
629
errno = PGTYPES_DATE_ERR_ENOTDMY;
634
* here we found a month. token[token_count] and
635
* token_values[token_count] reflect the month's details.
637
* only the month can be specified with a literal. Here we can do a
638
* quick check if the month is at the right position according to
639
* the format string because we can check if the token that we
640
* expect to be the month is at the position of the only token
641
* that already has a value. If we wouldn't check here we could
642
* say "December 4 1990" with a fmt string of "dd mm yy" for 12
645
if (fmt_token_order[token_count] != 'm')
647
/* deal with the error later on */
648
token_values[token_count] = -1;
650
free(month_lower_tmp);
653
/* terminate the tokens with ASCII-0 and get their values */
654
for (i = 0; i < 3; i++)
656
*(str_copy + token[i][1] + 1) = '\0';
657
/* A month already has a value set, check for token_value == -1 */
658
if (token_values[i] == -1)
661
token_values[i] = strtol(str_copy + token[i][0], (char **) NULL, 10);
662
/* strtol sets errno in case of an error */
664
token_values[i] = -1;
666
if (fmt_token_order[i] == 'd')
667
tm.tm_mday = token_values[i];
668
else if (fmt_token_order[i] == 'm')
669
tm.tm_mon = token_values[i];
670
else if (fmt_token_order[i] == 'y')
671
tm.tm_year = token_values[i];
675
if (tm.tm_mday < 1 || tm.tm_mday > 31)
677
errno = PGTYPES_DATE_BAD_DAY;
681
if (tm.tm_mon < 1 || tm.tm_mon > 12)
683
errno = PGTYPES_DATE_BAD_MONTH;
687
if (tm.tm_mday == 31 && (tm.tm_mon == 4 || tm.tm_mon == 6 || tm.tm_mon == 9 || tm.tm_mon == 11))
689
errno = PGTYPES_DATE_BAD_DAY;
693
if (tm.tm_mon == 2 && tm.tm_mday > 29)
695
errno = PGTYPES_DATE_BAD_DAY;
699
/* XXX: DBCENTURY ? */
701
*d = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - date2j(2000, 1, 1);