## Time Structure Problems

14 posts / 0 new
Author
Message

For my clock project I have decided to keep time in the unix time format. I have created the following structure to store a time/date in a human readable format. When I compile my makeTime method the compiler tells me of integer overflows and when the code is run the produced unix time is always wrong. I have tried running the code as a program on my linux computer and it works as expected. Is their some AVR specific issue im unaware of?

```typedef struct {
uint8_t second;
uint8_t minute;
uint8_t hour;
uint8_t dayOfWeek; // day of week, Sunday is day 1
uint8_t day;
uint8_t month;
uint8_t year;
} date_time;```

Time_t is defined as

`typedef uint32_t time_t;`

I ported the following algorithm from the Arduino time library.

```time_t TimeKeeper::makeTime(date_time *dateTime) {
// assemble time elements into time_t
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
uint8_t i;
time_t seconds = 0;

// seconds from 1970 till 1 jan 00:00:00 of the given year
seconds = dateTime->year * (SECS_PER_DAY * 365);
for (i = 0; i < dateTime->year; i++ ) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // add extra days for leap years
}
}

// add days for this year, months start from 1
for (i = 1; i < dateTime->month; i++ ) {
if ((i == 2) && LEAP_YEAR(dateTime->year)) {
seconds += SECS_PER_DAY * 29;
}
else {
seconds += SECS_PER_DAY * monthDays[i - 1]; //monthDay array starts from 0	//seconds = 1321809540;
}
}

if(dateTime->day != 0) {
seconds += (dateTime->day - 1) * SECS_PER_DAY;
}
seconds += dateTime->hour * SECS_PER_HOUR;
seconds += dateTime->minute * SECS_PER_MIN;
seconds += dateTime->second;

return seconds;
}```

Quote:

Is their some AVR specific issue im unaware of?

time_t is almost certainly "int". On an AVR that is 16 bits wide, on a real computer it will be 32 bits wide.

You already used uint8_t so stick to these numbered bit widths and use uint32_t if you really mean a variable that is 32 bits wide. The whole point of is that uint32_t is going to be 32 bits wide whether it's on a 8-bit, 16-bit, 32-bit or 64-bit processor.

I ran the exact same code including having time_t as uint32_t on my desktop. I want time_t to be 32 bits so that the clock can last at least 100 years

When using uint8_t members of your structure, you need to cast them up to the size of the resulting values, or the results will be calculated as int/uint, which is only 16 bits for AVR C. For example, since there are as many as 31 * 86400 seconds in a month, your intermediate calculation (SECS_PER_DAY * monthDays[]) needs to be uint32_t. So cast your dateTime values to uint32_t before the calculations wherever required (uint16_t is enough for the minutes multiplication, if you want to get picky).

Last Edited: Wed. Nov 23, 2011 - 06:31 PM

Quote:

I want time_t to be 32 bits so that the clock can last at least 100 years

Also "unixtime" is uint32_t so it has to be at least 32 bits wide anyway. BTW it will NOT be good for 100 years. The problem is due to occur in 2037 so you only have 26 years left on 32 bit Unixtime.

BTW any decent POSIX operating system should already have the source for things like mktime() etc. available so I don't see a lot of point in reinventing the wheel?

See for example:

http://lxr.linux.no/#linux+v3.1....

Last Edited: Wed. Nov 23, 2011 - 06:38 PM

Because unless im mistaken avr doesn't have a time.h library thus I have to write my on mktime function. Also the 2038 problem occurs in signed implementations of time_t if I remember correctly. I don't have any need to handle dates before 1970.

Quote:

thus I have to write my on mktime function.

No I'm saying take one from an existing operating system Linux/LibC would appear to be the obvious candidate if you don't mind using GPL. If you do take a look at Free BSD.

Oh ok, thank you I didnt think of that

Quote:

When I compile my makeTime method the compiler tells me of integer overflows and when the code is run the produced unix time is always wrong.

Which lines posted below give the warnings?
What does the generated code look like for those lines?
C promotion rules are probably doing "int" for your SECS_PER_HOUR etc. Show the definitions.

The struct looks like one I'd use from an RTC. Are the times in binary or BCD?

Quote:

// seconds from 1970 till 1 jan 00:00:00 of the given year

I sidestepped this in a current app by making my timebase start later.

I'll share my version. The structure is in binary, converted from BCD from the RTC.

```//
// **************************************************************************
// *
// *	G E T _ T I M E
// *
// **************************************************************************
//
//	Set up and do a read from address 0 of the RTC.
//	Grab seven bytes of time stamp and place at bufptr.
//
//	Modified to produce both the BCD (directly from the DS1305/6) and the binary
//	(for further calculations) structures.
//
//	Returned value is the year, and -1 for invalid timestamp.
//
signed char						get_time			(struct bcdtime * timeptr, struct bintime * btimeptr)
{
unsigned char	hold_dayofweek;		// temporary holding value
unsigned char	bcdwork;			// temporary holding value
unsigned char	binwork;
signed char		retval;				// The (year-2000) for success; negative values for falure

SELECT_RTC = 1;				// select RTC

retval = 0;					// starting point; not a valid return value

spi(0x00);					// send command "read starting at 0"

// Seconds
bcdwork = spi(0x00);

binwork = ((bcdwork >> 4)*10) + (bcdwork & 0x0f);

if (binwork > 59)
{
retval -= 1;

binwork = 0;
bcdwork = 0;
}
timeptr->seconds = bcdwork;
btimeptr->seconds = binwork;

// Minutes
bcdwork = spi(0x00);

binwork = ((bcdwork >> 4)*10) + (bcdwork & 0x0f);

if (binwork > 59)
{
retval -= 2;

binwork = 0;
bcdwork = 0;
}
timeptr->minutes = bcdwork;
btimeptr->minutes = binwork;

// Hours (24-hour format)
bcdwork = spi(0x00);

binwork = ((bcdwork >> 4)*10) + (bcdwork & 0x0f);

if (binwork > 23)
{
retval -= 4;

binwork = 0;
bcdwork = 0;
}
timeptr->hours = bcdwork;
btimeptr->hours = binwork;

// Day of Week
//	hold_dayofweek = spi(0x00) << 5;				// day of weeek 1-7 into high 3 bits
hold_dayofweek = spi(0x00);		// day of weeek no longer used

// Date (Day of Month)
bcdwork = spi(0x00);

binwork = ((bcdwork >> 4)*10) + (bcdwork & 0x0f);

if (binwork > 31)
{
retval -= 8;

binwork = 1;
bcdwork = 1;
}
timeptr->date = bcdwork;
btimeptr->date = binwork;

// Month
bcdwork = spi(0x00);

binwork = ((bcdwork >> 4)*10) + (bcdwork & 0x0f);

if ((binwork > 12) || (bcdwork < 1))
{
retval -= 16;

binwork = 1;
bcdwork = 1;
}
timeptr->month = bcdwork;
btimeptr->month = binwork;

// Year
//	Accept somewhat arbitrary value of 2009-20025

bcdwork = spi(0x00);

binwork = ((bcdwork >> 4)*10) + (bcdwork & 0x0f);

if ((binwork > 25) || (bcdwork < 9))
{
retval -= 32;

binwork = 1;
bcdwork = 1;
}
else
{
if (retval >= 0)
{
// No errors
retval = binwork;
}
}
timeptr->year = bcdwork;
btimeptr->year = binwork;

SELECT_RTC = 0;			// de-select RTC

return retval;
}

```
```//
// **************************************************************************
// "Time" structure as returned by DS1305/DS1306 RTC:
// **************************************************************************
//	BCD format time
//	Byte	0:		seconds			(00-59)
//			1:		minutes			(00-59)
//			2:		hours			(01-12 or 00-23, plus 12-24/am-pm flag)
//			3:		day of week 	(1-7)	[1=Sunday or 1=Monday?]
//			4:		date in month	(01-31)
//			5:		month			(01-12)
//			6:		year			(00-99)
//
//	Internal "time" structure used to move & store timestamps in BCD.
//	The "dayofweek" field is combined with the 5 bits of "month"
//		in a single byte.  6-byte total timestamp.
//	REVISION:  day-of-week is dropped
//
//	Byte	0:		seconds			(00-59)
//			1:		minutes			(00-59)
//			2:		hours			(01-12 or 00-23, plus 12-24/am-pm flag)
//
//			3:		month			(01-12)
//			4:		date in month	(01-31)
//			5:		year			(00-99)

struct	bcdtime
{
unsigned char		seconds;
unsigned char		minutes;
unsigned char		hours;
unsigned char		month;
unsigned char		date;
unsigned char		year;
};
struct	bintime
{
unsigned char		seconds;
unsigned char		minutes;
unsigned char		hours;
unsigned char		month;
unsigned char		date;
unsigned char		year;
};

```

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

```//
// **************************************************************************
// *
// *	B I N T I M E 2 U N I X T I M E
// *
// **************************************************************************
//
utime_t 				bintime2unixtime	(struct bintime * btimeptr)
{
// Convert the BCD time From DS1305/6 to an unsigned long UNIX timestamp
//	of seconds since the epoch-- Jan. 1, 1970.
//
// As this app never needs to go back in time or specify arbitrary dates,
//	simplifications can be done by simply calculating the whole-year
//	seconds through an arbitrary year such as 2000, 2007, or 2008.
//
// Do a quick window check on the year.  It must be > 2008, and must be less than
//	an arbitrary year--2020?
utime_t			work;	// accumulator of seconds
signed int		mult;	// working

// Base seconds, 1970 through 2008
work = RTC_SEC_TILL_2009;

// How many years since?
mult = btimeptr->year - RTC_YEAR_MIN;
if ((mult < 0) || (mult > (RTC_YEAR_MAX - RTC_YEAR_MIN + 1)))
{
mult = 0;
}
// Add the whole years since start of 2009
work += (unsigned long)mult * RTC_SEC_PER_YEAR;

// Add a day for each leap year in the prior years
work += ((unsigned long)mult/4) * RTC_SEC_PER_DAY;

// Add a day for this year, if leap year and past Feb.
if ((mult+1) % 4 == 0)
{
// Leap year
if (btimeptr->month > 2)
{
// Past February
work += RTC_SEC_PER_DAY;
}
}

// Add the days up to this month.  Use a table
mult = btimeptr->month;
if ((mult < 1) || (mult > 12))
{
mult = 1;
}

work += ((unsigned long)rtc_days[mult-1]) * RTC_SEC_PER_DAY;

// Add the days this month, up to today
mult = btimeptr->date;
if ((mult < 1) || (mult > 31))
{
mult = 1;
}

work += ((unsigned long)(mult-1)) * RTC_SEC_PER_DAY;

// Add the seconds up to this hour
mult = btimeptr->hours;
if ((mult < 0) || (mult > 23))
{
mult = 1;
}

work += ((unsigned long)(mult)) * RTC_SEC_PER_HOUR;

// Add the seconds this hour
mult = (unsigned int)60 * (unsigned int)btimeptr->minutes + (unsigned int)btimeptr->seconds;

work += mult;

return work;
}

```

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

```//	RTC and TimeStamp Handling
//	==========================
//
#define	RTC_SEC_PER_HOUR	(60l * 60l)
#define	RTC_SEC_PER_DAY		(RTC_SEC_PER_HOUR * 24l)
#define	RTC_SEC_PER_YEAR	(RTC_SEC_PER_DAY * 365l)

// Seconds from Jan.1, 1970 through Dec. 31, 2008.
//	-- 39 years
//	-- Leap years: 72, 76, 80, 84, 88, 92, 96, 00, 04, 08
//
//	According to Excel, there are 14245 days between those two dates.
//		365 * 39 = 14235 so that would be 10 leap years.
//
#define	RTC_SEC_TILL_2009	(RTC_SEC_PER_DAY * 14245L)

#define	RTC_YEAR_MIN	9	// 2009, as an 8-bit binary year
#define	RTC_YEAR_MAX	25	// 2025, as an 8-bit binary year

// Elapsed days at the end of each month shown (ignoring leap years)
flash unsigned int		rtc_days[13] =
{
0,
31,		// J
59,		// F
90,		// M
120,	// A
151,	// M
181,	// J
212,	// J
243,	// A
273,	// S
304,	// O
334,	// N
365		// D
};

```

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

theusch wrote:
Quote:

When I compile my makeTime method the compiler tells me of integer overflows and when the code is run the produced unix time is always wrong.

Which lines posted below give the warnings?
What does the generated code look like for those lines?
C promotion rules are probably doing "int" for your SECS_PER_HOUR etc. Show the definitions.

The struct looks like one I'd use from an RTC. Are the times in binary or BCD?

Quote:

// seconds from 1970 till 1 jan 00:00:00 of the given year

I started off using an external rtc but then to save parts I switched to using my crystal along with the async timer. I have the timer overflows incrementing seconds. I get the integer overflow errors on the first 4 lines where seconds is being set to something. All numbers are binary.

My def for those definations follow.

```#define SECS_PER_MIN  60
#define SECS_PER_HOUR 60 * 60
#define SECS_PER_DAY  SECS_PER_HOUR * 24
#define DAYS_PER_WEEK 7
#define SECS_PER_WEEK SECS_PER_DAY * DAYS_PER_WEEK
#define SECS_PER_YEAR SECS_PER_WEEK * 52
```

secretformula wrote:

```#define SECS_PER_MIN  60
#define SECS_PER_HOUR 60 * 60
#define SECS_PER_DAY  SECS_PER_HOUR * 24
#define DAYS_PER_WEEK 7
#define SECS_PER_WEEK SECS_PER_DAY * DAYS_PER_WEEK
#define SECS_PER_YEAR SECS_PER_WEEK * 52
```

Try

```#define SECS_PER_MIN  60UL
#define SECS_PER_HOUR (60UL * 60UL)
#define SECS_PER_DAY  (SECS_PER_HOUR * 24UL)
#define DAYS_PER_WEEK 7UL
#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
```

I get the following

```../src/TimeKeeper.cpp:107:1: error: unrecognizable insn:
(insn 196 146 197 20 (set (concatn:HI [
(reg:QI 176)
(reg:QI 177 [+1 ])
])
(zero_extend:HI (reg:QI 156 [ dateTime_9(D)->hour ]))) ../src/TimeKeeper.cpp:102 -1
(nil))
../src/TimeKeeper.cpp:107:1: internal compiler error: in extract_insn, at recog.c:2137
Please submit a full bug report,
with preprocessed source if appropriate.
See  for instructions.
```

EDIT: Code works when optimizations are disabled.