Convert a human readable Date to a GMT date - c

I have a human readable date which is in local time. I need to convert this human readable time to a GMT time in a human readable form.
The human readable date I have is 30th March 2014 02:59.
When I convert the time to GMT I'm expecting the time to be 01:59 on the 30th but my conversion still comes out as 2:59.
I believe the only way to do the conversion is to convert the human time to an epoch and then convert this back to gmtime but doing this I still get 2:59.
Below is the code I am using:
struct tm t;
struct tm *gmtTimeStruct;
time_t t_of_day;
int year = atoi(date);
int month = atoi(date+5);
int day = atoi(date+8);
int hour = atoi(time);
int minutes = atoi(time+3);
char * gmtHumanTime = NULL;
printf("Year: %i Month: %i Day: %i Hour: %i Minutes: %i\n", year, month, day, hour, minutes);
t.tm_year = year - 1900;
t.tm_mon = month - 1;
t.tm_mday = day;
t.tm_hour = hour;
t.tm_min = minutes;
t.tm_sec = 59;
t.tm_isdst = 0;
t_of_day = mktime(&t);
printf("Epoch time: %ld\n", t_of_day);
gmtTimeStruct = gmtime(&t_of_day);
asprintf(&gmtHumanTime, "%s:%s", gmtTimeStruct->tm_hour, gmtTimeStruct->tm_min);
printf("GMT Human Time: %s", gmtHumanTime);
UPDATE 1
I've also tried change the t.tm_isdst flag from 0,1 and -1 to see if anything changes and it doesn't seem to. I know mktime has an issue with this flag as it can't always work out whether the time needs ammending due to DST or not but I'm not sure if timegm has the same issue.

Here's your problem:
The Daylight Saving Time flag (tm_isdst) is greater than zero if Daylight Saving Time is in effect, zero if Daylight Saving Time is not in effect, and less than zero if the information is not available.
By setting t.tm_isdst = 0; in your code, you are explicitly specifying that there is no daylight saving in effect. You should have provided a negative value.
Reference: http://www.cplusplus.com/reference/ctime/tm/
(Also, shouldn't the format string near the bottom be "%d:%02d"instead of "%s:%s"?)
EDIT
Sorry, I didn't notice you'd tried different values of tm_isdst. Perhaps it would help if you simplified your code a bit. This works perfectly on my system:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
struct tm t, *gmtTimeStruct, *localTimeStruct;
time_t t_of_day;
char *gmtHumanTime, *localHumanTime;
t.tm_year = 2014 - 1900;
t.tm_mon = 3 - 1;
t.tm_mday = 30;
t.tm_hour = 2;
t.tm_min = 59;
t.tm_sec = 59;
t.tm_isdst = -1;
t_of_day = mktime(&t);
gmtTimeStruct = gmtime(&t_of_day);
asprintf(&gmtHumanTime, "%d:%d", gmtTimeStruct->tm_hour, gmtTimeStruct->tm_min);
printf("GMT Human Time: %s\n", gmtHumanTime);
localTimeStruct = localtime(&t_of_day);
asprintf(&localHumanTime, "%d:%d", localTimeStruct->tm_hour, localTimeStruct->tm_min);
printf("Local Human Time: %s\n", localHumanTime);
return 0;
}
Output:
GMT Human Time: 1:59
Local Human Time: 2:59
Try compiling this on your system and see what you get. (Perhaps there is something wrong with your system's time zone setting?)

Related

Determine if DST is in effect for a specified timezone given a time_t

How could I determine if daylight savings time is in effect for a specified timezone given only a time_t? I've read that "In POSIX systems, a user can specify the time zone by means of the TZ environment variable." I was thinking that I could save the current TZ value (if set), change it to the TZ I'm interested in, call localtime() and check tm_isdst, and change TZ back to it's original value. I'm just not sure how portable that would be.
Is there a portable way to determine DST for a timezone given a time_t in C?
This is as portable as I could make it. I'd be interested in any better solution. What I've done is calculate the time from epoch until the start and end of DST in the America/New_York time zone for a given year, and test if the given time_t falls in between. This is specific to the America/New_York time zone but I would imagine that it could easily be adapted for another time zone, or adapted for any/all time zones, with some effort.
If using the GNU C Library, timegm can be used in place of getenv, mktime, setenv, but according to GNU.org:
mktime is essentially universally available. timegm is rather rare.
For the most portable conversion from a UTC broken-down time to a
simple time, set the TZ environment variable to UTC, call mktime, then
set TZ back.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
/***********************************************\
* In America/New_York:
* DST begins: second Sunday in March 02:00 local, after which EDT == UTC-04:00
* DST ends: first Sunday in November 02:00 local, after which EST == UTC-05:00
\***********************************************/
//Return 1 if the year at UTC is greater than the year in America/New_York at
//the given time t. In other words, at time t, is it between 00:00:00 UTC
//(midnight) Jan 1 and 05:00:00 UTC Jan 1. Return 0 if the year at UTC is the
//same as America/New_York at time t.
int UTCyearIsGreater(time_t when) {
time_t begin, end;
struct tm* tm;
tm = gmtime(&when);
if (tm->tm_mon == 11 && tm->tm_mday == 31 &&
(tm->tm_hour >= 19 && tm->tm_hour < 5)) {
return 1;
}
return 0;
}
//Return number of seconds from epoch until DST begins/began in America/New_York, the second Sunday in March (ssim).
//for the given year.
time_t ssim(int year) {
time_t t, t2;
int sim = 0;
struct tm tm = {0};
tm.tm_year = year;
tm.tm_mon = 2;
tm.tm_mday = 1;
tm.tm_hour = 7;
char* env;
env = getenv("TZ");
setenv("TZ", "UTC", 1);
t = mktime(&tm);
tm = *gmtime(&t);
while (sim < 2) {
if (tm.tm_wday == 0) {
sim += 1;
if (sim == 2) { break; }
}
tm.tm_mday += 1;
tm.tm_wday = 0;
t = mktime(&tm);
tm = *gmtime(&t);
}
t = mktime(&tm);
if (env == NULL) {
unsetenv("TZ");
} else {
setenv("TZ", env, 1);
}
return t;
}
//Return number of seconds from epoch until DST ends/ended in America/New_York, the first Sunday in November (fsin).
//for the given year.
time_t fsin(int year) {
time_t t;
struct tm tm = {0};
tm.tm_year = year;
tm.tm_mon = 10;
tm.tm_mday = 1;
tm.tm_hour = 6;
char* env;
env = getenv("TZ");
setenv("TZ", "UTC", 1);
t = mktime(&tm);
tm = *gmtime(&t);
while (1) {
if (tm.tm_wday == 0) { break; }
tm.tm_mday += 1;
tm.tm_wday = 0;
t = mktime(&tm);
tm = *gmtime(&t);
}
t = mktime(&tm);
if (env == NULL) {
unsetenv("TZ");
} else {
setenv("TZ", env, 1);
}
return t;
}
//Return 1 if DST is in effect in America/New_York at time t, return 0 otherwise
int DSTinNYC(time_t t) {
time_t beginDST, endDST;
struct tm* tm_ptr;
tm_ptr = gmtime(&t);
if (UTCyearIsGreater(t)) {
tm_ptr->tm_year -= 1;
}
beginDST = ssim(tm_ptr->tm_year);
endDST = fsin(tm_ptr->tm_year);
return (t >= beginDST && t < endDST);
}
int main() {
//test it
if (DSTinNYC(1461179392)) {
printf("CORRECT 20 Apr 2016 15:09:52 EDT\n");
} else {
printf("FAILED 20 Apr 2016 15:09:52 EDT\n");
}
if (DSTinNYC(1455993975)) {
printf("FAILED 20 Feb 2016 13:46:15 EST\n");
} else {
printf("CORRECT 20 Feb 2016 13:46:15 EST\n");
}
if (DSTinNYC(1571179392)) {
printf("CORRECT 15 Oct 2019 18:43:12 EDT\n");
} else {
printf("FAILED 15 Oct 2019 18:43:12 EDT\n");
}
//results checked with http://www.epochconverter.com/
return 0;
}

How to make hour, minute offset flags in C programming

I am using C on Unix. The program displays the time and I am trying to figure out how to offset the current time in minutes and hours.
This part of the code
while ( ( let = getopt(argc, argv, "alo:vh")) != -1 ) {
switch (let) {
case 'o': offset = atoi(optarg); break; }
and later in this part:
void clock(int sig, int time_expires)
{
time_t now;
struct tm *dateinfo;
(void) time(&now);
now = now + offset;
dateinfo = localtime( &now ); }
Makes an -o offset, which offsets the current time by a certain amount of seconds. For example, -o590 would offset the current time by 590 seconds.
I am trying to figure out how to do this same thing only with an -h flag that offsets the time by a certain amount of hours (like -h6 offsets the time by 6 hours) or by a -m flag which offsets the time by minutes.
I have tried dividing the current -o flag by 60 or 360 but that is not working. Can anyone point me in the right directions here?
To change time_t by so many hours, minutes, seconds in a portable fashion without relying on time_t is some integer type of seconds since 1970, use mktime()
time_t adjust(time_t t, int hour, int minute, int second) {
struct tm *dateinfo;
dateinfo = localtime(&t);
if (dateinfo == NULL) return (time_t) -1;
dateinfo->tm_hour += hour;
dateinfo->tm_min += minute;
dateinfo->tm_sec += second;
return mktime(dateinfo);
}
The time_t structure defines the number of seconds since Jan 1, 1970 UTC.
If you want to add n minutes you should do:
now+= n*60
And for n hours you should:
now+= n*3600
Alternatively you can use struct tm and access directly to the time quanta you wish to modify.
struct tm {
int tm_sec; /* seconds, range 0 to 59 */
int tm_min; /* minutes, range 0 to 59 */
int tm_hour; /* hours, range 0 to 23 */
int tm_mday; /* day of the month, range 1 to 31 */
int tm_mon; /* month, range 0 to 11 */
int tm_year; /* The number of years since 1900 */
int tm_wday; /* day of the week, range 0 to 6 */
int tm_yday; /* day in the year, range 0 to 365 */
int tm_isdst; /* daylight saving time */
};

C MongoDB driver DateTime update

Ok this is the scenario. I am reading txt file and inserting/updating (with bulk operations ) data to mongodb.
Inserting part works fine since I am creating bson document in a loop and doing mongoc_bulk_operation_insert.
My date comes in a format of 11:37:17.253 SAST JUN 05 2015
so I run it trough the function that removes .253 and SAST
and feed it to the convert_to_date function that you see down in the text.
This is how I create timestamp and append the date to the document:
int convert_to_date(char *dateString)
{
struct tm *parsedTime;
time_t rawTime;
time_t newTime;
int hour, minutes, seconds, millis, year, month, day;
if (sscanf(dateString, "%d:%d:%d %d %d %d",
&hour, &minutes, &seconds, &month, &day, &year) != EOF) {
time(&rawTime);
parsedTime = localtime(&rawTime);
parsedTime->tm_year = year - 1900;
parsedTime->tm_mon = month - 1;
parsedTime->tm_mday = day;
parsedTime->tm_hour = hour;
parsedTime->tm_min = minutes;
parsedTime->tm_sec = seconds;
newTime = mktime(parsedTime);
return newTime;
}
return 0;
}
/* this is somewhere in a loop */
time_t t = convert_to_date(s1);
bson_append_time_t(doc, field_map[y], strlen(field_map[y]), t);
// and after all if insert is supposed to happen i call
// mongoc_bulk_operation_insert(bulk, doc);
// and mongoc_bulk_operation_execute(bulk, &reply, &error);
That part is just fine when I look at the database I get
ISODate("2015-06-05T09:37:17.000Z")
So ther problem is when I try to update existing record.
When i do update i need to do a $set on all of the fields I want to update(upsert actually and I didnt find a way to update complete document at once)
it looks like this:
//update document with sessionid
query = BCON_NEW("SessionId", BCON_UTF8(sid));
update = BCON_NEW("$set", "{", "SomeDateTimeField",
BCON_DATE_TIME(ystack[y]), "}");
mongoc_bulk_operation_update(bulk, query, update, true);
The value of ystack[y] is 1433497048 which is valid timestamp but
i get in the db ISODate("1970-01-17T14:11:37.037Z")
I also found an example online for inserting the whole bson object as date like this:
int64_t currentTime = time(NULL) * 1000;
bson_t *date = BCON_NEW("$date", BCON_DATE_TIME(currentTime));
bson_t *update = BCON_NEW("$set", "{", "last_sync_time", date, "}");
But this produces \u0001 in the DB instead of valid date. I cant tell what is causing this, so much frustration for something that should be straight forward. This is the libbson api for the reference
https://api.mongodb.org/libbson/current/index.html
and c driver api
http://api.mongodb.org/c/1.1.8/
Ok I found the solution myself, should have read the api more carefully :)
The BCON_DATE_TIME accepts milliseconds instead of seconds which I passed to it.
I actually tried to multiply by 1000 before but since my var was int it could not store the correct value.
I had to declare it as
unsigned long long
Here is the changed code just for reference
long convert_to_long(char *dateString)
{
struct tm *parsedTime;
time_t rawTime;
time_t newTime;
int hour, minutes, seconds, millis, year, month, day;
if (sscanf(dateString, "%d:%d:%d %d %d %d",
&hour, &minutes, &seconds, &month, &day, &year) != EOF) {
time(&rawTime);
parsedTime = localtime(&rawTime);
parsedTime->tm_year = year - 1900;
parsedTime->tm_mon = month - 1;
parsedTime->tm_mday = day;
parsedTime->tm_hour = hour;
parsedTime->tm_min = minutes;
parsedTime->tm_sec = seconds;
newTime = mktime(parsedTime);
unsigned long long millis = newTime * 1000;
return millis;
}
return 0;
}

calculating time in C - AIX machine

I am editing time value using a variable of type struct tm (adding some seconds to tm->tm_sec), but I am getting wrong results after doing mktime(&t).
Doing so in Linux gets me proper results, but in AIX not. What could be the problem?
#include <stdio.h>
#include <time.h>
#include <langinfo.h>
#include <locale.h>
int main ()
{
struct tm tm;
struct tm *end;
time_t t;
char str[20] = {'\0'};
//if (strptime("7 Feb 2013 01:47:30", "%d %b %Y %H:%M:%S", &tm) == NULL)
if (strptime("2012-10-17-01-07-30", "%Y-%m-%d-%H-%M-%S", &tm) == NULL)
{printf("Error\n");
}
tm.tm_sec = (tm.tm_sec + 1200);
//tm.tm_sec = 12;
//t = mktime(&tm);
//t = t + 12;
//end =localtime(&t);
strftime(str,20,"%Y %m %d %H %M %S",&tm);
printf("str is %s\n",str);
return 0;
}
I believe the correct answer is to use time_t, which is a large number representing the time in seconds since midnight of 1 Jan 1970. Adding arbitrary number of seconds here becomes very trivial.
I expect that if you are just adding seconds to tm->tm_sec, it overflows, and that causes the result to be incorrect. If you are unlucky, you will need to ripple your change in seconds all the way through to year (adding 5 seconds to 31 Dec 2013 23:59:56 will take you to 01 Jan 2014 00:00:01). Which of course can be done, but instead of:
t =+ 5;
you get about a dozen steps along the line of
tm.tm_sec += 5;
if (tm.tm_sec >= 60)
{
tm.tm_sec -= 60;
tm.tm_min += 1;
if (tm.tm_min >= 60)
{
... And so on ...
}
}
It gets even more interesting if you overflow the days in a month, since you then have to take into account of the number of days in each month, 28, 29, 30 or 31 depending on which month [and if it's a leap-year or not].
This is effectively what Mats said:
#include <stdio.h>
#include <time.h>
#include <langinfo.h>
#include <locale.h>
int main ()
{
struct tm tm;
time_t t;
char str[20] = {'\0'};
if (strptime("2012-10-17-01-07-30", "%Y-%m-%d-%H-%M-%S", &tm) == NULL) {
printf("error\n");
}
t = mktime(&tm);
t += 1200;
tm = *localtime(&t);
strftime(str,20,"%Y %m %d %H %M %S",&tm);
printf("str is %s\n",str);
return 0;
}
Produces:
cc -o t t.c && ./t
str is 2012 10 17 02 27 30

Need to get Saturdays date of the week in linux C

I am trying to get Saturday's date of the week in Linux C. Using the function time and localtime, I got today's date and time details. How to proceed further to get Saturday's date?
#include <time.h>
#include <stdio.h>
#include <string.h>
int main()
{
char date[20];
struct tm *curr_tm = NULL;
time_t curr_time;
curr_time = time(NULL);
curr_tm = localtime(&curr_time);
curr_tm->tm_wday = 6;
//Refers to saturday.
printf("new date %d\t%d\t%d\n", curr_tm->tm_mday, curr_tm->tm_mon, curr_tm->tm_year+1900);
return 1;
}
How should I proceed with this?
struct tm orig;
// ...
// struct tm correctly set with everything within range.
orig.tm_mday += 6 - orig.tm_wday;
mktime(&orig);
tm_mday is the number of days since Sunday. Thus, 6 minus that is the number of days until Saturday (if today is Saturday it does nothing). This puts the structure out of range, which mktime fixes.
EDIT:
curr_time->tm_mday += 6 - curr_time->tm_wday;
mktime(curr_time);
Based on your code, the following will get you the next Saturday (today if it's Saturday).
#include <time.h>
#include <stdio.h>
#include <string.h>
int main() {
char date[20];
struct tm *curr_tm = NULL;
time_t curr_time;
curr_time = time(NULL);
curr_tm = localtime(&curr_time);
// Add the difference between todays day of week and Saturday, then re-make.
curr_tm->tm_mday += 6 - curr_tm->tm_wday;
mktime (curr_tm);
printf("new date %d\t%d\t%d\n",
curr_tm->tm_mday, curr_tm->tm_mon+1, curr_tm->tm_year+1900);
return 1;
}
You can replace the curr_tm->tm_mday += 6 - curr_tm->tm_wday; line with:
curr_tm->tm_mday += (curr_tm->tm_wday == 6) ? 7 : 6 - curr_tm->tm_wday;
to get next Saturday even if today is Saturday.

Resources