在Java中计算年龄,考虑闰年。

3
我希望能够计算考虑闰年的确切年龄天数。我在网上进行了研究,并找到了一些教程,但是考虑到闰年准确计算日期差异的教程似乎是链接[1]中的“最佳答案”,链接为:https://answers.yahoo.com/question/index?qid=20110629162003AAof4mT。我分析了代码,但有两个问题:
1)为什么在“计算生命天数”部分下面写入“days = days + leapYears”;
2)最后,在主方法中如何输入生日日期(以日、月和年表示)和当前日期(以日、月和年表示),并在其中查找这两个日期之间的天数差异?非常感谢您的帮助。提前致谢!
注:为方便起见,我已经包含了上述链接中的代码。
    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; 
} 
}

只需使用日历 - Scary Wombat
1
你想要根据基本原理自己计算,还是使用现有的JDK类? - Dawood ibn Kareem
5
请使用日期/时间API,而不是日历(Calendar) :) - Marko Topolnik
你可以自己尝试这样的事情,以便对“日历”工作的复杂性有所了解。如果你更关注快速获得正确结果,那么唯一可行的答案是使用Java8新增的新日期/时间API:http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html - GhostCat
1
特别是,你想要的JDK类被称为LocalDate和Period。不要陷入使用包含时区的类中。如果你尝试使用Calendar,很可能会遇到各种夏令时问题。 - Dawood ibn Kareem
3个回答

3
正确的方法是使用java.time包中的PeriodLocalDate类。但是,看起来您正在尝试重新计算它。
我建议的方法是编写一个类,让您可以为给定日期计算“日期号” - 也就是指定日期与过去某个任意日期之间的天数。然后,当您想要找到两个指定日期之间的天数时,只需计算两个日期的“日期号”,并将它们相减。
我已经为纯格里高利历编写了这样的类。在格里高利改革之前,这个类没有用处 - 我没有试图构建历史上准确的儒略/格里高利混合日历,例如JDK提供的那种。它所计算的过去任意日期是公元前2年12月31日。当然,这个日期并不真正属于格里高利历; 但对于我们在这里的目的来说,这并不重要。
由于您不太可能遇到格里高利改革之前的任何日期,因此这个类应该足以满足您的需求。我仍然建议在生产代码中使用PeriodLocalDate类,而不是这个类。这只是为了让您看到如何进行计算。
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;
        }
    }

}

2

tl;dr

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

使用java.time

如果你正在探索算法,请查看David Wallace的正确答案

如果你正在进行生产工作,则不要自己编写日期时间类。避免使用旧的日期时间类(.Date.Calendar等),使用java.time类。

ChronoUnit枚举具有出人意料的实用性,包括计算经过的时间。传递一对Temporal对象,在我们的情况下是LocalDate对象。

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

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧日期时间类,如java.util.Date.Calendarjava.text.SimpleDateFormat

Joda-Time项目现在处于维护模式,建议迁移到java.time。

要了解更多信息,请参见Oracle教程。并在Stack Overflow上搜索许多示例和解释。

许多java.time的功能被移植到Java 6和7中ThreeTen-Backport,并进一步适应AndroidThreeTenABP(请参见如何使用...)。

ThreeTen-Extra项目通过添加额外的类来扩展java.time。该项目是java.time可能未来添加的类的试验场所。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuarter等。


1
为了回答问题1,每个闰年你需要给这一年增加1天。作者首先通过计算如果所有年份都是正常的,那么已经度过了多少天来利用这一知识:
 days = days + (365 * yearsLived);

然后添加闰年的天数(记住1个闰年=额外1天)。

对于第二个问题: 该代码寻求出生日期(读作:开始日期)和到期日期(读作结束日期)之间的差异。因此,要计算起始日期和结束日期之间的差异,您必须将这些整数输入日期,并且代码将完成其余部分。


是的,我明白了。非常感谢大家提供的详细而深入的解释! - user_01

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接