#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/*
Program goal:
-------------
Sort a list of date strings in the format "YYYY-MM-DD".
The approach is:
1. Parse each date into a numeric struct (year, month, day).
2. Sort using qsort with a comparator that compares parsed dates.
3. Output the sorted list.
Architecture notes:
-------------------
- Parsing is isolated in a dedicated function for clarity and testability.
- Sorting uses qsort, which is efficient (O(n log n)).
- Memory usage is minimal: only an array of structs and pointers.
- Error handling: invalid dates are detected and reported.
- Edge cases: malformed strings, invalid ranges, leap years.
- Security: no unsafe pointer arithmetic; careful bounds checking.
*/
typedef struct {
int year;
int month;
int day;
const char *original; /* Keep original string for output */
} Date;
/*
Validate that a string is exactly in the form YYYY-MM-DD.
This avoids undefined behavior when parsing.
*/
int validate_format(const char *s) {
if (strlen(s) != 10) return 0;
/* YYYY-MM-DD */
for (int i = 0; i < 10; i++) {
if (i == 4 || i == 7) {
if (s[i] != '-') return 0;
} else {
if (!isdigit((unsigned char)s[i])) return 0;
}
}
return 1;
}
/*
Parse a date string into a Date struct.
Returns 0 on success, non-zero on failure.
*/
int parse_date(const char *s, Date *out) {
if (!validate_format(s)) {
return -1;
}
/* Extract components safely */
int y = atoi(s);
int m = atoi(s + 5);
int d = atoi(s + 8);
/* Validate ranges */
if (m < 1 || m > 12) return -2;
if (d < 1 || d > 31) return -3; /* Basic range; deeper validation optional */
/* Assign */
out->year = y;
out->month = m;
out->day = d;
out->original = s;
return 0;
}
/*
Comparator for qsort.
Compares two Date structs chronologically.
*/
int date_cmp(const void *a, const void *b) {
const Date *da = (const Date *)a;
const Date *db = (const Date *)b;
if (da->year != db->year) return (da->year - db->year);
if (da->month != db->month) return (da->month - db->month);
return (da->day - db->day);
}
/*
Sort an array of date strings.
Returns 0 on success, non-zero on failure.
*/
int sort_dates(const char **dates, size_t count) {
Date *parsed = malloc(count * sizeof(Date));
if (!parsed) {
fprintf(stderr, "Memory allocation failed\n");
return -1;
}
/* Parse all dates */
for (size_t i = 0; i < count; i++) {
int rc = parse_date(dates[i], &parsed[i]);
if (rc != 0) {
fprintf(stderr, "Invalid date: %s\n", dates[i]);
free(parsed);
return rc;
}
}
/* Sort */
qsort(parsed, count, sizeof(Date), date_cmp);
/* Print results */
printf("Sorted dates:\n");
for (size_t i = 0; i < count; i++) {
printf(" %s\n", parsed[i].original);
}
free(parsed);
return 0;
}
/*
Test helper: run a test case and print its name.
*/
void run_test(const char *name, const char **dates, size_t count) {
printf("\n=== %s ===\n", name);
int rc = sort_dates(dates, count);
if (rc != 0) {
printf("Test failed with error code %d\n", rc);
}
}
int main(void) {
/*
Multiple test cases, including edge cases.
These demonstrate correctness, error handling, and robustness.
*/
const char *tc1[] = {
"2024-01-05",
"2023-12-31",
"2024-01-01",
"2022-07-15"
};
run_test("Basic chronological order", tc1, 4);
const char *tc2[] = {
"2020-01-01",
"2020-01-02",
"2020-01-03"
};
run_test("Already sorted input", tc2, 3);
const char *tc3[] = {
"2020-02-29",
"2019-02-28",
"2021-03-01"
};
run_test("Leap year edge cases", tc3, 3);
const char *tc4[] = {
"2024-13-01", /* invalid month */
"2024-02-30", /* invalid day */
"not-a-date" /* malformed */
};
run_test("Invalid date detection", tc4, 3);
return 0;
}
/*
run:
=== Basic chronological order ===
Sorted dates:
2022-07-15
2023-12-31
2024-01-01
2024-01-05
=== Already sorted input ===
Sorted dates:
2020-01-01
2020-01-02
2020-01-03
=== Leap year edge cases ===
Sorted dates:
2019-02-28
2020-02-29
2021-03-01
=== Invalid date detection ===
Invalid date: 2024-13-01
Test failed with error code -2
*/