I'm using the formula as stated in wikipedia. There are some details that differ from your code:
- Month values are
3 = March, 4 = April, 5 = May, ..., 14 = February
. So, your else { month-=2; }
is not needed: only January and February must be adjusted to be 13 and 14, all the other months must remain unchanged.
- I changed the variables names to match wikipedia's formula:
- h is the day of the week (
0 = Saturday, 1 = Sunday, 2 = Monday, ..., 6 = Friday
)
- q is the day of the month
- m is the month (
3 = March, 4 = April, 5 = May, ..., 14 = February
)
- K the year of the century
- J is the zero-based century
Another detail (also explained in the same article), is that the modulo implementation may vary for negative numbers:
... −2 mod 7 is equal to positive 5. Unfortunately, the way most computer languages implement the remainder function, −2 mod 7 returns a result of −2.
In Java (I'm using JDK 1.8.0_144), this also happens (-2 % 7
is equals to -2
), and one way to fix is to just add 7 to the final result. So the code will be like this:
// adjust January to 13 and February to 14
if (month == 1 || month == 2) {
month += 12;
year--;
}
int j = year / 100;
int k = year % 100;
int q = day;
// using temporary variables to make code cleaner (IMO)
int tmp1 = (13 * (month + 1)) / 5;
int tmp2 = k / 4;
int tmp3 = j / 4;
int h = (q + tmp1 + k + tmp2 + tmp3 - (2 * j)) % 7;
// if result is negative, fix
if (h < 0) {
h += 7;
}
So, h
will be 0
for Saturday, 1
for Sunday and so on. I've tested with dates from years 1900 to 2100 and it worked fine.
In wikipedia's article, there's also an alternative formula to avoid negative results: just change - 2J
for + 5J
:
// change "- (2 * j)" to "+ (5 * j)"
int h = (q + tmp1 + k + tmp2 + tmp3 + (5 * j)) % 7;
This will get the same results, but h
will always be positive (so the if (h < 0)
is not needed).
Java new Date/Time API
If you're making this code for learning purposes, then it's fine. But for business applications, it's better to use proper date/time API's.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time
and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp
), but the classes and methods names are the same.
An easy way to get the day of the week from a specific date is to use the LocalDate
class, and then get the DayOfWeek
from it:
LocalDate dt = LocalDate.of(year, month, day);
DayOfWeek dayOfWeek = dt.getDayOfWeek();
// getValue() returns values from 1 (Monday) to 7 (Sunday)
int value = dayOfWeek.getValue();
The only difference is that the getValue()
method returns values according to ISO definition, from 1 (Monday) to 7 (Sunday). If you need the same values returned by the Zeller's method, though, you can do:
int zellerValue = (dayOfWeek.getValue() + 1) % 7;