⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sflcvsd.c

📁 短小精悍的C语言标准函数库。提供450个以上的可移植的算法和工具代码。
💻 C
字号:
/*  ----------------------------------------------------------------<Prolog>-
    Name:       sflcvsd.c
    Title:      Converts a string to a date
    Package:    Standard Function Library (SFL)

    Written:    1996/01/05  iMatix SFL project team <sfl@imatix.com>
    Revised:    1999/09/29

    Copyright:  Copyright (c) 1996-2000 iMatix Corporation
    License:    This is free software; you can redistribute it and/or modify
                it under the terms of the SFL License Agreement as provided
                in the file LICENSE.TXT.  This software is distributed in
                the hope that it will be useful, but without any warranty.
 ------------------------------------------------------------------</Prolog>-*/

#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:
    <TABLE>
        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.
    </TABLE>

    The date order must be one of:
    <TABLE>
        DATE_ORDER_YMD      Year month day.
        DATE_ORDER_DMY      Day month year.
        DATE_ORDER_MDY      Month day year.
    </TABLE>

    You can override the order using these flags:
    <TABLE>
        FLAG_D_ORDER_YMD    Year month day.
        FLAG_D_ORDER_DMY    Day month year.
        FLAG_D_ORDER_MDY    Month day year.
    </TABLE>

    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);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -