Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert between date/time and time-stamp without using std library routines

I am trying to implement in C two simple convertors, date/time to time-stamp and vice-versa, without any dependencies on time library routines (such as localtime, mktime, etc, mainly due to the fact that some of them are thread-unsafe).

I have previously posted a similar question under Convert date/time to time-stamp and vice versa, and would now like to submit it again with a few notable changes:

I have the following date/time structure:

typedef struct
{
    unsigned char second; // 0-59
    unsigned char minute; // 0-59
    unsigned char hour;   // 0-59
    unsigned char day;    // 1-31
    unsigned char month;  // 1-12
    unsigned char year;   // 0-99 (representing 2000-2099)
}
date_time_t;

I would like to have a second opinion on the following conversion routines (given a legal input):

static unsigned short days[4][12] =
{
    {   0,  31,  60,  91, 121, 152, 182, 213, 244, 274, 305, 335},
    { 366, 397, 425, 456, 486, 517, 547, 578, 609, 639, 670, 700},
    { 731, 762, 790, 821, 851, 882, 912, 943, 974,1004,1035,1065},
    {1096,1127,1155,1186,1216,1247,1277,1308,1339,1369,1400,1430},
};


unsigned int date_time_to_epoch(date_time_t* date_time)
{
    unsigned int second = date_time->second;  // 0-59
    unsigned int minute = date_time->minute;  // 0-59
    unsigned int hour   = date_time->hour;    // 0-23
    unsigned int day    = date_time->day-1;   // 0-30
    unsigned int month  = date_time->month-1; // 0-11
    unsigned int year   = date_time->year;    // 0-99
    return (((year/4*(365*4+1)+days[year%4][month]+day)*24+hour)*60+minute)*60+second;
}


void epoch_to_date_time(date_time_t* date_time,unsigned int epoch)
{
    date_time->second = epoch%60; epoch /= 60;
    date_time->minute = epoch%60; epoch /= 60;
    date_time->hour   = epoch%24; epoch /= 24;

    unsigned int years = epoch/(365*4+1)*4; epoch %= 365*4+1;

    unsigned int year;
    for (year=3; year>0; year--)
    {
        if (epoch >= days[year][0])
            break;
    }

    unsigned int month;
    for (month=11; month>0; month--)
    {
        if (epoch >= days[year][month])
            break;
    }

    date_time->year  = years+year;
    date_time->month = month+1;
    date_time->day   = epoch-days[year][month]+1;
}

I have tested this on an extensive amount of legal input (between 01/01/2000 and 31/12/2099). Any constructive comments would be appreciated (performance improvement suggestions, readability, etc)...

UPDATE - my ultimate goal here (due to which I am posting this question):

I have an STM32 (ARM-based cortex), with a timer configured to interrupt the CPU every 10ms. In addition, I have an RTC connected, from which I can read date/time (in 1sec resolution). Accessing the RTC is less efficient, so I would like to read it only once, and from then on, calculate the date/time using the 10ms timer interrupts. I wish to avoid using 'localtime', since I then have to protect it with a mutex. The only solution that comes to mind is implementing my own 'localtime', and as subsequent result - my own 'mktime' as well (my epoch in the code above counts seconds from the beginning of the year 2000).

like image 379
barak manos Avatar asked Nov 11 '22 15:11

barak manos


1 Answers

For a performance improvement, consider to not doing epoch_to_date_time() every second (or even every timer tick), but to selectively increment a time unit only when the lesser unit overflows, e. g. like

void another_second_passed(date_time_t *date_time)
{   // *date_time to persist from call to call, initialized once from RTC
    if (++date_time->second < 60) return;   // finished in 59 of 60 cases
    date_time->second = 0;
    if (++date_time->minute < 60) return;   // finished in 59 of 60 cases
    date_time->minute = 0;
    …
}
like image 119
Armali Avatar answered Nov 15 '22 04:11

Armali