Advertisement
Guest User

Untitled

a guest
Oct 23rd, 2014
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.19 KB | None | 0 0
  1. // Used for a quick validation of days in a month.
  2. // Note filler at MONTHDAYS[0] because months are 1-based.
  3. private static final int[] MONTHDAYS =
  4. { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  5.  
  6. private static final boolean isLeap(final int year) {
  7. return (year % 4) == 0 && (year % 100 != 0 || year % 400 == 0);
  8. }
  9.  
  10. /**
  11. * Calculate the day of the week (1=Monday, 7=Sunday) for the supplied
  12. * (valid) date.
  13. * @param year The year to calculate (year 1 or more recent)
  14. * @param month The month (1 through 12 for January through December)
  15. * @param day The Day-in-month (1 through 28/29/30/31 depending on the month)
  16. * @return The day of the week (1 represents Monday, 7 represents Sunday).
  17. * @throws IllegalArgumentException if the input values are not a valid date
  18. */
  19. public static final int computeDayOfWeek(final int year, final int month, final int day)
  20. throws IllegalArgumentException {
  21.  
  22. // easy checks for valid dates.
  23. if (year < 1 || month < 1 || month > 12 || day < 1 || day > MONTHDAYS[month]) {
  24. throw new IllegalArgumentException(String.format(
  25. "%04d-%02d-%02d is not a valid date", year, month, day));
  26. }
  27. // leap year validation
  28. if (day == 29 && month == 2 && !isLeap(year)) {
  29. throw new IllegalArgumentException(
  30. String.format(
  31. "%04d-%02d-%02d is not a valid date (Feb 29 but not a leap year)",
  32. year, month, day));
  33. }
  34.  
  35. // Forumla is here: https://en.wikipedia.org/wiki/Zeller%27s_congruence
  36. // Using the wikipedia's variable names:
  37.  
  38. // h = (q + floor((13*(m+1)/5)) + K + floor(K/4) + floor(J/4) + 5J)
  39.  
  40. // translate variable names in to algorithm components.
  41.  
  42. // q is the day of month.
  43. final int q = day;
  44. // m is the month, but Jan and Feb need to be month 13 and 14
  45. // respectively
  46. final int m = month + (month < 3 ? 12 : 0);
  47. // if the month is jan, or feb, then year is of the previous year.
  48. final int calcyear = year - (month < 3 ? 1 : 0);
  49. // K is the year-in-century
  50. final int K = calcyear % 100;
  51. // J is the century number
  52. final int J = calcyear / 100;
  53.  
  54. /*
  55. Algorithm works by following a concept of adding days in to a
  56. sequence, and performing modular arithmetic. The fundamental concept
  57. is that if you know one specific date's day-of-week, then all you
  58. need to do is calculate how many days you are in front of, or behind
  59. that day.
  60.  
  61. If you know the days, you can perform a %7 on that, and get the day
  62. difference.
  63.  
  64. If you choose your algorithm to start on a specific day and call it
  65. 0, then the difference from the %7 is simply the day. As it happens,
  66. to make things work well, starting with Saturday as day 0 is right.
  67. */
  68.  
  69. // how many days (possibly %7) are we offset at this point in time?
  70. int offset = 0;
  71.  
  72. /*
  73.  
  74. Now, how to calculate the days between. Well, there are 36524 days in
  75. a century, unless the century is divisible by 4, in which case there is
  76. an extra day. so, the days between epoch and now is the number of
  77. centuries * days-in-century + number-of-4-centuries.
  78.  
  79. But, because we only need extra days, we can do these things %7. So,
  80. since 36524%7 is 5, we need to add 5 days for each century since epoch.
  81. Additionally, we need to add another day for each of those special
  82. leap years that are divisible by 400
  83. */
  84.  
  85. // 5 days per century plus the number of 400 years too.
  86. offset += J * 5 + J / 4;
  87.  
  88. // now, inside a century, there's leap years....
  89. // a normal year has 365 days, which is 52 weeks and 1 day.
  90. // so, each year since the start, is 1 more day of offset. And, each
  91. // leap year adds another...
  92. offset += K + K / 4;
  93.  
  94. /*
  95. So, that gives us the right number of offset days to get us to the
  96. current year.
  97. Now, if we start our logical year on March 1st, we don't need to
  98. worry about the odd day at the end of february. Also, the number of
  99. days in a month, starting from March, is:
  100.  
  101. Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,Jan,Feb
  102. 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28/29
  103.  
  104. Notice how Aug and Jan are at positions 5 and 10?
  105.  
  106. Also, a 30-day month adds 2 days of offset, and a 31-day month adds 3
  107. days of offset. So, for each 30 day month we add 2 days, and for each
  108. 31 day month we add 3.
  109.  
  110. This formula can be hard-coded as (13 * (m + 1))/5
  111.  
  112. */
  113. offset += (13 * (m + 1)) / 5;
  114.  
  115. // Now all we need to do is add the days in our current month, to get
  116. // the final offset:
  117. offset += q;
  118.  
  119. // Then, the actual day of week is the zero-day + offset % 7
  120. int h = offset % 7;
  121.  
  122. // Now adjust the 0-6 based Saturday-Friday to a 1-7 based Monday-Sunday
  123. return ((h + 5) % 7) + 1;
  124. }
  125.  
  126. /*
  127. * method for testing only.
  128. */
  129. private static final void check(LocalDateTime then, boolean print) {
  130. // do an us vs. them comparison
  131. int us = computeDayOfWeek(
  132. then.get(ChronoField.YEAR),
  133. then.get(ChronoField.MONTH_OF_YEAR),
  134. then.get(ChronoField.DAY_OF_MONTH));
  135.  
  136. int them = then.get(ChronoField.DAY_OF_WEEK);
  137.  
  138. if (us != them || print) {
  139. System.out.printf("%14s %14s is %s%n",
  140. then.toString(), DayOfWeek.of(them), DayOfWeek.of(us));
  141. }
  142.  
  143. if (us != them) {
  144. throw new IllegalStateException(String.format(
  145. "Unable to correlate our calculation %d to the system %d",
  146. us, them));
  147. }
  148. }
  149.  
  150. public static void main(String[] args) {
  151.  
  152. LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.DAYS);
  153. LocalDateTime past = LocalDateTime.of(1, 1, 1, 0, 0);
  154.  
  155. LocalDateTime then = past;
  156. // test every day since 1 Jan, 0001 through to today
  157. while (then.isBefore(now)) {
  158. check(then, false);
  159. then = then.plusDays(1);
  160. }
  161. check(now, true);
  162.  
  163. then = LocalDateTime.of(2, 1, 1, 0, 0);
  164. // recheck, and print the first year's dates.
  165. while (then.isAfter(past)) {
  166. then = then.minusDays(1);
  167. check(then, true);
  168. }
  169.  
  170. LocalDateTime future = now.plusYears(1);
  171. then = now;
  172. // check, and print the next year's dates.
  173. while (then.isBefore(future)) {
  174. check(then, true);
  175. then = then.plusDays(1);
  176. }
  177. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement