Created
September 11, 2016 15:01
-
-
Save lrhn/92f0414cf6c97e3a42e2a29a36d3d9c0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
List<int> daysToYearMonthDay(int days) { | |
// Use 1st of March year -99 as base. | |
// This keeps the leap day of year 0, 400, ... in the first | |
// century, but place the Feb 29 day of the century at the very end. | |
days += 306 + 365 * 99 + 24; | |
const daysIn4Centuries = 365 * 400 + 97; | |
// Multiply by 4, then divide by days in 400 years. This is basically | |
// the same as dividing by 365.2425 * 100 - the average number of days | |
// in a century, but using only integer division. | |
var daysX4 = days * 4; | |
var centuries = daysX4 ~/ daysIn4Centuries; | |
var daysInCenturyX4 = daysX4.remainder(daysIn4Centuries); | |
if (daysInCenturyX4 < 0) { | |
daysInCenturyX4 += daysIn4Centuries; | |
centuries -= 1; | |
} // Don't have div compatible with %, so this is necessary for negative values. | |
daysInCenturyX4 &= ~3; | |
// Add a leap year in front, so the century + 1y starts and ends with a | |
// leap year (unless the last year is divisible by 100, but all previous | |
// years always have the same lengths: 366, 365 * 3, repeat). | |
daysInCenturyX4 += 366 * 4; | |
const daysIn4Years = 365 * 4 + 1; | |
var year = (centuries - 1) * 100 + daysInCenturyX4 ~/ daysIn4Years; | |
daysInCenturyX4 = daysInCenturyX4.remainder(daysIn4Years); | |
var daysInYear = daysInCenturyX4 ~/ 4; | |
// The 5-year periods March-July and August-December both have 153 days | |
// divided as 31-30-31-30-31. | |
// The third period, January-February, starts with 31-x as well, | |
// so all three periods can be treated equally. | |
var fiveMonths = daysInYear ~/ 153; | |
var daysIn5Months = daysInYear.remainder(153); | |
var daysIn5MonthsX2 = daysIn5Months * 2; | |
var month = daysIn5MonthsX2 ~/ 61; | |
var dayInMonth = daysIn5MonthsX2.remainder(61) ~/ 2; | |
month += 2 + fiveMonths * 5; | |
// Correct for having rotated January-February to end of year, | |
// all the previous months really belong to the preivous year. | |
// This is 1 if month is January or February, 0 otherwise. | |
int isJanFeb = fiveMonths >> 1; | |
month -= isJanFeb * 12; | |
year -= isJanFeb ^ 1; | |
return[year, month + 1, dayInMonth + 1]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment