Search code examples
cepoch

Manually Calculate GMT to Epoch


I am new here, please bear with me for any blunders.

I am trying to convert the time in GMT format to Unix Epoch (starting from 1970).

The procedure i am following for this is to, iterate over the years starting from 1970 till the given date finding out number of leap years and normal years and multiplying the number of days accordingly.

Then adding the number of days passed in a month, add day value and hours, minutes seconds from tm structure, as shown in below code.

The code is not working as expected , and i seem to be lost in some calculations, and i don't understand what it is.

Followig are the inputs i am trying , expected and actual output is provided here.

Normal years:
Input: Jan 20 19:00:01 2019 GMT
Expected output: 1548010801
Actual output: 1548097201 (which is Jan 21 19:00:01 2019 GMT, 1 day difference)

Leap Years:
Input: Dec 27 14:52:30 2020 GMT
Expected output: 1609080750
Actual output: 1609253550 (which is Dec 29 14:52:30 2020 GMT, 2 days difference)

I request help in finding the problem.

I have two Open Questions 1)I am not sure whether i need to worry about DST or not because i am getting the time as input in GMT format.
2)Is there a better way to calculate the epoch using some formulas expressions , rather than iterating and manually calculating.

There is already one existing post for same but it is using mktime and difftime.

I want to know what is the problem in my code and any better way to do in formulas expressions.

#include <stdio.h>
#include <string.h>
#include <time.h>

#define BASE_YEAR 1970

void print_time_readable_format(struct tm tm);
int convert_gmt_date_time_to_tm_format(char* gmt_time_fmt);
int check_year_is_leap_or_normal(int year);
int get_number_of_leap_years_from_base_year(int start_year, int end_year);
int convert_gmt_to_epoch(struct tm tm);

int main()
{
    int epoch = 0;
    //char gmt_time_fmt[] = "Jan 20 19:00:01 2019 GMT";
    char gmt_time_fmt[] = "Dec 27 14:52:30 2020 GMT";
    //char gmt_time_fmt[] = "Jan 01 00:00:01 1970 GMT";
    epoch = convert_gmt_date_time_to_tm_format(gmt_time_fmt);
    printf("time in GMT = %s and epoch is %d\n", gmt_time_fmt, epoch);
    
    return 0;
}

int convert_gmt_date_time_to_tm_format(char* gmt_time_fmt)
{
    struct tm tm;
    char tm_time_fmt[255];

    //set tm struture to 0
    memset(&tm, 0, sizeof(struct tm));
    // convert gmt_time_fmt to format required by 'tm' structure
    strptime(gmt_time_fmt, "%B %d %H:%M:%S %Y GMT", &tm);

    strftime(tm_time_fmt, sizeof(tm_time_fmt), "%s", &tm);
    printf("tm_time_fmt = %s\n", tm_time_fmt);

    print_time_readable_format(tm);
    
    return convert_gmt_to_epoch(tm);
    
    return 0;
}

int convert_gmt_to_epoch(struct tm tm)
{
    int days_by_month [2][12] = {
        /* normal years */
        { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
        /* leap years */
        { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
    };

    int current_year = tm.tm_year+1900;
    
    printf("current_year =%d\n", current_year);
    
    int total_years_passed = current_year - BASE_YEAR;
    
    printf("total_years_passed =%d\n", total_years_passed);
    
    int nleap_years_passed = get_number_of_leap_years_from_base_year(BASE_YEAR, current_year);
    
    int normal_years = total_years_passed - nleap_years_passed;
    
    printf("normal_years =%d\n", normal_years);
    
    int total_days_passed = (normal_years*365 + nleap_years_passed*366 );
    
    printf("total_days_passed =%d\n", total_days_passed);
    
    total_days_passed += days_by_month[check_year_is_leap_or_normal(current_year)][tm.tm_mon];
    
    printf("total_days_passed after adding month =%d\n", total_days_passed);
    
    total_days_passed += tm.tm_mday;
    
    printf("total_days_passed after adding day =%d\n", total_days_passed);
    
    total_days_passed  = total_days_passed*24 + tm.tm_hour;
    total_days_passed  = total_days_passed*60 + tm.tm_min;
    total_days_passed  = total_days_passed*60 + tm.tm_sec;
    
    printf("total_days_passed final =%d\n", total_days_passed);

    return total_days_passed;

}

int get_number_of_leap_years_from_base_year(int start_year, int end_year)
{
    int leap_year_count = 0;
    int year = start_year;
    
    while( year <= end_year)
    {
        if(check_year_is_leap_or_normal(year))
            leap_year_count++;
        year++;
    }
    
    printf("leap_year_count = %d\n", leap_year_count);
    
    return leap_year_count;
}

int check_year_is_leap_or_normal(int year)
{
    if( ( year%4 == 0 ) && ( ( year%400 == 0 ) || ( year%100 != 0)))
         return 1;
    else
        return 0;
}

void print_time_readable_format(struct tm tm)
{
    printf("tm.tm_year = %d ", tm.tm_year);
    printf("tm.tm_mon = %d ", tm.tm_mon);
    printf("tm.tm_mday = %d ",tm.tm_mday);
    printf("tm.tm_hour = %d ", tm.tm_hour); 
    printf("tm.tm_min = %d ", tm.tm_min );
    printf("tm.tm_sec = %d\n", tm.tm_sec );
}

Solution

  • Two calculation errors:

    1. How leap year is counted, it must exclude the very last year. The correct one should be:
      int nleap_years_passed = get_number_of_leap_years_from_base_year(BASE_YEAR, current_year - 1);
    2. Total days passed must also exclude the end date. The revised formula:
      total_days_passed += (tm.tm_mday - 1);

    Above two formula has caused the epoch to be off by 1 day each.