RTC Module (Part 7)

Part 123456, 7

calendar_s

Here are some new functions added to the PlainRTC libraries which drive the DS13x7, DS1306 and DS1394 series RTCs. These function are here to help in standardizing the date and time values to be recorded and read back by various applications for graphic reconstruction for example.

Although I am getting more and more a big fan of open source, my programming roots are deep in the Microsoft Visual Studio environment with whom I started in the early 90’s with VB3! I am getting quite an ol’timer… Anyway, programming Visual Studio is a nice way to delay Alzheimer!

In most data collect and data analysis applications that I designed, I used the serialized dates and times from Visual Studio, which is the same in Excel. In this way date and time are specified in a single float (32 bits) where the integer part is the number of elapsed days since the 1st of January 1900 and the fractional part is the elapsed time since 00:00:00.

Computing the number of elapsed days is not so hard as long as one keep the leap years rule in mind: every year which can be divided by 4 is a leap year except if if this year can be divided by 100 in case it is not a leap year. But there is an other exception in the Gregorian calendar: if this year can be divided by 400, IT IS a leap year! Examples are given below

  • 2000: can be divided by 400: it is a leap year
  • 2004: can be divided by 4 and not by 100: it is a leap year
  • 2100: can be divided by 4 and also by 100 (and not by 400) : it is NOT a leap year

Here is a the leap year checking function from the PlainRTC libraries:

uint8_t PlainRTC::IsLeapYear(uint16_t year)
{ 
	uint8_t res = 0x00;
	if ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)) {
		res = 0x01;
	}
	return(res);
}

The problem is that the programmers of Lotus-1-2-3, an almost forgotten although very popular  spreadsheet in the mid 80’s made a big mistake and programmed 1900 as a leap year! And it is not because 1900 can be divided by 4 and ALSO by 100 (And not by 400). As a consequence, entering the 29 Feb 1900 in Lotus gave no error, and serial dates between 1/1/1900 and 29/2/1900 are wrong.

At this time Microsoft was about to launch Excel and the marketing people convinced the developers to stick to the bug in order to insure full compatibility of software! So that the bug is also duplicated in the Microsoft products, including VB. Bizarre! This bug must be taken into account while programming day counters since 1/1/1900 such as in the DateSerial function from the PlainRTC libraries:

uint32_t PlainRTC::DateSerial(uint16_t year, uint8_t month, uint8_t dayOfMonth)
/* Compute date serial number since 1 Jan 1900 at 0:0:0 (which is serial number 1) */
{
	/* Integer part */
	/* 
	The "days" number must be offset by 1 because of the Lotus-1-2-3 bug 
	which was agreed by Excel for compatibility reasons. This means that DateSerial
	are wrong for dates which are less than 60	
	*/
	uint32_t days = 1;
	/* Add days from past years */
	for (uint16_t i = 1900; i < year; i++) {
		days += 365.0;
		if (IsLeapYear(i) == 0x01) {
			days += 1.0;
		}
	}
	/* Add days from past months */
	for (uint8_t i = 1; i < month; i++) {
		days += DaysInMonth(i, year);
	}
	/* Add days elapsed in this month */
	days += dayOfMonth;
	/* Build result */
	return(days);
}

 This function features an interesting sub routine which computes the number of days within any month, taking into consideration the leap years:

uint8_t PlainRTC::DaysInMonth(uint8_t month, uint16_t year)
{ 
	uint8_t days = (30 + ((month + (month >> 3)) % 2));
	if (month == 2) {
		if (IsLeapYear(year) == 0x01) {
			days -= 1;
		} else {
			days -= 2;
		}
	}
	return(days);
}

 Then I faced a frustrating situation. Designing a DateAndTimeSerial function should be as easy as summing the results from the DateSerial and the TimeSerial functions. Well, it is not! The lack of precision of the AVR floats leads to a resulting fractional value which is different from the computed TimeSerial. This means that both values should be stored apart in order to maintain a good time precision. This is no longer an issue if data is stored on an external chip.

PlainRTC libraries are available on request as usual.

HTH

Leave a Reply

You must be logged in to post a comment.