# OSDev.org

The Place to Start for Operating System Developers
 It is currently Wed May 29, 2024 2:35 am

 All times are UTC - 6 hours

 Page 1 of 1 [ 2 posts ]
 Print view Previous topic | Next topic
Author Message
 Post subject: RTC to POSIX timePosted: Sat Mar 09, 2024 5:12 pm
 Member

Joined: Tue Oct 10, 2023 7:40 pm
Posts: 52
I am having trouble figuing out how to convert the time i get from the RTC to a Unix Timestamp.

Code:
typedef struct {
uint8_t sec;
uint8_t min;
uint8_t hour;
uint8_t day;
uint8_t month;
uint64_t year;
} human_time_t;

//Needs to be converted to time_t

Does anyone know how to do this???

Top

 Post subject: Re: RTC to POSIX timePosted: Sat Mar 09, 2024 10:52 pm
 Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1618
Ah, time. Yes, that gets quite complicated quite fast. The issue is mostly to do with leap days, actually. I am using an abstraction I call the GDN, or Gregorian Day Number (similar to Julian Day Number), which is the number of days since January 1, year 1 (which is a hypothetical date; it never happened; but it is a good abstraction).

In the Gregorian calendar, there are 365 days in a normal year, 366 days in a leap year. With the basis being year 1, the nice thing is that a leap year, if it occurs, will always be at the end of a four-year cycle. And a normal four-year cycle will have 1461 days. In the Gregorian calendar, of course, all centuries that are not divisible by 4 end in a non-leap cycle. But we can deal with that one layer further up. A normal century has 76 normal years and 24 leap years, making a total of 36,524 days. A leap century of course has 36,525 days. And a cycle, a group of four centuries, has 146,097 days.

With these definitions out of the way, converting a calendar year to a GDN is quite simple. I am assuming the normal C struct tm here, because it is the standard type for this. This means you may have to convert the RTC time into this standard type before hand. That likely means subtracting 1900 from the year and 1 from the month. I am further assuming that you have a signed 64-bit type for time_t, because otherwise you get into trouble in January of 2038.
Code:
/* returns the GDN of January 1 of a given year */
int year_to_gdn(int year, int *is_leap) {
/* year is the Gregorian calendar year */
assert(year >= 1); /* C integer math with negative numbers is weird. And you don't really work with Roman times, right? */
assert(year < INT_MAX/146097); /* otherwise this overflows the calculation down there */
year--; /* makes the upcoming calculations easier */
int cycle = year / 400;
int year_in_cycle = year % 400;
int century = year_in_cycle / 100;
int year_in_century = year_in_cycle % 100;
int group = year_in_century / 4;
int year_in_group = year_in_century % 4;
if (is_leap) *is_leap = year_in_group == 3 && (year_in_century != 99 || year_in_cycle == 399);
return 146097 * cycle + 36524 * century + 1461 * group + 365* year_in_group;
}
Now you only have to calculate the number of days since January 1, and you have the GDN of the day you wanted.
Code:
int tm_to_gdn(const struct tm *tm) {
int is_leap;
static const unsigned short month_to_gdn[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
int gdn = year_to_gdn(tm->tm_year + 1900, &is_leap);
gdn += month_to_gdn[tm->tm_mon] + (is_leap && tm->tm_mon > 1);
gdn += tm->tm_mday - 1;
return gdn;
}
And now with all of that out of the way, we do have to calculate the GDN of January 1, 1970. Well, 1970 is 4 cycles, 3 centuries, 17 groups and 1 year after the start of the calendar, so:
Code:
#define GDN_EPOCH 719162

And with all of that out of the way, the remaining steps for converting the GDN to UNIX time and adding the time component as well are actually very simple:
Code:
time_t tm_to_time(const struct tm *tm) {
int gdn = tm_to_gdn(tm);
return (gdn - GDN_EPOCH) * 86400LL + tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
}
Leap seconds you don't really need to care about. Your computer likely stores time as UTC (there are very very few machines out there storing time as TAI), which ignores leap seconds. Indeed, the standard UNIX time APIs cannot show a leap second at all. Time stamps are kept in UTC, which has no leap seconds, and converted to struct tm with no additional info.

As for time zones, I was assuming the struct tm was in UTC. If not, then you have to find the timezone offset somehow and subtract it from the result to get UTC, because time_t is supposed to contain the time as UTC.

_________________
Carpe diem!

Top

 Display posts from previous: All posts1 day7 days2 weeks1 month3 months6 months1 year Sort by AuthorPost timeSubject AscendingDescending
 Page 1 of 1 [ 2 posts ]

 All times are UTC - 6 hours

#### Who is online

Users browsing this forum: Google [Bot] and 37 guests

 You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum

Search for:
 Jump to:  Select a forum ------------------ Operating System Development    OS Development    OS Design & Theory    Announcements, Test Requests, & Job openings Everything Else    General Programming    General Ramblings    Auto-Delete Forum OSDev.org    OSDev Wiki    About this site