escape character '-' in a sscanf() function

Viewed 48

The problem is very simple, I have a date column in my database postgres, it returns the value as string in the format yyyy-mm-dd, in order to store it in my code, I need to parse the value. The function is also very simple:

int parse_date(const char * s, tbDate*res)
{
    int yyyy, mm, dd;
    if (sscanf(s, "%d %d %d", &yyyy, &mm, &dd) == 3
            || sscanf(s, "%d.%d.%d", &yyyy, &mm, &dd) == 3
            || sscanf(s, "%d/%d/%d", &yyyy, &mm, &dd) == 3
            || sscanf(s, "%d-%d-%d", &yyyy, &mm, &dd) == 3)
    {
        if (mm > 0 && mm < 13 && dd>0 && dd < 32)
        {
            res->day = dd;
            res->month = mm;
            res->year = yyyy;
            return TB_PARSE_SUCCESS;
        }
    }
    return TB_PARSE_FAILURE;
}

The first three versions of sscanf() work very well, but the follow sscanf(s, "%d-%d-%d", &yyyy, &mm, &dd) , the format that the database returns, are read as negative value, it means that if I pass a date like '2022-12-03', yyyy is correctly 2022, but mm and dd, are -12 and -3. I would avoid to put a date format function in the query of the database, and I would know if there is a way to escape the - charachter inside the sscanf().

2 Answers

date like '2022-12-03', yyyy is correctly 2022, but mm and dd, are -12 and -3.

That is because "2022-12-03" matched sscanf(s, "%d %d %d", &yyyy, &mm, &dd) == 3 first. Note that a " " in format "%d %d %d" match 0 or more white spaces.

Simply change order

if (   sscanf(s, "%d-%d-%d", &yyyy, &mm, &dd) == 3  // swapped
    || sscanf(s, "%d.%d.%d", &yyyy, &mm, &dd) == 3
    || sscanf(s, "%d/%d/%d", &yyyy, &mm, &dd) == 3
    || sscanf(s, "%d %d %d", &yyyy, &mm, &dd) == 3) // swapped

It comes me in mind that the sscanf() function works also if is a dirty answer for example it returns 3 also if you writes '2022/09/12fooobar', so I decided to change the function and improved the check of valid day, since I didn't find it in internet I post my own parse date function:

int parse_date(const char * s, tbDate*res)
{
    
    res->year=res->month=res->day=0;
    char separator;
    int acc=0;
    while (isspace(*s))
        s++;
    if(!(*s >= '0' && *s <= '9'))
        return TB_PARSE_FAILURE;
    while (*s >= '0' && *s <= '9')
        acc = (acc << 3) + (acc << 1) + *s++ - '0';///fast way to multiply for 10
    if(!*s) return TB_PARSE_FAILURE;
    separator=*s;
    s++;
    res->year=acc;
    acc=0;

    if(!(*s >= '0' && *s <= '9'))
        return TB_PARSE_FAILURE;
    while (*s >= '0' && *s <= '9')
        acc = (acc << 3) + (acc << 1) + *s++ - '0';
    res->month=acc;
    acc=0;
    if(*s!=separator)
            return TB_PARSE_FAILURE;
    s++;
   if(!(*s >= '0' && *s <= '9'))
        return TB_PARSE_FAILURE;
    while (*s >= '0' && *s <= '9')
        acc = (acc << 3) + (acc << 1) + *s++ - '0';
    res->day=acc;
    while(isspace(*s))
        s++;
    if(*s) return TB_PARSE_FAILURE;
        switch (res->month)
        {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                if( res->day>0 && res->day<32)
                   return TB_PARSE_SUCCESS;
                else
                    return TB_PARSE_FAILURE;
            case 4:
            case 6:
            case 9:
            case 11:
               if( res->day>0 && res->day<31)
                    return TB_PARSE_SUCCESS;
                else return TB_PARSE_FAILURE;
            case 2:
               if(res->day>0 && res->day<29)
                    return TB_PARSE_SUCCESS;
                else if (res->day==29 &&(res->year%400==0 ||(res->year%4==0 && res->year%100!=0)   ) )
                    return TB_PARSE_SUCCESS;
                else
                    return TB_PARSE_FAILURE;
            default:
                return TB_PARSE_FAILURE;
            }

    return TB_PARSE_FAILURE;
}

It skips spaces at begin and at the end, but not inside the numbers. It checks if you use the same separator and check if the date is valid or less.

Related