I'm sure this question is answered elsewhere, but I cannot find it on Google or SO, so here goes.
In C/C++, I want to convert a relative time in format dd-hh:mm:ss provided by
ps -o etime
to an absolute UTC formatted date.
This doesn't seem like it should be very hard. Supposing I have already got a function to produce the relative time stored in struct tm format:
struct tm *starting_rel_time = my_reltime_converstion(...);
time_t t = time(0);
struct tm *current_abs_time = localtime(&t);
what I want is basically the opposite of difftime:
struct *tm starting_abs_time = current_abs_time - starting_rel_time;
Now, I can write my own function to do the conversion, but it's a nightmare because of all the carry operations and special conditions (leap years etc.). Surely there is a way to do this in the C/C++ libraries?
Use Boost::Date_Time libraries.
Convert the DD-HH:MM::SS to seconds with simple math; it's relative-time, so just multiply and add. Then, query the current time() in seconds (assuming it's "relative to now"), and add them. Then use gmtime to convert back to a struct tm.
There is no such language as C/C++.
If you're asking about C, I suggest representing dates internally with a simple numeric type, and converting to and from struct tm only when necessary. If you only need to cover a few decades, then you could use time_t and convert using the standard gmtime and mktime library functions. To cover a wider timespan, you could use a Julian day representation.
If you're asking about C++, I suggest the Boost.Date_Time library. Of course, the C library functions are still available if they meet your needs.
What you're trying to do doesn't make sense. You cannot add two dates.
(And difftime doesn't return a date, nor a time_t.)
In practice, on most, if not all implementations, time_t will be an
integral type with the number of seconds since some specific "epoch".
On such machines, you can add or subtract an integral number of seconds
from a time_t to get a new time, at least if all of the times you're
interested in are in the interval supported by time_t (roughly between
1970 and 2038 on most Unix platforms). This, along with gmtime,
mktime and localtime is probably sufficient for your needs. Note
especially that mktime is required to "correct" it's tm input: you
can, for example, take a tm, add 5 to the field tm_mday, call
mktime on it, and get the correct values for a date five days in the
future—all of the necessary carry operations and special
conditions are handled in mktime.
If this is not sufficient, C++11 has both a time_point and a
duration class, with (from a quick glance) seems to have all of the
functionality you could possibly need.
Related
I have the following string
2013-04-12 16:00:15.041
What is the C++ way to convert this string into a 64bit UNIX timestamp? Most question on here deal with only having the timestamp until seconds but in my case I also need to include the milliseconds.
Parse the string into its components and construct a std::chrono::time_point. In C++20, you will have the utc_clock in <chrono>, that is, a utc_time, or else std::chrono::local_t. In C++11 and up, you have std::chrono::system_clock.
There’s sort of a chicken-and-egg problem in converting the broken-down-time into STL time objects, though: usually, doing that gives you your answer with just the C library. You can use std::get_time(), on an istringstream if necessary, to convert your string to a tm and the C standard library function mktime() to convert the tm to a time_t, which you can then pass to std::chrono::system_clock::from_time_t()to convert to a std::chrono::time_point—except that, on UNIX/Linux, time_t is already a UNIX timestamp!
Your example has no time zone specified, so you might or might not need to do time-zone conversion.
The utc_clock uses the same Epoch as POSIX, January 1, 1970, so the time_since_epoch() member function of utc_time/time_point gives you the answer as a std::chrono::duration. If you are using system_clock instead, the Epoch is implementation-defined (but almost always the same Epoch, too), so you will want to find a time_point for 12:00 AM January 1, 1970, and subtract it from the time_point you calculate to get a duration. You can convert this duration, call it moment, into seconds with std::chrono::seconds(moment).count(). You can then convert to int64_t or uint64_t (from <cstdint>) if you want to be sure to have an exactly 64-bit value.
There are a few different UNIX time formats, but if you want to convert your time in milliseconds into a C/POSIX timespec with nanoseconds, rather than the obsolete formats in <sys/time.h>, set .tv_nsec to the number of milliseconds times one million, or convert from std::chrono::milliseconds to std::chrono::nanoseconds.
If the purpose of this is to work with filesystem timestamps, you might want std::filesystem::file_time_type.
I need the local (with timezone offset) current time in seconds since epoch. The following code looks a bit clumzy because it creates an intermediate temporary structure tm which is superfluous. Why do I have to get time_t then convert it to tm in order to return to time_t? Is there a better way?
time_t ct = time(0);
tm lct = tm();
localtime_s(&lct, &ct);
ct = _mkgmtime(&lct);
If you want to get the local time (with time zone and DST applied) in portable C, then yes, it's generally a two-step procedure: starting with your time-since-the-epoch, first call localtime, then do something with the resulting broken-down struct tm. (Usually what I do next is call strftime.)
You can also call ctime to get a local time string directly.
The reason there are a lot of different function calls involved is that, unfortunately, there are several different time formats in use. (And the reason for that is that dates and times are complicated!) You can represent time as seconds-since-1970. You can represent it as a struct tm. You can represent it as a string (in one of several zillion formats). In Unix and Linux, you can represent it as a struct timeval or a struct timespec.
But one thing there isn't a straightforward or standard way to do, as you've discovered, is get local time as seconds-since-1970. But the reason for that is that it's not a very useful representation. In general, there are two things you might want to do with a date/time value: (1) perform computations on it or (2) display it to the user. If you want to display it to the user, you probably want to display it in local time, so there are lots of ways of converting to local time in human-readable format in any format you want. (As I said, the usual way is to call localtime, then strftime.) But if you want to perform computations, really the only way to do those is using seconds-since-1970 in UTC, because that makes all the other hairy problems go away. How many days are there in the month? Is it a leap year? What time zone are we in? Is daylight saving time in effect?
If you try to represent local time as seconds-since-1970, though, you're probably fibbing. For example, right now, the time is 1460383736, which is 14:08:56 UTC. Where I'm sitting, that's 10:08:56 EDT (U.S. Eastern time, DST in effect). So I suppose I could say that's 1460369336 seconds since 1970, local time. But, again where I'm sitting, 1460369336 seconds ago was not midnight on January 1, 1970 -- it was actually 11 pm on December 31, 1969. It's off by an hour, and the reason is that DST was not in effect on January 1, 1970.
So, bottom line, I would encourage you to rethink the way you're handling local times, because while it's possible to compute this "seconds-since-1970 as local time" value, it's an odd thing to do, and it's likely to cause you various problems which will be much harder to work around than if you used a more straightforward scheme.
But, if you really want to, here are two ways you might be able to determine the offset between UTC and local time, without calling gmtime or _mkgmtime:
Call localtime, and look at the tm_gmtoff field. (Unfortunately, this field is nonstandard, and not present on all systems.)
Call the obsolete ftime function, and look at the timezone field of struct timeb. (Here there are several gotchas: not only is ftime obsolete and nonstandard, but the timezone field is in minutes, and it's positive for zones west of Greenwich, while tm_gmtoff is negative.)
But, anyway, those would more or less directly give you the number to add to or subtract from your UTC seconds-since-1970 value to get "local" seconds-since-1970.
Here is a way to do this computation using the C++11/14 <chrono> library plus this free, open-source timezone library to do the conversion to local time.
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto now = floor<seconds>(system_clock::now());
auto s = current_zone()->to_local(now) - local_days{1970_y/jan/1};
cout << s.count() << '\n';
}
You first discover your current IANA timezone with current_zone(). Then you get the current time with system_clock::now() and truncate it to seconds. Next you can convert that to your local time, and then subtract the result from any epoch you desire (1970-01-01 in this example).
The result is of type std::chrono::seconds.
All this being said, I share the same reservations about doing this as described in Steve Summit's answer.
If you instead decide to represent the timestamp as a string, that is also easily done:
auto now = make_zoned(current_zone(), floor<seconds>(system_clock::now()));
auto str = format("%F %T %z", now);
str has type std::string. This just output for me:
2016-04-11 11:42:50 -0400
which is my current local time (truncated to seconds), and my current local UTC offset.
If in the future you decide that seconds-precision is too coarse, you can easily change the above code to any other precision by just changing one line:
floor<milliseconds>(system_clock::now());
and now the contents of str would look like:
2016-04-11 11:42:50.368 -0400
I have been searching for over an hour but I simply seem to not be able to find the solution!
I am looking for a function that gives me a similar struct as GetLocalTime on Windows does. The important thing for me is that this struct has hours, minutes, seconds and milliseconds.
localtime() does not include milliseconds and therefore I cannot use it!
I would apprechiate a solution that uses the standard library or another very small library since I am working on a Raspberry Pi and connot use large libraries like boost!
As it was mentioned above, there are not direct equivalent. If you can use C++ 11, <chrono> header allows to get the same result, but not in single call. You can use high_resolution_clock to get current Unix time in milliseconds, then you can get localtime C function to get time without milliseconds, and use current Unix time in milleseconds to find milliseconds count. It looks like you will have to write your own GetLocalTime implementation, but with C++ 11 it will not be complex.
GetLocalTime is not a usual Linux function.
Read time(7), you probably want clock_gettime(2), or (as commented by Joachim Pileborg), the older gettimeofday(2)
If you need some struct giving all of hours, minutes, seconds, milliseconds you have to code that yourself using localtime(3) and explicitly computing the millisecond part.
Something like the below code is printing the time with milliseconds
struct timespec ts = {0,0};
struct tm tm = {};
char timbuf[64];
if (clock_gettime(CLOCK_REALTIME, &ts))
{ perror("clock_gettime"), exit(EXIT_FAILURE);};
time_t tim = ts.tv_sec;
if (localtime(&tim, &tm))
{ perror("localtime"), exit(EXIT_FAILURE);};
if (strftime(timbuf, sizeof(timbuf), "%D %T", &tm))
{ perror("strftime"), exit(EXIT_FAILURE);};
printf("%s.%03d\n", timbuf, (int)(ts.tv_nsec/1000000));
You can use a combination of:
clock_gettime(CLOCK_REALTIME); returns local time, up to millisecond (of course constrained by the actual clock resolution); does not care of local timezone information, returns UTC time. Just use millisecond information (from tv_nsec field).
time(); returns local time, up to the second - no millisecond - also UTC time. time() results (a time_t) is easy to convert to the final format.
then convert time() result using localtime_r(); this sets up a structure very similar to Windows SYSTEMTIME; result is up to the second, and takes into account local timezone information.
finally set up the millisecond field using clock_gettime() results.
These routines are documented, not deprecated, portable.
You may need to call tzset() once (this sets the timezone information - a C global variable - from operating system environment - probably a heavy operation).
I need to store date in flat file. Is there any simple metod to get number of days since particular date (for example since 1 AD) using standard libary in C++11? The only one I know is to:
obtain std::tm structure, set all time values to 0
convert it to std::time_t (I do not know proper method yet)
divide time_t to get the resolution of one day
Can I perform it easier?
What you said sounds reasonable to start with. You can convert the struct tm to time_t using mktime. Note that mktime interprets its input as local time according to the system/environment settings, and there is no UTC counterpart that's as widely available without relying on "extra" libraries, but maybe that's not a problem for you.
Let's say I'm given a string such as this: "2009-4-9", meaning April 9th, 2009. Assuming for starters, that I am only concerned with local time (I have concerns about other time zones as well, but I'll tackle the easy stuff first). What is the best way to know if Daylight Savings was in effect at that time?
You can assume that the system has the properly updated timezone files such as /etc/localtime and I am interested in portable solutions. Either c or c++ are acceptable as well.
You can also assume that I only care about past or present dates, never the future.
Currently I have a "hack" which looks like this (I know localtime_r is a library extension, but it's close enough to the same functionality that I can make it portable)
struct tm tm;
// convert the time string to a broken down structure in localtime
if(strptime("2009-4-9", "%Y-%m-%d", &tm)) {
// convert the broken down structure into seconds since epoch
const time_t t = mktime(&tm);
// convert it back to a broken down structure, this seems pointless at first...
// but libc will fill out fields such as tm_isdst
localtime_r(&t, &tm);
printf("DST : %d\n", tm.tm_isdst);
}
While this works (and seems to be a pretty effective way), I feel like it's silly to be converting back and forth. Is there a better way?
You do not need to call localtime_r() at all. mktime() normalises the struct tm that you pass it, including setting tm_isdst to a nonnegative value if it was passed in set to -1. So all you need is:
struct tm tm = { 0 };
// convert the time string to a broken down structure in localtime
if(strptime("2009-4-9", "%Y-%m-%d", &tm)) {
tm.tm_isdst = -1;
// normalise the broken down structure - this calculates tm_isdst
mktime(&tm);
printf("DST : %d\n", tm.tm_isdst);
}
This behaviour of mktime() is required by the C standard; for example, C99 says:
The mktime function converts the broken-down time, expressed as
local time, in the structure pointed to by timeptr into a calendar
time value with the same encoding as that of the values returned by
the time function. The original values of the tm_wday and
tm_yday components of the structure are ignored, and the original
values of the other components are not restricted to the ranges
indicated above.276 On successful completion, the values of
the tm_wday and tm_yday components of the structure are set
appropriately, and the other components are set to represent the
specified calendar time, but with their values forced to the ranges
indicated above; the final value of tm_mday is not set until
tm_mon and tm_year are determined.
with footnote 276 explicitly addressing the tm_isdst member:
276) Thus, a positive or zero value for tm_isdst causes the mktime
function to presume initially that Daylight Saving Time, respectively,
is or is not in effect for the specified time. A negative value causes
it to attempt to determine whether Daylight Saving Time is in effect
for the specified time.
The conversion from UTC to local time is not a reversible function. For example, in the autumn when the clocks jump back an hour, the local times between 02:00 and 03:00 occur twice. Given a time in this interval, it's not possible to determine whether the local time happened at a specific UTC time in local daylight time, or one hour later in local standard time.
I have done many time conversions before in C and I think you pretty much have it the way I would have done it too. As far as I know, localtime_r and its relatives may be your only option (unless there is some third party date library.) Of course, Greg Hewgill is correct for the "gray hour" between time switches.