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().
CodePudding user response:
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
CodePudding user response:
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@0==0 ||(res->year%4==0 && res->year0!=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.