How to cause std::mktime to fail? - c++

I used std::mktime to set/get time in my program. The man page tells:
The values in time are permitted to be outside their normal ranges.
and also that it returns -1 if it can not represent the result as std::time_t
I tried everything in my unit tests to make this function fail, but without a success.
These are just some attempts:
#include <iostream>
#include <iomanip>
#include <ctime>
#include <stdlib.h>
int main()
{
setenv("TZ", "/usr/share/zoneinfo/America/New_York", 1); // POSIX-specific
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
std::cout << "Today is " << std::put_time(&tm, "%c %Z")
<< " and DST is " << (tm.tm_isdst ? "in effect" : "not in effect") << '\n';
tm.tm_year = 550;
tm.tm_year = 123456789;
tm.tm_mon = 88;
tm.tm_mday = 200;
//tm.tm_mon -= 100; // tm_mon is now outside its normal range
std::mktime(&tm); // tm_dst is not set to -1; today's DST status is used
std::cout << "100 months ago was " << std::put_time(&tm, "%c %Z")
<< " and DST was " << (tm.tm_isdst ? "in effect" : "not in effect") << '\n';
}
So, how do I set parameters to make this function fails?
As suggested in comments, I tried INT_MAX and it still doesn't fail. So, this:
tm.tm_year = std::numeric_limits< decltype( tm.tm_year ) >::max();
if ( -1 == std::mktime(&tm) )
std::cout << "std::mktime() failed)" << '\n';
is still not going to make it fail.
$CXX --version
aarch64-pdm3-linux-g++ (GCC) 6.4.0

Your sample does fail for the suggested input std::numeric_limits< decltype( tm.tm_year ) >::max(). Your sample does not check for the -1 return value. If you add
auto err = std::mktime(&tm); // tm_dst is not set to -1, err is.
err is set to -1 for this input.
You can see the failure at https://ideone.com/GUcM3N
You can also see the failure at http://coliru.stacked-crooked.com/a/8288579ec8924d7e
Output is identical on both services:
Today is Thu Apr 19 09:07:40 2018 EDT and DST is in effect
100 months ago was Thu ? 200 09:07:40 -2147481749 EDT and DST was in effect
-1

mktime will fail to update a tm struct when an overflow occurs, e.g. when year is set to INT_MAX and more than 12 months are added (over a year). Like this:
std::tm tm;
tm.tm_year = INT_MAX;
tm.tm_mon = 13;
std::time_t rc = std::mktime(&tm);
It will also fail to update a tm struct if you set the year to INT_MIN, the month to zero and subtract one day (set tm_mon to 0 and tm_mday to -1):
std::tm tm;
tm.tm_year = INT_MIN;
tm.tm_mon = 0;
tm.tm_mday = -1;
std::time_t rc = std::mktime(&tm);
It will not fail when year is set to INT_MAX if the combination of month and tm_mday is still less than a year, since that is still representable!
However as MSalters mentions this won't always result in a -1 as return value. Sometimes mktime will return -1 when it cannot normalize the time-stamp to a valid tm struct. But depending on the implementation mktime could also only return a -1 when the time cannot be expressed in time_t (which could be never since time_t is "unspecified"). In the latter case it is actually dangerous to assume the tm struct is normalized to a valid value.

Related

Microseconds epoch to a Date string using chrono

I have been looking around to get what I want but I couldn't find anything hence my question (hopefully not a duplicate!)
I am looking to get a microsecond resolution epoch time (to be converted to a Date string) of the clock perhaps using chrono.
Following is what works for me for seconds resolution:
auto secondsEpochTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
std::cout << "Date string = " << ctime(&secondsEpochTime);
However when I change seconds to microseconds, ctime doesn't seem to reflect the correct date.
auto microSecondsEpochTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
std::cout << "Date string = " << ctime(&microSecondsEpochTime); // incorrect Date
Unfortunately std::chrono is not complete to provide a full answer to your question. You will have to use parts of the C library until C++23 at least otherwise you might end up with a race-prone implementation.
The idea is to get the timestamp and convert it to an integer as microseconds since epoch (1970-01-01).
Then use localtime_r to get the local time broken down in year/month/day/hour/minute/seconds and print it to string.
Finally append the milliseconds as an int padded to 3 digits and return the entire result as an std::string.
constexpr static int64_t ONEMICROSECOND = 1000000;
static std::string nowstr() {
auto now = std::chrono::system_clock::now();
auto onems = std::chrono::microseconds(1);
int64_t epochus = now.time_since_epoch()/onems;
time_t epoch = epochus/ONEMICROSECOND;
struct tm tms{};
localtime_r( &epoch, &tms );
char buf[128];
size_t nb = strftime( buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tms );
nb += ::sprintf( &buf[nb], ".%06d", int(epochus%ONEMICROSECOND) );
return std::string( buf, nb );
}
If you run this as-is it will likely return the timestamp in GMT. You will heave to set your timezone programatically if not set in the environment (as it happens with compiler explorer/Godbolt.
int main() {
setenv("TZ", "/usr/share/zoneinfo/America/New_York", 1);
std::cout << nowstr() << std::endl;
}
Results in
Program stdout
2022-10-01 22:51:03.988759
Compiler explorer link: https://godbolt.org/z/h88zhrr73
UPDATE: if you prefer to use boost::format (std::format is still incomplete on most compilers unfortunately) then you can do
static std::string nowstr() {
auto now = std::chrono::system_clock::now();
auto onems = std::chrono::microseconds(1);
int64_t epochus = now.time_since_epoch()/onems;
time_t epoch = epochus/ONEMICROSECOND;
struct tm tms{};
localtime_r( &epoch, &tms );
std::ostringstream ss;
ss << boost::format( "%04d-%02d-%02d %02d:%02d:%02d.%06d" )
% (tms.tm_year+1900) % (tms.tm_mon+1) % tms.tm_mday
% tms.tm_hour % tms.tm_min % tms.tm_sec
% (epochus%ONEMICROSECOND);
return ss.str();
}
You will have to use parts of the C library until C++23 at least
Umm... If your platform supports the full C++20 spec (at least with regards to format and chrono):
#include <chrono>
#include <format>
#include <iostream>
int
main()
{
auto tp = std::chrono::system_clock::now();
std::chrono::zoned_time zt{std::chrono::current_zone(),
std::chrono::time_point_cast<std::chrono::microseconds>(tp)};
std::cout << "Date string = " << std::format("{:%a %b %e %T %Y}", zt) << '\n';
}
Sample output:
Date string = Sat Oct 1 23:32:24.843844 2022

From xs:dateTime to std::chrono::timepoint [duplicate]

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;
}
} // ()

Valid Date function using <ctime> mktime

I wanted a function that would take three inputs of day, month, year and tell me whether it is valid or not. Then using the example on http://www.cplusplus.com/reference/ctime/mktime/
I tried to implement my function:
bool ValidDate(int d, int m, int y)
{
struct tm *timeinfo;
time_t rawtime;
time (&rawtime);
timeinfo = localtime(&rawtime);
timeinfo->tm_year = y - 1900;
timeinfo->tm_mon = m - 1;
timeinfo->tm_mday = d;
if (mktime(timeinfo) == -1 )
return false;
else return true;
}
The problem is that the function is returning not as i want it to.
e.g im checking like
if (ValidDate(4,13,2010)) // out put is valid
std::cout << "valid\n";
else std::cout << "Invalid\n";
ValidDate(4,22,2010) // valid
ValidDate(344,13,2010) //valid
ValidDate(4,133,2010) //valid
ValidDate(31,12, 1920) //invalid
ValidDate(31,9,2010) //valid
ValidDate(4,9,2010) //valid
Why? thanks.
EDIT:
all dates entered were invalid except 31,12,1920 and 4,9,2010 and non of the outputs were correct.
mktime return is as follow :
Time since epoch as a std::time_t object on success or -1 if time cannot be represented as a std::time_t object.
std::time_t is defined as follow :
Arithmetic type capable of representing times.
Although not defined, this is almost always a integral value holding the number of seconds (not counting leap seconds) since 00:00, Jan 1 1970 UTC, corresponding to POSIX time.
So 31/12/1920 cannot be represented into a std::time_t as it is before the epoch.
As for the other invalid dates that are reported as valid, mktime also states :
The values in [the parameter] are permitted to be outside their normal ranges.
Here is the example taken from cppreference :
#include <iostream>
#include <iomanip>
#include <ctime>
int main()
{
std::time_t t = std::time(NULL);
std::tm tm = *std::localtime(&t);
std::cout << "Today is " << std::put_time(&tm, "%c %Z") <<'\n';
tm.tm_mon -= 100; // tm_mon is now outside its normal range
std::mktime(&tm);
std::cout << "100 months ago was " << std::put_time(&tm, "%c %Z") << '\n';
}
Output is :
Today is Wed Dec 28 09:56:10 2011 EST
100 months ago was Thu Aug 28 10:56:10 2003 EDT

using strptime converting string to time but getting garbage

I have a problem with using strptime() function in c++.
I found a piece of code in stackoverflow like below and I want to store string time information on struct tm. Although I should get year information on my tm tm_year variable, I always get a garbage.Is there anyone to help me ? Thanks in advance.
string s = dtime;
struct tm timeDate;
memset(&timeDate,0,sizeof(struct tm));
strptime(s.c_str(),"%Y-%m-%d %H:%M", &timeDate);
cout<<timeDate.tm_year<<endl; // in the example below it gives me 113
cout<<timeDate.tm_min<<endl; // it returns garbage
**string s will be like "2013-12-04 15:03"**
cout<<timeDate.tm_year<<endl; // in the example below it gives me 113
it is supposed to give you value decreased by 1900 so if it gives you 113 it means the year is 2013. Month will also be decreased by 1, i.e. if it gives you 1, it is actually February. Just add these values:
#include <iostream>
#include <sstream>
#include <ctime>
int main() {
struct tm tm{};
std::string s("2013-12-04 15:03");
if (strptime(s.c_str(), "%Y-%m-%d %H:%M", &tm)) {
int d = tm.tm_mday,
m = tm.tm_mon + 1,
y = tm.tm_year + 1900;
std::cout << y << "-" << m << "-" << d << " "
<< tm.tm_hour << ":" << tm.tm_min;
}
}
outputs 2013-12-4 15:3

C++: mktime bug under MinGW on Windows Vista?

So I'm trying to convert dates in the format "2000-01-01" into integers representing the number of days since some arbitrary origin (e.g. 1900/01/01) so I can treat them as integer indices. To do this I wrote a conversion function which works fine on MinGW under Windows XP but not under Vista. I've added some logging code:
int dateStrToInt(string date) {
int ymd[3];
tm tm1, tm0;
istringstream iss(date);
string s;
for (int i = 3; i; --i) {
getline(iss, s, '-');
ymd[3-i] = str2<int>(s);
}
cout << ymd[0] << ' ' << ymd[1] << ' ' << ymd[2] << ' ' << endl;
tm1.tm_year = ymd[0] - 1900;
tm1.tm_mon = ymd[1] - 1;
tm1.tm_mday = ymd[2];
time_t t1 = mktime(&tm1);
tm0.tm_year = 0;
tm0.tm_mon = 0;
tm0.tm_mday = 0;
time_t t0 = mktime(&tm0);
//cout << "times: " << mktime(&origin) << ' ' << mktime(&time) << endl;
cout << "times: " << t0 << ' ' << t1 << endl;
cout << "difftime: " << difftime(t1, t0) << endl;
return difftime(mktime(&tm1), mktime(&tm0)) / (60*60*24);
}
int i = dateStrToInt("2000-01-01");
and the output I get from that is
2000 1 1
times: -1 -1
difftime: 0
which seems clearly wrong. What can I do about this?
EDIT: as the answer below says, there seems to be a problem with years prior to 1970. To avoid this I've handrolled my own day-counting function:
int dateStrToInt(string date) {
int ymd[3];
istringstream iss(date);
string s;
for (int i = 0; i < 3; ++i) {
getline(iss, s, '-');
ymd[i] = str2<int>(s);
}
const static int cum_m_days[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
int year = ymd[0]+10000, month = ymd[1], day = ymd[2];
int days = year*365 + cum_m_days[month-1] + day;
// handle leap years
if (month <= 2)
--year;
days = days + (year/4) - (year/100) + (year/400);
return days;
}
It's not necessarily a good idea leaving all of those other struct tm fields at their default (random in this case) values.
The standard is not overly explicit about what fields need to be set before calling mktime but it does say that it sets tm_wday and tm_yday based on the other fields, and that those other fields are not restricted to being valid.
One thing the standard does show is example code which sets all fields except those two mentioned above so that's what I'd be aiming for.
Try to change the segment that calculates the times from:
tm1.tm_year = ymd[0] - 1900;
tm1.tm_mon = ymd[1] - 1;
tm1.tm_mday = ymd[2];
time_t t1 = mktime(&tm1);
tm0.tm_year = 0;
tm0.tm_mon = 0;
tm0.tm_mday = 0;
time_t t0 = mktime(&tm0);
to something like:
// Quick and dirty way to get decent values for all fields.
time_t filled_in;
time (&filled_in);
memcpy (&tm1, localtime ( &filled_in ), sizeof (tm1));
memcpy (&tm0, &tm1, sizeof (tm0));
// Now do the modifications to relevant fields, and calculations.
tm1.tm_year = ymd[0] - 1900;
tm1.tm_mon = ymd[1] - 1;
tm1.tm_mday = ymd[2];
time_t t1 = mktime(&tm1);
tm0.tm_year = 0;
tm0.tm_mon = 0;
tm0.tm_mday = 0;
time_t t0 = mktime(&tm0);
In addition, some experimentation with CygWin under XP results in mktime alway seeming to return -1 for struct tm structures where the tm_year is less than two. Whether that's an actual bug or not is questionable since I've often found that implementations don't always support dates before the epoch (Jan 1, 1970).
Some UNIXes did allow you to specify tm_year values less than 70 and they could often use these "negative" values of time_t to access years back to 1970.
But, since the standard doesn't really go into that, it's left to the implementation. The relevant bit of the C99 standard (and probably earlier iterations), which carries forward to C++, is found in 7.23.1/4:
The range and precision of times representable in clock_t and time_t are implementation-defined.
The safest bet would be to use a date after the start of the epoch as the baseline date. This is shown in the following code:
#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <cstring>
#include <cstdlib>
int dateStrToInt(std::string date) {
int ymd[3];
tm tm1, tm0;
std::istringstream iss(date);
std::string s;
// Test code.
ymd[0] = 2000; ymd[1] = 1; ymd[2] = 1;
std::cout << ymd[0] << ' ' << ymd[1] << ' ' << ymd[2] << ' ' << std::endl;
time_t filled_in;
time (&filled_in);
std::memcpy (&tm0, localtime ( &filled_in ), sizeof (tm0));
std::memcpy (&tm1, &tm0, sizeof (tm1));
tm1.tm_year = ymd[0] - 1900;
tm1.tm_mon = ymd[1] - 1;
tm1.tm_mday = ymd[2];
time_t t1 = mktime(&tm1);
tm0.tm_year = 1970 - 1900; // Use epoch as base date.
tm0.tm_mon = 0;
tm0.tm_mday = 1;
time_t t0 = mktime(&tm0);
std::cout << "times: " << t0 << ' ' << t1 << std::endl;
std::cout << "difftime: " << difftime(t1, t0) << std::endl;
return difftime(mktime(&tm1), mktime(&tm0)) / (60*60*24);
}
int main (void) {
int i = dateStrToInt("2000-01-01");
double d = i; d /= 365.25;
std::cout << i << " days, about " << d << " years." << std::endl;
return 0;
}
This outputs the expected results:
2000 1 1
times: 31331 946716131
difftime: 9.46685e+08
10957 days, about 29.9986 years.
As an addendum, POSIX has this to say:
4.14 Seconds Since the Epoch
A value that approximates the number of seconds that have elapsed since the Epoch. A Coordinated Universal Time name (specified in terms of seconds (tm_sec), minutes (tm_min), hours (tm_hour), days since January 1 of the year (tm_yday), and calendar year minus 1900, (tm_year)) is related to a time represented as seconds since the Epoch, according to the expression below.
If the year is <1970 or the value is negative, the relationship is undefined. If the year is >=1970 and the value is non-negative, the value is related to a Coordinated Universal Time name according to the C-language expression, where tm_sec, tm_min, tm_hour, tm_yday, and tm_year are all integer types:
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
The relationship between the actual time of day and the current value for seconds since the Epoch is unspecified.
How any changes to the value of seconds since the Epoch are made to align to a desired relationship with the current actual time is implementation-defined. As represented in seconds since the Epoch, each and every day shall be accounted for by exactly 86400 seconds.
Note: The last three terms of the expression add in a day for each year that follows a leap year starting with the first leap year since the Epoch. The first term adds a day every 4 years starting in 1973, the second subtracts a day back out every 100 years starting in 2001, and the third adds a day back in every 400 years starting in 2001. The divisions in the formula are integer divisions; that is, the remainder is discarded leaving only the integer quotient.
In other words (see "If the year is <1970 or the value is negative, the relationship is undefined"), use dates before 1970 at your own risk.