20

I have created a class with two fields that need to be dates, start_date and date_passed. I have been researching the best way in java to have dates in a YYYY MM DD format that allows for easy date subtraction, and the ability to "make-up" a date, say in the future for example.

Example of what I'd like it to do...

library.circulate_book("Chemistry", **start date here**) //actual date or random date
library.pass_book("Chemistry", **Date Passed here**) //random date such as 5 days after start date
int days_had = Date_Passed - start_date

So far, I've found plenty of ways to format dates using Calendars and Date classes, but have yet to find one that looks like it would work considering most dates end up as Strings. Any suggestions/small examples are greatly appreciated! Also, any links to examples would be awesome!

Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373
Bob
  • 1,344
  • 3
  • 29
  • 63

6 Answers6

32

tl;dr

To move from one date to another by adding/subtracting a number of days.

LocalDate.now(
    ZoneId.of( "Pacific/Auckland" ) 
)
.minusDays( 5 )

To calculate the number of days, months, and years elapsed between two dates.

ChronoUnit.DAYS.between( start , stop )

Parsing

First you must parse your string inputs into date-time objects. Then you work on preforming your business logic with those objects.

Stop thinking of date-time values as strings, that will drive you nuts. We work with date-time objects in our code; we exchange data with users or other apps using a String representation of that date-time object.

In Java 8 and later, use the java.time framework. See Tutorial.

You want only a date, without time-of-day, so we can use the LocalDate class.

That funky double-colon syntax is a method reference, a way to say what method should be called by other code.

String input = "2015 01 02";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "yyyy MM dd" );
LocalDate localDate = formatter.parse ( input , LocalDate :: from );

Current date

Determining today’s date requires a time zone. For any given moment, the date varies around the globe by zone.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate todayTunis = LocalDate.now( z ) ;

If you want the JVM’s current default time zone, call ZoneId.systemDefault.

Subtracting Dates

This has been addressed many times before on StackOveflow.com. For example, How to subtract X days from a date using Java calendar?. For details, see other Answers such as this one by me and this one by me for more details. Tip: "elapsed" is a key search word.

Use ChronoUnit.DAYS enum to calculate count of days elapsed.

LocalDate weekLater = localDate.plusDays ( 7 );
long daysElapsed = java.time.temporal.ChronoUnit.DAYS.between( todayTunis , weekLater ) ;

Dump to console.

System.out.println ( "localDate: " + localDate + " to " + weekLater + " in days: " + daysElapsed );

localDate: 2015-01-02 to 2015-01-09 in days: 7

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Excellent response, I'm looking through the LocalDate methods, and I may be reading it wrong, but is there a method that returns just the local date, for example `2015 04 11` for today? – Bob Nov 04 '15 at 22:41
  • Would `now()` be able to be subtracted from dates later in the program such as `LocalDate localDate = formatter.parse ( input , LocalDate :: from );` – Bob Nov 04 '15 at 22:47
  • @user38254 You still seem stuck on conflating date-time values with their String representations. To get "today", just call `LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) ;)`. The time zone is crucial in determining a date (think about it!). For a String, use the same formatter seen above: `String output = today.format( formatter );`. Search for other StackOveflow pages on this topic. – Basil Bourque Nov 04 '15 at 22:52
  • The problem with this answer is that `.getDays` won't get the absolute number of days between two dates, it will get you the days modulo the months/years. The `ChronoUnit` approach doesn't have this drawback. – Logister Aug 16 '21 at 18:47
  • @Logister You are correct, my code was flawed. Now corrected. Thanks for flagging that. – Basil Bourque Aug 16 '21 at 20:39
19

The best way to do this in Java-8 is not the flawed answer of Basil Bourque but this approach:

String startDate = "2016 01 02";
String passedDate = "2016 02 29";

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
LocalDate date1 = LocalDate.parse(startDate, formatter);
LocalDate date2 = LocalDate.parse(passedDate, formatter);

long elapsedDays = ChronoUnit.DAYS.between(date1, date2);
System.out.println(elapsedDays); // 58 (correct)

The suggested use of java.time.Period.getDays() is dangerous and often wrong as soon as the elapsed duration exceeds one month. The whole Period is P1M27D so this code effectively only queries the partial amount of elapsed days (there is also an elapsed month):

System.out.println(Period.between(date1, date2).getDays()); // 27 (WRONG!!!)

A satisfying solution using the classes java.util.Date, GregorianCalendar etc. is hard to find. You can use the answer of Tacktheritrix but have to be aware of the fact that the calculated count of elapsed days might differ due to the sub-day-parts of java.util.Date and is also not reliable because of ignoring day-light-saving switches (where the clock jumps by one hour in many parts of the world).

Side note: At least 8 external libraries offer good answers to your problem, too. But I think, your simple use-case does not justify the embedding of an extra library unless you are not yet on Java-8. Any alternative solution how to count the elapsed days between two dates would not be easier than in Java-8 - only similar. And since you accepted the Java-8-related answer of Basil Bourque, I assume that you are indeed on Java-8 so I leave out the answer how to solve your problem with other libraries.

Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
  • 1
    The date time changes in Java 8 were supposed to make life easy, but finding this stuff sure feels hard sometimes :) – Mike Nov 26 '19 at 21:26
  • @Mike Date and time handling *is* inherently difficult. Any library pretending it is not, is likely to cause errors for you. – Ole V.V. Feb 26 '22 at 05:48
3

Use the Java 8 Date API or Joda, no need for new inventions.

You can find some examples here: http://examples.javacodegeeks.com/core-java/java-8-datetime-api-tutorial/

Sae1962
  • 1,122
  • 15
  • 31
Emerson Cod
  • 1,990
  • 3
  • 21
  • 39
  • Joda has a daysBetween method that would probably serve the purpose. – Mark Sholund Nov 04 '15 at 19:08
  • What exactly is joda and how do I get it? – Bob Nov 04 '15 at 19:30
  • no it's actually blocked on our systems at school, the luck right? – Bob Nov 04 '15 at 19:36
  • 1
    To make this a real answer, instead of a link-only answer, please show **how** to do this with java 8 date api and/or joda time. – Erwin Bolwidt Nov 04 '15 at 20:03
  • i though about this - but there are thousands of examples already fully qualified around the net - why create yet another one... – Emerson Cod Nov 04 '15 at 20:09
  • @EmersonCod "why create yet another one"… because StackOverflow.com is intended to be an authoritative source of programming question-answers. Your Answer is supposed to be the best hit provided by Google/Bing on this topic. Like Wikipedia, but narrowly-focused. If you find the same kind of question, mark this a Duplicate. – Basil Bourque Nov 04 '15 at 21:08
1

Here's an answer using Calendar:

Calendar cal = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();

cal2.setTime(cal.getTime());
cal2.add(Calendar.DAY_OF_YEAR, 5);
System.out.println((cal2.getTimeInMillis() - cal.getTimeInMillis()) / (1000d * 60 * 60 * 24));
Evan LaHurd
  • 977
  • 2
  • 14
  • 27
1
    LocalDate startDate = LocalDate.of(2009, 9, 1);
    LocalDate today = LocalDate.now();
    Period p = Period.between(startDate, today);
    System.out.println("Years " + p.getYears());
    System.out.println("Months " + p.getMonths());
    System.out.println("Days " + p.getDays());
0

If you are stuck withan older java you can use SimpleDateFormat.

    //For substraction
    long differenceInMillis = date1.getTime() - date2.getTime();

    //Date Format
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MM dd");
    String dateAsString = dateFormat.format(date1); //To get a text representation
    Date dateParsed = dateFormat.parse(dateAsString); //To get it back as date
liloargana
  • 165
  • 1
  • 10