閏年について気になったので調べてしまった。Wikipedia便利。
暦については、いろいろと罠が多いので、気になったときに確認をしています。例えば「明治 – Wikipedia」を読めばわかりますが、明治5年には12月3日から12月31日までの29日間が存在しません。
まぁ、それはいいとして、「閏年 – Wikipedia」をみたら、ユリウス暦、グレゴリオ暦、日本における閏年の根拠法、 修正ユリウス暦といったあたりを気にする必要がありそうです。日本だと「日本における閏年の根拠法」を根拠としてプログラムを書いた方が良さそうです。グレゴリオ暦と一致しているようなので、「year%4 == 0 && year%100 != 0 || year%400 == 0」で計算して良さそうですが…
ちなみに、2800年2月28日の次の日から、グレゴリオ暦と修正ユリウス暦では「ずれ」が生じるようなのですが、知りませんでした。
さて、Javaで実装すると次のようになります。「&&」は「||」よりも優先順位が高いので必要ないのですが、どちらが先に計算されるのかわかりやすくするために「( )」で明示的にしています。
$ cat LeapYear.java
public class LeapYear {
public static void main(String[] args) {
int y = Integer.parseInt(args[0]);
boolean result = (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0));
System.out.println(result);
}
}
public class LeapYear {
public static void main(String[] args) {
int y = Integer.parseInt(args[0]);
boolean result = (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0));
System.out.println(result);
}
}
ついでに、こんなテストプログラムを作成してみました。1行で計算式を表しているSimpleクラスの方がいいのですが、定義どおりに書き下ろすと、Basicクラスのような感じになります。動作確認のためのプログラムは、JUnitで実装するのが普通ですが、ライブラリとか追加するのが面倒だったので普通のプログラムにしました。
$ cat LeapYear.java
public class LeapYear {
interface Checker {
boolean isLeap(int y);
}
static class Simple implements Checker {
public boolean isLeap(int y) {
return (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0));
}
}
static class Basic implements Checker {
public boolean isLeap(int y) {
boolean result = false;
if (y % 4 == 0) {
result = true;
if (y % 100 == 0) {
result = false;
if (y % 400 == 0) {
result = true;
}
}
}
return result;
}
}
public static void main(String[] args) {
LeapYear.Checker app1 = new LeapYear.Simple();
LeapYear.Checker app2 = new LeapYear.Basic();
System.out.println("1900:false," + app2.isLeap(1900));
System.out.println("1996:true ," + app2.isLeap(1996));
System.out.println("1999:false," + app2.isLeap(1999));
System.out.println("2000:true ," + app2.isLeap(2000));
System.out.println("2001:false," + app2.isLeap(2001));
System.out.println("2004:true ," + app2.isLeap(2004));
int start = 1900;
int end = 2800;
for (int y = 1900 ; y<=2800 ; y+=4) {
boolean result1 = app1.isLeap(y);
boolean result2 = app2.isLeap(y);
String s = result1 ? ", " : ",";
if (result1 != result2) {
System.out.println(y + ":" + result1 + s + result2);
}
}
}
}
public class LeapYear {
interface Checker {
boolean isLeap(int y);
}
static class Simple implements Checker {
public boolean isLeap(int y) {
return (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0));
}
}
static class Basic implements Checker {
public boolean isLeap(int y) {
boolean result = false;
if (y % 4 == 0) {
result = true;
if (y % 100 == 0) {
result = false;
if (y % 400 == 0) {
result = true;
}
}
}
return result;
}
}
public static void main(String[] args) {
LeapYear.Checker app1 = new LeapYear.Simple();
LeapYear.Checker app2 = new LeapYear.Basic();
System.out.println("1900:false," + app2.isLeap(1900));
System.out.println("1996:true ," + app2.isLeap(1996));
System.out.println("1999:false," + app2.isLeap(1999));
System.out.println("2000:true ," + app2.isLeap(2000));
System.out.println("2001:false," + app2.isLeap(2001));
System.out.println("2004:true ," + app2.isLeap(2004));
int start = 1900;
int end = 2800;
for (int y = 1900 ; y<=2800 ; y+=4) {
boolean result1 = app1.isLeap(y);
boolean result2 = app2.isLeap(y);
String s = result1 ? ", " : ",";
if (result1 != result2) {
System.out.println(y + ":" + result1 + s + result2);
}
}
}
}
実行結果は次の通り。動作確認ができました。
1900:false,false
1996:true ,true
1999:false,false
2000:true ,true
2001:false,false
2004:true ,true
1996:true ,true
1999:false,false
2000:true ,true
2001:false,false
2004:true ,true
まぁ、ちょっとしたお遊びですね…