计算闰年的Java代码

42

我正在阅读《Java 的艺术与科学》一书,它展示了如何计算闰年。该书使用了 ACM Java Task Force 的库。

这是该书使用的代码:

import acm.program.*;

public class LeapYear extends ConsoleProgram {
    public void run()
    {

        println("This program calculates leap year.");
        int year = readInt("Enter the year: ");     

        boolean isLeapYear = ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0));

        if (isLeapYear)
        {
            println(year + " is a leap year.");
        } else
            println(year + " is not a leap year.");
    }

}

现在,这是我计算闰年的方法。

import acm.program.*;

public class LeapYear extends ConsoleProgram {
    public void run()
    {

        println("This program calculates leap year.");
        int year = readInt("Enter the year: ");

        if ((year % 4 == 0) && year % 100 != 0)
        {
            println(year + " is a leap year.");
        }
        else if ((year % 4 == 0) && (year % 100 == 0) && (year % 400 == 0))
        {
            println(year + " is a leap year.");
        }
        else
        {
            println(year + " is not a leap year.");
        }
    }
}

我的代码有问题吗?还是应该使用书中提供的代码?

编辑:上述两个代码都可以正常工作,我想问的是哪个代码是计算闰年的最佳方法。


1
最好的代码是使用可信赖的库。Cletus 建议使用 Calendar 类就是一个很好的例子。 - Thorbjørn Ravn Andersen
1
这里有一个类似的讨论帖子。https://dev59.com/zVvUa4cB1Zd3GeqPrDTN#7395759 - CharithJ
1
这本书中的方法是针对公历的,你的方法是错误的(每400年才有闰年?),而每四年才是针对儒略历的。 - caw
@dipu 对于400的倍数会失败。 - Dawood ibn Kareem
java.time.Year是你的好朋友。请参见答案 - Basil Bourque
显示剩余2条评论
23个回答

100

正确的实现方式是:

public static boolean isLeapYear(int year) {
  Calendar cal = Calendar.getInstance();
  cal.set(Calendar.YEAR, year);
  return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}

但是如果你要重新发明这个轮子,那么:

public static boolean isLeapYear(int year) {
  if (year % 4 != 0) {
    return false;
  } else if (year % 400 == 0) {
    return true;
  } else if (year % 100 == 0) {
    return false;
  } else {
    return true;
  }
}

4
+1表示赞同使用库代码。我建议在其上添加一条注释,因为400是100的倍数,所以你要先测试400而不是100,与原始代码相比。 - Thorbjørn Ravn Andersen
该行代码应为 cal.getActualMaximum(cal.DAY_OF_YEAR); - Olofu Mark
1
+1 给 @OlofuMark 指出这一点。但是有一个小改动:应该是 Calendar.getActualMaximum(Calendar.DAY_OF_YEAR); 因为 DAY_OF_YEAR 是一个静态成员,因此应该以静态方式访问。 =) - PinoyCoder
返回 year % 100 == 0 ? year % 400 == 0 : year % 4 == 0; - Nick Dandoulakis
6
更好的写法是- return new GregorianCalendar(year, 1, 1).isLeapYear(); - Dawood ibn Kareem
显示剩余5条评论

44

java.time.Year::isLeap

使用java.time包中的Year类和isLeap方法来实现此功能:

java.time.Year.of(year).isLeap();

3
当前年份是闰年吗?(使用Java 8)Year.now().isLeap() - Sheamus O'Halloran
1
@SheamusO'Halloran 最好指定您期望的时区,而不是隐式地依赖JVM当前默认的时区,因为该时区可以在JVM中的任何应用程序中的任何代码随时更改:Year.now( ZoneId( "America/Montreal" ) ).isLeap() - Basil Bourque
Year.isLeap(<YEAR>) 使用Java 8直接检查输入的年份是否为闰年。 - Dinesh

31

我建议你将这段代码放入一个方法中,并创建一个单元测试。

public static boolean isLeapYear(int year) {
    assert year >= 1583; // not valid before this date.
    return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}

在单元测试中

assertTrue(isLeapYear(2000));
assertTrue(isLeapYear(1904));
assertFalse(isLeapYear(1900));
assertFalse(isLeapYear(1901));

如果我是一位讲师,我会首先教授单元测试。在数学和工程数学课程中,将结果通过公式进行检查是正常的做法;而单元测试则是让计算机为您的程序执行相同的操作。 - Pete Kirkham
我正在跟随斯坦福大学的编程方法论视频课程,讲师使用《Java编程艺术与科学》一书来教授编程方法论,而不是教授Java语言。我现在正在第四章,但到目前为止还没有涉及单元测试的主题。 - Ibn Saeed
我有点困惑,为什么1583年之前的年份无效? - jcw
2
@jcw 1583年是公历的第一年。不同的国家在不同的年份采用了它,使得1583年到采用年之间的年份更加复杂。(一个混乱程度的想法可以参考http://en.wikipedia.org/wiki/February_30)无论如何,在公历创建之前使用公历都是没有意义的。 - Peter Lawrey

12
new GregorianCalendar().isLeapYear(year);

11

维基百科上伪代码的翻译,转化为最紧凑的Java代码

(year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0))

仅仅是为了说一下:请始终包含您用来编写答案的任何来源链接(即使像维基百科这样易于找到)。 - Till Helge

6

5

来自JAVA的GregorianCalendar源代码:

/**
 * Returns true if {@code year} is a leap year.
 */
public boolean isLeapYear(int year) {
    if (year > changeYear) {
        return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
    }

    return year % 4 == 0;
}

changeYear是指儒略历变为格里高利历的年份(1582年)。

儒略历每四年设闰年,而格里高利历把不能被400整除的世纪年份排除在闰年之外。

您可以在格里高利历文档中找到更多信息。


5

如果您正在使用Java 8:

java.time.Year.of(year).isLeap()

以下是该方法的Java实现:

public static boolean isLeap(long year) {
        return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
    }

3
在软件开发中,重复通常是错误的。在任何工程学科中,形式应该遵循功能原则。当你有三个分支来处理只有两种可能性(即闰年或非闰年)时,这通常是不必要的。
将测试代码写在一行内的机制并不存在这个问题,但一般情况下,最好将测试代码封装成一个函数,函数接收一个int类型表示年份并返回一个boolean类型表示该年份是否为闰年。这样你可以对它进行其他操作而非仅仅将结果打印到控制台,并且可以更轻松地进行测试。
对于那些已知会超出性能预算的代码,通常需要安排测试顺序,以便避免冗余测试并提前返回结果。维基百科的示例就是这么做的——对于大多数年份,你需要计算模400、模100和模4的值,但对于少数年份,你只需要计算模400或模400和模100的值。虽然这种优化在性能上只会带来微小的改善(最好只有每100个输入数据中的一个受益),但它也意味着代码没有重复部分,程序员需要打的字也更少。

2
你可以向 GregorianCalendar 类询问此事:
boolean isLeapyear = new GregorianCalendar().isLeapYear(year);

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