How to safely clock_cast days? - c++

I'm using HowardHinnant/date in lieu of the new C++20 calendar/timezone facilities that are not yet available in Clang/GCC. My question applies equally to both implementations: How do I safely clock_cast time_points having days duration?
When I try:
using namespace date; // or using std::chrono in C++20
tai_time<days> tai{days{42}};
sys_days sys = clock_cast<std::chrono::system_clock>(tai);
I get a "no viable conversion" error for the last statement. It turns out the result uses a duration that is common_type<days, seconds>, which comes from utc_clock::to_sys() that's used in the conversion. The common duration type is std::chrono::seconds, so it's normal that it can't be directly converted to a days duration.
I can get it to compile if I use an explicit duration_cast:
using namespace date; // or using std::chrono in C++20
tai_time<days> tai{days{42}};
auto casted = clock_cast<std::chrono::system_clock>(tai);
sys_days sys{std::chrono::duration_cast<days>(casted.time_since_epoch())};
... but I'm worried that the result might by off by a day due to the truncation (especially for dates preceding the epoch). Is this the correct way to do what I'm trying to do? Should I be using floor instead of duration_cast?
Why is there even a std::common_type_t<Duration, std::chrono::seconds> in utc_clock::to_sys() anyway? Shouldn't it simply return the same duration type?

The reason that the clock_cast insists on at least seconds precision is because the offset between the epochs of system_clock and tai_clock has a precision of seconds:
auto diff = sys_days{} - clock_cast<system_clock>(tai_time<days>{});
cout << diff << " == " << duration<double, days::period>{diff} << '\n';
Output:
378691210s == 4383.000116d
So a days precision cast would be lossy. Here's another way of looking at it:
cout << clock_cast<tai_clock>(sys_days{2021_y/June/1}) << '\n';
Output:
2021-06-01 00:00:37
I.e. TAI is currently 37 seconds ahead of system_clock in calendrical terms.
If all you want is the date, I recommend round<days>(result):
cout << round<days>(clock_cast<tai_clock>(sys_days{2021_y/June/1})) << '\n';
Output:
2021-06-01 00:00:00
One could conceivably use floor in one direction and ceil in the other, but that would be very error prone. round will do fine since the offset from an integral number of days is currently only 37 seconds and growing quite slowly.

Related

What does system_clock::now() value after seconds represent in C++ 20?

I'm exploring the timestamp in C++ 20 returned from system_clock::now() and when I print out the returned std::chrono::time_point it prints the date and time in the format YYYY-MM-DD HH:MM:SS.xxxxxxx.
Any idea what the xxxxxxx value is? I assumed microseconds initially but I realised microseconds are to six decimal places whereas this value is always to seven.
I'm running this in VS 2022.
#include <iostream>
#include <chrono>
int main()
{
using namespace std::chrono;
auto timeDate = zoned_time{ current_zone(), system_clock::now() };
std::cout << timeDate << std::endl; // Output: 2022-07-29 08:55:22.8582577 BST
}
It's the fractional part of a second.
The output you're getting comes from operator<<(zoned_time). This outputs the time in the format "{:L%F %T %Z}" (see cppreference.com). The %T part of the format is equivalent to "%H:%M:%S" (reference) and the %S specifies the seconds as a decimal floating-point number with a precision matching that of the precision of the input (i.e. the precision of
system_clock::now() in your case).

Seconds since Epoch to formatted time [duplicate]

How can I convert number of seconds since 1970 to DateTime in c++?
I am getting the time in the below format:
1296575549:573352
The left part of the colon is in seconds and the right part in micro seconds.
Please help.
Thanks,
Syd
Try and use gmtime() (see http://www.cplusplus.com/reference/clibrary/ctime/gmtime/) or localtime() to convert a time_t to a struct tm
Use boost::Date_Time to do this. Code below assumes _interval is number of seconds since 1970. Note this code example doesn't handle the micro-second portion, but I am sure it could be modified to do so.
#include <boost/date_time/gregorian/gregorian_types.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
double interval(1296575549.0f);
boost::posix_time::ptime m_DateTime = ptime(date(1970, 1, 1),
time_duration(0, 0, 0,
time_duration::ticks_per_second() *
(time_duration::fractional_seconds_type)_interval));
Three important things about converting time using C/C++ library.
gmtime() or localtime() from standard library convert from time_t to struct tm, but the resolution for time_t is seconds from epoch. So fractional seconds will not count.
mktime() converts backwards from struct tm to time_t, but it will return -1 if the input date is out of range. (Reference: year 2038 problem)
If you are not using 64bit timestamp, even you run programs on 64bit machines, you still have year 2038 problem. There are 64bit version functions like gmtime64(), localtime64(), mktime64() that may resolve the year out of range issue. (Reference)
New answer for a very old question. Rationale: Better tools.
Starting in C++11, one can easily store this quantity in a std::chrono::system_clock::time_point:
#include <chrono>
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace std::chrono;
istringstream in{"1296575549:573352"};
long si, usi;
char sep;
in >> si >> sep >> usi;
system_clock::time_point tp{seconds{si} + microseconds{usi}};
}
Though the epoch of system_clock is unspecified in C++11, every implementation is tracking Unix Time (time since 1970-01-01 00:00:00 UTC, excluding leap seconds). Different implementations will have different precisions for system_clock::time_point, but you don't really have to care about that when converting into system_clock::time_point as shown above. The <chrono> library will just do the right thing.
Starting in C++20 the Unix Time measure of system_clock::time_point will be specified, and you will be able to stream it out in a human readable form:
cout << tp << '\n';
For this example that will output:
2011-02-01 15:52:29.573352
On some platforms there may be a few trailing zeroes on this output, depending on the precision of system_clock::time_point on that platform. This is a UTC date/time. There will also be ways to convert this to a particular time zone if desired (in C++20).
You can experiment with this part of C++20 today by using Howard Hinnant's date/time library. This will require an additional #include "date/date.h" ("date/tz.h" for time zone functionality), and a using namespace date; to enable the system_clock::time_point streaming operator.
If it is known that your count of seconds includes leap seconds (Unix Time stamps don't), C++20 also provides a way to deal with that:
Just change system_clock to utc_clock in the above example and the new output will be:
2011-02-01 15:52:05.573352
which accounts for the 24 leap seconds inserted prior to this date.
As an example of duration available in seconds, let's assume you want to have an idea of the running time of your program:
#include <ctime>
time_t startRawTime;
time( &startRawTime );
//...your program performs computations...
time_t endRawTime;
time( &endRawTime );
time_t elapsedSec = difftime( endRawTime, startRawTime );
// but elapsedSec can be any duration, e.g. 3734, as long as it is in seconds
tm * ptm = gmtime( &elapsedSec );
printf( "elapsed time: %02dh %02dm %02ds\n",
ptm->tm_hour,
ptm->tm_min,
ptm->tm_sec );
You would get something like this for instance:
elapsed time: 01h 02m 14s
Well, the most complex case:
86'400 s/day
31'557'600 s/year (365.25 d)
1296575549/31557600 = 41 years
1296575549-41*31557600 = 2'713'949 s
2713949/86400 ==> 31 d
2713949-31*86400 =35'549
35549/(60*60) = 9h
35549-9*60*60 = 3'149
3'149/(60*60) = 0h
3'149- 0* (60*60) = 3'149
3149/60 = 52 m
3149-52*60 = 29s
--> year = 1970 + 41
--> month = 1 + 0
--> day = 31 (+1?)
--> Time 00:52:29
==> 2011, Jan 31, 00:52:29 GMT
To calculate the month from the day, you need to copy paste an isLeapYear function, because of February.
Hmm, looks like one also needs to account for the leap years until last leap year, which were subtracted in surplus. Hmm, effects of daylight saving time...
Go sleep !

Error in comparing two std::chrono::time_point instances

I have two std::chrono::time_point instances in variables exp and time. exp has a time in the future and time is the current time. But when I compare them as in this snippet:
std::time_t t_exp = std::chrono::system_clock::to_time_t(exp);
std::time_t t_time = std::chrono::system_clock::to_time_t(time);
std::cout << std::ctime(&t_exp) << std::ctime(&t_time) << (time > exp) << std::endl;
I get output:
Sat Apr 26 01:39:43 4758
Fri May 29 18:11:59 2020
1
Which is wrong because exp is in the year 4758 and time is in the year 2020.
Where am I going wrong?
t_exp is -4243023785
This value of time_t corresponds to 1835-07-18 22:16:55 (assuming the Unix epoch and a precision of seconds, neither of which are specified by the standard, but are common).
Apparently the implementation of ctime on your platform can't handle dates this far in the past, which is a little surprising as 1835 is not very far in the past.
The value of exp is -4243023785 times a million or a billion (depending on the precision of system_clock on your platform) and is stored with a signed 64 bit integer (there is no overflow). Thus time > exp == 1 is correct (time is 1590775919s converted to the precision of system_clock).
Sat Apr 26 01:39:43 4758 corresponds to a time_t of 87990716383.
I see nothing wrong with your use of the chrono library in the above code.
Update
The value 87990716383 is being converted to a time_point using from_time_t()
Ah, this combined with the knowledge that on your platform the precision of system_clock is nanoseconds tells me that you are experiencing overflow on the construction of exp.
This is not the code you have:
std::time_t t_exp = std::chrono::system_clock::to_time_t(exp);
std::time_t t_time = std::chrono::system_clock::to_time_t(time);
std::cout << std::ctime(&t_exp) << std::ctime(&t_time) << (time > exp) << std::endl;
The code you have looks something like:
// ...
std::time_t t_exp = 87990716383;
auto exp = std::chrono::system_clock::from_time_t(t_exp);
std::cout << std::ctime(&t_exp) << std::ctime(&t_time) << (time > exp) << std::endl;
On your platform, system_clock stores nanoseconds since 1970-01-01 00:00:00 UTC in a signed 64 bit integer. The maximum storable date (system_clock::time_point::max()) on your platform is:
2262-04-11 23:47:16.854775807
Beyond this, the underlying storage of nanoseconds overflows.
When 87990716383 (seconds) is converted in from_time_t, it is multiplied by a billion which overflows. The overflowed value is -4243003985547758080 which corresponds to the date 1835-07-19 03:46:54.452241920.
You can get a larger range by using a coarser precision, for example:
std::time_t t_exp = 87990716383;
time_point<system_clock, microseconds> exp{seconds{t_exp}};
// exp == 4758-04-26 01:39:43.000000

What happened when constructing a variable of std::chrono::milliseconds from "LLONG_MAX seconds"?

When constructing a variable of std::chrono::milliseconds from "LLONG_MAX seconds", the result t_milli.count() is -1000
auto t_max_seconds = std::chrono::seconds(LLONG_MAX);
auto t_milli = std::chrono::milliseconds(t_max_seconds);
As far as I can see, somehow "-1" came from "LLONG_MAX", and "1000" was the ratio.
(for "microseconds", the result is -1'000'000)
I wonder what happend here, an overflow or undefined-behavior?
You are getting signed overflow in the conversion from seconds to milliseconds.
On your machine both seconds and milliseconds are represented by signed 64 bit integers. But to convert seconds to milliseconds, the library multiplies by 1000.
You are effectively doing this:
cout << LLONG_MAX*1000 << '\n';
which on my machine prints out:
-1000
std::chrono::seconds is a std::chrono::duration<>, just like std::chrono::milliseconds. As you figured, std::chrono::milliseconds has a ratio of std::milli, which is std::ratio<1,1000>.
The Undefined Behavior here is that the representation of std::chrono::seconds may have as little as 35 bits. Only std::chrono::nanoseconds is required to have a (signed) 64 bit representation, and LLONG_MAX could even be higher than that.
Of course you can define using seconds = std::chrono::duration<std::int64_t>;. There's nothing magical about std::chrono::seconds. Converting that to milliseconds has its own overflow risk, naturally.
I suspect you want std::chrono::seconds::max().

How can I convert number of seconds since 1970 to DateTime in c++?

How can I convert number of seconds since 1970 to DateTime in c++?
I am getting the time in the below format:
1296575549:573352
The left part of the colon is in seconds and the right part in micro seconds.
Please help.
Thanks,
Syd
Try and use gmtime() (see http://www.cplusplus.com/reference/clibrary/ctime/gmtime/) or localtime() to convert a time_t to a struct tm
Use boost::Date_Time to do this. Code below assumes _interval is number of seconds since 1970. Note this code example doesn't handle the micro-second portion, but I am sure it could be modified to do so.
#include <boost/date_time/gregorian/gregorian_types.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
double interval(1296575549.0f);
boost::posix_time::ptime m_DateTime = ptime(date(1970, 1, 1),
time_duration(0, 0, 0,
time_duration::ticks_per_second() *
(time_duration::fractional_seconds_type)_interval));
Three important things about converting time using C/C++ library.
gmtime() or localtime() from standard library convert from time_t to struct tm, but the resolution for time_t is seconds from epoch. So fractional seconds will not count.
mktime() converts backwards from struct tm to time_t, but it will return -1 if the input date is out of range. (Reference: year 2038 problem)
If you are not using 64bit timestamp, even you run programs on 64bit machines, you still have year 2038 problem. There are 64bit version functions like gmtime64(), localtime64(), mktime64() that may resolve the year out of range issue. (Reference)
New answer for a very old question. Rationale: Better tools.
Starting in C++11, one can easily store this quantity in a std::chrono::system_clock::time_point:
#include <chrono>
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace std::chrono;
istringstream in{"1296575549:573352"};
long si, usi;
char sep;
in >> si >> sep >> usi;
system_clock::time_point tp{seconds{si} + microseconds{usi}};
}
Though the epoch of system_clock is unspecified in C++11, every implementation is tracking Unix Time (time since 1970-01-01 00:00:00 UTC, excluding leap seconds). Different implementations will have different precisions for system_clock::time_point, but you don't really have to care about that when converting into system_clock::time_point as shown above. The <chrono> library will just do the right thing.
Starting in C++20 the Unix Time measure of system_clock::time_point will be specified, and you will be able to stream it out in a human readable form:
cout << tp << '\n';
For this example that will output:
2011-02-01 15:52:29.573352
On some platforms there may be a few trailing zeroes on this output, depending on the precision of system_clock::time_point on that platform. This is a UTC date/time. There will also be ways to convert this to a particular time zone if desired (in C++20).
You can experiment with this part of C++20 today by using Howard Hinnant's date/time library. This will require an additional #include "date/date.h" ("date/tz.h" for time zone functionality), and a using namespace date; to enable the system_clock::time_point streaming operator.
If it is known that your count of seconds includes leap seconds (Unix Time stamps don't), C++20 also provides a way to deal with that:
Just change system_clock to utc_clock in the above example and the new output will be:
2011-02-01 15:52:05.573352
which accounts for the 24 leap seconds inserted prior to this date.
As an example of duration available in seconds, let's assume you want to have an idea of the running time of your program:
#include <ctime>
time_t startRawTime;
time( &startRawTime );
//...your program performs computations...
time_t endRawTime;
time( &endRawTime );
time_t elapsedSec = difftime( endRawTime, startRawTime );
// but elapsedSec can be any duration, e.g. 3734, as long as it is in seconds
tm * ptm = gmtime( &elapsedSec );
printf( "elapsed time: %02dh %02dm %02ds\n",
ptm->tm_hour,
ptm->tm_min,
ptm->tm_sec );
You would get something like this for instance:
elapsed time: 01h 02m 14s
Well, the most complex case:
86'400 s/day
31'557'600 s/year (365.25 d)
1296575549/31557600 = 41 years
1296575549-41*31557600 = 2'713'949 s
2713949/86400 ==> 31 d
2713949-31*86400 =35'549
35549/(60*60) = 9h
35549-9*60*60 = 3'149
3'149/(60*60) = 0h
3'149- 0* (60*60) = 3'149
3149/60 = 52 m
3149-52*60 = 29s
--> year = 1970 + 41
--> month = 1 + 0
--> day = 31 (+1?)
--> Time 00:52:29
==> 2011, Jan 31, 00:52:29 GMT
To calculate the month from the day, you need to copy paste an isLeapYear function, because of February.
Hmm, looks like one also needs to account for the leap years until last leap year, which were subtracted in surplus. Hmm, effects of daylight saving time...
Go sleep !