Leap Year in C: Gregorian Rule and Five Programs
A leap year in C can be checked with the Gregorian rule: a year is leap if it is divisible by 400, or divisible by 4 but not by 100. Thus 2000 is a leap year, but 1900 and 2100 are not. A leap year has 366 days, with February 29 as the extra date.

Logical-operator program
The program accepts one decimal year from 1 through 9999, with optional surrounding whitespace and leading zeros. It rejects year 0, negative years, signs, decimal fractions and extra tokens. It applies the Gregorian rule throughout that range (the proleptic Gregorian calendar); it does not claim every country actually used that calendar in earlier centuries. Historical dates may require the local calendar and adoption date.
#include <stdio.h>
#include <ctype.h>
/* One unsigned decimal token, surrounding whitespace, then EOF. */
static int read_bounded(unsigned int *out, unsigned int limit) {
int ch;
unsigned int value = 0;
do { ch = getchar(); } while (ch != EOF && isspace(ch));
if (ch < '0' || ch > '9') return 0;
do {
unsigned int digit = (unsigned int)(ch - '0');
if (value > limit / 10 ||
(value == limit / 10 && digit > limit % 10)) return 0;
value = value * 10 + digit;
ch = getchar();
} while (ch >= '0' && ch <= '9');
while (ch != EOF && isspace(ch)) ch = getchar();
if (ch != EOF) return 0;
*out = value;
return 1;
}
int main(void) {
unsigned int year;
if (!read_bounded(&year, 9999) || year == 0) {
fputs("Enter one year from 1 to 9999.\n", stderr);
return 1;
}
int leap = year % 400 == 0 ||
(year % 4 == 0 && year % 100 != 0);
printf("%u: %s\n", year, leap ? "leap year" : "common year");
return 0;
}
Run logical leap-year check online
Sample input
2000
Expected output
Reference output for the displayed program, not a live execution result.
2000: leap year
The remainder operator % tests divisibility. The parentheses make the exception clear: divisible by 100 cancels the every-four-years rule, unless divisible by 400 restores it. The parser checks the whole batch input through EOF and bounds the value before arithmetic. If you type input in a terminal, finish stdin with your terminal's EOF mechanism or pipe the input; the online runner sends a completed stdin value.
Nested if statements
This version follows the decision tree explicitly. First ask whether the year is divisible by 4. If not, it is common. Otherwise check the century exception, then the 400-year exception to that exception.
#include <stdio.h>
#include <ctype.h>
/* One unsigned decimal token, surrounding whitespace, then EOF. */
static int read_bounded(unsigned int *out, unsigned int limit) {
int ch;
unsigned int value = 0;
do { ch = getchar(); } while (ch != EOF && isspace(ch));
if (ch < '0' || ch > '9') return 0;
do {
unsigned int digit = (unsigned int)(ch - '0');
if (value > limit / 10 ||
(value == limit / 10 && digit > limit % 10)) return 0;
value = value * 10 + digit;
ch = getchar();
} while (ch >= '0' && ch <= '9');
while (ch != EOF && isspace(ch)) ch = getchar();
if (ch != EOF) return 0;
*out = value;
return 1;
}
int main(void) {
unsigned int year;
if (!read_bounded(&year, 9999) || year == 0) {
fputs("Enter one year from 1 to 9999.\n", stderr);
return 1;
}
int leap;
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) leap = 1;
else leap = 0;
} else leap = 1;
} else leap = 0;
printf("%u: %s\n", year, leap ? "leap year" : "common year");
return 0;
}
Run nested leap-year check online
Sample input
2000
Expected output
Reference output for the displayed program, not a live execution result.
2000: leap year
Conditional-operator version
The conditional operator is right-associative. This expression tests 400 first, then 100, then 4. Compact syntax is not automatically clearer; prefer the logical or nested version if a reader is still learning operator precedence.
#include <stdio.h>
#include <ctype.h>
/* One unsigned decimal token, surrounding whitespace, then EOF. */
static int read_bounded(unsigned int *out, unsigned int limit) {
int ch;
unsigned int value = 0;
do { ch = getchar(); } while (ch != EOF && isspace(ch));
if (ch < '0' || ch > '9') return 0;
do {
unsigned int digit = (unsigned int)(ch - '0');
if (value > limit / 10 ||
(value == limit / 10 && digit > limit % 10)) return 0;
value = value * 10 + digit;
ch = getchar();
} while (ch >= '0' && ch <= '9');
while (ch != EOF && isspace(ch)) ch = getchar();
if (ch != EOF) return 0;
*out = value;
return 1;
}
int main(void) {
unsigned int year;
if (!read_bounded(&year, 9999) || year == 0) {
fputs("Enter one year from 1 to 9999.\n", stderr);
return 1;
}
int leap = year % 400 == 0 ? 1 :
year % 100 == 0 ? 0 : year % 4 == 0;
printf("%u: %s\n", year, leap ? "leap year" : "common year");
return 0;
}
Run conditional leap-year check online
Sample input
2000
Expected output
Reference output for the displayed program, not a live execution result.
2000: leap year
Reusable leap-year function
Keep the calendar rule in one function when using it in more than one place. The function's contract is a validated year from 1 through 9999; the caller below enforces that contract. A nonzero return means leap.
#include <stdio.h>
#include <ctype.h>
/* One unsigned decimal token, surrounding whitespace, then EOF. */
static int read_bounded(unsigned int *out, unsigned int limit) {
int ch;
unsigned int value = 0;
do { ch = getchar(); } while (ch != EOF && isspace(ch));
if (ch < '0' || ch > '9') return 0;
do {
unsigned int digit = (unsigned int)(ch - '0');
if (value > limit / 10 ||
(value == limit / 10 && digit > limit % 10)) return 0;
value = value * 10 + digit;
ch = getchar();
} while (ch >= '0' && ch <= '9');
while (ch != EOF && isspace(ch)) ch = getchar();
if (ch != EOF) return 0;
*out = value;
return 1;
}
int is_leap(unsigned int year) {
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
int main(void) {
unsigned int year;
if (!read_bounded(&year, 9999) || year == 0) {
fputs("Enter one year from 1 to 9999.\n", stderr);
return 1;
}
int leap = is_leap(year);
printf("%u: %s\n", year, leap ? "leap year" : "common year");
return 0;
}
Sample input
2000
Expected output
Reference output for the displayed program, not a live execution result.
2000: leap year
Print leap years in a bounded range
Edit first and last in this no-stdin example. Both endpoints are included. The range must contain at most 400 years and remain inside 1–9999. These bounds limit output and prevent the loop counter from wrapping. Check endpoint order before subtracting unsigned values. A range with no qualifying years prints an explicit message.
#include <stdio.h>
int is_leap(unsigned int year) {
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
int main(void) {
unsigned int first = 1896, last = 1904;
if (first < 1 || last > 9999 || first > last || last - first > 399) {
fputs("Use an ordered range of at most 400 years within 1..9999.\n", stderr);
return 1;
}
int found = 0;
for (unsigned int year = first; year <= last; ++year) {
if (is_leap(year)) {
printf("%s%u", found ? " " : "", year);
found = 1;
}
}
if (!found) printf("No leap years");
putchar('\n');
return 0;
}
Run leap years in a range online
Expected output
Reference output for the displayed program, not a live execution result.
1896 1904
Century edge cases and trace
| Year | Divisible by 4? | By 100? | By 400? | Result |
|---|---|---|---|---|
| 1896 | Yes | No | No | Leap |
| 1900 | Yes | Yes | No | Common |
| 2000 | Yes | Yes | Yes | Leap |
| 2024 | Yes | No | No | Leap |
| 2025 | No | No | No | Common |
| 2100 | Yes | Yes | No | Common |
| 2400 | Yes | Yes | Yes | Leap |
For 1900 the first alternative (% 400 == 0) is false. The second alternative starts true (% 4 == 0), but its % 100 != 0 part is false. The complete expression is false. For 2000 the first alternative is already true, so || short-circuits; the result is leap.
The Gregorian cycle has 97 leap years per 400 years: 100 multiples of four, minus four century years, plus the century divisible by 400. This gives an average calendar year of 365.2425 days. The rule approximates the seasonal year; “the Earth takes exactly 365.25 days” is not an accurate explanation.
Complexity and common mistakes
Each single-year check performs a constant number of operations: O(1) time and O(1) auxiliary space. Testing a range of k years is O(k) time and O(1) extra space, excluding the output.
- Testing only
% 4misclassifies 1900 and 2100. - Rejecting every century year misclassifies 2000 and 2400.
- Writing
year % 4 = 0confuses assignment with comparison and is invalid here. - A leap year test is not a complete date validator: the month, day, leap-day rule and calendar policy still matter.
- C's time/date library and operating-system timestamp limits are separate from this arithmetic example; this program does not convert years to timestamps.
Practice and related lessons
Try 1, 4, 100, 400 and 9999: common, leap, common, leap and common. Change the range to 2001–2003: it should print No leap years. For 2000–2399, count 97 leap years. Test 0, -2000, 10000 and 2000 2004: all should fail input validation.
See program structure, functions, and factorial for another bounded-input arithmetic program. Open a run link to load the exact code and input into the online C compiler.
Frequently asked questions
Is every fourth year a leap year?
Not in the Gregorian calendar: century years must also be divisible by 400. The simpler every-four-years rule belongs to the Julian calendar, not this program's policy.
Can this program handle BCE dates or year zero?
No. Its documented range is 1–9999 with no year zero. Astronomical year numbering and historical calendar conversion require a different policy and implementation.