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 sec开发者_如何学编程ond 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.
The C Snippets archive has some date and time functions. Update: unfortunately, the C snippets archive is now defunct. I have updated the link to point to the web archive of the page.
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.
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.
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.
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)));
}
精彩评论