I'm trying to make a program wherein the user will input his/her birthday and the program will compute how many days, months, years, hours and minutes they've lived.
I've searched Google and I see there's a way to split the date into three to four parts.
I've copied this code and it seems to be working. It's just that I don't understand it. All the forums I've read don't help much either. Can anyone explain it to me?
time_t t = time(NULL);
tm* timePtr = localtime(&t);
cout << "seconds= " << timePtr->tm_sec << endl;
cout << "minutes = " << timePtr->tm_min << endl;
cout << "hours = " << timePtr->tm_hour << endl;
cout << "day of month = " << timePtr->tm_mday << endl;
cout << "month of year = " << timePtr->tm_mon << endl;
cout << "year = " << timePtr->tm_year + 1900 << endl;
cout << "weekday = " << timePtr->tm_wday << endl;
cout << "day of year = " << timePtr->tm_yday << endl;
cout << "daylight savings = " << timePtr->tm_isdst << endl;
In most computing environments, dates and times are a unified concept. The C runtime library (hence also C++) provides the type time_t which measures time (and dates) as the number of seconds since 1970-01-01T00:00:00 UTC.
The localtime() function takes a time_t and converts that into the calendar-like fields which humans are accustomed to, according to the local timezone (which is obtained from the computer—the timezone can also be specified specifically). There is another very similar call, gmtime() which does not consider the local timezone, but always uses the UTC timezone, formerly called GMT.
To do what you want, accept from the user their birth date, birth time, and timezone, and convert that into a time_t. Then subtract that from the current time() value. The difference is the number of seconds they have been alive. To be friendly, use gmtime() to convert that to years, months, days, hours, minutes, and seconds.
This is not standard C++ code. This is (also) POSIX code.
(The recent C++11 standard gives you <chrono> but few compilers implement it; on Linux you'll need GCC 4.6 or 4.7; there is also <ctime>)
See this answer to a question very related to yours.
As for time, gettimeofday, localtime, strftime they have well written reference documentation in the manual page. What don't you understand?
(follow the links I gave you and read the linked pages carefully).
Related
The point in time "2016-Dec-31 23:59:60" is a valid time. It is 1483228826 seconds after "1970-Jan-1". The ":60" in the seconds display is correct, because a leap second was inserted there (see wikipedia leap seconds).
My Question: How do I convert a std::chrono::utc_clock::time_point with this time to local time (or any timezone) and print it including the ":60" for the seconds part?
std::chrono::utc_clock::time_point then;
then += seconds(1483228826);
// Output: 2016-12-31Â 23:59:60
cout << std::format("{}", time_point_cast<seconds>(then));
// Now convert it to local time
// But this will fail, since zoned_time requires a sys_time
auto local = zoned_time{"Europe/Berlin", then};
My problem is: zoned_time, which is responsible for handling time zones, only accepts a sys_time, not a utc_time. But sys_time ignores leap seconds.
If I convert my utc_time to sys_time before constructing a zoned_time, my output will be "..23:59:59" and the leap second is lost.
auto local = zoned_time{"Europe/Berlin", clock_cast<system_clock>(then));
cout << std::format("{}", time_point_cast<seconds>(local));
I think the leap second should be shown even in local time. The leap day after all is also shown in local time.
Unfortunately there is no clean way to do this. The time zone database that is used does not recognize leap seconds.
However there is always a way to work around things.
For seconds precision output it is fairly easy to just brute-force things by running then through get_leap_second_info to find out if then points into a leap second:
utc_seconds then{};
then += 1483228826s;
cout << format("{}", then) << '\n';
auto local = zoned_time{"Europe/Berlin", clock_cast<system_clock>(then)};
auto [is_leap_second, elapsed] = get_leap_second_info(then);
if (is_leap_second)
cout << format("{:%F %H:%M:60}", local) << '\n';
else
cout << format("{}", local) << '\n';
Output:
2017-01-01 00:59:60
This can be extended to subsecond precision, but is just a little messier. Here it is for milliseconds:
utc_time<milliseconds> then{};
then += 1483228826023ms;
cout << format("{}", then) << '\n';
auto local = zoned_time{"Europe/Berlin", clock_cast<system_clock>(then)};
auto [is_leap_second, elapsed] = get_leap_second_info(then);
if (is_leap_second)
{
cout << format("{:%F %H:%M:60.}", local);
cout << setfill('0') << setw(3) << (then-floor<seconds>(then))/1ms << '\n';
}
else
cout << format("{}", local) << '\n';
Output:
2017-01-01 00:59:60.023
So in a nutshell, find out if you're in a leap second. If you're not just print the local time. Otherwise hard-wire the ":60", and compute/format the subseconds if applicable.
I was expecting the following code should print different time stamps t1 and t2, however the result shows t1 and t2 are the same. Where did I make the mistake?
#include<iostream>
#include<ctime>
using namespace std;
int main()
{
time_t t1 = time(NULL);
cout << "time now " << ctime(&t1) << endl;
time_t t2 = t1 + 10000.0;
cout << "time now " << ctime(&t1) << endl << " time later " << ctime(&t2) <<endl;
}
Result:
time now Thu Apr 28 20:37:03 2016
time now Thu Apr 28 20:37:03 2016
time later Thu Apr 28 20:37:03 2016
The answer to your question can be found in the manual page for the ctime() function:
The return value points to a statically allocated string which might
be overwritten by subsequent calls to any of the date and time
functions.
ctime() returns a pointer to an internal buffer it uses. Every time it's called, it returns a pointer to the same buffer:
cout << "time now " << ctime(&t1) << endl << " time later " << ctime(&t2) <<endl;
For this line of code, your compiler generated code that calls ctime() twice, then executes the << operator. But on the second call to ctime(), it overwrote the buffer with the second time, so when the << operator formats the output, because the result of the first call to ctime() is the same pointer, and the buffer that it points to has been overwritten by the second call to ctime(), you get the same time printed twice.
Thank you for posting a Minimal, Complete, and Verifiable example.
What is ctime actually returning? From cppreference:
Pointer to a static null-terminated character string holding the textual representation of date and time. The string may be shared between std::asctime and std::ctime, and may be overwritten on each invocation of any of those functions.
It likely works out that on your compiler, the later ctime() gets called first, then the newer ctime(), then both operator<<()s get evaluated - which emit the same char*. As a result of the unspecified order, your code has undefined behavior. On some compilers, it could work as you hoped it would! On yours, it happens not to.
If you separate out the two calls:
cout << "time now " << ctime(&t1) << endl;
cout << " time later " << ctime(&t2) <<endl;
you'd definitely and consistently see different values.
Quote from N1570 7.27.3 Time conversion functions:
Except for the strftime function, these functions each return a pointer to one of two
types of static objects: a broken-down time structure or an array of char. Execution of
any of the functions that return a pointer to one of these object types may overwrite the
information in any object of the same type pointed to by the value returned from any
previous call to any of them and the functions are not required to avoid data races with
each other.
This suggests that the contents pointed by what is returned from ctime() can be overwritten by another call of ctime(), so you will have to copy the result to use the result in one expression with no sequence point in that.
Try this:
#include<iostream>
#include<ctime>
#include<string>
using namespace std;
int main()
{
time_t t1 = time(NULL);
cout << "time now " << ctime(&t1) << endl;
time_t t2 = t1 + 10000.0;
string t1s = ctime(&t1);
string t2s = ctime(&t2);
cout << "time now " << t1s << endl << " time later " << t2s <<endl;
}
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";
I need to convert times from UTC to a timezone selected by the user. I also have to convert from user input in that time zone to store in UTC.
Currently timezones are defined in Olson format ("America/Los Angeles").
A solution was easy on Linux with timegm, but I can not find a cross platform solution (or any solution) that does the exact same thing on Windows.
I can not use Boost.Date_Time (http://www.boost.org/doc/libs/1_57_0/doc/html/date_time.html) because it does not support historical timezone changes such as varying DST periods over the years. Someone apparently submitted a patch years ago but it does not appear to have been accepted.
The only other solution that seems plausible is to use data and code from: https://www.iana.org/time-zones
Has anyone tried this, or do you have a better idea?
Here is a cross platform, open source C++11/C++14 timezone library that wraps the IANA timezone database, including all historical data. The IANA database is the currently maintained Olson database.
An example from this library is:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std::chrono_literals;
using namespace date;
auto departure = make_zoned("America/New_York",
local_days{dec/30/1978} + 12h + 1min);
auto flight_length = 14h + 44min;
auto arrival = make_zoned("Asia/Tehran",
departure.get_sys_time() + flight_length);
std::cout << "departure NYC time: " << departure << '\n';
std::cout << "flight time is " << make_time(flight_length) << '\n';
std::cout << "arrival Tehran time: " << arrival << '\n';
}
which outputs:
departure NYC time: 1978-12-30 12:01:00 EST
flight time is 14:44
arrival Tehran time: 1978-12-31 11:45:00 IRST
Note the date: December of 1978. This is historically accurate (as far as timezone handling is concerned). The paper goes on to demonstrate how the same flight on the next day has a different arrival time because of a timezone change in Tehran.
The example above does not handle leap seconds. However if you actually do want to handle leap seconds, the library can handle it with a small amount of extra work:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std::chrono_literals;
using namespace date;
auto departure = make_zoned("America/New_York",
local_days{dec/31/1978} + 12h + 1min);
auto departure_utc = to_utc_time(departure.get_sys_time());
auto flight_length = 14h + 44min;
auto arrival = make_zoned("Asia/Tehran",
to_sys_time(departure_utc + flight_length));
std::cout << "departure NYC time: " << departure << '\n';
std::cout << "flight time is " << make_time(flight_length) << '\n';
std::cout << "arrival Tehran time: " << arrival << '\n';
}
departure NYC time: 1978-12-31 12:01:00 EST
flight time is 14:44
arrival Tehran time: 1979-01-01 11:14:59 IRST
This is all described in detail at:
http://howardhinnant.github.io/date/tz.html
and freely available at:
https://github.com/HowardHinnant/date
I have looked at many examples seem to address this simple case. The string I want to parse is:
"2012-06-01 16:45:34 EDT"
I have tried to create a local_time_input_facet with the folloiwng:
"%Y-%m-%d %H:%M:%S %Z"
The zone pointer of the local_date_time object is always not set. Reading the documentation is confusing:
%Z *!
Full time zone name (output only). This flag is ignored when using the time_facet with a ptime.
"EDT" // Eastern Daylight Time
Has anyone done this before?
UPDATE: I have updated the code to illustrate the problem a little better:
using namespace std;
using namespace boost::local_time;
int main()
{
stringstream ss;
// Set up the input datetime format.
local_time_input_facet *input_facet
= new local_time_input_facet("%Y-%m-%d %H:%M:%S %ZP");
ss.imbue(std::locale(ss.getloc(), input_facet));
local_date_time ldt(not_a_date_time),ldt1(not_a_date_time);
// Read a time into ldt
ss.str("2012-06-01 17:45:34 EDT");
ss >> ldt;
ss.str("2012-06-01 17:45:34 CDT");
ss >> ldt1;
std::cerr << (ldt - ldt1).total_seconds() << std::endl;
// Write the time to stdout.
cout << "Full Time:\t" << ldt.to_string() << endl;
cout << "Local time:\t" << ldt.local_time() << endl;
cout << "Time zone:\t" << ldt.zone_as_posix_string() << endl;
cout << "Zone abbrev:\t" << ldt.zone_abbrev() << endl;
cout << "Zone offset:\t" << ldt.zone_abbrev(true) << endl;
cout << "Full Time:\t" << ldt1.to_string() << endl;
cout << "Local time:\t" << ldt1.local_time() << endl;
cout << "Time zone:\t" << ldt1.zone_as_posix_string() << endl;
cout << "Zone abbrev:\t" << ldt1.zone_abbrev() << endl;
cout << "Zone offset:\t" << ldt1.zone_abbrev(true) << endl;
return 0;
}
OUTPUT:
0
Full Time: 2012-Jun-01 17:45:34 EDT
Local time: 2012-Jun-01 17:45:34
Time zone: EDT+00
Zone abbrev: EDT
Zone offset: +0000
Full Time: 2012-Jun-01 17:45:34 CDT
Local time: 2012-Jun-01 17:45:34
Time zone: CDT+00
Zone abbrev: CDT
Zone offset: +0000
The Bug
According to boost's documentation here: http://www.boost.org/doc/libs/1_57_0/doc/html/date_time/date_time_io.html#date_time.format_flags
%Z is:
Full time zone name (output only).
It also says to %ZP:
Posix time zone string (available to both input and output).
So you need to change %Z to %ZP. Now your timestamps will parse. However, you'll notice that the zone offset will not be set.
Posix Time Zone Strings
For a Posix time zone string, you'll need to specify at least the zone abbreviation and the offset from UTC, e.g. EST-5.
A full Posix time zone string is formatted as:
"std offset dst [offset],start[/time],end[/time]"
with no spaces, according to http://www.boost.org/doc/libs/1_57_0/doc/html/date_time/local_time.html#date_time.local_time.posix_time_zone
Here are some examples of full Posix time zone strings for EST and PST:
EST-5EDT,M3.2.0,M11.1.0
PST-8PDT,M4.1.0,M10.1.0
This contains the information on when Daylight Savings Time is in effect.
However, you may be able to get away with EDT-4 in your case, depending on what you're doing with it.
It should be noted that as simple as Posix time zones are, they are limited in that they don't account for historical changes in time zone rules. I think it's best to just avoid working with time zones in the first place.
What if I can't control the input format?
As the OP noted in the comments, the offsets are not listed in his input timestamps. One solution is to read the zone abbreviation from the end of the input timestamp (e.g. "EDT"), and check it against a map of known zone abbreviations:
std::map<std::string, std::string> zone_map;
zone_map["EST"] = "EST-5EDT,M4.1.0,M10.5.0"; // Eastern Standard Time
zone_map["EDT"] = zone_map["EST"]; // Eastern Daylight Time
zone_map["PST"] = "PST-8PDT,M4.1.0,M10.1.0"; // Pacific Standard Time
zone_map["PDT"] = zone_map["PST"]; // Pacific Daylight Time
// ...
(Note the DST zones above should be the same as the standard time zones.)
You might also get away with with just storing simple UTC offsets:
zone_map["EST"] = "EST-5"; // Eastern Standard Time
zone_map["EDT"] = "EDT-4"; // Eastern Daylight Time
// ...
Working Example
Here's an example that uses a built-in timezone database:
#include <map>
#include <string>
#include <sstream>
#include <iostream>
#include <boost/date_time/local_time/local_time.hpp>
using namespace boost::local_time;
int main()
{
// A little database of time zones.
std::map<std::string, std::string> zone_map;
zone_map["EST"] = "EST-5EDT,M4.1.0,M10.5.0"; // Eastern Standard Time
zone_map["EDT"] = zone_map["EST"]; // Eastern Daylight Time
zone_map["PST"] = "PST-8PDT,M4.1.0,M10.1.0"; // Pacific Standard Time
zone_map["PDT"] = zone_map["PST"]; // Pacific Daylight Time
// ...
// This is our input timestamp.
std::string timestamp = "2012-06-01 16:45:34 EDT";
// Replace time zone abbrev with full Posix time zone.
const size_t abbrev_pos = timestamp.find_last_of(' ') + 1;
const std::string abbrev = timestamp.substr(abbrev_pos);
timestamp.replace(abbrev_pos, std::string::npos, zone_map[abbrev]);
std::cout << "Time stamp with full timezone: " << timestamp << std::endl;
// Set up the input datetime format.
local_time_input_facet *input_facet = new local_time_input_facet("%Y-%m-%d %H:%M:%S %ZP");
std::stringstream ss;
ss.imbue(std::locale(ss.getloc(), input_facet));
// This is our output date time.
local_date_time ldt(not_a_date_time);
// Read the timestamp into ldt.
ss.str(timestamp);
ss >> ldt;
// Write the time to stdout.
std::cout << "Full Time:\t" << ldt.to_string() << std::endl
<< "Local time:\t" << ldt.local_time() << std::endl
<< "Time zone:\t" << ldt.zone_as_posix_string() << std::endl
<< "Zone abbrev:\t" << ldt.zone_abbrev() << std::endl
<< "Zone offset:\t" << ldt.zone_abbrev(true) << std::endl;
return 0;
}
This outputs:
Time stamp with full timezone: 2012-06-01 16:45:34 EST-5EDT,M4.1.0,M10.5.0
Full Time: 2012-Jun-01 16:45:34 EDT
Local time: 2012-Jun-01 16:45:34
Time zone: EST-05EDT+01,M4.1.0/02:00,M10.5.0/02:00
Zone abbrev: EDT
Zone offset: -0400
While the solution here may not be ideal, it's the only way I can see of doing it.