I have a collection of unix timestamps I am converting to boost (1.65.1) dates but the conversions seem to break down when they get too far in the future. Anything around 2040 and beyond seems to be wrapping in some way back to post 1900.
Given the following code...
{
std::time_t t = 1558220400;
boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
std::cout << "Date: " << date << std::endl;
}
{
std::time_t t = 2145500000;
boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
std::cout << "Date: " << date << std::endl;
}
{
std::time_t t = 2500000000;
boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
std::cout << "Date: " << date << std::endl;
}
... I get the following output...
Date: 2019-May-18
Date: 2037-Dec-27
Date: 1913-Feb-13
... however I am expecting the following output...
Expected output:
Date: 2019-May-18
Date: 2037-Dec-27
Date: 2049-Mar-22
Is there something I am doing wrong here?
It appears that you're experiencing the Year 2038 problem.
The largest number representable by 32 bit signed integer is 2'147'483'647. 2'147'483'647 seconds since 00:00:00 UTC on 1st of January 1970 (the UNIX epoch) is 03:14:07 UTC on 19th of January 2038. Any UNIX time after that is unrepresentable using a 32 bit signed integer.
Either std::time_t on the system is 32 bits, or it is converted into 32 bits inside the boost library. You can see from the source that boost converts the input into long using static_cast (and still does in version 1.70). long is 32 bits for example on windows, even on 64 bit architectures. It is 64 bits on many other systems such as 64 bit Linux.
In C++20 this can now look like:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
{
std::time_t t = 1558220400;
auto date = floor<days>(system_clock::from_time_t(t));
std::cout << "Date: " << date << '\n';
}
{
std::time_t t = 2145500000;
auto date = floor<days>(system_clock::from_time_t(t));
std::cout << "Date: " << date << '\n';
}
{
std::time_t t = 2500000000;
auto date = floor<days>(system_clock::from_time_t(t));
std::cout << "Date: " << date << '\n';
}
}
Output:
Date: 2019-05-18
Date: 2037-12-27
Date: 2049-03-22
If your time_t is 32 bits, then the above isn't quite sufficient to fix the problem. In that case, you must avoid the C API completely. This looks like:
{
auto t = 1558220400;
auto date = floor<days>(sys_seconds{seconds{t}});
std::cout << "Date: " << date << '\n';
}
{
auto t = 2145500000;
auto date = floor<days>(sys_seconds{seconds{t}});
std::cout << "Date: " << date << '\n';
}
{
auto t = 2500000000;
auto date = floor<days>(sys_seconds{seconds{t}});
std::cout << "Date: " << date << '\n';
}
If your vendor isn't shipping this part of C++20 yet, a free, open-source preview that works with C++11/14/17 is available.1
Just add:
#include "date/date.h"
...
using namespace date;
1 Full disclosure: I am the lead author of this library. I am not pursuing any financial gain from this effort. But sometimes people get grumpy if I don't fully disclose this information.
As noted by eerorika this is caused by integer overflow since the boost::posix_time::from_time_t is casting the 64bit time_t value to a 32 bit long (on Windows).
If you are in a pinch and find yourself in the same position then you can use the following function to perform the conversion:
boost::gregorian::datetimet_to_date(time_t t)
{
auto time = boost::posix_time::ptime(boost::gregorian::date(1970,1,1));
int64_t current_t = t;
long long_max = std::numeric_limits<long>::max();
while(current_t > 0)
{
long seconds_to_add = 0;
if(current_t >= long_max)
seconds_to_add = long_max;
else
seconds_to_add = static_cast<long>(current_t);
current_t -= seconds_to_add;
time += boost::posix_time::seconds(seconds_to_add);
}
return time.date();
}
Related
I have a windows filetime (for example, 132522078890080000)
In Python I easily can convert it like that. Result is 1607716289 (Sat Dec 12 2020 00:51:29 GMT+0500 (Yekaterinburg Standard Time)). That's right!
But in C++ I tried to convert it like that
#include <iostream>
#include <ctime>
#define WINDOWS_TICK 10000000
#define SEC_TO_UNIX_EPOCH 11644473600LL
uint64_t WindowsTickToUnixSeconds(uint64_t windowsTicks)
{
return (uint64_t)(windowsTicks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
}
int main() {
const uint64_t in_raw = 132522078890080000;
auto unix_timestamp = WindowsTickToUnixSeconds(in_raw);
time_t out = unix_timestamp;
std::cout << "Timestamp: " << unix_timestamp << std::endl;
std::cout << "Local: " << asctime(localtime(&out)) << std::endl;
std::cout << "GMT: " << asctime(gmtime(&out)) << std::endl;
return 0;
}
And result of that is
Timestamp: 1607734289
Local: Sat Dec 12 05:51:29 2020
GMT: Sat Dec 12 00:51:29 2020
As you can see the timestamps are different (1607734289, 1607716289, difference is 5 hours (because timezone is Asia/Yekaterinburg)).
I can easily subtract 5 hours but in that case it won't work in another timezone.
So how can I get correct timestamp?
Consider a historic date string of format:
Thu Jan 9 12:35:34 2014
I want to parse such a string into some kind of C++ date representation, then calculate the amount of time that has passed since then.
From the resulting duration I need access to the numbers of seconds, minutes, hours and days.
Can this be done with the new C++11 std::chrono namespace? If not, how should I go about this today?
I'm using g++-4.8.1 though presumably an answer should just target the C++11 spec.
std::tm tm = {};
std::stringstream ss("Jan 9 2014 12:35:34");
ss >> std::get_time(&tm, "%b %d %Y %H:%M:%S");
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
GCC prior to version 5 doesn't implement std::get_time. You should also be able to write:
std::tm tm = {};
strptime("Thu Jan 9 2014 12:35:34", "%a %b %d %Y %H:%M:%S", &tm);
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
New answer for old question. Rationale for the new answer: The question was edited from its original form because tools at the time would not handle exactly what was being asked. And the resulting accepted answer gives a subtly different behavior than what the original question asked for.
I'm not trying to put down the accepted answer. It's a good answer. It's just that the C API is so confusing that it is inevitable that mistakes like this will happen.
The original question was to parse "Thu, 9 Jan 2014 12:35:34 +0000". So clearly the intent was to parse a timestamp representing a UTC time. But strptime (which isn't standard C or C++, but is POSIX) does not parse the trailing UTC offset indicating this is a UTC timestamp (it will format it with %z, but not parse it).
The question was then edited to ask about "Thu Jan 9 12:35:34 2014". But the question was not edited to clarify if this was a UTC timestamp, or a timestamp in the computer's current local timezone. The accepted answer implicitly assumes the timestamp represents the computer's current local timezone because of the use of std::mktime.
std::mktime not only transforms the field type tm to the serial type time_t, it also performs an offset adjustment from the computer's local time zone to UTC.
But what if we want to parse a UTC timestamp as the original (unedited) question asked?
That can be done today using this newer, free open-source library.
#include "date/date.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace date;
istringstream in{"Thu, 9 Jan 2014 12:35:34 +0000"};
sys_seconds tp;
in >> parse("%a, %d %b %Y %T %z", tp);
}
This library can parse %z. And date::sys_seconds is just a typedef for:
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
The question also asks:
From the resulting duration I need access to the numbers of seconds, minutes, hours and days.
That part has remained unanswered. Here's how you do it with this library.
#include "date/date.h"
#include <chrono>
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace date;
istringstream in{"Thu, 9 Jan 2014 12:35:34 +0000"};
sys_seconds tp;
in >> parse("%a, %d %b %Y %T %z", tp);
auto tp_days = floor<days>(tp);
auto hms = hh_mm_ss<seconds>{tp - tp_days};
std::cout << "Number of days = " << tp_days.time_since_epoch() << '\n';
std::cout << "Number of hours = " << hms.hours() << '\n';
std::cout << "Number of minutes = " << hms.minutes() << '\n';
std::cout << "Number of seconds = " << hms.seconds() << '\n';
}
floor<days> truncates the seconds-precision time_point to a days-precision time_point. If you subtract the days-precision time_point from tp, you're left with a duration that represents the time since midnight (UTC).
The type hh_mm_ss<seconds> takes any duration convertible to seconds (in this case time since midnight) and creates a {hours, minutes, seconds} field type with getters for each field. If the duration has precision finer than seconds this field type will also have a getter for the subseconds. Prior to C++17, one has to specify that finer duration as the template parameter. In C++17 and later it can be deduced:
auto hms = hh_mm_ss{tp - tp_days};
Finally, one can just print out all of these durations. This example outputs:
Number of days = 16079d
Number of hours = 12h
Number of minutes = 35min
Number of seconds = 34s
So 2014-01-09 is 16079 days after 1970-01-01.
Here is the full example but at milliseconds precision:
#include "date/date.h"
#include <chrono>
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace std::chrono;
using namespace date;
istringstream in{"Thu, 9 Jan 2014 12:35:34.123 +0000"};
sys_time<milliseconds> tp;
in >> parse("%a, %d %b %Y %T %z", tp);
auto tp_days = floor<days>(tp);
hh_mm_ss hms{tp - tp_days};
std::cout << tp << '\n';
std::cout << "Number of days = " << tp_days.time_since_epoch() << '\n';
std::cout << "Number of hours = " << hms.hours() << '\n';
std::cout << "Number of minutes = " << hms.minutes() << '\n';
std::cout << "Number of seconds = " << hms.seconds() << '\n';
std::cout << "Number of milliseconds = " << hms.subseconds() << '\n';
}
Output:
2014-01-09 12:35:34.123
Number of days = 16079d
Number of hours = 12h
Number of minutes = 35min
Number of seconds = 34s
Number of milliseconds = 123ms
This library is now part of C++20, but is in namespace std::chrono and found in the header <chrono>.
This is rather C-ish and not as elegant of a solution as Simple's answer, but I think it might work. This answer is probably wrong but I'll leave it up so someone can post corrections.
#include <iostream>
#include <ctime>
int main ()
{
struct tm timeinfo;
std::string buffer = "Thu, 9 Jan 2014 12:35:00";
if (!strptime(buffer.c_str(), "%a, %d %b %Y %T", &timeinfo))
std::cout << "Error.";
time_t now;
struct tm timeinfo2;
time(&now);
timeinfo2 = *gmtime(&now);
time_t seconds = difftime(mktime(&timeinfo2), mktime(&timeinfo));
time(&seconds);
struct tm result;
result = *gmtime ( &seconds );
std::cout << result.tm_sec << " " << result.tm_min << " "
<< result.tm_hour << " " << result.tm_mday;
return 0;
}
Cases covered (code is below):
since a give date until now
long int min0 = getMinutesSince( "2005-02-19 12:35:00" );
since the epoch until now
long int min1 = getMinutesSince1970( );
between two date+hours (since the epoch until a given date)
long int min0 = getMinutesSince1970Until( "2019-01-18 14:23:00" );
long int min1 = getMinutesSince1970Until( "2019-01-18 14:27:00" );
cout << min1 - min0 << endl;
Complete code:
#include <iostream>
#include <chrono>
#include <sstream>
#include <string>
#include <iomanip>
using namespace std;
// ------------------------------------------------
// ------------------------------------------------
long int getMinutesSince1970Until( string dateAndHour ) {
tm tm = {};
stringstream ss( dateAndHour );
ss >> get_time(&tm, "%Y-%m-%d %H:%M:%S");
chrono::system_clock::time_point tp = chrono::system_clock::from_time_t(mktime(&tm));
return
chrono::duration_cast<chrono::minutes>(
tp.time_since_epoch()).count();
} // ()
// ------------------------------------------------
// ------------------------------------------------
long int getMinutesSince1970() {
chrono::system_clock::time_point now = chrono::system_clock::now();
return
chrono::duration_cast<chrono::minutes>( now.time_since_epoch() ).count();
} // ()
// ------------------------------------------------
// ------------------------------------------------
long int getMinutesSince( string dateAndHour ) {
tm tm = {};
stringstream ss( dateAndHour );
ss >> get_time(&tm, "%Y-%m-%d %H:%M:%S");
chrono::system_clock::time_point then =
chrono::system_clock::from_time_t(mktime(&tm));
chrono::system_clock::time_point now = chrono::system_clock::now();
return
chrono::duration_cast<chrono::minutes>(
now.time_since_epoch()-
then.time_since_epoch()
).count();
} // ()
// ------------------------------------------------
// ------------------------------------------------
int main () {
long int min = getMinutesSince1970Until( "1970-01-01 01:01:00" );
cout << min << endl;
long int min0 = getMinutesSince1970Until( "2019-01-18 14:23:00" );
long int min1 = getMinutesSince1970Until( "2019-01-18 14:27:00" );
if ( (min1 - min0) != 4 ) {
cout << " something is wrong " << endl;
} else {
cout << " it appears to work !" << endl;
}
min0 = getMinutesSince( "1970-01-01 01:00:00" );
min1 = getMinutesSince1970( );
if ( (min1 - min0) != 0 ) {
cout << " something is wrong " << endl;
} else {
cout << " it appears to work !" << endl;
}
} // ()
I made two functions that are to calculate beginning timestamp of the day (i.e. at 00:00:00 of the day) and the hour (starting from 1 and up to 24) of a given epoch timestamp.
#include <cstdint>
#include <ctime>
const uint8_t FIRST_HOUR = 0x01; // 01, 02, ..., 24
const uint32_t SECS_PER_HOUR = 3600; // 3600 secs per hour
uint32_t CalcDaiBaseTimestamp(uint32_t in_ts) {
time_t ts = in_ts;
struct tm timeinfo = *localtime(&ts);
timeinfo.tm_hour = 0;
timeinfo.tm_min = 0;
timeinfo.tm_sec = 0;
time_t tmp_base_ts = mktime(&timeinfo);
return (uint32_t)tmp_base_ts;
}
void CalcDaiBaseTimestampAndHour(uint32_t in_ts,
uint32_t& base_ts,
uint8_t& hour_nth) {
base_ts = CalcDaiBaseTimestamp(in_ts);
hour_nth = (in_ts - base_ts) / SECS_PER_HOUR + FIRST_HOUR;
}
CalcDaiBaseTimestampAndHour is invoked from multiple threads.
The code is compiled with g++ (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4 and the program runs on Ubuntu 14.04 x64.
Most of time my program works well, but I have sometimes observed some "weird" result as shown below:
(timestamp: 1554459477.500) -> (base: 1553990400, hour_nth: 131)
While the correct result should be:
(timestamp: 1554459477.500) -> (base: 1554422400 / hour_nth: 11)
Because:
1554459477.500 = 2019-04-05 10:17:57.500
base_ts = 2019-04-05 00:00:00 = 1554422400
hour_nth = 11
Since the issue happens sometimes so I would suppose that the reason could be thread-safety of some ctime - related functions.
What could cause the "weird" results? Please help me troubleshoot this! If the reason is actually thread-safety of the ctime - related functions then how could I work around this (with some C++ 11 standard library e.g.)?
Could you please show me how to work around this using the date library?
Reference link: https://github.com/HowardHinnant/date
Code:
#include "date/date.h"
#include <iomanip>
#include <iostream>
int
main()
{
using namespace std::chrono;
using namespace date;
using dsec = duration<double>;
sys_time<dsec> tp{dsec{1554459477.500}};
std::cout << std::setprecision(3) << std::fixed
<< tp.time_since_epoch().count()
<< " = " << round<milliseconds>(tp) << '\n';
sys_seconds base_ts = floor<days>(tp);
std::cout << "base_ts = " << base_ts << " = "
<< base_ts.time_since_epoch().count() << '\n';
auto hour_nth = floor<hours>(tp - base_ts) + hours{1};
std::cout << "hour_nth = " << hour_nth.count() << '\n';
}
Output:
1554459477.500 = 2019-04-05 10:17:57.500
base_ts = 2019-04-05 00:00:00 = 1554422400
hour_nth = 11
Notes:
There exist no thread safety issues here.
As long as you don't need time zone support, "date/date.h" is a single-header, header-only library.
Everything above is UTC.
Documentation: https://howardhinnant.github.io/date/date.html
I have a time_t value of 1530173696 which represents Thursday, June 28, 2018 8:14:56 AM.
I want to round down the time to the nearest hour. Specifically, down to 1530172800, which represent Thursday, June 28, 2018 8:00:00 AM. So, my idea is to convert this time_t to a tm struct, and then assign its sec and min values to 0.
However, after I do that, and after I convert the modified tm back to a time_t value, the value I get is way off. I get a value of 1530158400 which represents Thursday, June 28, 2018 4:00:00 AM. That's 4 hours off. Even checking values of up to 8:59:59 AM still gives the rounded down value of 4:00:00 AM.
I wrote the code below to demonstrate the problem. I use VisulStudio 2017.
I don't understand what I am doing wrong. I appreciate any help. Thanks.
#include <iostream>
#include <time.h>
bool equalTMs(tm& tm1, tm& tm2);
void printTM(tm& myTM);
int main()
{
tm myTM;
time_t datetime = 1530173696;
//datetime = 1530176399; // to check the time_t value of 8:59 AM
gmtime_s(&myTM, &datetime);
myTM.tm_sec = 0;
myTM.tm_min = 0;
time_t myTime_T = mktime(&myTM);
tm sanityCheckTM;
time_t roundedDownToNearestHour = 1530172800;
gmtime_s(&sanityCheckTM, &roundedDownToNearestHour);
time_t sanityCheckTimeT = mktime(&sanityCheckTM);
std::cout << "datetime: " << datetime << std::endl;
std::cout << "myTime_T: " << myTime_T << std::endl;
std::cout << std::endl;
std::cout << "roundedDownToNearestHour: " << roundedDownToNearestHour << std::endl;
std::cout << "sanityCheckTimeT: " << sanityCheckTimeT << std::endl;
std::cout << std::endl;
std::cout << "myTM and sanityCheckTM equal? " << (equalTMs(myTM, sanityCheckTM) ? "true" : "false") << std::endl;
std::cout << "\nmyTM:-\n\n";
printTM(myTM);
std::cout << "\nsanityCheckTM:-\n\n";
printTM(sanityCheckTM);
std::cout << "\n";
time_t _time_t = 1530158400;
tm _tm;
gmtime_s(&_tm, &_time_t);
std::cout << "_time_t: " << _time_t << std::endl;
std::cout << "_tm and sanityCheckTM equal? " << (equalTMs(_tm, sanityCheckTM) ? "true" : "false") << std::endl;
std::cout << "\n_tm:-\n\n";
printTM(_tm);
}
void printTM(tm& myTM)
{
std::cout << "tm_sec: " << myTM.tm_sec << std::endl;
std::cout << "tm_min: " << myTM.tm_min << std::endl;
std::cout << "tm_hour: " << myTM.tm_hour << std::endl;
std::cout << "tm_mday: " << myTM.tm_mday << std::endl;
std::cout << "tm_mon: " << myTM.tm_mon << std::endl;
std::cout << "tm_year: " << myTM.tm_year << std::endl;
std::cout << "tm_wday: " << myTM.tm_wday << std::endl;
std::cout << "tm_yday: " << myTM.tm_yday << std::endl;
std::cout << "tm_isdst: " << myTM.tm_isdst << std::endl;
}
bool equalTMs(tm& tm1, tm& tm2)
{
return (tm1.tm_sec == tm2.tm_sec)
&& (tm1.tm_min == tm2.tm_min)
&& (tm1.tm_hour == tm2.tm_hour)
&& (tm1.tm_mday == tm2.tm_mday)
&& (tm1.tm_mon == tm2.tm_mon)
&& (tm1.tm_year == tm2.tm_year)
&& (tm1.tm_wday == tm2.tm_wday)
&& (tm1.tm_yday == tm2.tm_yday)
&& (tm1.tm_isdst == tm2.tm_isdst);
}
gmtime_s() returns a tm that is expressed in UTC time. You pass that to mktime(), which expects the tm to be expressed in LOCAL time instead. Your StackOverflow profile says you are located in Abu Dhabi, whose time zone is GMT+4. That is why you have a 4-hour discrepancy.
Use localtime_s() instead of gmtime_s().
Since 1530173696 is being used as a Unix Time (UTC excluding leap seconds), this can be solved without involving time zones.
Howard Hinnant's date/time library can be used to solve this problem, and to check that you're getting the right answer. However, skip to the end of this answer if you want to see how to do this very simply without the use of any library at all.
1530173696 is a count of seconds since 1970-01-01 UTC. If you want to convert this into a human readable date/time, one can:
#include "date/date.h"
#include <iostream>
int
main()
{
time_t datetime = 1530173696;
date::sys_seconds tp{std::chrono::seconds{datetime}};
using date::operator<<;
std::cout << tp << '\n';
}
which outputs:
2018-06-28 08:14:56
This does nothing but validate the input. Furthermore tp is nothing more than a std::chrono::time_point based on system_clock but with a precision of seconds. You can round this down to the hour with:
tp = floor<std::chrono::hours>(tp);
Here floor can be grabbed from "date.h" under namespace date, or if you have C++17 or later, you can use std::chrono::floor. You can use "date.h" to print tp out again and you will get:
2018-06-28 08:00:00
(as desired). To turn this back into a time_t, simply extract the duration, and then the count:
time_t myTime_T = tp.time_since_epoch().count();
This will have the value 1530172800 as expected.
Finally, if you do not need to print these time stamps out in a human readable form, you can do the math quite easily yourself:
time_t myTime_T = datetime / 3600 * 3600;
This is essentially the same operation as:
tp = floor<std::chrono::hours>(tp);
except that the floor version will continue to get the correct answer when the input is negative (a timestamp prior to 1970-01-01 00:00:00 UTC). The "manual" implementation will round up to the next hour when given a negative input.
I've problem with use of the std::localtime function. When I transform a std::time_t to a local struct tm, it always use the american daylight saving time whereas I want to use the european one (France).
UTC : 03/19/16 16:56 is transformed in Local : 03/19/16 18:56.
At this date, normally, local is 17:56 (UTC+1). The DST happens on the 27th in France.
After several tests, it seems that the used DST is based on the american rule : DST happens on second sunday in march.
I've also changed the TZ environement variable, but it also fails.
`
if( putenv( "TZ=CET-1CEST-2,M3.5.0/2,M10.5.0/3" ) != 0 ) {
std::cout << "Unable to set TZ" << std::endl;
} else {
tz = getenv("TZ");
if (tz) std::cout << tz << std::endl;
else std::cout << "TZ not defined" << std::endl; tzset();
}
struct std::tm t;
t.tm_sec = 0;
t.tm_min = 56;
t.tm_hour = 18;
t.tm_mday = 19;
t.tm_mon = 3-1;
t.tm_year = 2016-1900;
t.tm_isdst = -1;
std::time_t tt = std::mktime(&t);
std::cout << "UTC: " << std::put_time(std::gmtime(&tt), "%c %Z") << '\n'; // UTC : 03/19/16 16:56
std::cout << "local: " << std::put_time(std::localtime(&tt), "%c %Z") << '\n'; // Local : 03/19/16 18:56 (not waited result)
`
As a precision, I use the bcc32c compiler (the embarcadero C++ clang based computer).
I hope I'm clear enough and you'll be able to help me.
Thanks in advance
If you have C++11 (or later) available, which includes <chrono>, and if you are willing to work with the <chrono> system. And if you are willing to use this free, open-source timezone library. Then this is a very simple matter:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
auto zt = make_zoned("Europe/Paris", sys_days{2016_y/mar/19} + 18h + 56min);
std::cout << format("%m/%d/%y %H:%M %Z", zt.get_sys_time()) << '\n';
std::cout << format("%m/%d/%y %H:%M %Z", zt) << '\n';
}
Output:
03/19/16 18:56 UTC
03/19/16 19:56 CET
In short, you form one zoned_time by pairing a timezone of your choosing
("Europe/Paris"), with a sys_time (UTC). Then you can format that zoned_time both by extracting the sys_time back out of it, and by formatting the zoned_time itself, which will use the local_time of the zoned_time.
I attempted to use formatting that was consistent with your comments. You could of course you any format you want. You can also include any std::locale your system supports as the first argument in the calls to format (which %c would take advantage of).
You can also extract individual fields both from the sys_time and from the local_time if you need to, or do other date or time computations. This is more than just a formatting library.
If you are starting with a time_t, there is a static member function of std::chrono::system_clock to convert a time_t to a system_clock::time_point. You can then use that time_point in place of sys_days{2016_y/mar/19} + 18h + 56min in this example:
auto zt = make_zoned("Europe/Paris", system_clock::from_time_t(t));