Julian Day Number

From OSDev Wiki
Jump to navigation Jump to search

Any OS needs a format for "time". There would seem to be no real standard for when an OS should start measuring the "beginning of time". But there is in fact one that approaches being a standard.

Basic Definition

There is a measurement of time that is used in Astronomy, called Julian Day Number (JDN). For reasons regarding historical calendar systems, it starts measuring time from noon, UTC, January 1, 4713 BC, and it measures time in days with a floating point value. Because it is a sort-of standard, there is code available for converting JDN's to calendar dates.

In any case, once you have divided out the seconds and fractions of seconds from any custom time format, you are left with "days". Which means that the difference between your number and the Julian Day Number is merely an offset. Of course, it is possible to truncate JDNs, and use them as integers.

Date Calculations

The JDN for Y2K was 2451545. If you pretend there was a "Year 0" (or alternately, 1 BC), then Jan 1 of that year was JDN 1721060.

To calculate (standard) Gregorian dates: 400 years have exactly 146097 days.

To calculate leap years: if a year is a multiple of 4 then it's a leap year, unless it happens to be a new century and it can't be divided by 400.

Code Examples

uint32_t days2years[401];

#define LEAP_FLAG		0x80000000

// the days2years table covers 400 years and contains:
// 1) the day number at the START of the year
// 2) the "century" (year / 100) shifted up 24 bits
// 3) a leap year flag
void BuildDaysTbl()
{
	int i;
	int j = 0;
	uint32_t days = 366;
	days2years[0] = LEAP_FLAG;
	while (days < 0x40000000)
	{
		i = 24;
		while (--i >= 0)
		{
			days2years[++j] = days;
			days += 365;
			days2years[++j] = days;
			days += 365;
			days2years[++j] = days;
			days += 365;
			days2years[++j] = days | LEAP_FLAG;
			days += 366;
		}
		days2years[++j] = days;
		days += 365;
		days2years[++j] = days;
		days += 365;
		days2years[++j] = days;
		days += 365 + 0x10000000;
		days2years[++j] = days;
		days += 365;
	}
}


// convert a Julian day # to a Gregorian year, with a "remainder", and a leap year flag
// outputs: *doy = day of year (jan 1 = 0) with leapyear flag, *cent = century AD
uint32_t JDN_to_GregYear(uint32_t jdn, uint16_t *doy, uint32_t *cent)
{
	uint32_t greg_yr = 0;
	uint32_t qcen, temp, temp2;

	temp -= 1721060;			// make temp align with jan1, "year 0" quadcentury boundary
	qcen = temp / 146097;		// 400 years contain 146097 days
	temp %= 146097;				// get the remainder days within the 400 yr span
	temp2 = temp;				// save a copy
	// do an approximate conversion to years -- sometimes one too low
	if (temp > 73048)			// calculation for years 200 to 399
	{
		temp -= 73048;			// this shifts the calculation by 1 day
		greg_yr= 200;
	}
	greg_yr += (temp << 10) / 374014;	// 374014 is a magic number

	if ((days2years[greg_yr + 1] & 0xfffffff) <= temp2)	// fix if greg_yr is off by 1
		++greg_yr;
	temp = days2years[greg_yr];
	*doy = (uint16_t) (temp2 - (temp & 0xfffffff));
	*cent = ((temp & 0x30000000) >> 24) + qcen * 4;
	if ((int32_t) temp < 0)
		*doy |= 0x8000;		// set the leapyear flag on "day of year"

	return greg_yr;
}


// convert Jan 1 of a Gregorian year to a Julian day #
// "sign" bit of the return value is set on a leap year
uint32_t GregYear_to_JDN(uint32_t greg_yr)
{
	uint32_t jdn = (greg_yr / 400) * 146097;	// convert quadcenturies to days
	uint32_t temp = greg_yr % 400;
	// add in the days from the remaining years, and adjust jan1, "year 0" to actual JDN
	jdn += (days2years[temp] & 0xfffffff) + 1721060;
	jdn |= days2years[temp] & 0x80000000;		// copy the leap year bit
	return jdn;
}

See Also

Articles

External Links