/*===========================================================================* * * * sflcvsd.c - * * * * Copyright (c) 1991-2003 iMatix Corporation * * * * ------------------ GPL Licensed Source Code ------------------ * * iMatix makes this software available under the GNU General * * Public License (GPL) license for open source projects. For * * details of the GPL license please see www.gnu.org or read the * * file license.gpl provided in this package. * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program in the file 'license.gpl'; if * * not, write to the Free Software Foundation, Inc., 59 Temple * * Place - Suite 330, Boston, MA 02111-1307, USA. * * * * You can also license this software under iMatix's General Terms * * of Business (GTB) for commercial projects. If you have not * * explicitly licensed this software under the iMatix GTB you may * * only use it under the terms of the GNU General Public License. * * * * For more information, send an email to info@imatix.com. * * -------------------------------------------------------------- * *===========================================================================*/ #include "prelude.h" /* Universal header file */ #include "sflconv.h" /* Prototypes for functions */ #include "sfldate.h" /* Date library functions */ #include "sflstr.h" /* String functions */ /* ---------------------------------------------------------------------[<]- Function: conv_str_date Synopsis: Converts a string to a date. The supposed format of the date is defined by the format argument, which can be one of: DATE_YMD_COMPACT Year month day. DATE_YMD_DELIM Year month day. DATE_YMD_SPACE Year month day. DATE_YMD_COMMA Year month day. DATE_YM_COMPACT Year and month only; day is zero. DATE_YM_DELIM Year and month only; day is zero. DATE_YM_SPACE Year and month only; day is zero. DATE_MD_COMPACT Month and day only; year is zero. DATE_MD_DELIM Month and day only; year is zero. DATE_MD_SPACE Month and day only; year is zero.
The date order must be one of: DATE_ORDER_YMD Year month day. DATE_ORDER_DMY Day month year. DATE_ORDER_MDY Month day year.
You can override the order using these flags: FLAG_D_ORDER_YMD Year month day. FLAG_D_ORDER_DMY Day month year. FLAG_D_ORDER_MDY Month day year.
Returns the date as a long integer, YYYYMMDD. The conversion is pretty relaxed and allows strings like: 010195, 1-1-95, 1-Jan-95, 1jan95, 01jan95, 1 1 95, etc. The input string must be null-terminated. Returns -1 in case of an invalid date or format. If the short formats (DATE_DM_..., DATE_YM_...) are used, the missing field is always set to 0 and cannot be supplied in the string. If the date was empty, i.e. contains no usable digits, returns 0. ---------------------------------------------------------------------[>]-*/ long conv_str_date ( const char *string, int flags, int format, int order) { static char *month_name [] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; static byte month_days [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, order_ptr [][3] = /* Year, Month, Day pointers */ { /* for various orders & formats */ /* YY MM DD YYYY MM DD YY MM YYYY MM MM DD */ { 0, 2, 4 }, { 0, 4, 6 }, { 0, 2, 99 }, { 0, 4, 99 }, { 99, 0, 2 }, { 4, 2, 0 }, { 4, 2, 0 }, { 2, 0, 99 }, { 2, 0, 99 }, { 99, 2, 0 }, { 4, 0, 2 }, { 4, 0, 2 }, { 2, 0, 99 }, { 2, 0, 99 }, { 99, 0, 2 }, }; char date_digits [9], /* 8 digits of date */ month_letters [4] = "???", /* 3 characters of month */ ch; /* Next character in date */ long feedback; /* Returned date; -1 = error */ int digits, /* Number of digits in string */ delimiters, /* Number of delimiters in date */ digitseq, /* Number of digits in sequence */ count, /* Number of month letters */ year, /* Date year value */ month, /* Date month value */ day, /* Date day value */ order_index, /* Index into order table */ y_ptr, /* Where is year in date? */ m_ptr, /* Where is month in date? */ d_ptr, /* Where is day in date? */ date_order = order; /* Actual date order */ Bool had_month; /* Did we already get a month? */ ASSERT (format >= DATE_FORMAT_FIRST && format <= DATE_FORMAT_LAST); ASSERT (order >= DATE_ORDER_FIRST && order <= DATE_ORDER_LAST); conv_reason = 0; /* No conversion errors so far */ if (flags & FLAG_D_ORDER_YMD) date_order = DATE_ORDER_YMD; else if (flags & FLAG_D_ORDER_DMY) date_order = DATE_ORDER_DMY; else if (flags & FLAG_D_ORDER_MDY) date_order = DATE_ORDER_MDY; /* Collect date digits */ digits = 0; /* Nothing collected so far */ digitseq = 0; /* No digits in sequence */ feedback = 0; /* No errors so far */ delimiters = 0; /* We allow up to 2 delimiters */ had_month = FALSE; /* True after 3-letter month seen */ do { ch = *string++; if (isdigit (ch)) { if (digits < 8) { digitseq++; date_digits [digits++] = ch; } else { conv_reason = CONV_ERR_DATE_SIZE; feedback = -1; /* Too many digits */ } } else { /* Fill-up to even number of digits */ if (digits > (digits / 2) * 2) { date_digits [digits] = date_digits [digits - 1]; date_digits [digits - 1] = '0'; digits++; } /* 3 or 5 in a row is not allowed */ if (digitseq == 3 || digitseq == 5) { conv_reason = CONV_ERR_REJECT_3_5; feedback = -1; } digitseq = 0; /* If a letter, try to match against a month */ if (isalpha (ch)) { if (had_month) { conv_reason = CONV_ERR_MULTIPLE_MONTH; feedback = -1; } else { for (count = 0; isalpha (ch); ) { if (count < 3) month_letters [count++] = (char) tolower (ch); ch = *string++; } string--; /* Move back to char after month */ if (count < 3) { conv_reason = CONV_ERR_BAD_MONTH; feedback = -1; /* Too few letters */ } month_letters [3] = 0; for (count = 0; count < 12; count++) if (streq (month_letters, month_name [count])) { count++; date_digits [digits++] = (char) (count / 10 + '0'); date_digits [digits++] = (char) (count % 10 + '0'); had_month = TRUE; break; } if (!had_month) { conv_reason = CONV_ERR_BAD_MONTH; feedback = -1; /* Month not found */ } } } else if (ispunct (ch)) /* Skip any delimiter */ if ((++delimiters > 2) || (format > DATE_YMD_LAST && delimiters > 1)) { conv_reason = CONV_ERR_MULTIPLE_DELIM; feedback = -1; /* Multiple delimiters */ } } } until (ch == 0); /* Return zero date if empty */ if (digits == 0) return (feedback); /* Calculate offset in date_digits for various date order & formats */ order_index = (date_order - 1) * 5; /* Each row has 5 items */ if (format <= DATE_YMD_LAST) { if (digits == 6) ; /* nothing */ else if (digits == 8) order_index += 1; } else if (format <= DATE_YM_LAST) { date_digits [digits++] = '0'; date_digits [digits++] = '0'; if (digits == 6) order_index += 2; else if (digits == 8) order_index += 3; else { conv_reason = CONV_ERR_DATE_SIZE; return (-1); /* Error - bad date size */ } } else if (format <= DATE_MD_LAST) { date_digits [digits++] = '0'; date_digits [digits++] = '0'; if (digits == 6) order_index += 4; else { conv_reason = CONV_ERR_DATE_SIZE; return (-1); /* Error - bad date size */ } } /* Decode order to pick-up offset of day/month/year in date_digits */ y_ptr = order_ptr [order_index][0]; m_ptr = order_ptr [order_index][1]; d_ptr = order_ptr [order_index][2]; # define DIGIT(x) (date_digits [(x)] - '0') if (y_ptr != 99) { year = DIGIT (y_ptr) * 10 + DIGIT (y_ptr + 1); if (digits == 8) year = DIGIT (y_ptr + 2) * 10 + DIGIT (y_ptr + 3) + year * 100; if (year < 50) year += 2000; else if (year < 100) year += 1900; } else year = 0; if (m_ptr != 99) { month = DIGIT (m_ptr) * 10 + DIGIT (m_ptr + 1); if (month == 0 || month > 12) { conv_reason = CONV_ERR_OUT_OF_RANGE; feedback = -1; } } else month = 0; if (d_ptr != 99) { day = DIGIT (d_ptr) * 10 + DIGIT (d_ptr + 1); if ((day == 0 || day > (int) month_days [month - 1]) || (month == 2 && day == 29 && !leap_year (year))) { conv_reason = CONV_ERR_OUT_OF_RANGE; feedback = -1; } } else day = 0; if (feedback == 0) feedback = year * 10000L + month * 100 + day; return (feedback); } /* ---------------------------------------------------------------------[<]- Function: conv_str_day Synopsis: Converts a string to a day. The string contains a day name in English. Only the first three letters of the name are significant. Returns a value 0..6 for Sunday to Saturday, or -1 if the name is not a valid day. ---------------------------------------------------------------------[>]-*/ int conv_str_day ( const char *string) { static char *day_name [] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; int day; char day_comp [4]; /* Get up to three letters of day name and convert to lower case */ strncpy (day_comp, string, 3); day_comp [3] = '\0'; strlwc (day_comp); /* I don't like magic constants, but "7" is pretty safe for now */ for (day = 0; day < 7; day++) if (streq (day_name [day], day_comp)) break; return (day >= 7? -1: day); }