How to generate date::local_time from std::chrono_time_point - c++

I'm using Howard Hinnant's time zone library.
https://howardhinnant.github.io/date/tz.html
My question: Is it possible to construct a date::local_time object from a std::chrono::time_point?
What I want to to:
// 'tp' exists and is some std::chrono::time_point object
auto locTime = date::local_time<std::chrono::milliseconds>(tp);
The constructor for this doesn't exist so a get a compilation error.
How do I do this (in nice and clean C++17)?
Background:
My ultimate goal is to compare a std::filesystem::file_time_type to a date::local_time<std::chrono::milliseconds>.
I do
auto fileTimeTp = std::chrono::clock_cast<std::chrono::system_clock>(someDirectoryEntryObject.last_write_time());
which gives my my std::chrono::time_point for the file date, but that's where I'm stuck...

This is a two part answer ...
Part 1
My question: Is it possible to construct a date::local_time object from a std::chrono::time_point?
I'm going to assume that std::chrono::time_point refers to std::chrono::system_clock::time_point (each clock has its own family of std::chrono::time_point).
Yes, it is possible. Background: system_clock::time_point is defined as Unix Time which is a close approximation to UTC. So to go from system_clock::time_point (also know as sys_time in the date library / C++20) to local_time, you need to pair the sys_time with a time_zone. This could be your computer's current local time zone, or any other IANA time zone.
To get the computer's current local time zone:
auto tz = date::current_zone();
The type of tz is a date::time_zone const*. time_zone has a member function called to_local that will translate a sys_time into a local_time:
auto locTime = tz->to_local(system_clock::now());
The precision of locTime will match whatever the precision of the input sys_time is.
If you would like to use some other time zone, then you can use date::locate_zone to get a date::time_zone const* to that time zone.
auto locTime = date::locate_zone("America/New_York")->local_time(system_clock::now());
Part 2
My ultimate goal is to compare a std::filesystem::file_time_type to a date::local_time<std::chrono::milliseconds>.
Ah, this won't really involve local_time at all. And unfortunately, file_clock is not implemented in the time_zone library.
In C++20, this will be quite easy: Given a file_time and a sys_time you can convert either to the other using clock_cast:
if (clock_cast<system_clock>(ftp) >= system_clock::now())
...
However in C++17 it is harder, and non-portable. The the time_zone library makes it easier, but not easy.
You first have to deduce the epoch of std::filesystem::file_time_type on your platform. This will be different depending on which implementation of std::filesystem::file_time_type you are using.
Existing epochs include:
* 1970-01-01 00:00:00 UTC
* 1601-01-01 00:00:00 UTC
* 2174-01-01 00:00:00 UTC
Then you subtract the sys_time epoch (sys_days{1970_y/1/1}) and the file_time epoch (e.g. sys_days{1601_y/1/1}), and add/subtract that epoch to convert from one measure to the other.
For example:
constexpr auto diff = sys_days{1970_y/1/1} - sys_days{1601_y/1/1};
file_time_type ftp = ...
system_clock::time_point tp{ftp.time_since_epoch() - diff};
That's unfortunately quite messy, and I'm looking forward to clock_cast working with file_clock in C++20.

Related

Convert double to zoned_time using Howard Hinnant's date library

I have a double representing the time in days since midnight (local time zone) 1 Jan 1970 and a string representing the time zone. I would like to convert these to a
date::zoned_time
using Howard Hinnant's date and time zone library.
The background is I need to convert date-times to and from doubles to use in an analytics library. I will also receive date-times as doubles from excel in a local or user-specified time zone.
Here is one attempt I made
using namespace date;
using namespace std::chrono;
typedef date::zoned_time<std::chrono::seconds> datetime;
const double UNIX_MINUS_EXCEL_EPOCH = 25569.0;
const double SECONDS_PER_DAY = 24.0 * 60.0 * 60.0;
datetime timePointFromDouble(double x)
{
double seconds = (x - UNIX_MINUS_EXCEL_EPOCH) * SECONDS_PER_DAY;
system_clock::duration d = duration_cast<system_clock::duration>(duration<double>(seconds));
system_clock::time_point t = system_clock::time_point(d);
auto xx = make_zoned("America/Chicago", t);
return xx;
}
It doesn't compile because the result of make_zoned has the wrong type. Also, I am not convinced it correctly maps the input time in days to the output date-time because of leap seconds and days where daylight saving changes.
Specification:
x is a measure of days since 1899-12-30 00:00:00 in America/Chicago.
Solution:
using datetime = date::zoned_seconds;
datetime
timePointFromDouble(double x)
{
using namespace date;
using namespace std::chrono;
using ddays = duration<double, days::period>;
constexpr auto excel_epoch = local_days{1_d/January/1970} -
local_days{30_d/December/1899};
return datetime{"America/Chicago",
local_seconds{round<seconds>(ddays{x} - excel_epoch)}};
}
Explanation:
The reason your version doesn't compile is because of the conversion to system_clock::time_point, which in practice has a precision of microseconds or finer. But your result type has a precision of seconds, so the library is refusing to implicitly truncate your high-precision t to your lower-precision xx.
The easiest way to fix this is to time_point_cast<seconds>(t). But there's more fun to be had...
<chrono> lives and dies by handling the conversions for you. Any time you're doing the conversions yourself, you should opt for removing those conversions in favor of letting <chrono> do them. This will usually simplify your code, and it may just catch a conversion error.
<chrono> knows about how to convert among various durations, but not about the Excel epoch, so that's one conversion we can't avoid. But we can express that epoch in a higher level language than the mysterious constant 25569.0.
So, from the top:
date::zoned_seconds is a simpler way to write date::zoned_time<std::chrono::seconds>. It is just a convenience typedef.
ddays is a custom duration unit which represents 1 day with a double. This is convenient for converting the scalar input x directly into a <chrono> duration. It is best to get into the <chrono> type system as soon as possible.
The epoch difference is the amount of time between 1970-01-01 and 1899-12-30. The units will be days as I've coded it, but that is an unimportant detail. <chrono> takes care of the units for you.
I'm using local_days as opposed to sys_days to compute the epoch difference. This is a largely symbolic gesture to communicate that the epoch is in a local time, not UTC. It doesn't make a difference in the actual value of the constant that is computed.
Because of the way you worded the question, I assumed you would prefer day-month-year ordering in the code. This is a purely stylistic choice.
If you are writing this in C++11, excel_epoch will have to be made const instead of constexpr. The difference is that C++11 has to compute this constant at run-time and C++14 and later can compute it at compile-time.
When converting from double-based units to integral-based units, I like to use round instead of duration_cast. The difference is that round chooses the nearest representable value, and duration_cast truncates towards zero to the nearest representable value. The round strategy is more likely to result in stable round-trip conversions between the double and integral representations, whereas truncation is more likely to expose one-off differences due to round-off error in the double representation.
The last line has to explicitly get us from double-based to integral-based units, and has to specify seconds to match with the return type, but does not have to worry about converting days into seconds.
The last line uses local_seconds to convert the duration into a time_point because this duration represents a measure in the local time of America/Chicago, as opposed to a measure in UTC. This fixes the epoch to 1899-12-30 00:00:00 in America/Chicago as opposed to 1899-12-30 00:00:00 UTC.
The result does not take leap seconds into account. This is the correct thing to do, because neither does Excel nor system_clock. Just about every computer-based time-keeping protocol out there doesn't count leap seconds. Here is a good description of Unix Time. If you want to convert to a system that counts leap seconds, this library can do that too. It is called utc_clock/utc_time.
The result does take daylight savings time into account for Chicago, including the changes to the daylight savings rules over the years, as best as the IANA database can do (which is exact as far as I know).

How to convert a datetime string with milliseconds to UNIX timestamp

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.

C++ : how to get a long value from json and convert it to time_point

I'm quite new to c++ so thera are a bunch of questions I've got but for now this one drives my crazy:
I've ot a json response and want to parse one object as long (because it's a timestamp). After that I want tp parse that long to a time_point object via
chrono::system_clock::from_time_t(...);
So this is what I got for now:
auto last_change_date_long = (long long)json_troubleticket["lastChangeDate"].int_value();
time_t last_change_date_raw = time_t(last_change_date_long);
auto last_change_date = chrono::system_clock::from_time_t(last_change_date_raw);
It compiles, but if i run this (while I know the value for lastChangeDate is 1480702672000) it's result is
2147483647000 ...
Does anyone have a suggestion what went wrong?
This will do it:
auto i = 1480702672000;
std::chrono::system_clock::time_point tp{std::chrono::milliseconds{i}};
Note that the above is not guaranteed to work by the standard because the epoch of system_clock is unspecified. However all implementations are currently using Unix Time, and I have an informal agreement with the implementors that they will not deviate from this while I try to standardize this existing practice.
The reason you're seeing the behavior you have is that your json is counting milliseconds since 1970-01-01 00:00:00 UTC, but time_t typically counts seconds (though that is also not specified by the standard). So at the point where you create last_change_date_raw from last_change_date_long, you're implicitly converting milliseconds to seconds. This would result in a date midway through the year 48891. The implementation of from_time_t is likely freaking out about that (overflowing).
Fwiw, this particular time point represents:
2016-12-02 18:17:52.000 UTC

How to get the local current time in seconds since epoch in C++ (MSVS)?

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

Get current timestamp in microseconds since epoch?

I have a below code from which we are trying to get current timestamp in microseconds since epoch time but we are using steady_clock.
inline uint64_t get_timestamp()
{
std::chrono::time_point<std::chrono::steady_clock> ts = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(ts.time_since_epoch()).count();
}
Is this the right way to do that since as per my understanding steady_clock is used to measure the passage of time not to get the current time of day? Or should I use system_clock for this like as shown below:
inline uint64_t get_timestamp()
{
std::chrono::time_point<std::chrono::system_clock> ts = std::chrono::system_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(ts.time_since_epoch()).count();
}
I need to use std::chrono package only since that's what all our code is using.
The epochs of the chrono clocks are unspecified. But practically you can think of the chrono clocks this way:
The epoch of steady_clock is the time your application launched plus a signed random offset. I.e. you can't depend upon the epoch being the same across application launches. But the epoch will remain stable while an application is running.
The epoch of system_clock is time since New Years 1970, not counting leap seconds, in the UTC timezone. Different implementations implement this with varying precision: libc++ counts microseconds, VS counts 1/10 of microseconds, and gcc counts nanoseconds.
high_resolution_clock is sometimes a type alias for steady_clock and sometimes a type alias for system_clock.
For a time stamp in microseconds I recommend first defining this type alias:
using time_stamp = std::chrono::time_point<std::chrono::system_clock,
std::chrono::microseconds>;
Store that, instead of uint64_t. The type safety of this type will save you countless run time errors. You'll discover your errors at compile time instead.
You can get the current time_stamp with:
using namespace std::chrono;
time_stamp ts = time_point_cast<microseconds>(system_clock::now());
Another possibility for people who couldn't get other solutions to work:
uint64_t microseconds_since_epoch = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();