开发者

Difference in days between two dates in Java?

开发者 https://www.devze.com 2023-01-08 01:12 出处:网络
I need to find the number of days between two dates: one is from a report and one is the current date. My snippet:

I need to find the number of days between two dates: one is from a report and one is the current date. My snippet:

  int age=calculateDifference(agingDate, today);

Here calculateDifference is a private method, agingDate and today are 开发者_如何学JAVADate objects, just for your clarification. I've followed two articles from a Java forum, Thread 1 / Thread 2.

It works fine in a standalone program although when I include this into my logic to read from the report I get an unusual difference in values.

Why is it happening and how can I fix it?

EDIT :

I'm getting a greater number of days compared to the actual amount of Days.

public static int calculateDifference(Date a, Date b)
{
    int tempDifference = 0;
    int difference = 0;
    Calendar earlier = Calendar.getInstance();
    Calendar later = Calendar.getInstance();

    if (a.compareTo(b) < 0)
    {
        earlier.setTime(a);
        later.setTime(b);
    }
    else
    {
        earlier.setTime(b);
        later.setTime(a);
    }

    while (earlier.get(Calendar.YEAR) != later.get(Calendar.YEAR))
    {
        tempDifference = 365 * (later.get(Calendar.YEAR) - earlier.get(Calendar.YEAR));
        difference += tempDifference;

        earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
    }

    if (earlier.get(Calendar.DAY_OF_YEAR) != later.get(Calendar.DAY_OF_YEAR))
    {
        tempDifference = later.get(Calendar.DAY_OF_YEAR) - earlier.get(Calendar.DAY_OF_YEAR);
        difference += tempDifference;

        earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
    }

    return difference;
}

Note :

Unfortunately, none of the answers helped me solve the problem. I've accomplished this problem with the help of Joda-time library.


I would suggest you use the excellent Joda Time library instead of the flawed java.util.Date and friends. You could simply write

import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;

Date past = new Date(110, 5, 20); // June 20th, 2010
Date today = new Date(110, 6, 24); // July 24th 
int days = Days.daysBetween(new DateTime(past), new DateTime(today)).getDays(); // => 34


I might be too late to join the game but what the heck huh? :)

Do you think this is a threading issue? How are you using the output of this method for example? OR

Can we change your code to do something as simple as:

Calendar calendar1 = Calendar.getInstance();
    Calendar calendar2 = Calendar.getInstance();
    calendar1.set(<your earlier date>);
    calendar2.set(<your current date>);
    long milliseconds1 = calendar1.getTimeInMillis();
    long milliseconds2 = calendar2.getTimeInMillis();
    long diff = milliseconds2 - milliseconds1;
    long diffSeconds = diff / 1000;
    long diffMinutes = diff / (60 * 1000);
    long diffHours = diff / (60 * 60 * 1000);
    long diffDays = diff / (24 * 60 * 60 * 1000);
    System.out.println("\nThe Date Different Example");
    System.out.println("Time in milliseconds: " + diff
 + " milliseconds.");
    System.out.println("Time in seconds: " + diffSeconds
 + " seconds.");
    System.out.println("Time in minutes: " + diffMinutes 
+ " minutes.");
    System.out.println("Time in hours: " + diffHours 
+ " hours.");
    System.out.println("Time in days: " + diffDays 
+ " days.");
  }


The diff / (24 * etc) does not take Timezone into account, so if your default timezone has a DST in it, it can throw the calculation off.

This link has a nice little implementation.

Here is the source of the above link in case the link goes down:

/** Using Calendar - THE CORRECT WAY**/  
public static long daysBetween(Calendar startDate, Calendar endDate) {  
  //assert: startDate must be before endDate  
  Calendar date = (Calendar) startDate.clone();  
  long daysBetween = 0;  
  while (date.before(endDate)) {  
    date.add(Calendar.DAY_OF_MONTH, 1);  
    daysBetween++;  
  }  
  return daysBetween;  
}  

and

/** Using Calendar - THE CORRECT (& Faster) WAY**/  
public static long daysBetween(final Calendar startDate, final Calendar endDate)
{
  //assert: startDate must be before endDate  
  int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;  
  long endInstant = endDate.getTimeInMillis();  
  int presumedDays = 
    (int) ((endInstant - startDate.getTimeInMillis()) / MILLIS_IN_DAY);  
  Calendar cursor = (Calendar) startDate.clone();  
  cursor.add(Calendar.DAY_OF_YEAR, presumedDays);  
  long instant = cursor.getTimeInMillis();  
  if (instant == endInstant)  
    return presumedDays;

  final int step = instant < endInstant ? 1 : -1;  
  do {  
    cursor.add(Calendar.DAY_OF_MONTH, step);  
    presumedDays += step;  
  } while (cursor.getTimeInMillis() != endInstant);  
  return presumedDays;  
}


java.time

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

Duration

The Duration class represents a span of time as a number of seconds plus a fractional second. It can count days, hours, minutes, and seconds.

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println(duration.toDays());

ChronoUnit

If all you need is the number of days, alternatively you can use the ChronoUnit enum. Notice the calculation methods return a long rather than int.

long days = ChronoUnit.DAYS.between( then, now );


import java.util.Calendar;
import java.util.Date;

public class Main {
    public static long calculateDays(String startDate, String endDate)
    {
        Date sDate = new Date(startDate);
        Date eDate = new Date(endDate);
        Calendar cal3 = Calendar.getInstance();
        cal3.setTime(sDate);
        Calendar cal4 = Calendar.getInstance();
        cal4.setTime(eDate);
        return daysBetween(cal3, cal4);
    }

    public static void main(String[] args) {
        System.out.println(calculateDays("2012/03/31", "2012/06/17"));

    }

    /** Using Calendar - THE CORRECT WAY**/
    public static long daysBetween(Calendar startDate, Calendar endDate) {
        Calendar date = (Calendar) startDate.clone();
        long daysBetween = 0;
        while (date.before(endDate)) {
            date.add(Calendar.DAY_OF_MONTH, 1);
            daysBetween++;
        }
        return daysBetween;
    }
}


It depends on what you define as the difference. To compare two dates at midnight you can do.

long day1 = ...; // in milliseconds.
long day2 = ...; // in milliseconds.
long days = (day2 - day1) / 86400000;


Solution using difference between milliseconds time, with correct rounding for DST dates:

public static long daysDiff(Date from, Date to) {
    return daysDiff(from.getTime(), to.getTime());
}

public static long daysDiff(long from, long to) {
    return Math.round( (to - from) / 86400000D ); // 1000 * 60 * 60 * 24
}

One note: Of course, dates must be in some timezone.

The important code:

Math.round( (to - from) / 86400000D )

If you don't want round, you can use UTC dates,


Illustration of the problem: (My code is computing delta in weeks, but same issue applies with delta in days)

Here is a very reasonable-looking implementation:

public static final long MILLIS_PER_WEEK = 7L * 24L * 60L * 60L * 1000L;

static public int getDeltaInWeeks(Date latterDate, Date earlierDate) {
    long deltaInMillis = latterDate.getTime() - earlierDate.getTime();
    int deltaInWeeks = (int)(deltaInMillis / MILLIS_PER_WEEK);
    return deltaInWeeks; 
}

But this test will fail:

public void testGetDeltaInWeeks() {
    delta = AggregatedData.getDeltaInWeeks(dateMar09, dateFeb23);
    assertEquals("weeks between Feb23 and Mar09", 2, delta);
}

The reason is:

Mon Mar 09 00:00:00 EDT 2009 = 1,236,571,200,000
Mon Feb 23 00:00:00 EST 2009 = 1,235,365,200,000
MillisPerWeek = 604,800,000
Thus,
(Mar09 - Feb23) / MillisPerWeek =
1,206,000,000 / 604,800,000 = 1.994...

but anyone looking at a calendar would agree that the answer is 2.


I use this funcion:

DATEDIFF("31/01/2016", "01/03/2016") // me return 30 days

my function:

import java.util.Date;

public long DATEDIFF(String date1, String date2) {
        long MILLISECS_PER_DAY = 24 * 60 * 60 * 1000;
        long days = 0l;
        SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); // "dd/MM/yyyy HH:mm:ss");

        Date dateIni = null;
        Date dateFin = null;        
        try {       
            dateIni = (Date) format.parse(date1);
            dateFin = (Date) format.parse(date2);
            days = (dateFin.getTime() - dateIni.getTime())/MILLISECS_PER_DAY;                        
        } catch (Exception e) {  e.printStackTrace();  }   

        return days; 
     }


Look at the getFragmentInDays methods in this apache commons-lang class DateUtils.


Based on @Mad_Troll's answer, I developed this method.

I've run about 30 test cases against it, is the only method that handles sub day time fragments correctly.

Example: If you pass now & now + 1 millisecond that is still the same day. Doing 1-1-13 23:59:59.098 to 1-1-13 23:59:59.099 returns 0 days, correctly; allot of the other methods posted here will not do this correctly.

Worth noting it does not care about which way you put them in, If your end date is before your start date it will count backwards.

/**
 * This is not quick but if only doing a few days backwards/forwards then it is very accurate.
 *
 * @param startDate from
 * @param endDate   to
 * @return day count between the two dates, this can be negative if startDate is after endDate
 */
public static long daysBetween(@NotNull final Calendar startDate, @NotNull final Calendar endDate) {

    //Forwards or backwards?
    final boolean forward = startDate.before(endDate);
    // Which direction are we going
    final int multiplier = forward ? 1 : -1;

    // The date we are going to move.
    final Calendar date = (Calendar) startDate.clone();

    // Result
    long daysBetween = 0;

    // Start at millis (then bump up until we go back a day)
    int fieldAccuracy = 4;
    int field;
    int dayBefore, dayAfter;
    while (forward && date.before(endDate) || !forward && endDate.before(date)) {
        // We start moving slowly if no change then we decrease accuracy.
        switch (fieldAccuracy) {
            case 4:
                field = Calendar.MILLISECOND;
                break;
            case 3:
                field = Calendar.SECOND;
                break;
            case 2:
                field = Calendar.MINUTE;
                break;
            case 1:
                field = Calendar.HOUR_OF_DAY;
                break;
            default:
            case 0:
                field = Calendar.DAY_OF_MONTH;
                break;
        }
        // Get the day before we move the time, Change, then get the day after.
        dayBefore = date.get(Calendar.DAY_OF_MONTH);
        date.add(field, multiplier);
        dayAfter = date.get(Calendar.DAY_OF_MONTH);

        // This shifts lining up the dates, one field at a time.
        if (dayBefore == dayAfter && date.get(field) == endDate.get(field))
            fieldAccuracy--;
        // If day has changed after moving at any accuracy level we bump the day counter.
        if (dayBefore != dayAfter) {
            daysBetween += multiplier;
        }
    }
    return daysBetween;
}

You can remove the @NotNull annotations, these are used by Intellij to do code analysis on the fly


You say it "works fine in a standalone program," but that you get "unusual difference values" when you "include this into my logic to read from report". That suggests that your report has some values for which it doesn't work correctly, and your standalone program doesn't have those values. Instead of a standalone program, I suggest a test case. Write a test case much as you would a standalone program, subclassing from JUnit's TestCase class. Now you can run a very specific example, knowing what value you expect (and don't give it today for the test value, because today changes over time). If you put in the values you used in the standalone program, your tests will probably pass. That's great - you want those cases to keep working. Now, add a value from your report, one that doesn't work right. Your new test will probably fail. Figure out why it's failing, fix it, and get to green (all tests passing). Run your report. See what's still broken; write a test; make it pass. Pretty soon you'll find your report is working.


Hundred lines of code for this basic function???

Just a simple method:

protected static int calculateDayDifference(Date dateAfter, Date dateBefore){
    return (int)(dateAfter.getTime()-dateBefore.getTime())/(1000 * 60 * 60 * 24); 
    // MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
}


public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}


ThreeTen-Extra

The Answer by Vitalii Fedorenko is correct, describing how to perform this calculation in a modern way with java.time classes (Duration & ChronoUnit) built into Java 8 and later (and back-ported to Java 6 & 7 and to Android).

Days

If you are using a number of days routinely in your code, you can replace mere integers with use of a class. The Days class can be found in the ThreeTen-Extra project, an extension of java.time and proving ground for possible future additions to java.time. The Days class provides a type-safe way of representing a number of days in your application. The class includes convenient constants for ZERO and ONE.

Given the old outmoded java.util.Date objects in the Question, first convert them to modern java.time.Instant objects. The old date-time classes have newly added methods to facilitate conversion to java.time, such a java.util.Date::toInstant.

Instant start = utilDateStart.toInstant(); // Inclusive.
Instant stop = utilDateStop.toInstant();  // Exclusive.

Pass both Instant objects to factory method for org.threeten.extra.Days.

In the current implementation (2016-06) this is a wrapper calling java.time.temporal.ChronoUnit.DAYS.between, read the ChronoUnit class doc for details. To be clear: all uppercase DAYS is in the enum ChronoUnit while initial-cap Days is a class from ThreeTen-Extra.

Days days = Days.between( start , stop );

You can pass these Days objects around your own code. You can serialize to a String in the standard ISO 8601 format by calling toString. This format of PnD uses a P to mark the beginning and D means “days”, with a number of days in between. Both java.time classes and ThreeTen-Extra use these standard formats by default when generating and parsing Strings representing date-time values.

String output = days.toString();

P3D

Days days = Days.parse( "P3D" );  


This code calculates days between 2 date Strings:

    static final long MILLI_SECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
    static final String DATE_FORMAT = "dd-MM-yyyy";
    public long daysBetween(String fromDateStr, String toDateStr) throws ParseException {
    SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
    Date fromDate;
    Date toDate;
    fromDate = format.parse(fromDateStr);
    toDate = format.parse(toDateStr);
    return (toDate.getTime() - fromDate.getTime()) / MILLI_SECONDS_IN_A_DAY;
}


If you're looking for a solution that returns proper number or days between e.g. 11/30/2014 23:59 and 12/01/2014 00:01 here's solution using Joda Time.

private int getDayDifference(long past, long current) {
    DateTime currentDate = new DateTime(current);
    DateTime pastDate = new DateTime(past);
    return currentDate.getDayOfYear() - pastDate.getDayOfYear();
} 

This implementation will return 1 as a difference in days. Most of the solutions posted here calculate difference in milliseconds between two dates. It means that 0 would be returned because there's only 2 minutes difference between these two dates.


You should use Joda Time library because Java Util Date returns wrong values sometimes.

Joda vs Java Util Date

For example days between yesterday (dd-mm-yyyy, 12-07-2016) and first day of year in 1957 (dd-mm-yyyy, 01-01-1957):

public class Main {

public static void main(String[] args) {
    SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

    Date date = null;
    try {
        date = format.parse("12-07-2016");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    //Try with Joda - prints 21742
    System.out.println("This is correct: " + getDaysBetweenDatesWithJodaFromYear1957(date));
    //Try with Java util - prints 21741
    System.out.println("This is not correct: " + getDaysBetweenDatesWithJavaUtilFromYear1957(date));    
}


private static int getDaysBetweenDatesWithJodaFromYear1957(Date date) {
    DateTime jodaDateTime = new DateTime(date);
    DateTimeFormatter formatter = DateTimeFormat.forPattern("dd-MM-yyyy");
    DateTime y1957 = formatter.parseDateTime("01-01-1957");

    return Days.daysBetween(y1957 , jodaDateTime).getDays();
}

private static long getDaysBetweenDatesWithJavaUtilFromYear1957(Date date) {
    SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

    Date y1957 = null;
    try {
        y1957 = format.parse("01-01-1957");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    return TimeUnit.DAYS.convert(date.getTime() - y1957.getTime(), TimeUnit.MILLISECONDS);
}

So I really advice you to use Joda Time library.


I did it this way. it's easy :)

Date d1 = jDateChooserFrom.getDate();
Date d2 = jDateChooserTo.getDate();

Calendar day1 = Calendar.getInstance();
day1.setTime(d1);

Calendar day2 = Calendar.getInstance();
day2.setTime(d2);

int from = day1.get(Calendar.DAY_OF_YEAR);
int to = day2.get(Calendar.DAY_OF_YEAR);

int difference = to-from;
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号