I'm trying to calculate exact age of person using difference between now and the date of birth. I'm getting difference in seconds, which, I suppose is correct value. Then i'd like to convert seconds into struct tm, using gmtime(). But it is giving me a tm_year on 70 bigger than it must be, and tm_mday on 1 bigger than must be. It seems to be clear about tm_mday- the range of it is from 1 to 31, I can just subtract 1 from, whereas tm_year is the years from 1900. OK, so why does gmtime() give me +70 years?
#include <iostream>
#include <ctime>
using namespace std;
int main() {
int min,h,d,m,y;
struct tm bd = {0};
cout << "Enter birth date in the format: hh:min/dd.mm.yyyy"<<endl;
scanf("%d:%d/%d.%d.%d",&h,&min,&d,&m,&y);
bd.tm_year = y-1900;
bd.tm_mon = m-1;
bd.tm_mday = d;
bd.tm_hour = h;
bd.tm_min = min;
time_t now = time(NULL);
cout << "NOW: "<<now<<" BD: "<<mktime(&bd)<<endl;
time_t seconds = difftime(now,mktime(&bd));//(end,beginning)
cout <<"seconds elapsed: "<< seconds<<endl;
struct tm * age;
age = gmtime (&seconds);
cout << "year" << age->tm_year << endl;
cout << "mon" << age->tm_mon << endl;
cout << "mday" << age->tm_mday << endl;
cout << "hour" << age->tm_hour << endl;
cout << "min" << age->tm_min << endl;
cout << "sec" << age->tm_sec << endl;
}
output:
Enter birth date in the format: hh:min/dd.mm.yyyy
13:28/04.03.2021
NOW: 1614853702 BD: 1614853680
seconds elapsed: 22
year 70
mon 0
mday 1
hour 0
min 0
sec 22
It is translating "unix epoch time", which is seconds since 1970, to a date.
It is not converting seconds to an amount of days/months/years. There is fundamentally no such conversion. 30 days can be less than or more than a month. 365 days can be a year, or 1 day less than a year. 24 times 60 times 60 seconds can be less than a day when a leap second happens.
Seconds after a point in time is a date. But seconds does not uniquely map to a number of days/months/years.
Find the two points in time - dates - and compare/subtract components to do that.
Related
This question already has answers here:
Converting seconds to hours and minutes and seconds
(11 answers)
Closed 9 months ago.
I am trying to solve a problem where I have a total seconds variable, from which I am trying to determine the hours, minutes and seconds.
I do not want to use any external libraries for this task.
What I have noticed is that my seconds variable seems to result in 1 less than the actual value when it is in int form,
but when it is in double form the answer is correct. Why is this?
I would welcome a different approach, perhaps using the remainder operator.
// Example program
#include <iostream>
#include <string>
int main()
{
int total_seconds;
total_seconds = 3870;
int hours, minutes, seconds;
double total_h, total_m, total_s;
int total_hours_int, total_minutes_int;
total_h = (double)total_seconds / 3600;
total_hours_int = total_seconds / 3600;
hours = total_hours_int;
total_m = (total_h - total_hours_int) * 60;
total_minutes_int = (total_h - total_hours_int) * 60;
minutes = total_minutes_int;
total_s = ((double)total_m - total_minutes_int) * 60;
seconds = ((double)total_m - total_minutes_int) * 60;
//seconds = (double)total_s;
std:: cout << hours;
std:: cout << minutes;
std:: cout << total_s;
std:: cout << seconds;
}
Output : 143029
Update:
The answer below was given before the C++98 tag was added to the question.
The chono library is available since C++11, so you can use it only from that version onwards.
You haven't given any context for this task.
My asnwer below assumes you need to solve the problem in any valid C++ manner (i.e. that it is not mandatory the caculate the numbers "by hand").
If this is the case, you can use the C++ chrono library for that, as shown below. This solution is shorter and less error-prone, and avoids the type issues you had altogether.
The main class I used is std::chrono::duration and it's helper types (as you can see in the link), as well as std::chrono::duration_cast.
#include <iostream>
#include <chrono>
int main()
{
int total_seconds = 3870;
std::chrono::seconds total_secs(total_seconds);
auto hours = std::chrono::duration_cast<std::chrono::hours>(total_secs);
auto mins = std::chrono::duration_cast<std::chrono::minutes>(total_secs - hours);
auto secs = std::chrono::duration_cast<std::chrono::seconds>(total_secs - hours - mins);
std::cout << "totals seconds: " << total_secs.count() << std::endl;
std::cout << " hours: " << hours.count() << std::endl;
std::cout << " minutes: " << mins.count() << std::endl;
std::cout << " seconds: " << secs.count() << std::endl;
}
Output:
totals seconds: 3870
hours: 1
minutes: 4
seconds: 30
I've reopened answear since it was updated to C++98.
Before C++11 it can be done nicely using standard library:
#include <iostream>
#include <string>
#include <ctime>
int main()
{
int seconds;
while (std::cin >> seconds) {
std::tm t = {};
t.tm_sec = seconds;
t.tm_mday = 1;
mktime(&t);
t.tm_hour += t.tm_yday * 24;
char buf[32];
strftime(buf, sizeof(buf), "%H:%M:%S", &t);
std::cout << t.tm_yday << ' ' << seconds << " = " << buf << '\n';
}
return 0;
}
https://godbolt.org/z/ceWWfoP6P
Problem Summary
I have two strings in the form YYYY-MM-DD:hh:mm:ss and I would like to calculate the time difference between them. For example, the difference between 2021-10-01:03:44:34 and 2021-10-01:03:44:54, should be 20 seconds. However, the result I get is 0.
Code
I have tried the following:
#include <iomanip>
#include <iostream>
using namespace std;
using timestamp = time_t;
auto StringToTimestamp(const string& timeString) -> timestamp {
tm tm {};
stringstream ssBuffer(timeString);
ssBuffer >> get_time(&tm, "%Y-%b-%d:%H:%M:%S");
cout << tm.tm_year << " " << tm.tm_mon << " " << tm.tm_mday << " "
<< tm.tm_hour << " "<< tm.tm_min << " " << tm.tm_sec << " " << endl;
return mktime(&tm);
}
int main() {
string beg = {"2021-10-01:03:44:34"s};
string end = {"2021-10-01:03:44:54"s};
timestamp begTm = StringToTimestamp(beg);
timestamp endTm = StringToTimestamp(end);
double diff = difftime(endTm, begTm);
cout << "Time difference is " << diff << endl;
return 0;
}
Output
121 0 0 0 0 0
121 0 0 0 0 0
Time difference is 0
Expected Output
2021 10 01 03 44 34
2021 10 01 03 04 54
Time difference is 20
Why is the output as such? How can I fix this?
EDIT
I changed this line "%Y-%b-%d:%H:%M:%S" to "%Y-%m-%d:%H:%M:%S" and now the output is
121 9 1 3 44 34
121 9 1 3 44 54
Time difference is 20
Why are the year and month "incorrect"?
You use the conversion specifier%b to get the month but it should be %m:
ssBuffer >> get_time(&tm, "%Y-%m-%d:%H:%M:%S");
%b - parses the month name, either full or abbreviated, e.g. Oct (non-numeric)
%m - parses the month as a decimal number (range [01,12]), leading zeroes permitted but not required
The year and month are correct. 121 is the number of years since 1900 and 9 is the month, zero-based [0,11], which is what's specified for std::tm.
So we are supposed to convert 453456 seconds into years, days, hours, minutes, and seconds.
However, I cannot seem to get past years.
Here is what I have so far:
#include<iostream>
using namespace std;
int main (){
int secDis;
int years;
const int yearsSec = 31536000;
int days;
cout << "Please give me the time of travel in seconds.";
cin >> secDis;
years = secDis/yearsSec;
days = (yearsSec%secDis) / 1440; /*idk if I am on the right track*/
cout << "You have been traveling for: "
<< years << days;
If it is 453456 seconds it should be 0 years 5 days 5 hours 57 minutes and 36 secs.
//I hate my math skills.
You have the order of secDis and yearsSec reversed in the line to compute the number of days.
Also, the number of seconds in a day are: 24 x 60 x 60, which is 86400. You are off by an order of 60.
The number of seconds left after the number of years is (secDis % yearsSec).
Hence, you need to use:
days = (secDis % yearsSec ) / 86400;
#HowardHinnant has provided a Swiss-army-knife date library for such questions, see this Q&A, after which using
std::cout << human_readable_difference(second_point{453456s},
second_point{0s});}
will indeed print:
0 years 0 months 5 days 5 hours 57 minutes 36 seconds
I really like TemplateRex's answer! (upvoted it).
But here's a way to solve this problem with just the <chrono> library, assuming your definition that a year is 365 days (a pretty coarse assumption):
#include <chrono>
#include <iostream>
int
main()
{
using namespace std;
using namespace std::chrono;
using days = duration<int, ratio_multiply<ratio<24>, hours::period>>;
using years = duration<int, ratio_multiply<ratio<365>, days::period>>;
auto s = 453456s;
auto y = duration_cast<years>(s);
s -= y;
auto d = duration_cast<days>(s);
s -= d;
auto h = duration_cast<hours>(s);
s -= h;
auto m = duration_cast<minutes>(s);
s -= m;
std::cout << y.count() << " years "
<< d.count() << " days "
<< h.count() << " hours "
<< m.count() << " minutes "
<< s.count() << " seconds\n";
}
<chrono> already has units hours, minutes and seconds. Just add two more units for days and years, and now you can just use duration_cast to successively truncate the seconds into coarser units, and then subtract that truncation out from the seconds (a modulo operation). Just continue with finer and finer units until you are down to your finest unit (seconds in this example). The above program outputs:
0 years 5 days 5 hours 57 minutes 36 seconds
This lets <chrono> do all the conversions for you, reducing the chance of errors.
You want the remainder from the division secDis / yearsSec, which is secDis % yearsSec – not yearsSec % secDis.
(That is, to get the remainder, replace /with %.)
I believe it gets easier if you define the number of seconds in each unit of time explicitly:
// Let the computer do the computing.
const int perMinute = 60;
const int perHour = 60 * perMinute;
const int perDay = 24 * perHour;
const int perYear = 365 * perDay;
and spell out every step of the computations:
int years = totalSeconds / perYear;
int daySeconds = totalSeconds % perYear;
int days = daySeconds / perDay;
int hourSeconds = daySeconds % perDay;
int hours = hourSeconds / perHour;
// ...
i tried doing this:
struct Den_t
{
int day, month, year;
};
int main()
{
struct Den_t* Datum = new struct Den_t;
struct Den_t* Dnes = new struct Den_t;
time_t theTime = time(NULL);
struct tm aTime;
localtime_s(&aTime, &theTime);
Dnes->day = aTime.tm_mday;
Dnes->month = aTime.tm_mon + 1;
Dnes->year = aTime.tm_yday + 1900;
cin >> Datum->day >> Datum->month >> Datum->year;
if (Dnes->year - Datum->year >= 18 )
cout << "full aged " << endl;
else
cout << "not full aged " << endl;
system("PAUSE");
return 0;
}
but i somehow cant understand what should i even compare and decrement,could someone explain me
what else i need to do to tell people's date for example in float by
comparing year,month and day of actual time and date user inputs in
the program?
You have an issue with your code logic here.
For example:
Datum is 31/12/1982
Dnes is 01/01/2000
The year difference is 18 but the age is 17 and 2 days.
Consider using standard library functions instead of reinventing the wheel.
difftime could be useful, for example
This is a very dirty example, but it would do the work:
time_t dnes;
time(&dnes);
// Set datum here ...
cin >> Datum->day >> Datum->month >> Datum->year;
datum.tm_mday = Datum->day;
datum.tm_mon = Datum->month - 1;
datum.tm_yday = Datum->year - 1900;
datum->tm_yday+=18;
if (difftime(dnes, mktime(&datum)) <0 )
cout << "not full aged " << endl;
else
cout << "full aged " << endl;
Using these libraries:
http://howardhinnant.github.io/date/date.html
http://howardhinnant.github.io/date/tz.html
This is how I would tackle the problem. First the code, then the explanation:
#include "tz.h"
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << "Enter birthday [day month year]:";
int di, mi, yi;
std::cin >> di >> mi >> yi;
if (std::cin.fail())
{
std::cout << "Invalid date\n";
return 1;
}
auto y = year{yi};
if (!y.ok())
{
std::cout << "Invalid year\n";
return 1;
}
auto m = month(mi);
if (!m.ok())
{
std::cout << "Invalid month\n";
return 1;
}
auto d = day(di);
if (!d.ok())
{
std::cout << "Invalid day\n";
return 1;
}
auto birthday = y/m/d;
if (!birthday.ok())
{
std::cout << "Invalid birthday\n";
return 1;
}
auto local_time = current_zone()->to_local(system_clock::now());
auto today = year_month_day{floor<days>(local_time)};
auto age = today.year() - birthday.year();
if (birthday + age > today)
--age;
if (age >= years{18})
std::cout << "full aged at " << age.count() << "\n";
else
std::cout << "not full aged at " << age.count() << "\n";
}
I would first go to some trouble to check the validity of the user input. What I have below seems like a minimum:
It must be integral input.
Each integral input must have a reasonable value (e.g. month must be in the range [1, 12].
The combination y/m/d must be a valid date.
A more robust program might give the user some feedback on what he input, and give him another chance to correct his mistake.
Once assured we have a valid birthday, we need to get the current date in the local timezone. This:
auto local_time = current_zone()->to_local(system_clock::now());
gets the local time.
This local time can be converted to a local year, month and day with:
auto today = year_month_day{floor<days>(local_time)};
This computation follows the custom that your birthday begins at the local midnight, regardless of what time of day (and where on the planet) you were actually born. In other words, once the local year/month/day is established, this problem is independent of the local time zone, and even the local time of day.
Next, the current age is computed:
auto age = today.year() - birthday.year();
if (birthday + age > today)
--age;
The difference between the years of today and the birthday is a first approximation to the age. This approximation is refined by computing the date on which your birthday falls this year. If this year's birthday is still in the future, then by custom we count that as one year younger. If we were doing something that leaned less towards a legal system, and more towards scientific work, we might well compute in other ways, such as rounding to the nearest year (also easy to do with this library).
If the birthday is on Feb 29, the above code still works: birthday + age will result (75% chance) in an invalid date, e.g.: feb/29/2015. However this invalid date will correctly compare greater than feb/28/2015 and less than mar/1/2015, exactly as we need it to! Invalid dates are your friend, not your enemy -- you just have to know they exist and what to do about them.
Now it is a simple matter to report your findings:
if (age >= years{18})
std::cout << "full aged at " << age.count() << "\n";
else
std::cout << "not full aged at " << age.count() << "\n";
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.