3

I would like to calculate exact age in days considering leap years. I researched on the web, found some tutorials, but with leap years that exactly calculates difference of dates in days seems to be the one in [1]: https://answers.yahoo.com/question/index?qid=20110629162003AAof4mT this link, which is "best answer" . I analyzed code but have two questions:

1) Why it writes "days = days + leapYears" below "Calculate days lives" section and

2) Finally, how can I input birthday date with day,month and year and current day,month,year and find difference of these two dates in days within this code in the main method? I really appreciate your help. Thanks in advance !

PS: For convenience, I have included the code from the link I showed above:

    public class Days { 


      static int leapYear(int year) { 
int leap; 
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { 
leap = 1; 
} 
else { 
leap = 0; 
} 
return leap; 
} 

static int daysBefore(int month, int day, int year){ 
int days = 0; 
int monthDays[] = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 
if (leapYear(year) == 1){ 
monthDays[1] = 29; 
} 
for (int b = 0; b < month - 1; b++){ 
days = days + monthDays[b]; 
} 
days = days + day; 
return days; 
} 
public static void main(String[] args) { 
//Birth date 
int birthMonth = 0; 
int birthDay = 0; 
int birthYear = 0; 

//Due date 
int dueMonth = 0; 
int dueDay = 0; 
int dueYear = 0; 

//(1) Calculate years lived 
int yearsLived = dueYear - birthYear + 1; 

//(2) Calculate leap years 
int leapYears = 0; 
for (int year = birthYear; year < dueYear+1; year++) 
{ 
leapYears = leapYears + leapYear(year); 
} 

//(3) Calculate the number of days in your birth year before birth 
int daysBeforeBirth = daysBefore(birthMonth, birthDay, birthYear); 

//(4) Calculate the number of days remaining in the current year after the due date 
int daysRemaining = 365 - daysBefore(dueMonth, dueDay, dueYear); 

//Calculate days lived 
int days = 0; 
days = days + (365 * yearsLived); 
days = days + leapYears; 
days = days - daysBeforeBirth; 
days = days - daysRemaining; 
} 
}
user_01
  • 447
  • 3
  • 9
  • 16
  • Just use Calendar – Scary Wombat Sep 14 '16 at 08:05
  • 1
    Do you want to calculate it yourself from first principles, or to use the existing JDK classes? – Dawood ibn Kareem Sep 14 '16 at 08:06
  • 5
    Just use the Date/time API, not Calendar :) – Marko Topolnik Sep 14 '16 at 08:07
  • You can do stuff like that yourself to get a glimpse of understanding how complicated "calendar" work is. If you are more focused on getting correct results quickly, then there is only viable answer: use the new Date/Time Apis added with Java8: http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html – GhostCat Sep 14 '16 at 08:14
  • 1
    In particular, the JDK classes you want are called LocalDate and Period. Don't get sucked into using the ones that incorporate a timezone. And if you try to use Calendar, you'll probably trip over all kinds of issues with daylight savings. – Dawood ibn Kareem Sep 14 '16 at 08:17

3 Answers3

3

The correct way to do this is with the Period and LocalDate classes in the java.time package. However, it seems that you're trying to reinvent the calculation for yourself.

The way that I would recommend doing this is to write a class that lets you calculate a "day number" for a given date - that is, the number of days between the specified date, and some arbitrary date in the past. Then when you want to find the number of days between two specified dates, you can just use calculate the "day number" for both dates, and subtract them.

I have done that here, for a purely Gregorian calendar. This class is no good before the Gregorian cutover - I haven't tried to build a historically accurate Julian/Gregorian hybrid calendar, such as the JDK provides. And the arbitrary date in the past that it calculates day numbers from is 31 December, 2BC. This date, of course, isn't really part of the Gregorian calendar; but for our purposes here, it doesn't matter.

Since you're unlikely to encounter any dates before the Gregorian cutover, this class should be more than adequate for your purposes. I still recommend using the Period and LocalDate classes instead of this one, for production code. This is just here so you can see how to do the calculations.

public class GregorianDate {

    private final int day;
    private final int month;
    private final int year;

    private static final int[] DAYS_PER_MONTH = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    public GregorianDate(int day, int month, int year) {
        this.day = day;
        this.month = month;
        this.year = year;
    }

    public boolean isValid() {
        return month >= 1 && month <= 12 && day >= 1 && day <= daysThisMonth();
    }

    public static int daysBetween(GregorianDate from, GregorianDate to) {
        return to.dayNumber() - from.dayNumber();
    }

    public static int daysBetween(int fromDay, int fromMonth, int fromYear, int toDay, int toMonth, int toYear) {
        return daysBetween(new GregorianDate(fromDay, fromMonth, fromYear), new GregorianDate(toDay, toMonth, toYear));
    }

    private int daysThisMonth() {
        return (isLeapYear() && month == 2) ? 29 : DAYS_PER_MONTH[month];
    }

    private int dayNumber() {
        return year * 365 + leapYearsBefore() + daysInMonthsBefore() + day;
    }

    private boolean isLeapYear() {
        return ( year % 4 == 0 &&  year % 100 != 0 ) || year % 400 == 0;  
    }

    private int leapYearsBefore() {
        return year / 4 - year / 100 + year / 400;
    }

    private int daysInMonthsBefore() {
        switch(month) {
        case 1: 
            return 0;
        case 2:
            return 31;
        default:
            // Start with the number in January and February combined
            int toReturn = isLeapYear() ? 60 : 59; 
            for (int monthToConsider = 3; monthToConsider < month; monthToConsider++) {
                toReturn += DAYS_PER_MONTH[monthToConsider];
            }
            return toReturn;
        }
    }

}
Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
2

tl;dr

long ageInDays = 
    ChronoUnit.DAYS.between( 
        LocalDate.of( 1960 , 1 , 2 ) , 
        LocalDate.now( ZoneId.of( "America/Montreal" ) )
    );

Using java.time

If you are exploring the algorithm, see the apparently correct Answer by David Wallace.

If you are doing this for productive work, then do not roll your own date-time classes. Avoid the old date-time classes (.Date, .Calendar, etc.) and use the java.time classes.

The ChronoUnit enum has a surprising amount of utility including calculating elapsed time. Pass a couple of Temporal objects, in our case LocalDate objects.

LocalDate start = LocalDate.of( 1960 , 1 , 2 ) ;
LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) ) ;
long ageInDays = ChronoUnit.DAYS.between( start , today ) ;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to java.time.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.

Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

To answer question 1. every leapyear you add 1 day to the year. The writer uses this knowledge by first calculating how many days have been lived if all years were normal:

 days = days + (365 * yearsLived);

and then adds the number of leapyears(remember 1 leapyear = 1 extra day).

For the second question: the code seeks the difference between birthDate(read: startDate) and dueDate(read endDate). So to calculate the difference between start and end you have to give these integers the the date input and the code will do the rest.

ziraak
  • 131
  • 3
  • 14