C++ subtract days in string format YYYYMMDD - c++

I'm struggling a little getting some C++ code to cooperate. Is there a straight forward way to subtract two dates that are strings? I'm trying to get the number of days between them. They are strings, YYYYMMDD. Is the Boost library the right direction to go?

Yes, Boost Date_Time can certainly do that even others may suggest "lighter" alternatives.
Here is the example about the days alive which does almost exactly your problem (just add a second date parsing):
/* Short example that calculates the number of days since user was born.
* Demonstrates comparisons of durations, use of the day_clock,
* and parsing a date from a string.
*/
#include "boost/date_time/gregorian/gregorian.hpp"
#include <iostream>
int
main()
{
using namespace boost::gregorian;
std::string s;
std::cout << "Enter birth day YYYY-MM-DD (eg: 2002-02-01): ";
std::cin >> s;
try {
date birthday(from_simple_string(s));
date today = day_clock::local_day();
days days_alive = today - birthday;
days one_day(1);
if (days_alive == one_day) {
std::cout << "Born yesterday, very funny" << std::endl;
}
else if (days_alive < days(0)) {
std::cout << "Not born yet, hmm: " << days_alive.days()
<< " days" <<std::endl;
}
else {
std::cout << "Days alive: " << days_alive.days() << std::endl;
}
}
catch(...) {
std::cout << "Bad date entered: " << s << std::endl;
}
return 0;
}

Related

Problem when converting from time_t to tm then back to time_t

I have a time_t value of 1530173696 which represents Thursday, June 28, 2018 8:14:56 AM.
I want to round down the time to the nearest hour. Specifically, down to 1530172800, which represent Thursday, June 28, 2018 8:00:00 AM. So, my idea is to convert this time_t to a tm struct, and then assign its sec and min values to 0.
However, after I do that, and after I convert the modified tm back to a time_t value, the value I get is way off. I get a value of 1530158400 which represents Thursday, June 28, 2018 4:00:00 AM. That's 4 hours off. Even checking values of up to 8:59:59 AM still gives the rounded down value of 4:00:00 AM.
I wrote the code below to demonstrate the problem. I use VisulStudio 2017.
I don't understand what I am doing wrong. I appreciate any help. Thanks.
#include <iostream>
#include <time.h>
bool equalTMs(tm& tm1, tm& tm2);
void printTM(tm& myTM);
int main()
{
tm myTM;
time_t datetime = 1530173696;
//datetime = 1530176399; // to check the time_t value of 8:59 AM
gmtime_s(&myTM, &datetime);
myTM.tm_sec = 0;
myTM.tm_min = 0;
time_t myTime_T = mktime(&myTM);
tm sanityCheckTM;
time_t roundedDownToNearestHour = 1530172800;
gmtime_s(&sanityCheckTM, &roundedDownToNearestHour);
time_t sanityCheckTimeT = mktime(&sanityCheckTM);
std::cout << "datetime: " << datetime << std::endl;
std::cout << "myTime_T: " << myTime_T << std::endl;
std::cout << std::endl;
std::cout << "roundedDownToNearestHour: " << roundedDownToNearestHour << std::endl;
std::cout << "sanityCheckTimeT: " << sanityCheckTimeT << std::endl;
std::cout << std::endl;
std::cout << "myTM and sanityCheckTM equal? " << (equalTMs(myTM, sanityCheckTM) ? "true" : "false") << std::endl;
std::cout << "\nmyTM:-\n\n";
printTM(myTM);
std::cout << "\nsanityCheckTM:-\n\n";
printTM(sanityCheckTM);
std::cout << "\n";
time_t _time_t = 1530158400;
tm _tm;
gmtime_s(&_tm, &_time_t);
std::cout << "_time_t: " << _time_t << std::endl;
std::cout << "_tm and sanityCheckTM equal? " << (equalTMs(_tm, sanityCheckTM) ? "true" : "false") << std::endl;
std::cout << "\n_tm:-\n\n";
printTM(_tm);
}
void printTM(tm& myTM)
{
std::cout << "tm_sec: " << myTM.tm_sec << std::endl;
std::cout << "tm_min: " << myTM.tm_min << std::endl;
std::cout << "tm_hour: " << myTM.tm_hour << std::endl;
std::cout << "tm_mday: " << myTM.tm_mday << std::endl;
std::cout << "tm_mon: " << myTM.tm_mon << std::endl;
std::cout << "tm_year: " << myTM.tm_year << std::endl;
std::cout << "tm_wday: " << myTM.tm_wday << std::endl;
std::cout << "tm_yday: " << myTM.tm_yday << std::endl;
std::cout << "tm_isdst: " << myTM.tm_isdst << std::endl;
}
bool equalTMs(tm& tm1, tm& tm2)
{
return (tm1.tm_sec == tm2.tm_sec)
&& (tm1.tm_min == tm2.tm_min)
&& (tm1.tm_hour == tm2.tm_hour)
&& (tm1.tm_mday == tm2.tm_mday)
&& (tm1.tm_mon == tm2.tm_mon)
&& (tm1.tm_year == tm2.tm_year)
&& (tm1.tm_wday == tm2.tm_wday)
&& (tm1.tm_yday == tm2.tm_yday)
&& (tm1.tm_isdst == tm2.tm_isdst);
}
gmtime_s() returns a tm that is expressed in UTC time. You pass that to mktime(), which expects the tm to be expressed in LOCAL time instead. Your StackOverflow profile says you are located in Abu Dhabi, whose time zone is GMT+4. That is why you have a 4-hour discrepancy.
Use localtime_s() instead of gmtime_s().
Since 1530173696 is being used as a Unix Time (UTC excluding leap seconds), this can be solved without involving time zones.
Howard Hinnant's date/time library can be used to solve this problem, and to check that you're getting the right answer. However, skip to the end of this answer if you want to see how to do this very simply without the use of any library at all.
1530173696 is a count of seconds since 1970-01-01 UTC. If you want to convert this into a human readable date/time, one can:
#include "date/date.h"
#include <iostream>
int
main()
{
time_t datetime = 1530173696;
date::sys_seconds tp{std::chrono::seconds{datetime}};
using date::operator<<;
std::cout << tp << '\n';
}
which outputs:
2018-06-28 08:14:56
This does nothing but validate the input. Furthermore tp is nothing more than a std::chrono::time_point based on system_clock but with a precision of seconds. You can round this down to the hour with:
tp = floor<std::chrono::hours>(tp);
Here floor can be grabbed from "date.h" under namespace date, or if you have C++17 or later, you can use std::chrono::floor. You can use "date.h" to print tp out again and you will get:
2018-06-28 08:00:00
(as desired). To turn this back into a time_t, simply extract the duration, and then the count:
time_t myTime_T = tp.time_since_epoch().count();
This will have the value 1530172800 as expected.
Finally, if you do not need to print these time stamps out in a human readable form, you can do the math quite easily yourself:
time_t myTime_T = datetime / 3600 * 3600;
This is essentially the same operation as:
tp = floor<std::chrono::hours>(tp);
except that the floor version will continue to get the correct answer when the input is negative (a timestamp prior to 1970-01-01 00:00:00 UTC). The "manual" implementation will round up to the next hour when given a negative input.

C++ - Comparing only time

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.

add_month removed from boost::gregorian?

I'm using boost::gregorian to perform date calculations. I would like to use add_month as per the example (current as of 1.63 http://www.boost.org/doc/libs/1_63_0/doc/html/date_time/examples.html)
/* Simple program that uses the gregorian calendar to progress by exactly
* one month, irregardless of how many days are in that month.
*
* This method can be used as an alternative to iterators
*/
#include "boost/date_time/gregorian/gregorian.hpp"
#include <iostream>
int main()
{
using namespace boost::gregorian;
date d = day_clock::local_day();
add_month mf(1);
date d2 = d + mf.get_offset(d);
std::cout << "Today is: " << to_simple_string(d) << ".\n"
<< "One month from today will be: " << to_simple_string(d2)
<< std::endl;
return 0;
}
However, this gives the error message
month.cpp: In function `int main()':
month.cpp:33:5: error: `add_month' was not declared in this scope
add_month mf(1);
^
month.cpp:35:19: error: `mf' was not declared in this scope
date d2 = d + mf.get_offset(d);
^
Indeed. The example is outdated. In fact I don't remember seeing this feature so it might be long out-of-date.
I recommend the following approach instead:
/* Simple program that uses the gregorian calendar to progress by exactly
* one month, irregardless of how many days are in that month.
*
* This method can be used as an alternative to iterators
*/
#include "boost/date_time/gregorian/gregorian.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"
#include <iostream>
int main()
{
using namespace boost::gregorian;
date d = day_clock::local_day(),
prev = d - months(1),
next = d + months(1);
std::cout << "Today is: " << to_simple_string(d) << ".\n"
<< "One month before today was: " << to_simple_string(prev) << "\n"
<< "One month from today will be: " << to_simple_string(next) << "\n";
}
Which printed (for me):
Today is: 2017-Mar-23.
One month before today was: 2017-Feb-23
One month from today will be: 2017-Apr-23

c++ get years between date chosen by user and actual date(counting days,months,years)

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";

How to achieve time localization in c++?

I'm using the time_t and ctime header to get the time in my app, and it's all OK. The problem is that, the week days and months are displayed in English and i want them to be displayed in my language (Portuguese). How do I do that?
Here's my code:
time_t tempo = time(NULL);
char *data = ctime(&tempo);
cout << data << endl;
And this is the actual output:
"Fri Aug 21 20:00:55 2015"
I was thinking in something like this:
"20:00:55 Sex 21 Ago 2015"
Where Sex is Fri, and Ago is Aug.
I even used this:
setlocale(LC_ALL,".<code_page>");
My full code is this (It's in Portuguese, so the variables names can be weird):
#include <iostream>
#include <iomanip>
#include <ctime>
#include <string>
#include <fstream>
#include <locale>
#include <stdlib.h>
void titulo();
int numOrdem(int a);
using namespace std;
using namespace boost;//I'm experimenting with this, and not working...
int main()
{
int ordem = 0;
int mesa = 0;
int turno = 0;
int quantidade = 0;
int ordemCheck = 0;
string anterior = " ";
string atual;
titulo();
while(true)
{
setlocale(LC_ALL,"");
time_t tempo = time(NULL);
char *data = ctime(&tempo);
minhaOrdem:
cout << "Insira o número da ordem: ";
cin >> ordem;
ordemCheck = numOrdem(ordem);
if (ordemCheck < 7 || ordemCheck > 7)
{
cout << "Ordem incorreta.\n";
goto minhaOrdem;
}
/*ofstream arquivo;
arquivo.open("ordersOnly.txt", ios::app);
arquivo << ordem << endl;
arquivo.close();
ifstream ler;
ler.open("ordersOnly.txt");
if(ler)
{
while(ler >> atual)
{
if(atual == anterior)
{
cout << "Ordem " << atual << " já requisitada!\n";
goto minhaOrdem;
}
anterior = atual;
}
} Here I'm trying to catch an duplicated value...*/
cout << "Insira a quantidade de peças: ";
cin >> quantidade;
minhaMesa:
cout << "Insira o número da mesa: ";
cin >> mesa;
if(mesa < 1 || mesa > 6)
{
cout << "Mesa incorreta.\n";
goto minhaMesa;
}
meuturno:
cout << "Insira seu turno: ";
cin >> turno;
switch (turno)
{
case 100:
break;
case 200:
break;
case 300:
break;
default:{cout << "Turno Incorreto.\n";goto meuturno;}
}
cout << "\n\n";
arquivo.open("ordensDifipro.txt", ios::app);
arquivo << "Ordem: " << ordem << endl;
arquivo << "Quantidade de peças: " << quantidade << endl;
arquivo << "Mesa: RL" << mesa << endl;
arquivo << "Turno: " << turno << endl;
arquivo << "Data: " << data << endl;
arquivo << endl;
arquivo.close();
}//Main while
}// int main
void titulo()
{
cout << setw(62) << "Requisitor de Ordens - Difipro - v 1.1\n\n\n\n\n";
}
int numOrdem(int a)
{
int counter = 0;
while(a > 0)
{
a /= 10;
counter++;
}
return counter;
}
I hope it helps!
Your question is OS specific, I'm focusing on Linux and perhaps other POSIX OSes
You could parse the time string with strptime(3) and convert the time to string with strftime(3), at least if the time fits in a Unix time_t (this would always be the case for time between 1970 i.e. the Unix Epoch and 2038 because of the Y2038 problem) e.g. using mktime(3) & localtime(3). Read locale(7) about some localization & internationalization things. You might need to call setlocale(3) appropriately.
In C++11 use <chrono> (see also this); see this time_point example to start.
Beware that distant times (e.g. early 1900s) can become quite complex, see this
Addenda: if you want your date to be in Portuguese, either set your LANG or LC_ALL environment variable (see environ(7)) to pt_PT.UTF-8 (perhaps by adding export LANG=pt_PT.UTF-8 in your ~/.bashrc file on Linux, if your login shell is bash) or replace the setlocale(LC_ALL,""); with setlocale(LC_ALL,"pt_PT.UTF-8"); in your program.
BTW, Windows has its documentation on setlocale
You should probably have a look at the Boost Locale library.
http://www.boost.org/doc/libs/1_59_0/libs/locale/doc/html/index.html
Especially the part on formatting and parsing of time formatting:
http://www.boost.org/doc/libs/1_59_0/libs/locale/doc/html/formatting_and_parsing.html
It allows you to portably output things like date time values using the locale settings of the system it's being run on. There is an example doing exactly what tou are trying to do in a just two lines of code. It also does so without asking you to write platform dependant code. You may want to look at other parts of Boost Locale too, including message translation to make it truly work cross language.
Try to avoid using low level platform API's or C++14 features (as an alternate answer seems to suggest) unless you are sure your code will never be used on an other platform or in a C++ project not using C++11/C++14. Boost mostly is your best bet at writing stuff like this portably.