user17277981
user17277981

Reputation:

How do you check if date is in correct format

For example in input.txt file there is date 20210405. Date is in rrrrmmdd format without any '/' or '.'. How do you check if date is in correct format not in 20211405?. My code works only for numbers that dont have zero in day for example 05,07,08, my code works only for 10 or 11. How do I fix that?

int main() {
  char load[50];
  long year, day, month;
  int lenght, lenght2, lenght3;
  int i = 0;
  FILE *file;
  file = fopen("input.txt", "r");

  while (fgets(load, sizeof load, file) != NULL) {
    if (i == 0) {
      if (strlen(load) == 8) {
        year = strtol(load, NULL, 10);
        month = year;
        day = year;
        year = year * 0.0001;
        lenght = (log10(abs(year))) + 1;

        if (lenght == 4) {
          day = day % 100;
          lenght2 = (log10(abs(day))) + 1;

          if (lenght2 == 2 && day <=31) {
            month = (month % 10000) - 30;
            month = month / 100;
            lenght3 = (log10(abs(day))) + 1;

            if (month <= 12 && lenght2 == 2) {
              printf("Datum: %s", load);
            } else {
              printf("Invalid input.");
            }
          } else {
            printf("Invalid input.");
          }
        } else {
          printf("Invalid input.");
        }
      } else {
            printf("Invalid input.");
      }
    }
  }
}

Upvotes: 1

Views: 201

Answers (2)

chqrlie
chqrlie

Reputation: 144750

It is a bad idea to use floating point arithmetics to split the value into the year, month and day parts. The main reason for this is floating point numbers are represented internally in base 2 so 0.0001 is not represented exactly, so converting the multiplication result to long may cause truncation of the value.

You should instead use integer arithmetics this way:

  long value = strtol(load, NULL, 10);
  year = value / 10000;
  month = (value / 100) % 100;
  day = value % 100;

You could also use sscanf() to parse the line directly into 3 int values:

#include <ctype.h>
#include <stdio.h>
#include <string.h>

int main() {
    char load[50];
    int year, day, month;
    int i = 0;
    FILE *file = fopen("input.txt", "r");

    if (file == NULL) {
        fprintf(stderr, "cannot open input.txt: %s\n", strerror(errno));
        return 1;
    }
    while (fgets(load, sizeof load, file) != NULL) {
        if (i++ == 0) {
            static const int mlen[] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
            char c[2];

            /* accept up to 4 bytes for year, 2 for month, 2 for day and check for a trailing newline */
            if (sscanf(load, "%4d%2d%2d%1[\n]", &year, &month, &day, c) != 4) {
                fprintf(stderr, "invalid date line: %s", load);
                break;
            }
            if (year < 1901 || year > 2099
            ||  month < 1 || month > 12
            ||  day < 1 || day > mlen[month]
            ||  (day == 29 && month == 2 && year % 4 != 0)) {
                fprintf(stderr, "invalid date: %d/%d/%d\n", day, month, year);
                break;
            }
            printf("Datum: %s", load);
        } else {
            /* handle other lines */
            ...
        }
    }
    fclose(file);
    return 0;
}

Upvotes: 0

Gerhardh
Gerhardh

Reputation: 12404

You have a few flaws in your code:

  1. fgets includes a \n at the end of your string if it is found in the file. Unless you read the last line where no more \n is present, you will get 9 characters in your buffer.
    Your condition if (strlen(load) == 8) will fail in the other cases.

  2. You should not use floating point operations on integers.
    year = year * 0.0001; In best case it is identical to year = year / 10000;, in worst case you get some rounding errors.

  3. You check the "length" of the value for day and month by taking log10. That means that the numerical value must be >10 to get a length of 2. That is exactly what you are complaining about.
    A proper check for valid valued would be to check whether the numerical value is in proper range.

  4. For some reason to reduce month by 30. That doesn't really make much sense.

  5. You don't use variable i at all. The corresponding if (i==0) is rather useless.

A fixed version could look like this:

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

int main(void) {
  char load[50];
  long year, day, month;
  bool valid = false;

  FILE* file = fopen("input.txt", "r");
  // TODO: Check for NULL

  while (fgets(load, sizeof load, file) != NULL) {

    // Remove trailing '\n' 
    int len = strlen(load);
    if (load[len-1] == '\n') {
      load[len-1] = 0;
    }

    // Do we have exactly 8 digits?
    if (strlen(load) == 8) {

      long value =  strtol(load, NULL, 10);
      year = value / 10000;
      month = (value / 100) % 100;
      day = value % 100;

      if (year >= 1000) {
          if (day > 0 && day <= 31) {
            if (month > 0 && month <= 12) {
              valid = true;
            }
          }
        }
      }

      if (valid) {
        printf("Datum: %s", load);
      }
      else {
        printf("Invalid input.");
      }
    }
  }
}

Upvotes: 1

Related Questions