Manually Calculate GMT to Epoch - c

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 );
}

Two calculation errors:
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);
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.

Related

How to get the difference of timestamps with millisecond resolution?

I need to find the difference of 2 time stamps having format YYYY DD MM HH MM SS MSC. For example, the difference of 2022 08 13 08 17 20 512 and 2022 08 13 08 17 20 000 should return 512 msec.
I have gone through the posts in How to convert from UTC to local time in C?.
mktime using struct tm pointer has a provision to represent till seconds resolution. Which function should we use to include milliseconds portion as well for computation?
C provides difftime to figure out the number of seconds and timegm is helpful for the UTC timezone. Watch out for that tricky tm_mon element (number of months since January, so August is really 7 not 8).
Maybe extend on the result from difftime by adding the msec difference?
Something like this?
#include <stdio.h>
#include <time.h>
#include <unistd.h>
struct TIME {
struct tm tm;
unsigned int tm_msec;
};
double difftime_ms( struct TIME *time2, struct TIME *time1 )
{
time_t t1 = timegm( &time1->tm );
time_t t2 = timegm( &time2->tm );
return ( 1000.0 * difftime( t2, t1 ) + ( time2->tm_msec - time1->tm_msec ) );
}
int main()
{
struct TIME time1 = { .tm = { .tm_year = 2022, .tm_mon = (8-1), .tm_mday = 13, .tm_hour = 8, .tm_min = 17, .tm_sec = 20} , .tm_msec = 000 };
struct TIME time2 = { .tm = { .tm_year = 2022, .tm_mon = (8-1), .tm_mday = 13, .tm_hour = 8, .tm_min = 17, .tm_sec = 20} , .tm_msec = 512 };
printf( "Difference is %.2f msec\n", difftime_ms( &time2, &time1 ) );
return 0;
}

How to calculate tm_yday given tm_mday and tm_mon?

The 2nd answer in How do I convert "2012-03-02" into unix epoch time in C? does provides the solution. But it uses tm_yday instead of tm_mday and tm_mon of tm structure.
My Input is human readable date and time and the desired output is UNIX epoch time.
int main(int argc, char *argv[])
{
char timeString[80] = {"05 07 2021 00 33 51"}; //epoch is 1620347631000
struct tm my_tm = {0};
if(sscanf(timeString, "%d %d %d %d %d %d", &my_tm.tm_mon, &my_tm.tm_mday, &my_tm.tm_year, &my_tm.tm_hour, &my_tm.tm_min, &my_tm.tm_sec)!=6)
{
/* ... error parsing ... */
printf(" sscanf failed");
}
// In the below formula, I can't use my_tm.tm_yday as I don't have the value for it.
//I want to use my_tm.tm_mday and tm_mon.
printf("%d",my_tm.tm_sec + my_tm.tm_min*60 + my_tm.tm_hour*3600 + my_tm.tm_yday*86400 +
(my_tm.tm_year-70)*31536000 + ((my_tm.tm_year-69)/4)*86400 -
((my_tm.tm_year-1)/100)*86400 + ((my_tm.tm_year+299)/400)*86400 );
return EXIT_SUCCESS;
}
So, in other words, I'm looking for a replacement for my_tm.tm_yday*86400 in terms of my_tm.tm_mday and my_tm.tm_mon
A year in the range "date of adoption of Gregorian calendar" to INT_MAX and a month in the range 1 to 12 can be converted to a zero-based "year day" in the range 0 to 365 by the following helper function:
int yday(int year, int month)
{
static const short int upto[12] =
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
int yd;
yd = upto[month - 1];
if (month > 2 && isleap(year))
yd++;
return yd;
}
That uses the following helper function isyear that takes a year in the range "date of adoption of Gregorian calendar" to INT_MAX and returns 0 if the year is not a leap year, 1 if the year is a leap year:
int isleap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

C: add days to dates OS-independent, before 1970

I want to use mktime to add days to my date. Code taken from here, tm struct initialization from here. This code woks on Linux (gcc):
#include <stdio.h>
#include <time.h>
int main(int argc, char **argv)
{
struct tm t = { 0 };
t.tm_mday += 40;
time_t try1 = mktime(&t);
/* show result */
printf("%d %d %d %d %s", t.tm_year, t.tm_mon, t.tm_mday, try1, ctime(&try1));
return 0;
}
The program returns expected: 0 1 9 2089348096 Fri Feb 9 00:00:00 1900
However when I compile the same code on Windows using
GNU C11 (Rev4, Built by MSYS2 project) version 5.2.0 (x86_64-w64-mingw32)
compiled by GNU C version 5.2.0, GMP version 6.0.0, MPFR version 3.1.3, MPC version 1.0.3
I get this output: 0 0 40 -1 (null).
How can I add days to dates on Windows?
UPDATE. Turns out mktime on windows works only if the effective date is after Jan 1 1970. Hilarious. Here's a small example:
int main(int argc, char **argv)
{
struct tm t = { 0 };
t.tm_year = 70;
t.tm_mday = 1;
t.tm_mday += 40;
time_t try1 = mktime(&t);
/* show result */
printf("%d %d %d %d %s", t.tm_year, t.tm_mon, t.tm_mday, try1, ctime(&try1));
return 0;
}
MS Docs:
The range of the _mkgmtime32 function is from midnight, January 1, 1970, UTC to 23:59:59 January 18, 2038, UTC. The range of _mkgmtime64 is from midnight, January 1, 1970, UTC to 23:59:59, December 31, 3000, UTC. An out-of-range date results in a return value of -1. The range of _mkgmtime depends on whether _USE_32BIT_TIME_T is defined. If not defined (the default) the range is that of _mkgmtime64; otherwise, the range is limited to the 32-bit range of _mkgmtime32.
The question still stands. Is there any way to add days to dates independent of OS? Preferably not limited to after Jan 1 1970.
void second_calculate()
{
//47 year ------>1970+47=2017
//12 day ------>12 leap year 29 february!!
//test=0 ------> 00:00:00 01.01.1970
int _sec = 1;
int _min = 60;
int _hour = 60 * 60;
int _day = 24 * 60 * 60;
int _year = 365 * 24 * 60 * 60;
long long test =
47 * _year // 1970 +47 ----> 2017
+ 12 * _day // leap year ----> 47/4
+ 40 * _day // new year +40 day ----> february 10
+ 22 * _hour // evening 22:00:00
+0* _min //
+0* _sec; //
//grennwich
tm* ptm3 = gmtime(&test);
printf("date: %s\n", asctime(ptm3)); //22:00:00 10.02.2017
}

strptime to strftime for day of the week

I'm using the following format string with strptime
// Populates input_tm with an input string of Monthname Date, Year
strptime(input_string, "%B %d, %Y", &input_tm);
// Output the day of the week from input_tm
strftime(output_string, TIME_BUFSZ, "%A", &input_tm);
After the call to strftime the output_string contains "?"
What additional fields to I need to populate in input_tm for strftime to work?
Edit:
Figured it out by looking at the source of strftime
The required struct field is tm_wday.
If I set input_tm.tm_wday to something manually before the call to strftime it gives me an output.
So now my question is, is there a standard C function that will compute the weekday from a given date?
This works for me — the call to mktime() resolves the problems you got:
#include <stdio.h>
#include <time.h>
enum { TIME_BUFSZ = 256 };
int main(void)
{
char input_string[] = "November 18, 2014";
char output_string[TIME_BUFSZ];
struct tm input_tm = { 0 };
char *end = strptime(input_string, "%B %d, %Y", &input_tm);
if (*end != '\0')
printf("Unused data: [[%s]]\n", end);
printf("tm_year = %d; tm_mon = %d; tm_mday = %d; tm_wday = %d; tm_yday = %d\n",
input_tm.tm_year, input_tm.tm_mon, input_tm.tm_mday,
input_tm.tm_wday, input_tm.tm_yday);
time_t t0 = mktime(&input_tm);
printf("tm_year = %d; tm_mon = %d; tm_mday = %d; tm_wday = %d; tm_yday = %d\n",
input_tm.tm_year, input_tm.tm_mon, input_tm.tm_mday,
input_tm.tm_wday, input_tm.tm_yday);
printf("t0 = %lld\n", (long long)t0);
size_t nbytes = strftime(output_string, TIME_BUFSZ, "%A", &input_tm);
printf("in: [[%s]]\n", input_string);
printf("out (%zu): [[%s]]\n", nbytes, output_string);
return 0;
}
Example run (GCC 4.8.2 on Mac OS X 10.9.2 Mavericks):
tm_year = 114; tm_mon = 10; tm_mday = 18; tm_wday = 0; tm_yday = 0
tm_year = 114; tm_mon = 10; tm_mday = 18; tm_wday = 2; tm_yday = 321
t0 = 1416297600
in: [[November 18, 2014]]
out (7): [[Tuesday]]
This agrees with the result from cal 11 2014:
November 2014
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

localtime returns seeing inconsistent results for tm_wday

Not sure if this is a timezone problem: currently on SGT.
Following code reports Fri, 17 April 2009 as day of week 6 (??), but Fri, Jan 2 as day of the week 5.
#include <time.h>
#include <stdio.h>
#define ONE_DAY 86400
int main() {
time_t StartDate = (time_t) 1239984000;
struct tm StartDateStruct = *localtime(&StartDate);
fprintf(stdout, "Date is %s", asctime(gmtime(&StartDate)));
fprintf(stdout, "Day of the week is = %i\n\n", StartDateStruct.tm_wday);
StartDate = (time_t) 0;
for (int i=0; i<7; i++) {
StartDate = ONE_DAY * i;
StartDateStruct = *localtime(&StartDate);
fprintf(stdout, "Date is %s", asctime(gmtime(&StartDate)));
fprintf(stdout, "Day of the week is = %i\n", StartDateStruct.tm_wday);
}
}
f19 3.11.4-201.fc19.x86_64, gcc 4.8.1 / clang 3.3
Thanking you in advance.
Michael

Resources