• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Bear Bibeault
  • Junilu Lacar
  • Martin Vashko
Sheriffs:
  • Jeanne Boyarsky
  • Tim Cooke
  • Knute Snortum
Saloon Keepers:
  • Ron McLeod
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
Bartenders:
  • Scott Selikoff
  • salvin francis
  • Piet Souris

Finding the next leap year

 
Ranch Hand
Posts: 62
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm writing a simple program involving Java's Date and Time classes. I'm trying to calculate and output the next leap year, but I'm having trouble converting integers to components of a LocalDate object:

Line 14 proves that it is possible to convert an integer to a component of a LocalDate object. We now need to fix the errors on lines 18 and 31:

Line 18 and 31 error: "Cannot invoke isLeapYear() on the primitive type int".

Any bright ideas?
 
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have lots of bright ideas, but bright ideas isn't what you want. You need not to try calling methods on primitives because primitives don't have methods. Please tell us where that method comes from and what object it is called on and what parameters it takes.
Don't try to squeeze such long lines onto people's screens.
 
Campbell Ritchie
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Sam Peterson wrote:. . . Line 14 proves that it is possible to convert an integer to a component of a LocalDate object. . . .

No, it doesn't. That isn't what line 14 does at all.
 
Sam Peterson
Ranch Hand
Posts: 62
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:I have lots of bright ideas, but bright ideas isn't what you want. You need not to try calling methods on primitives because primitives don't have methods.


So we need to convert year back from integer to a LocalDate datatype in order for the if statement on line 39 (in the updated code below) to work? If so, how do we do that?

Campbell Ritchie wrote:Please tell us where that method comes from and what object it is called on and what parameters it takes.


I did some output tests (see the comments in my updated code below):

Line 8 creates the LocalDate object. Lines 9 and 16 modify some of the objects integer parameters. My method-call-output-test on line 21 shows that we successfully passed in the same LocalDate object. So I presume that means we're also dealing with the same LocalDate object when we call the findNextLeapYear method on line 28.

Campbell Ritchie wrote:

Sam Peterson wrote:. . . Line 14 proves that it is possible to convert an integer to a component of a LocalDate object. . . .

No, it doesn't. That isn't what line 14 does at all.


Can you elaborate please? All I see is, "Create a new variable of type integer with a name of year, and assign it to the year integer parameter in the LocalDate date object." What am I supposed to see instead?
 
Campbell Ritchie
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Sam Peterson wrote:. . . convert year back from integer to a LocalDate datatype . . .

No. Why have you got year in the first place? Why didn't you call the method on the date object?

. . . I did some output tests (see the comments in my updated code below): . . .

But that code won't compile.

. . . "Create a new variable of type integer with a name of year, and assign it to the year integer parameter in the LocalDate date object." What am I supposed to see instead?

That is the other way round from what you said originally. Don't say, “integer,” which isn't a datatype. Don't say, “parameter,” because you don't have any parameters visible in line 14. You might call it a field, but line 14 doesn't actually show that LocalDate has a year field as an int, only that there is a method returning an int representing the year.
What you are doing is creating an int local variable called year and then finding you can't call the isLeapYear() method on it.
 
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In case you wanted an easier way, java.time.Year has an isLeap() method.
 
Sam Peterson
Ranch Hand
Posts: 62
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Why have you got year in the first place? Why didn't you call the method on the date object?


I guess I forgot to try date.plusYears(1); in the else statement of the while loop. However, I have a new problem in the updated code below:

I get an infinite loop that outputs:
2017 is not a leap year.

What's wrong?
 
Campbell Ritchie
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Haven't looked at the code, but I am going to do some guessing.
LocalDate is immutable. We see the same problem with Strings and other immutable classes. If you simply write myDate.plusYears(1): that returns a new LocalDate object which you would have to assign to something. If you don't use the new object, you will get the old object, which is unchanged, and your loop will start from where it started last time. So you will move into the realms of infinite loops 
 
Campbell Ritchie
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
By the way: don't write == false or == true or similar expressions with !=. Not only are they poor style, but they are also error‑prone because every now and again somebody writes = and now has two errors for the price of one.
Never while (b == false) ... nor while (b != true) ...
Always while (!b) ...
Never while (b == true) ... nor while (b != false) ...
Always while (b) ...
 
Sam Peterson
Ranch Hand
Posts: 62
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Haven't looked at the code, but I am going to do some guessing.
LocalDate is immutable. We see the same problem with Strings and other immutable classes. If you simply write myDate.plusYears(1): that returns a new LocalDate object which you would have to assign to something. If you don't use the new object, you will get the old object, which is unchanged, and your loop will start from where it started last time. So you will move into the realms of infinite loops 


That does make sense for something like this:

because t is separate from dt.

But not here:

With the output being:
2014-03-01
2017-03-01
2017-03-01 does not take place during a leap year.
Starting with 2017-03-01 from findNextLeapYear method.
Again, at the start of the findNextLeapYear method, the date object is currently = 2017-03-01

In the same method that the date object was created in, the object is currently = 2017-03-01


With the consistency of 2017-03-01, it seems to me that we're passing around the correct date object (not integer like I was trying to do before, but an actual LocalDate object).
And yet somehow, the date.plusYears(1); method call on line 52 is still failing and allowing an infinite loop that outputs 2017 is not a leap year.

Any bright ideas?
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Your logic is very convoluted. If you spent a couple of minutes trying to think of the simplest and shortest way to say this in plain English, you might find a more straightforward solution. I came up with 6 lines of code for getNextLeapYear.

Here's how I'd explain it in plain English:

Given a year Y,
If Y is a leap year, display a message saying "Y is a leap year" and exit
Otherwise, keep adding 1 to Y until you get YL, a leap year. Then display a message saying "Y is not a leap year. The next leap year after Y is YL."

Including the display statements, the program to do the above would be around 20 lines of code in all, including class and method headers, import statements, and closing braces.
 
Campbell Ritchie
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Sam Peterson wrote:. . .  That does make sense for something like this: . . . But not here: . . .

What on earth does that mean? I find you were doing exactly what I thought you were doing, and you go and say it doesn't make sense.
 
Bartender
Posts: 3668
151
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
An old fashioned way is, given a year t, calculate directly the next multiple of four, and test if that is a leap year.
 
Sam Peterson
Ranch Hand
Posts: 62
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:What on earth does that mean? I find you were doing exactly what I thought you were doing, and you go and say it doesn't make sense.


In particular, I was talking about this portion of your quote:

Campbell Ritchie wrote:If you don't use the new object, you will get the old object, which is unchanged, and your loop will start from where it started last time.


Take another look at the code sample:

t is an older object than dt, so it makes perfect sense that t's change doesn't alter dt.

For my other code though, we use the same variable name, date, which is also the same LocalDate object throughout the program, so it makes no sense in that case that the date.plusYears(1); on line 52 doesn't do anything:

Junilu Lacar wrote:I came up with 6 lines of code for getNextLeapYear.


In what way do your 6 lines of code differ from the logic of this:

And how does your version avoid the infinite while loop problem? If you know a better way, I'm very interested.
 
Campbell Ritchie
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Kindly don't change old posts after they have been replied to.
 
Sheriff
Posts: 6448
172
Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Sam Peterson: please don't edit old posts that have been replied to.  For one thing, these edits rarely get seen, and for another, it can make the posts below seem out of place.  Make a new post with your edits.  I have reverted your recent edits (around 1:09).
 
Sam Peterson
Ranch Hand
Posts: 62
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Kindly don't change old posts after they have been replied to.


I meant to copy and paste a quote, but I forgot to go paste it in my latest post. I apologize.
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The six lines I wrote actually work whereas yours doesn't. That's the main difference. The problem with the code you showed is that LocalDate is an immutable object whereas the call to plusYears() indicates you forgot that. This is your bug that causes the infinite loop. Read the API documentation for that method again and really understand what it does.

https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/time/LocalDate.html
 
Campbell Ritchie
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Sam Peterson wrote:. . . Take another look at the code sample:

t is an older object than dt, so it makes perfect sense that t's change doesn't alter dt.

No, dt is another immutable object; it won't change its state regardless.

. . . it makes no sense in that case that the date.plusYears(1); on line 52 doesn't do anything: . . .

Last time I saw it, that was line 39, which does do something. I have already told you what. I also told you how to correct your error. Kindly read the documentation for the loacl date class, as well as the date time section in the Java™ Tutorials.
Line 52 is commented out. Always use multiple // comments for commenting out, never /* comments */.
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Also, the method you wrote requires a Boolean to be passed in when it's not necessary. It's cruft that can be eliminated.
 
Sam Peterson
Ranch Hand
Posts: 62
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:The problem with the code you showed is that LocalDate is an immutable object whereas the call to plusYears() indicates you forgot that. This is your bug that causes the infinite loop. Read the API documentation for that method again and really understand what it does.


I don't suppose you would be willing to tell me what you used instead of the plusYears() method call?

Also, if my LocalDate date object is immutable, then why did this work:
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Sam Peterson wrote:
Also, if my LocalDate date object is immutable, then why did this work:


It's staring you right in the face.

Find the ONE difference between what you're doing on lines 2 and 5 above versus what you're doing on line 7 below (or rather, what you're NOT doing on line 7).

Don't smack yourself too hard on the forehead when you figure out what the difference is.
 
Sam Peterson
Ranch Hand
Posts: 62
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:Find the ONE difference between what you're doing on lines 2 and 5 above versus what you're doing on line 7 below (or rather, what you're NOT doing on line 7).


Bro, forgive me, but what's obvious to you is not obvious to me. All my tiny mind could speculate about that was this:

And all that accomplished was another infinite loop that prints out:
We are at 2017, which is not a leap year. Incrementing the year again.
I feel like I've shot, blown up, drowned, and set my enemies on fire and they still won't die.

Junilu Lacar wrote:
It's staring you right in the face.


What is staring at me right in the face? The fact that it works there, but decides not to work in the while loop of the findNextLeapYear method makes zero sense. Why does that not make sense to me? Well,
the documentation says "Returns a copy of this LocalDate with the specified number of years added...This instance is immutable and unaffected by this method call. " I have no compiler errors that claim "date cannot be resolved to a variable", so I can only presume scope isn't a problem, and I therefore still don't understand why a copy of my LocalDate object cannot be made in the while loop the same way it was in the main method. 2017 still needs to be incremented 3 times to become a leap year, so calling the plusYears method only once instead of using a while loop will not get the job done. I'm very sorry, but that's all I can figure out.
 
Sheriff
Posts: 24753
59
Eclipse IDE Firefox Browser MySQL Database
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You don't think that



and



are different in some significant way?

Well, clearly they are different and not just because they use different numbers of years. But I suspect you don't recognize that difference as "significant" because you have the common beginner confusion between variables and objects.

When the documentation says that a LocalDate object is immutable, that means that the state of the object -- the information contained in the object -- cannot be changed. So "date.plusYears(1)" does not change the state of the object referred to by the "date" variable. But as the documentation says, the result of calling that method returns a (reference to a) new LocalDate object whose state includes the changes to the state.

Now, notice that the code which is working nicely takes that reference and assigns it to your "date" variable. After that happens, "date" is referring to a different object, one which is a modified version of the original object.

But the code which isn't working nicely does nothing with that reference. And the "date" variable continues to refer to the original object, which of course hasn't been modified because it's an immutable object.


 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Sam Peterson wrote:All my tiny mind could speculate about that was this:


Don't sell yourself short. If you just replace lines 3-4 with the statement from your other example that actually works, you'd have a great implementation that's exactly 6 lines long.
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
LocalDate is immutable the same way a String is immutable.

If you do this:

you're going to see "foo" not "foobar"

Similarly, with this

you still get Jan 1, 2019, not Jan 1, 2020
 
Saloon Keeper
Posts: 21311
140
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:LocalDate is immutable the same way a String is immutable.

If you do this:

you're going to "foo" not "foobar"

Similarly, with this

you still get Jan 1, 2019, not Jan 1, 2020



On the other hand:


you're going to "foobar"

Similarly, with this


See the difference?
 
Sam Peterson
Ranch Hand
Posts: 62
1
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Paul Clapham wrote:You don't think that



and



are different in some significant way?


Junilu Lacar wrote:Don't smack yourself too hard on the forehead when you figure out what the difference is.


You win. This has given me a giant leap in the correct conceptual direction:
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Sam Peterson wrote:You win.


No, you win.

We're just relieved.    
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here's another way we found to solve it (credit to bartender Piet for the idea):
 
Greenhorn
Posts: 4
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:Here's another way we found to solve it (credit to bartender Piet for the idea):



The idea is good but I think we should stick to APIs otherwise  going to modulos and formulas is reinventing the wheeland error prone.
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

kiros haregewoine wrote:The idea is good but I think we should stick to APIs otherwise  going to modulos and formulas is reinventing the wheeland error prone.

How is it reinventing the wheel? There's no method already available to do this so what exactly is being reinvented with that? And calculating rather than brute force checking with a loop is not reinventing either.

As far as being error prone, that's what testing addresses. OP had way more errors coming up with his solution than we did coming up with that.
 
Saloon Keeper
Posts: 10873
234
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:And calculating rather than brute force checking with a loop is not reinventing either.


Although you're correct in this case, I do have to commend Kiros' knee-jerk reaction to performing arithmetic on dates and times. In general, that's a bad idea.
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:Although you're correct in this case, I do have to commend Kiros' knee-jerk reaction to performing arithmetic on dates and times. In general, that's a bad idea.


Agreed, in general it's a bad idea. Fiddling around with the math for calculating time differences, adding, subtracting, and most other things related to temporal values should be done through the API whenever possible. Those are the kind of things I would concur are attempts to reinvent the wheel and are most likely to be error prone.
 
Campbell Ritchie
Marshal
Posts: 66575
251
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
...but as we have seen before, there isn't a nextLeapYear() method or similar. So calculating the next leap year is one of the few things that have to be done “by hand”.
 
Junilu Lacar
Marshal
Posts: 14501
240
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Right. On the other hand, what if you had to calculate something like "four score and seven years ago," I suppose I'd rather stick to breaking it down into smaller steps and using API calls. It really depends on how complicated the calculations can get and the degree of variability you have to account for. With leap year, there are only three variations to account for: % 400 == 0, % 100 == 0, and % 4 == 0. That makes the math relatively straightforward.
 
I will suppress my every urge. But not this shameless plug:
Java file APIs (DOC, XLS, PDF, and many more)
https://products.aspose.com/total/java
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!