C++ convert from System::DateTime to time_t [duplicate] - c++

I have already found several answers related to converting a std::time_t value to System::DateTime and back. However, almost all answers seem to neglect that the type of std::time_t is actually undefined in the standard. Most solutions just cast std::time_t to whatever needed or apply arithmetic operations to a std::time_t object which is possible since it's an arithmetic type, but there is no specification about the result of such an operation. I know that most compilers define time_t as an int of some size but the fact alone that it has changed from int32 to int64 in many implementations recently shows that changes are indeed possible.
So I've come up with this solution which should work with any type of std::time_t. It works from what I have seen. But I was wondering - Are there any possible pitfalls I might be unaware of?
template <>
inline System::DateTime marshal_as(const std::time_t &from_object)
{
// Returns DateTime in Local time format from time_t (assumed to be UTC)
const auto unix_epoch = makeUtcTime(1970, 1, 1, 0, 0, 0);
const auto unix_epoch_dt = System::DateTime(1970, 1, 1, 0, 0, 0, System::DateTimeKind::Utc);
const auto secondsSinceEpoch = std::difftime(from_object, unix_epoch);
return const_cast<System::DateTime&>(unix_epoch_dt).AddSeconds(secondsSinceEpoch).ToLocalTime();
} // end of System::DateTime marshal_as(const std::time_t &from_object)
template <>
inline std::time_t marshal_as(const System::DateTime &from_object)
{
// Returns time_t in UTC format from DateTime
auto from_dt = const_cast<System::DateTime&>(from_object).ToUniversalTime();
return makeUtcTime(from_dt.Year, from_dt.Month, from_dt.Day, from_dt.Hour, from_dt.Minute, from_dt.Second);
} // end of std::time_t marshal_as(const System::DateTime &from_object)
3 assumptions were made:
Resulting std::time_t should be in UTC since it doesn't contain any info on localization
Resulting System::DateTime should be local time since System::DateTime::Now returns a localized DateTime
makeUtcTime is a helper function creating a std::tm from the values supplied and creates a UTC std::time_t out of it. This is currently implemented using _mkgmtime because our interop code can safely rely on the existence of Microsoft extensions. However, a UTC version of mktime is readily available in other compilers as well (standard mktime expects local time).
2 less important things to consider:
The const_cast is necessary because the marshal_as-template expects a const T& as parameter and I can't access the properties of a const .NET value-type object. However there might be a better solution.
Should the unix_epoch... stuff be static const?
(I wasn't sure if this should be posted on "Programmers Exchange" since it's more of a discussion but since it's a very specific C++ question I thought SO might be the better place to ask)

It just isn't very productive to insist on a "standard conformant" way to make this conversion. The only place where an std::time_t and a System::DateTime are ever going to meet is covered by the Ecma-372 standard. Of which there is right now, and surely will ever be, only one implementation. The Mono project could be assumed to be the most likely source of another one, but right now they appear entirely uninterested in providing a mixed-mode implementation, the only reason you'd ever consider using C++/CLI.
std::time_t is steadily marching towards the Y2K38 disaster. With Microsoft pre-emptively having done something about it, and really having to do so because they went for LLP64, but everybody else counting on their LP64 data model keeping them out of trouble. In other words, no remaining 32-bit processors still running in 2038. This could well be a self-fulfilling prophesy.
Regardless, the conversion is going to have to work with elapsed seconds since 1/1/1970. And that can be a 32-bit or a 64-bit integral value, depending on implementation. The only warranty I can give is that this code is at least good until 2038:
#include <ctime>
using namespace System;
public ref class Conversions {
public:
static DateTime time_t2DateTime(std::time_t date) {
double sec = static_cast<double>(date);
return DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind::Utc).AddSeconds(sec);
}
static std::time_t DateTime2time_t(DateTime date) {
TimeSpan diff = date.ToUniversalTime() - DateTime(1970, 1, 1);
return static_cast<std::time_t>(diff.TotalSeconds);
}
};

Here's the solution my team arrived at:
DateTime represents the number of mixed-fractional days since midnight December 30, 1899, expressed as a double. I believe that this epoch date was used to account for the fact that 1900 was not a leap year, and it allows for an extra two days (Why two and not one? - It is not clear to me why Dec 31, 1899 was not chosen as their epoch.)
So a DateTime of 2.50 would be equivalent to January 1, 1900 12:00:00 , (i.e. the fraction represents 1/2 the day - 12PM).
We calculated that Jan 1, 1970 - the Unix Epoch - is 25569 days after the DateTime Epoch.
So the equivalent formula would be:
#include <time.h>
System::DateTime toDateTime(time_t &t)
{
return 25569.0 + t / 86400.0; // ensure you use floating point math or you will truncate the fractional portion
}

Related

Current time and date in C++20 days

I fast read the C++ reference about new chrono classes but I found them a little bit complicated.
So, the question is, how to re-write this code in C++20, to get year, month, day, hour, minute, second?
Are there any changes? I ask because of this minor problem with std::localtime: It is thread-unsafe. tm will be destroyed after next call to std::localtime.
std::time_t t = std::time(nullptr);
std::tm *tm = std::localtime(&t);
int year = tm->tm_year + 1900;
int month = tm->tm_mon + 1;
int day = tm->tm_mday;
int hour = tm->tm_hour;
int minute = tm->tm_min;
int second = tm->tm_sec;
#include <chrono>
int
main()
{
using namespace std::chrono;
// Get a local time_point with system_clock::duration precision
auto now = zoned_time{current_zone(), system_clock::now()}.get_local_time();
// Get a local time_point with days precision
auto ld = floor<days>(now);
// Convert local days-precision time_point to a local {y, m, d} calendar
year_month_day ymd{ld};
// Split time since local midnight into {h, m, s, subseconds}
hh_mm_ss hms{now - ld};
// This part not recommended. Stay within the chrono type system.
int year{ymd.year()};
int month = unsigned{ymd.month()};
int day = unsigned{ymd.day()};
int hour = hms.hours().count();
int minute = hms.minutes().count();
int second = hms.seconds().count();
}
I've tried to explain what each line of code does with a comment. I'm happy to elaborate further if anything is not clear.
This is all threadsafe.
More information:
Another and perhaps even more concise way to compute now is:
auto now = current_zone()->to_local(system_clock::now());
This results in the exact same type and value for now.
I used zoned_time above because it is (in general) a higher-level abstraction than calling member functions of a time_zone directly. In both examples the type of now is a simple std::chrono::time_point that is offset from system_clock::time_point by the UTC offset associated with the time_zone at this point in time.
In contrast zoned_time carries much more information. For example it knows about:
The name of the time_zone.
The abbreviation for the time_zone at this point in time.
The UTC offset of the time_zone at this point in time, and subsequently can produce both the local time and the UTC equivalent time.
The valid range of the offset/abbreviation pair.
Therefore a zoned_time is much more flexible for jobs like formatting as it can display the abbreviation and/or the UTC offset. And a zoned_time can also be more easily used to find out the equivalent time in other time zones.
Nevertheless, all of that extra information isn't actually used in this simple example, and is thus why I'm exposing the alternative of calling the to_local() member function of time_zone directly.
For the simple case of finding the local time, both techniques have identical behavior and performance, so it comes down to a matter of readability for choosing the preferred approach.

Does struct tm store time zone information as its data member

Consider the following C++ code
#include <ctime>
#include <iostream>
int main()
{
std::time_t now = std::time(nullptr);
struct tm local = *std::localtime(&now);
struct tm gm = *std::gmtime(&now);
char str[20];
std::strftime(str, 20, "%Z", &local);
std::cout << str << std::endl; // HKT
std::strftime(str, 20, "%Z", &gm);
std::cout << str << std::endl; // UTC
return 0;
}
So stored in now is an unambiguous integral value, while local and gm are struct tm that store human-readable date/time information. Then I print out the formatted information (timezone) based only on the struct tm objects.
According to the cplusplus reference, the data members of struct tm are
tm_sec
tm_min
tm_hour
tm_mday
tm_mon
tm_year
tm_wday
tm_yday
tm_isdst
If that's all that a struct tm contains, how does the program know that the timezone information from it? That is, how does it know that the timezone is HKT for local, and that the timezone is UTC for gm?
If that's not all that a struct tm contains, please explain how it stores timezone information.
By the way, though the demo code is in C++, I guess this question in essence stands as a legitimate C question as well.
The C standard says in 7.27.1 Components of time:
The tm structure shall contain at least the following members, in
any order. The semantics of the members and their normal ranges are
expressed in the comments.318)
int tm_sec; // seconds after the minute — [0, 60]
int tm_min; // minutes after the hour — [0, 59]
int tm_hour; // hours since midnight — [0, 23]
int tm_mday; // day of the month — [1, 31]
int tm_mon; // months since January — [0, 11]
int tm_year; // years since 1900
int tm_wday; // days since Sunday — [0, 6]
int tm_yday; // days since January 1 — [0, 365]
int tm_isdst; // Daylight Saving Time flag
(emphasis is mine)
That is, implementations are allowed to add additional members to tm, as you found with glibc/time/bits/types/struct_tm.h. The POSIX spec has nearly identical wording.
The result is that %Z (or even %z) can not be considered portable in strftime. The spec for %Z reflects this:
%Z is replaced by the locale’s time zone name or abbreviation, or
by no characters if no time zone is determinable. [tm_isdst]
That is, vendors are allowed to throw up their hands and simply say: "no time zone was determinable, so I'm not outputting any characters at all."
My opinion: The C timing API is a mess.
I am attempting to improve things for the upcoming C++20 standard within the <chrono> library.
The C++20 spec changes this from "no characters" to an exception being thrown if the time_zone abbreviation is not available:
http://eel.is/c++draft/time.format#3
Unless explicitly requested, the result of formatting a chrono type
does not contain time zone abbreviation and time zone offset
information. If the information is available, the conversion
specifiers %Z and %z will format this information (respectively).
[ Note: If the information is not available and a %Z or %z
conversion specifier appears in the chrono-format-spec, an exception
of type format_­error is thrown, as described above. — end note ]
Except that the above paragraph is not describing C's strftime, but a new format function that operates on std::chrono types, not tm. Additionally there is a new type: std::chrono::zoned_time (http://eel.is/c++draft/time.zone.zonedtime) that always has the time_zone abbreviation (and offset) available and can be formatted with the afore mentioned format function.
Example code:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std;
using namespace std::chrono;
auto now = system_clock::now();
std::cout << format("%Z\n", zoned_time{current_zone(), now}); // HKT (or whatever)
std::cout << format("%Z\n", zoned_time{"Asia/Hong_Kong", now}); // HKT or HKST
std::cout << format("%Z\n", zoned_time{"Etc/UTC", now}); // UTC
std::cout << format("%Z\n", now); // UTC
}
(Disclaimer: The final syntax of the formatting string in the format function is likely to be slightly different, but the functionality will be there.)
If you would like to experiment with a preview of this library, it is free and open source here: https://github.com/HowardHinnant/date
Some installation is required: https://howardhinnant.github.io/date/tz.html#Installation
In this preview, you will need to use the header "date/tz.h", and the contents of the library are in namespace date instead of namespace std::chrono.
The preview library can be used with C++11 or later.
zoned_time is templated on a std::chrono::duration which specifies the precision of the time point, and is deduced in the example code above using C++17's CTAD feature. If you are using this preview library in C++11 or C++14, the syntax would look more like:
cout << format("%Z\n", zoned_time<system_clock::duration>{current_zone(), now});
Or there is a non-proposed-for-standardization helper factory function which will do the deduction for you:
cout << format("%Z\n", make_zoned(current_zone(), now));
(#CTAD_eliminates_factory_functions)
Thanks for all the comments to the question which help pointing to the right direction. I post some of my own research below. I speak based on an archived repo of GNU C Library that I found on the GitHub. Its version is 2.28.9000.
In glibc/time/bits/types/struct_tm.h there is
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
# ifdef __USE_MISC
long int tm_gmtoff; /* Seconds east of UTC. */
const char *tm_zone; /* Timezone abbreviation. */
# else
long int __tm_gmtoff; /* Seconds east of UTC. */
const char *__tm_zone; /* Timezone abbreviation. */
# endif
};
It seems that struct tm does store time zone information, at least in this implementation.
One of the reasons date and time programming is so difficult is that it's fundamentally at least a somewhat difficult problem: "Thirty days hath September", and sexagesimal arithmetic, and time zones, and daylight saving time, and leap years, and let's not even talk about leap seconds.
But the other reason it's difficult is that all too many libraries and languages make a perfect mess of it, and C is unfortunately no exception. (C++ is trying to do better, as Howard mentions in his answer.)
Even though everybody knows global variables are Bad, C's date/time functions basically use a couple of them. In effect, the concept of "this system's current time zone" is a global variable, and the global data which describes that time zone is shared willy-nilly between localtime and strftime and a number of other functions.
So strftime can fill in %z and %Z based on that global data, even if it isn't passed in as part of a struct tm value.
That's obviously a suboptimal arrangement, and it would start causing real problems if there were a way for a program to dynamically change the time zone it wants to use for localtime and the rest. (And this arrangement persists in part because there's not actually a good, portable, Standard way for a program to change the local time zone it's using.)
Over the years there have been various half-hearted attempt to clean some of the mess up (while preserving backwards compatibility, of course). One of those attempts involves the extended tm_gmtoff and tm_zone fields you've discovered in some systems' versions of struct tm. Those additions are a huge improvement -- I can't imagine doing serious date/time programming on a system without them -- but they're still not Standard, and there are still plenty of systems that don't have them (not even with the "hidden" spellings __tm_gmtoff and __tm_zone).
You can read much more about the sordid history of date/time support in C in this paper: Time, Clock, and Calendar Programming In C, by Eric Raymond.

How do I set a C++ time_t object to the epoch?

This question is similar to the following:
convert epoch to time_t
Converting time_t to int
but I don't quite have my answer there.
If you want to get the current date/time you can call time(0) or time(NULL) like in the following standard example:
// current date/time based on current system
time_t now = time(0);
I want to define a function which will return a time_t and allows the client to pass an optional default return value in the event of an error. Further, I want to set a default on that "default" argument. This provides symmetry within a library I have with one-to-one counter parts across several languages, so I'm not looking to redesign all that.
My thought was to set the default return to the epoch. Then, a client could in theory easily evaluate that return, and decide that an epoch coming back was more than likely (if not always) an indication of it being invalid. I can think of some alternatives, but nothing clean, that also fits my existing patterns.
Is there a short and sweet way to make my function signature have a default value for this object equal to the epoch? For instance
...myfunc(...., const time_t &defVal=time(0) );
would be perfect if 0 meant the epoch rather than the current date/time!
The function std::time() returns the number of seconds since the epoch as a std::time_t. Therefore to find zero seconds after the epoch set std::time_t to zero:
std::time_t t = 0;
So you could do something like:
void myfunc(const std::time_t& defVal = 0)
What is wrong with using 0? (time_t)0 represents the epoch itself (if you want to find the actual epoch date/time, pass (time_t)0 to gmtime() or localtime()).
time_t myfunc(...., time_t defVal = 0 );
Or, you could use (time_t)-1 instead, which is not a valid time, as time() returns (time_t)-1 on error, and time_t represents a positive number of seconds since the epoch.
time_t myfunc(...., time_t defVal = (time_t)-1 );
Either way provides the user with something that is easily compared, if they don't provide their own default value.

How to properly convert a unix timestamp string to time_t in C++11?

Lets say we have a text file and read some timestamp from there into a local variable "sTime":
std::string sTime = "1440966379" // this value has been read from a file.
std::time_t tTime = ? // this instance of std::time_t shall be assigned the above value.
How do I convert this string properly into std::time assuming:
We may use STL means only (no boost).
We use the C++11 standard
We don't know which CPU architecture/OS we're using (it should work cross plattform)
We can not make any (static) assumptions on how time_t is internally defined. Of course we know that in most cases it will be an integral type, probably of 32 or 64 bit length, but according to cppreference.com the actual typedef of time_t is not specified. So atoi, atol, atoll, strtoul ... etc. are out of question at least until we have made sure by other means that we actually did pick the correct one out of those possible candidates.
This will keep your time in a standards-approved format:
Need #include <chrono>
std::string sTime = "1440966379"; // this value has been read from a file.
std::chrono::system_clock::time_point newtime(std::chrono::seconds(std::stoll(sTime)));
// this gets you out to a minimum of 35 bits. That leaves fixing the overflow in the
// capable hands of Misters Spock and Scott. Trust me. They've had worse.
From there you can do arithmetic and compares on time_points.
Dumping it back out to a POSIX timestamp:
const std::chrono::system_clock::time_point epoch = std::chrono::system_clock::from_time_t(0);
// 0 is the same in both 32 and 64 bit time_t, so there is no possibility of overflow here
auto delta = newtime - epoch;
std::cout << std::chrono::duration_cast<std::chrono::seconds>(delta).count();
And another SO question deals with getting formatted strings back out:
How to convert std::chrono::time_point to std::tm without using time_t?

How safe is it to assume time_t is in seconds?

I'm doing a lot of calculations with times, building time objects relative to other time objects by adding seconds. The code is supposed to run on embedded devices and servers. Most documentations say about time_t that it's some arithmetic type, storing usually the time since the epoch. How safe is it to assume that time_t store a number of seconds since something? If we can assume that, then we can just use addition and subtraction rather than localtime, mktime and difftime.
So far I've solved the problem by using a constexpr bool time_tUsesSeconds, denoting whether it is safe to assume that time_t uses seconds. If it's non-portable to assume time_t is in seconds, is there a way to initialize that constant automatically?
time_t timeByAddingSeconds(time_t theTime, int timeIntervalSeconds) {
if (Time_tUsesSeconds){
return theTime + timeIntervalSeconds;
} else {
tm timeComponents = *localtime(&theTime);
timeComponents.tm_sec += timeIntervalSeconds;
return mktime(&timeComponents);
}
}
The fact that it is in seconds is stated by the POSIX specification, so, if you're coding for POSIX-compliant environments, you can rely on that.
The C++ standard also states that time_t must be an arithmetic type.
Anyway, the Unix timing system (second since the Epoch) is going to overflow in 2038. So, it's very likely that, before this date, C++ implementations will switch to other non-int data types (either a 64-bit int or a more complex datatype). Anyway, switching to a 64-bit int would break binary compatibility with previous code (since it requires bigger variables), and everything should be recompiled. Using 32-bit opaque handles would not break binary compatibility, you can change the underlying library, and everything still works, but time_t would not a time in seconds anymore, it'd be an index for an array of times in seconds. For this reason, it's suggested that you use the functions you mentioned to manipulate time_t values, and do not assume anything on time_t.
If C++11 is available, you can use std::chrono::system_clock's to_time_t and from_time_t to convert to/from std::chrono::time_point, and use chrono's arithmetic operators.
If your calculations involve the Gregorian calendar, you can use the HowardHinnant/date library, or C++20's new calendar facilities in chrono (they have essentially the same API).
There is no requirement in standard C or in standard C++ for the units that time_t represents. To work with seconds portably you need to use struct tm. You can convert between time_t and struct tm with mktime and localtime.
Rather than determine whether time_t is in seconds, since time_t is an arithmetic type, you can instead calculate a time_t value that represents one second, and work with that. This answer I wrote before explains the method and has some caveats, here's some example code (bad_time() is a custom exception class, here):
time_t get_sec_diff() {
std::tm datum_day;
datum_day.tm_sec = 0;
datum_day.tm_min = 0;
datum_day.tm_hour = 12;
datum_day.tm_mday = 2;
datum_day.tm_mon = 0;
datum_day.tm_year = 30;
datum_day.tm_isdst = -1;
const time_t datum_time = mktime(&datum_day);
if ( datum_time == -1 ) {
throw bad_time();
}
datum_day.tm_sec += 1;
const time_t next_sec_time = mktime(&datum_day);
if ( next_sec_time == -1 ) {
throw bad_time();
}
return (next_sec_time - datum_time);
}
You can call the function once and store the value in a const, and then just use it whenever you need a time_t second. I don't think it'll work in a constexpr though.
My two cents: on Windows it is in seconds over time but the time it takes for one second to increment to the next is usually 18*54.925 ms and sometimes 19*54.925. The reason for this is explained in this post.
(Answering own question)
One answer suggests that as long as one is using posix, time_t is in seconds and arithmetic on time_t should work.
A second answer calculates the time_t per second, and uses that as a factor when doing arithmetic. But there are still some assumptions about time_t made.
In the end I decided portability is more important, I don't want my code to fail silently on some embedded device. So I used a third way. It involves storing an integer denoting the time since the program starts. I.e. I define
const static time_t time0 = time(nullptr);
static tm time0Components = *localtime(&time0);
All time values used throughout the program are just integers, denoting the time difference in seconds since time0. To go from time_t to this delta seconds, I use difftime. To go back to time_t, I use something like this:
time_t getTime_t(int timeDeltaSeconds) {
tm components = time0Components;
components.tm_sec += timeDeltaSeconds;
return mktime(&components);
}
This approach allows making operations like +,- cheap, but going back to time_t is expensive. Note that the time delta values are only meaningful for the current run of the program. Note also that time0Components has to be updated when there's a time zone change.