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
Related
I am struggling to execute a simple task. I want to take a date parameter from the command line argument and increment it several times by 1 day. The basic program should:
read the command line argument as the starting date, and
loop several times, incrementing that date by 1 day every time.
I convert the char* command line argument to a struct tm, then convert the struct tm to a time_t and add 60 * 60 * 24 = 1 day to it. I convert it back to struct tm to print it.
Here is the code:
#include <iostream>
#include <cstdlib>
#include <string>
#include "time.h"
int main(int argc, char *argv[])
{
char* start_date;
tm tm_start_date = {}; // solution: needs initialization
start_date = argv[1];
strptime(start_date, "%Y-%m-%d", &tm_start_date); // YYYY-MM-DD
char ch_stmt_date[11] = "";
time_t t_stmt_date = 0;
tm tm_stmt_date = {}; // solution: needs initialization;
tm_stmt_date = tm_start_date;
// time_t -> tm localtime_r(time_t, tm)
// tm -> time_t mktime(tm) returns time_t
std::cout << "start_date: " << start_date << " / tm_start_date: " << std::to_string(1900 + tm_start_date.tm_year) + std::to_string(tm_start_date.tm_mon + 1) +
std::to_string(tm_start_date.tm_mday) << std::endl;
// increment by 1 day per iteration
for (int i=0; i<5; i++)
{
// tm -> t_time
t_stmt_date = mktime(&tm_stmt_date);
std::cout << "t_stmt_date: " << t_stmt_date << std::endl;
// + 1 day
t_stmt_date += 60*60*24;
std::cout << "t_stmt_date: " << t_stmt_date << std::endl;
// time_t -> tm
localtime_r(&t_stmt_date, &tm_stmt_date);
strftime (ch_stmt_date, 11, "%Y-%m-%d", &tm_stmt_date);
std::cout << "ch_stmt_date: " << ch_stmt_date << std::endl;
}
return EXIT_SUCCESS;
}
The start date is correctly read and parsed into the struct tm.
However, subsequently I get one of two behaviors of the program:
Either I get a -1 on the first call of t_stmt_date = mktime(&tm_stmt_date); and a value of t_stmt_date: 86399 (1970-01-02) in the output. The rest of the loop then works correctly and iterates 5 times, incrementing 1970-01-02 by 1 day.
Or, the same code using the same command line parameter parses a nonsensical value on the first call of t_stmt_date = mktime(&tm_stmt_date); in the loop which is not a valid date, which, however is also correctly incremented by 60*60*24 on each of the 5 loops.
At this point I am desperate to understand the issue. I am working on Ubuntu 20.04 using gcc.
Thanks for your help.
Edit: Initializing the struct tm did the trick!
[NOTE]
You explicitly mention "using localtime and mktime" in the question's title, but I wasn't sure though after reading the rest of the text if that was mandatory, or you just needed to get a task done.
If you cannot use other libraries, just let me know and I'll remove this answer.
You could use std::chrono and Howard Hinnant's date library (C++11 onwards, header-only).
Or, should you be able to use a C++20 compiler, you would only need std::chrono.
[Demo]
#include <chrono>
#include <iostream> // cout
#include <sstream> // istringstream
#include <string>
#include "date/date.h"
int main()
{
namespace ch = std::chrono;
namespace dt = date;
const std::string start_date{"2022-01-31"}; // date
std::istringstream iss{ start_date }; // to string stream
dt::sys_days start_day{}; // to a time point with a day duration
dt::from_stream(iss, "%Y-%m-%d", start_day);
for (auto day{start_day}, end_day{start_day + dt::days{3}};
day < end_day;
day += dt::days{1}) // with which we can do date arithmetic
{
std::cout << dt::format("%Y-%m-%d\n", day);
}
}
// Outputs:
//
// 2022-01-31
// 2022-02-01
// 2022-02-02
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;
}
} // ()
Is there a way to only compare time in C++?
For instance, given a array of predefined time (in string, e.g. "04:00", "07:00", "13:00") I want to be able to compare the current time with the range of time given in the array to see which fits.
I have tried strptime but I keep getting the undefined error. I have included time.h and stdio.h.
#include <stdio.h>
#include <ctime>
#include <time.h>
int main() {
//GET CURRENT TIME
time_t now = time(0);
tm *ltm = localtime(&now);
cout << "Time: "<< ltm->tm_hour << ":";
cout << ltm->tm_min << ":";
cout << ltm->tm_sec << endl;
//TRYING TO CONVERT THE TIME FROM STRING TO PROCEED FOR FURTHER COMPARISON
struct tm tm;
std::string s("04:00");
if (strptime(s.c_str(), "%H:%M", &tm)) { //error strptime is undefined
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;
}
//ATTEMPT TO DO TIME COMPARISON HERE
}
Am I missing out on any library?
Is there an easier/alternate way to do this?
Please guide me. Thanks
Timestamps from your example all have the same fixed format: two-digit hours, colon, two-digit minutes.
Which means they are perfectly subject to lexicographic comparison, via operator< on std::strings, std::strcmp, std::strncmp, std::memcmp, and so forth.
What's a standard way to get a date time in ISO8601 format on Windows using C++? Specifically, I would like it to be formatted as:
2017-02-22T10:00:00.123-05:00
2017-02-22T10:00:00.123 >>> -05:00 <<< # how to print the offset?
I was looking into combining the output of GetLocalTime and GetTimeZoneInformation, but this looks esoteric. There are similar questions on SO, however, I've not found a single one that prints UTC offset in a desired format. Is there a better approach?
The format specifier %z gives you the timezone offset as described in the documentation (e.g. MSDN on strftime) but lefts out the ':'. You can use it like this to get the ':' into your string:
struct tm tmNow;
time_t now = time(NULL); // Get the current time
_localtime64_s(&tmNow, &now);
char bufferTime[26];
char bufferTimezoneOffset[6];
size_t tsizTime = strftime(bufferTime, 26, "%Y-%m-%dT%H:%M:%S", &tmNow); // The current time formatted "2017-02-22T10:00:00"
size_t tsizOffset = strftime(bufferTimezoneOffset, 6, "%z", &tmNow); // The timezone offset -0500
strncpy_s(&bufferTime[tsizTime], 26, bufferTimezoneOffset, 3); // Adds the hour part of the timezone offset
bufferTime[tsizTime + 3] = ':'; // insert ':'
strncpy_s(&bufferTime[tsizTime + 4], 26, &bufferTimezoneOffset[3], 3); // Adds the minutes part of the timezone offset
puts(bufferTime); // Your output: "2017-02-22T10:00:00-05:00"
I left out the milliseconds, as they are not part of the localtime as far as I know.
Maybe something like this. We call GetLocalTime and GetTimeZoneInformation then pass it to the function which returns formatted string.
This is written quickly, not tested besides observing the fact it returns correct result on my machine now. It operates on the fact that SYSTEMTIME has a member Bias where UTC = Localtime + Bias and Bias is set in minutes. So get hours by dividing by 60 and taking absolute value of that. Then we get the minutes in similar way and set the sign depending on if Bias > 0
#include <Windows.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <cmath>
std::string format_system_time(const SYSTEMTIME& sys_time, const TIME_ZONE_INFORMATION& time_zone)
{
std::ostringstream formatted_date_time;
formatted_date_time << std::setfill('0');
formatted_date_time << sys_time.wYear << "-" << std::setw(2) << sys_time.wMonth << "-" <<
std::setw(2) << sys_time.wDay << "T" << std::setw(2) << sys_time.wHour << ":" <<
std::setw(2) << sys_time.wMinute << ":" << std::setw(2) << sys_time.wSecond << "." <<
std::setw(3) << sys_time.wMilliseconds;
//UTC = localtime + bias; bias is in minutes
int utc_offset_hours = time_zone.Bias / 60;
int utc_offset_minutes = std::abs(time_zone.Bias - (utc_offset_hours * 60));
char offset_sign = time_zone.Bias > 0 ? '-' : '+';
formatted_date_time << offset_sign << std::setw(2) << std::abs(utc_offset_hours) << ":" << utc_offset_minutes;
return formatted_date_time.str();
}
int main(int argc, char* argv[])
{
SYSTEMTIME date_and_time;
GetLocalTime(&date_and_time);
TIME_ZONE_INFORMATION time_zone;
GetTimeZoneInformation(&time_zone);
auto& formatted_date_time = format_system_time(date_and_time, time_zone);
return 0;
}
I don't think there is a drop-in solution for c++ on Windows. The closest you can get is InternetTimeFromSystemTime but it is only documented to support RFC1123.
You probably have to code it yourself with GetLocalTime + GetTimeZoneInformation + wsprintf (or GetTimeZoneInformationForYear if you are not dealing with the current time).
Using Howard Hinnant's free, open-source timezone library, which works on VS-2013 and later, but does require some installation:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std;
using namespace std::chrono;
using namespace date;
auto zt = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
cout << format("%FT%T%Ez\n", zt);
}
This just output for me:
2017-02-22T17:29:03.859-05:00
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.