views:

102

answers:

4

I have an embedded system that currently keeps track of seconds until an event is supposed to occur using a real-time clock driven by a watch crystal.

Now it needs to keep track of the actual date and time. So, I need to be able to calculate the day, month, year, hour, minute and second from a start date/time and offset in seconds.

Could anyone point me in the right direction for taking into account leap years, daylight savings time (DST) and other complications?


Hardware solutions are not an option as this feature is being added to an existing product. An RTC peripheral is integrated into the MCU chosen for the next generation device.

A: 

The following function determines whether a given year is a leap year:

bool is_leap_year(int year)
{
    return ((0 == year % 400) || ((0 == year % 4) && (0 != year % 100)));
}
Judge Maygarden
+1  A: 

Calendar code can be a bit complex - if the C runtime library you're using doesn't have such support built-in (and some way to integrate your clock counter to it) you might consider looking at the code in P.J. Plauger's "The Standard C Library" and adapting it to your needs.

Michael Burr
+1  A: 

The C Snippets archive has some date and time functions.

See also "Julian day", Wikipedia, which includes formulas for Julian date calculation.

A "julian date calculation" Google search should uncover more if you want to search further.

Craig McQueen
+1  A: 

I'm bored, couldn't resist trying a solution. Here's a prototype in ruby - should be clear enough to translate to C.

Given offset and a start date stored as: Baseyear, Baseday, Basesec where day 0 = Jan1, you can calculate the date as

#initialize outputs
year= Baseyear
day = Baseday
sec = Basesec+offset

#days & seconds remaining in the current year
is_leap = is_leap_year(year)
days_remaining = 365+(is_leap ? 1 : 0) - day
secs_remaining = SEC_PER_DAY*days_remaining

#advance by year
while (sec>=secs_remaining)
  sec-=secs_remaining
  year+=1
  is_leap = is_leap_year(year)
  days_remaining = 365+(is_leap ? 1 : 0)
  secs_remaining = SEC_PER_DAY*days_remaining
  day=0 
end

#sec holds seconds into the current year, split into days+seconds
day += sec / SEC_PER_DAY
day = day.to_i #cast to int
sec %= SEC_PER_DAY

#lookup month
for i in (0..11)
  dpm = DAYS_PER_MONTH[i] # =[31,28,31,30,...]
  if (i==1 && is_leap) 
   dpm+=1
  end
  if day < dpm
   month = i
   break
  else
    day-=dpm
  end
end

day+=1 #1-based
hour = sec/3600
min = (sec%3600)/60
sec = sec%60
puts "%s %d, %d @ %02d:%02d:%02d" % [MONTHNAME[month],day,year, hour, min, sec]

It should be easy to add a check that the day is between the begin and end days for DST in the current locale, and adjust the hour accordingly.

AShelly