UTC time from a timestamp - c

I'm trying to get the UTC time from a localtime. Vice versa it works OK. I only have problems when I want to convert a localtime to a UTC one in order to update an RTC clock. This is what I'm doing:
Say that t is a local timestamp.
char *tz = "GMT-2GMT,M3.5.0/3,M10.5.0/4";
t = 1311444000; // 23/07/11 18:00:00
set_TZ(tz);
gmt_time = gmtime(&t);
mktime(gmt_time);
printf("GMT Time: %s\r\n",asctime (gmt_time));
This gives me 18:00:00, when it should be minus the timezone.
How do I fix this problem?

The problem here is that time_t is supposed to represent the number of seconds (simplification, see footnote) since the epoch. This is not affected by time zones. If you add a time zone offset to a time_t, none of the functions will work as expected. Part of the problem is that it is sometimes impossible to know how to correctly convert such a value to a POSIX timestamp -- when the clocks roll back in the fall, where I live the clocks will read 1:30 AM twice during the same day, and without more information you can't figure out what the UTC time is.
You'll need to convert to local calendar time before you can convert to a POSIX timestamp, at which point you can convert to UTC calendar time.
// NOTE: not reentrant
time_t local_to_posix(time_t t) {
struct tm *tm;
time_t tt;
tm = gmtime(&t); // No timezone compensation
tm->tm_isdst = -1; // Let mktime figure out daylight savings
// NOTE: this WILL be wrong for one hour each year
tt = mktime(tm); // Converts local time to POSIX timestamp
return tt;
}
You can then pass the result to gmtime, which will give you the UTC calendar time.
Footnote: Strictly speaking, these timestamps are not UTC and do not measure the number of seconds since epoch. They are POSIX timestamps, which count seconds since epoch as if there were no leap seconds. This is probably irrelevant unless your realtime clock is an atomic clock.

After further tests the following code this seems to work. Im in UTC+2 and currently in DST. The returned time for UTC is correct. I will to further test without DST and see what is returned.
struct tm tm;
struct tm *local_time;
char *tz = "GMT-2GMT,M3.5.0/3,M10.5.0/4";
time_t t;
tm.tm_hour = 18;
tm.tm_min = 0;
tm.tm_sec = 0;
tm.tm_wday = 0;
tm.tm_yday = 0;
tm.tm_year = (2011) - 1900;
tm.tm_mday = 22;
tm.tm_mon = 7 - 1;
tm.tm_isdst = -1;
set_TZ(tz);
t = mktime(&tm);
local_time = localtime(&t);
printf("Local Time: %s\r\n",asctime (local_time ));
local_time = gmtime(&t);
printf("UTC Time: %s\r\n",asctime (local_time ));
Output:
Local Time: Fri Jul 22 18:00:00 2011
UTC Time: Fri Jul 22 15:00:00 2011

Related

C - mktime altering passed time_t value

I need the functionality for a user to enter a timestamp, store that timestamp on the disk, then display that timestamp at a later time. I also need to be able to determine the distance between two timestamps.
My current solution is to use the time.h utility as follows:
To read a given timestamp of the form "MO/DA/YEAR HR:MI"
time_t readable_to_epoch(char* str) {
int mo, da, yr, hr, mi;
sscanf(str, "%d/%d/%d %d:%d", &mo, &da, &yr, &hr, &mi);
struct tm timeinfo;
time_t out;
timeinfo.tm_mon = mo - 1;
timeinfo.tm_mday = da;
timeinfo.tm_year = yr - 1900;
timeinfo.tm_hour = hr;
timeinfo.tm_min = mi;
timeinfo.tm_sec = 0;
out = mktime(&timeinfo);
return out;
}
To convert a given time_t into a human readable form:
// WARNING returned string must be freed;
char* epoch_to_readable(time_t time) {
return asctime(localtime(&time));
}
My issue is that if I pass in the input:
06/19/2018 12:52
The conversion to time_t and back is as desired. Here is the test harness output:
original: 06/19/2018 12:52
epoch time: 1529437920
converted readable: Tue Jun 19 12:52:00 2018
But when I pass in the input:
01/01/2022 10:00
The converted readable seems to be one hour behind:
original: 01/01/2022 10:00
epoch time: 1641056400
converted readable: Sat Jan 1 09:00:00 2022
I have no idea why this is happening, and google isn't helping. What do I need to do to fix this?
You did not set timeinfo.tm_isdst, so you're getting undefined behaviour. You should use -1.
Daylight Saving Time flag. The value is positive if DST is in effect, zero if not and negative if no information is available
Note that working with local times without offset is inherently buggy. Because of Daylight Saving Time, time stamps from two hours each year are indistinguishable from each other.

Why does mktime give me an hour less?

I would like to see if at 00:00:00 on January 1, 1970 it actually corresponds to 0 seconds, and I wrote the following:
#include <stdio.h>
#include <time.h>
int main(void) {
int year = 1970;
struct tm t = {0};
t.tm_mday = 1; // January
t.tm_year = year - 1900;
t.tm_hour = 0;
t.tm_isdst = -1;
printf("%ld\n", mktime(&t));
return 0;
}
it gives me a value of -3600. Where am I wrong?
PS: tested with GCC v.10.1. I tried with another compiler under another architecture and it gives me back the correct value.
The time info you provide to mktime() is in local time, so the timezone matters even if summer time / daylight savings time does not.
You can fool your program by telling it you're in UTC:
$ gcc mytime.c -o mytime
$ ./mytime
28800 <-- Pacific time in the US
$ TZ=GMT0 ./mytime
0
The mktime function takes a time in local time. Apparently, 00:00:00 at your local time was one hour before the epoch. Launch the program with TZ set to UTC.
I would like to see if at 00:00:00 on January 1, 1970 it actually corresponds to 0 seconds, and I wrote the following:
00:00:00 on January 1, 1970 GMT, UTC corresponds to 0 seconds.
00:00:00 on January 1, 1970 Italia time corresponds to -3600 seconds.
Set timezone to UTC and then call mktime(). Unfortunately C does not have a portable way to do this, so the suggested code is only illustrative.
setenv("TZ", "UTC", 1);
tzset();
....
mktime(&t)
time_t does not necessarily match long. Recommend casting to a wide type.
// printf("%ld\n", mktime(&t));
printf("%lld\n", (long long) mktime(&t));
t.tm_mday = 1; // January misleads. .tm_mday is the day of the month, not January.
.tm_mon is the months since January so the initialization to 0 matches January.
Concerns about DST apply here only if the local time was using DST in January.
As other answers indicate, mktime works in your local time zone. However, many operating systems offer a related function timegm that works in UTC. This slight modification of your program prints 0, as expected, on my computer:
#include <stdio.h>
#include <time.h>
int main(void)
{
int year = 1970;
struct tm t = {0};
t.tm_mday = 1; // January
t.tm_year = year - 1900;
t.tm_hour = 0;
t.tm_isdst = -1;
printf("%ld\n", timegm(&t));
return 0;
}
Regrettably, this function is not standardized. You may have to define a special "feature selection macro" to get your time.h to declare it.

converting milliseconds to date in C

Is there any way of converting milliseconds to date in C?
What I am trying to do is write a small application in C that can return the financial year and the like(quarter, week) given the start month and isCurentYear bool, where the input might be milliseconds or a date!
In the first place, is there any way by which this can be achieved in C?
And if so, in the process of finding out a way of converting milliseconds to date
I have found out that the use of time_t takes the current millis of our system and by creating a structure pointing to it,it permits us to extract the year,month, date, sec etc!
Refer the below code:
#include <sys/time.h>
#include<stdio.h>
#include<time.h>
void main()
{
time_t t = time(000);
//time_t t = time(0);
struct tm tm = *localtime(&t);
printf("now: %d-%d-%d %d:%d:%d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
And also, can time_t be used to store millisecond values so that it can be converted to date using tm struct?
Function time_t time(time_t* timer) returns the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC. In addition, if the input argument timer != NULL, then the function also sets this argument to the same value (so you probably have no reason to call it with anything else but NULL).
Function struct tm* localtime(const time_t* timer) takes the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC, and returns a structure that represents the equivalent time & date. If you're working on a multi-threaded application, then please note that this function is not thread safe.
As to your question - is there any way for converting milliseconds to time & date - yes, but:
Take into consideration that the milliseconds will be considered as of 00:00 hours, Jan 1, 1970 UTC.
Since the time_t type is 32-bit long, you will not be able to convert 4G*1000 milliseconds or more.
Here is a function for converting milliseconds to time & date:
struct tm* GetTimeAndDate(unsigned long long milliseconds)
{
time_t seconds = (time_t)(milliseconds/1000);
if ((unsigned long long)seconds*1000 == milliseconds)
return localtime(&seconds);
return NULL; // milliseconds >= 4G*1000
}
For those of us who were searching the web for an answer to apply to embedded c applications, think pic 32 programming here is the mathematical calculation:
Date in Epoch_seconds = ( (epoch_seconds / 1000) / 86400 ) + 25569
Resulting in a 5 digit answer which is 10 bits long format dd/MM/yyyy
(Note: the slashes are encoded in the result here so when converting to human readable date please account for it)
Where one day = 86400 ms
and the date 1970/1/1 = 25569
example:=( (1510827144853/1000) / 86400 ) + 25569 = 43055
put 43055 in excel and format cell to date dd/MM/yyyy and it gives you 16/11/2017
Perhaps, you are looking for strftime function.
char text[100];
time_t now = time(NULL);
struct tm *t = localtime(&now);
strftime(text, sizeof(text)-1, "%d %m %Y %H:%M", t);
printf("Current Date: %s", text);

Convert milliseconds to time_t

I have the number of milliseconds starting from 2004 to a specific date. I want to convert this to time_t to display it with ctime()?
Perhaps there is another method to visualize the date by this milliseconds timestamp, does anyone have one?
Assuming that by “starting from 2004” you mean “starting from 2004 at 00:00 UTC”, then
time_t time = 1072915200 + millis / 1000;
Beware that ctime() will display the result in local time instead of UTC.
time_t only has an accuracy of one second. If this is ok for you, you can calculate the time_t value for 2004-01-01 using mktime() and add it to your time-value in seconds.
struct tm tp;
memset(&tp, 0, sizeof(tp));
tp.tm_mday = 1;
tp.tm_mon = 0;
tp.tm_year = 2004 - 1900;
time_t offset = mktime(&tp);
time_t realtime = offset + yourtime / 1000;
printf("%s", ctime(&realtime));

Get the time zone GMT offset in C

I'm using the standard mktime function to turn a struct tm into an epoch time value. The tm fields are populated locally, and I need to get the epoch time as GMT. tm has a gmtoff field to allow you to set the local GMT offset in seconds for just this purpose.
But I can't figure out how to get that information. Surely there must be a standard function somewhere that will return the offset? How does localtime do it?
Just do the following:
#define _GNU_SOURCE /* for tm_gmtoff and tm_zone */
#include <stdio.h>
#include <time.h>
/* Checking errors returned by system calls was omitted for the sake of readability. */
int main(void)
{
time_t t = time(NULL);
struct tm lt = {0};
localtime_r(&t, &lt);
printf("Offset to GMT is %lds.\n", lt.tm_gmtoff);
printf("The time zone is '%s'.\n", lt.tm_zone);
return 0;
}
Note: The seconds since epoch returned by time() are measured as if in Greenwich.
How does localtime do it?
According to localtime man page
The localtime() function acts as if it called tzset(3) and sets the
external variables tzname with information about the current timezone,
timezone with the difference between Coordinated Universal
Time (UTC) and local standard time in seconds
So you could either call localtime() and you will have the difference in timezone or call tzset():
extern long timezone;
....
tzset();
printf("%ld\n", timezone);
Note: if you choose to go with localtime_r() note that it is not required to set those variables you will need to call tzset() first to set timezone:
According to POSIX.1-2004, localtime() is required to behave as though
tzset() was called, while localtime_r() does not have this
requirement. For portable code tzset() should be called before
localtime_r()
The universal version of obtaining local time offset function is here.
I borrowed pieces of code from this answer in stackoverflow.
int time_offset()
{
time_t gmt, rawtime = time(NULL);
struct tm *ptm;
#if !defined(WIN32)
struct tm gbuf;
ptm = gmtime_r(&rawtime, &gbuf);
#else
ptm = gmtime(&rawtime);
#endif
// Request that mktime() looksup dst in timezone database
ptm->tm_isdst = -1;
gmt = mktime(ptm);
return (int)difftime(rawtime, gmt);
}
I guess I should have done a bit more searching before asking. It turns out there's a little known timegm function which does the opposite of gmtime. It's supported on GNU and BSD which is good enough for my purposes. A more portable solution is to temporarily set the value of the TZ environment variable to "UTC" and then use mktime, then set TZ back.
But timegm works for me.
This is the portable solution that should work on all standard C (and C++) platforms:
const std::time_t epoch_plus_11h = 60 * 60 * 11;
const int local_time = localtime(&epoch_plus_11h)->tm_hour;
const int gm_time = gmtime(&epoch_plus_11h)->tm_hour;
const int tz_diff = local_time - gm_time;
Add std:: namespace when using C++. The result is in hours in the range [-11, 12];
Explanation:
We just convert the date-time "1970-01-01 11:00:00" to tm structure twice - with the local timezone and with the GMT. The result is the difference between hours part.
The "11:00::00" has been chosen because this is the only time point (considering GMT) when we have the same date in the whole globe. Because of that fact, we don't have to consider the additional magic with date changing in the calculations.
WARNING
Previous version of my answer worked only on linux:
// DO NOT DO THAT!!
int timezonez_diff = localtime(&epoch_plus_11h)->tm_hour -
gmtime(&epoch_plus_11h)->tm_hour;
This may not work because the storage for result tm object returned as a pointer from localtime and gmtime may be shared (and it is on windows/msvc). That's whe I've introduced temporaries for calculation.
I believe the following is true in linux at least: timezone info comes from /usr/share/zoneinfo/. localtime reads /etc/localtime which should be a copy of the appropriate file from zoneinfo. You can see whats inside by doing zdump -v on the timezone file (zdump may be in sbin but you don't need elevated permissions to read timezone files with it). Here is a snipped of one:
/usr/share/zoneinfo/EST5EDT Sun Nov 6 05:59:59 2033 UTC = Sun Nov 6 01:59:59 2033 EDT isdst=1 gmtoff=-14400
/usr/share/zoneinfo/EST5EDT Sun Nov 6 06:00:00 2033 UTC = Sun Nov 6 01:00:00 2033 EST isdst=0 gmtoff=-18000
/usr/share/zoneinfo/EST5EDT Sun Mar 12 06:59:59 2034 UTC = Sun Mar 12 01:59:59 2034 EST isdst=0 gmtoff=-18000
/usr/share/zoneinfo/EST5EDT Sun Mar 12 07:00:00 2034 UTC = Sun Mar 12 03:00:00 2034 EDT isdst=1 gmtoff=-14400
/usr/share/zoneinfo/EST5EDT Sun Nov 5 05:59:59 2034 UTC = Sun Nov 5 01:59:59 2034 EDT
I guess you could parse this yourself if you want. I'm not sure if there is a stdlib function that just returns the gmtoff (there may well be but I don't know...)
edit: man tzfile describes the format of the zoneinfo file. You should be able to simply mmap into a structure of the appropriate type. It appears to be what zdump is doing based on an strace of it.
Here's a two-liner inspired by #Hill's and #friedo's answers:
#include <time.h>
...
time_t rawtime = time(0);
timeofs = timegm(localtime(&rawtime)) - rawtime;
Returns offset from UTC in seconds.
Doesn't need _GNU_SOURCE defined, but note that timegm is not a POSIX standard and may not be available outside of GNU and BSD.
Ended up with this. Sure tm_secs is redundant, just for a sake of consistency.
int timezone_offset() {
time_t zero = 0;
const tm* lt = localtime( &zero );
int unaligned = lt->tm_sec + ( lt->tm_min + ( lt->tm_hour * 60 ) ) * 60;
return lt->tm_mon ? unaligned - 24*60*60 : unaligned;
}
Here is my way:
time_t z = 0;
struct tm * pdt = gmtime(&z);
time_t tzlag = mktime(pdt);
Alternative with automatic, local storage of struct tm:
struct tm dt;
memset(&dt, 0, sizeof(struct tm));
dt.tm_mday=1; dt.tm_year=70;
time_t tzlag = mktime(&dt);
tzlag, in seconds, will be the negative of the UTC offset; lag of your timezone Standard Time compared to UTC:
LocalST + tzlag = UTC
If you want to also account for "Daylight savings", subtract tm_isdst from tzlag, where tm_isdst is for a particular local time struct tm, after applying mktime to it (or after obtaining it with localtime ).
Why it works:
The set struct tm is for "epoch" moment, Jan 1 1970, which corresponds to a time_t of 0.
Calling mktime() on that date converts it to time_t as if it were UTC (thus getting 0), then subtracts the UTC offset from it in order to produce the output time_t. Thus it produces negative of UTC_offset.
Here is one threadsafe way taken from my answer to this post:
What is the correct way to get beginning of the day in UTC / GMT?
::time_t GetTimeZoneOffset ()
{ // This method is to be called only once per execution
static const seconds = 0; // any arbitrary value works!
::tm tmGMT = {}, tmLocal = {};
::gmtime_r(&seconds, &tmGMT); // ::gmtime_s() for WINDOWS
::localtime_r(&seconds, &tmLocal); // ::localtime_s() for WINDOWS
return ::mktime(&tmGMT) - ::mktime(&tmLocal);
};

Resources