C function to check the validity of a date in DD.MM.YYYY format I was tasked with writing a function, that checks whether a date is valid in the following format: DD.MM.YYYY. Here's what I wrote: 

bool isValidDate(const char* date)
{
    int a, b, c;
    size_t len = strnlen(date, 255);
    for (size_t i = 0; i &lt; len; i++)
    {
        if (!isdigit(date[i]) &amp;&amp; date[i] != '.') return false;
    }
    int validConversions = sscanf_s(date, "%2d.%2d.%4d", &amp;a, &amp;b, &amp;c);
    return validConversions == 3 ? true : false;
}


From my testing I can conclude that this function does reasonably well: it doesn't accept anything except dots and digits, it expects to read exactly 3 numbers and it knows how many digits to read for the days and months. 

Something it doesn't check however, is whether the days are less than 31 or the months are less than 12. Another thing it doesn't do is that it doesn't check correct looking, but invalid dates such as 30.02.2013 or 31.06.2012. 

Solving that problem is trivial, but I don't wanna write 12 ifs for each month to check the dates. I'm starting to think I should just generate a data structure of valid dates and use that to check, i.e. if the passed date is there, then it's valid. 

Is that the best way to do it or are there other ways?
Use descriptive variable names

Instead of int a, b, c, give them more descriptive names:

int day, month year;


sscanf() will ignore trailing garbage

The function sscanf() will stop parsing after the last conversion. So the string "1.2.3...." will be cleared by your check for digits and period characters, and then sscanf() will read 3 integers, and returns 3. But obviously, this is an invalid date.

It would be best if you could have sscanf() determine the validity of the whole input string. One way is to use the %n conversion to check how many characters of the string were parsed so far, and then check that this corresponds to the whole string. This is how you can do that:

int day, month, year, chars_parsed;

if (sscanf(date, "%2d.%2d.%4d%n", &amp;day, &amp;month, &amp;year, &amp;chars_parsed) != 3)
    return false;

/* If the whole string is parsed, chars_parsed points to the NUL-byte
   after the last character in the string.
 */
if (date[chars_parsed] != 0)
    return false;


You don't need 12 ifs to check the month

You can just write:

if (month &lt; 1 || month &gt; 12)
    return false;


And similar for days and perhaps even years if you want to limit the allowed range.

Avoid redundant checks

It is likely that your program will normally handle valid date strings. So you want to optimize for this case. In your code, you are checking for the string to be empty at the start, but this is not necessary; if the input string is empty, then sscanf() will not return 3, so it will already correctly return false. Since most input strings will be valid, checking the string length is just a waste of CPU cycles.

Similarly, checking for each character to be a digit or a period is redundant if you just use sscanf() with the %n method to check that there was no trailing garbage after the year.

Better date checking

Just checking whether the month is between 1 and 12 and day between 1 and 31 is not enough. A given month might have less than 31 days. There are also leap years to consider. And if you want to allow dates far in the past, you run into the problem that we have had different calenders. To give an idea of how difficult the problem is, watch: https://www.youtube.com/watch?v=-5wpm-gesOY

One way to validate the date is to use the C library's date and time conversion routines. After scanning the day, month and year, create a struct tm from that, then convert it to seconds since epoch using mktime(). This might still accept invalid dates, but if you convert that back to a struct tm, you can check whether that conversion matched the original input:

int day, month, year,
sscanf(date, "%2d.%2d.%4d", &amp;day, &amp;month, &amp;year);

struct tm input = {
    .tm_mday = day,
    .tm_mon = month - 1,
    .tm_year = year - 1900,
};

time_t t = mktime(&amp;input); /* note, this might modify input */
struct tm *output = localtime(&amp;t); /* prefer localtime_r() on systems that support it */

if (day != output-&gt;tm_mday || month != output-&gt;tm_mon + 1|| year != output-&gt;tm_year + 1900)
    return false;


These routines will probably still not handle dates hundreds of years in the past correctly, but it should suffice for recent dates.