-2

According to the code in my textbook, and code I've found online for the same thing, checking for leap year in Java looks like this:

if (month == 2 && day == 29 && !(year % 400 == 0 || 
       (year % 4 == 0 && year % 100 != 0))) {
     // don't do stuff because it's not leap year
  }

My question is: Why can't you just write it like so?

if (month == 2 && day == 29 && !(year % 4 == 0)) {
    // don't do stuff because it's not leap year
}

Leap year is divisible by 4. So no matter how you cut it if (year % 4 == 0) is not true, then your year is not a leap year and the statement is false. The extra code in there seems redundant. Why is my textbook telling me to write it that way? Is there some kind of convention with Java I'm not aware of?

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
Jake Doe
  • 35
  • 5
  • 7
    `Leap year is divisible by 4` it is not true, https://en.wikipedia.org/wiki/Leap_year – Iłya Bursov Oct 30 '17 at 02:19
  • 3
    *These extra days occur in years which are multiples of four (with the exception of years divisible by 100 but not by 400)* – Scary Wombat Oct 30 '17 at 02:20
  • because `!(year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))` is not equivalent to `!(year % 4 == 0)` if you'll take `year = 100` it will return `true` in the first expression and `false` in the second. – Nir Alfasi Oct 30 '17 at 02:20
  • I think you misread this: `year % 100 != 0` – shmosel Oct 30 '17 at 02:22
  • 5
    It's not "some kind of convention with **Java**" you're not aware of, it's a convention with the **Gregorian calendar**. Century years (those divisible by 100), even though divisible by 4, are _not_ leap years unless they are divisible by 400. So, 1796 and 1804: leap years, 1800 not; 1896 and 1904: leap years, 1900 not; 1996, 2000, 2004: all leap years; 2096 and 2104: leap years, 2100 not. – Kevin Anderson Oct 30 '17 at 02:31
  • @Kevin Anderson That would be the answer I was looking for. I never knew that. You learn something new everyday – Jake Doe Oct 30 '17 at 02:43
  • 2
    Use the Java 8 Year class. `Year.isLeap(2000)` returns `true`. `Year.isLeap(1999)` returns `false`. `Year.isLeap(2100)` returns `false` because the Gregorian Calendar rules say it should. – Dawood ibn Kareem Oct 30 '17 at 02:43
  • 2
    To the nitwits who are down-voting the Q and the A's, this is a valid question. Most people were **taught** that a leap year is any year that is divisible by 4. People who know about this things know this is untrue. But I bet that your 10 year old or your 60 year old father doesn't. Most folks don't even know what "Gregorian" means. – Stephen C Oct 30 '17 at 03:07
  • And note that the Q is actually asking about some code that the OP found, not the best way to do leap day checks in production code. – Stephen C Oct 30 '17 at 03:08
  • And note that "Every leap year is divisible by 4" is actually true. It's just that you cannot conclude the reverse ("every year divisible by 4 is a leap year"), which is the logic error that the OP made. – Erwin Bolwidt Oct 30 '17 at 04:00
  • There’s [a class for that](https://docs.oracle.com/javase/10/docs/api/java/time/Year.html), as Kareem commented. Here's class doc link: [`java.time.Year.isLeap( 2018 )`](https://docs.oracle.com/javase/10/docs/api/java/time/Year.html#isLeap(long)) --> `boolean` – Basil Bourque Apr 10 '18 at 03:59

3 Answers3

3

My question is. Why can't you just write it like so?

if (month == 2 && day == 29 && !(year % 4 == 0)) {
   // don't do stuff because it's not leap year
}

Well ... you could, but it would not give the correct answer for the years 1900, 2100, etcetera1. The more complicated version is correct.

Basically, the rules for deciding when a given year is a leap year in the Gregorian calendar are more complicated than you learned in primary school. If you want more details:

Note also that the Gregorian calendar is less than perfect, as described here. However, it is so ingrained in conventional practice that the various calendrical reform proposals for "fixing" the perceived problems don't stand much chance. (At least, not while humanity is earth-bound :-) )


I'm just trying to figure out why the text is providing an if statement with more conditions than it actually needs ...

Because, the conditions are actually needed. See above!


From a Java software engineering perspective, a better idea would be to use the Java 8 java.time APIs to test for leap years rather than coding it yourself (and possibly getting it wrong!).


1 - You might say: "my code doesn't need to work in 2100, 'cos I will be dead before then". There is a simple two word retort to that. "Millenium bug".

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

The first piece of code in your question is correct, as (according to Wikipedia):

In the Gregorian calendar, each leap year has 366 days instead of the usual 365, by extending February to 29 days rather than the common 28. These extra days occur in years which are multiples of four (with the exception of years divisible by 100 but not by 400).

Although the way I would check for a leap year, using Java 8's java.time package, is like this:

boolean isLeapYear = Year.now().isLeap();

If you want to check if it's 29th February, you can use

boolean is29Feb = MonthDay.now().getDayOfMonth() == 29 && YearMonth.now().getMonth() == Month.FEBRUARY;
bcsb1001
  • 2,834
  • 3
  • 24
  • 35
0

I prefer if you can use the library, it removes the error checks.

public static boolean isLeapYear(int year) {
  Calendar cal = Calendar.getInstance();
  cal.set(Calendar.YEAR, year);
  return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}

This is pretty standard code, you can get anywhere.

Tushar Gupta
  • 15,504
  • 1
  • 29
  • 47
  • Nitpick: I think the OP is actually checking for leap day. – Nathan Hughes Oct 30 '17 at 02:27
  • I am quite sure by reading the post that it is for leap year. What makes u say for leap day? And its only feb 29 right ? – Tushar Gupta Oct 30 '17 at 02:29
  • @NathanHughes Title seems to indicate otherwise ? – Scary Wombat Oct 30 '17 at 02:30
  • 1
    @Scary: the posted code checks for a specific day. I think the OP’s terminology is less than rigorous. And for that matter why give old-n-busted calendar api when there’s jdk8 new hotness? – Nathan Hughes Oct 30 '17 at 02:33
  • It's kind of checking for both. If you provide the app with the date Feb 29th and a year. It checks to make sure that the year is actually a leap year, otherwise it throws an IllegalArgumentException – Jake Doe Oct 30 '17 at 02:34
  • @NathanHughes Valid point – Scary Wombat Oct 30 '17 at 02:36
  • It's not rigorous at all, I admit. Like other's have pointed out, there are already established methods for checking a date for leap year. I'm just trying to figure out why the text is providing an if statement with more conditions than it actually needs – Jake Doe Oct 30 '17 at 02:37