add_month removed from boost::gregorian? - c++

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

Related

Performing date time arithmetic in custom date time class

I have a very naive struct representing date time which I would like to perform arithmetic on:
struct MyDateTime
{
MyDateTime(int year, int month, int day, uint64_t nanos);
int year;
int month;
int day;
uint64_t nanosSinceMidnight;
};
I'd like to be able to add/subtract MyDateTime from another MyDateTime.
My idea was to make my struct a wrapper and use Boost internally.
I looked at Boost Posix Time:
https://www.boost.org/doc/libs/1_55_0/doc/html/date_time/examples.html#date_time.examples.time_math
But this seems to only be doing time math (not accounting for the date component).
I looked at Boost Gregorian Date but I couldn't see any time argument in the constructors.
What is the simplest way to use Boost, so I can perform datetime arithmetic?
As you may have realized by now, dates cannot be added.
Dates and timestamps are mathematically akin to tensors, in that their difference type is in a different domain.
When you commented that time_duration doesn't include a date, you still had a point though.
Because the time_duration might be the time-domain difference type (the difference type ptime) but we need an analog for the date-part of ptime, which is boost::gregorian::date.
Boost Gregorian dates are basically blessed tuples of (yyyy,mm,dd).So a natural difference type would just be a signed integral number of days. And that's exactly* what boost::gregorian::date_duration is:
boost::gregorian::date_duration x = date{} - date{};
boost::posix_time::time_duration y = ptime{} - ptime{};
Because that type is implemented in the Gregorian module you will get correct differences, even with special cases like leap days and other anomalies: https://www.calendar.com/blog/gregorian-calendar-facts/
So, you could in fact use that type as a difference type, just for the ymd part.
Simplify
The good news is, you don't have to bother: boost::posix_time::ptime encapsulates a full boost::gregorian::date, hence when you get a boost::posix_time::time_duration from subtracting ptimes, you will already get the number of days ciphered in:
#include <boost/date_time.hpp>
int main() {
auto now = boost::posix_time::microsec_clock::local_time();
auto later = now + boost::posix_time::hours(3);
auto tomorrow = later + boost::gregorian::days(1);
auto ereweek = later - boost::gregorian::weeks(1);
std::cout << later << " is " << (later - now) << " later than " << now
<< std::endl;
std::cout << tomorrow << " is " << (tomorrow - later) << " later than " << later
<< std::endl;
std::cout << ereweek << " is " << (ereweek - now) << " later than " << now
<< std::endl;
}
Starting from the current time we add 3 hours, 1 day and then subtract a week. It prints: Live On Coliru:
2021-Mar-28 01:50:45.095670 is 03:00:00 later than 2021-Mar-27 22:50:45.095670
2021-Mar-29 01:50:45.095670 is 24:00:00 later than 2021-Mar-28 01:50:45.095670
2021-Mar-21 01:50:45.095670 is -165:00:00 later than 2021-Mar-27 22:50:45.095670
Note that 24h is 1 day, and -165h is (7*24 - 3) hours ago.
There's loads of smarts in the Gregorian calendar module:
std::cout << date{2021, 2, 1} - date{2020, 2, 1} << std::endl; // 366
std::cout << date{2020, 2, 1} - date{2019, 2, 1} << std::endl; // 365
Taking into account leap days. But also knowing the varying lengths of a calendar month in context:
auto term = boost::gregorian::months(1);
for (date origin : {date{2021, 2, 17}, date{2021, 3, 17}}) {
std::cout << ((origin + term) - origin) << std::endl;
};
Prints 28 and 31 respectively.
Applying It To Your Type
I'd suggest keeping with the library difference type, as clearly you had not previously given it any thought that you needed one. By simply creating some interconversions you can have your cake and eat it too:
struct MyDateTime {
MyDateTime(int year = 1970, int month = 1, int day = 1, uint64_t nanos = 0)
: year(year),
month(month),
day(day),
nanosSinceMidnight(nanos) {}
operator ptime() const {
return {date(year, month, day),
microseconds(nanosSinceMidnight / 1'000)};
}
explicit MyDateTime(ptime const& from)
: year(from.date().year()),
month(from.date().month()),
day(from.date().day()),
nanosSinceMidnight(from.time_of_day().total_milliseconds() * 1'000) {}
private:
int year;
int month;
int day;
uint64_t nanosSinceMidnight;
};
Now, I would question the usefulness of keeping your MyDateTime type, but I realize legacy code exists, and sometimes you require a longer time period while moving away from it.
Nanoseconds
Nanosecond precision is not enabled by default. You need to [opt in to use that](https://www.boost.org/doc/libs/1_58_0/doc/html/date_time/details.html#boost-common-heading-doc-spacer:~:text=To%20use%20the%20alternate%20resolution%20(96,the%20variable%20BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG%20must%20be%20defined).
In the sample below I do.
Be careful that al the translation units in your project use the define, or you will cause ODR violations.
Live Demo
Adding some convenience operator<< as well:
Live On Coliru
#define BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
#include <boost/date_time.hpp>
#include <vector>
using boost::posix_time::ptime;
using boost::gregorian::date;
using boost::posix_time::nanoseconds;
struct MyDateTime {
MyDateTime(MyDateTime const&) = default;
MyDateTime& operator=(MyDateTime const&) = default;
MyDateTime(int year = 1970, int month = 1, int day = 1, uint64_t nanos = 0)
: year(year),
month(month),
day(day),
nanosSinceMidnight(nanos) {}
operator ptime() const {
return {date(year, month, day), nanoseconds(nanosSinceMidnight)};
}
/*explicit*/ MyDateTime(ptime const& from)
: year(from.date().year()),
month(from.date().month()),
day(from.date().day()),
nanosSinceMidnight(from.time_of_day().total_nanoseconds()) {}
private:
friend std::ostream& operator<<(std::ostream& os, MyDateTime const& dt) {
auto save = os.rdstate();
os << std::dec << std::setfill('0') << std::setw(4) << dt.year << "/"
<< std::setw(2) << dt.month << "/" << std::setw(2) << dt.day << " +"
<< dt.nanosSinceMidnight;
os.setstate(save);
return os;
}
int year;
int month;
int day;
uint64_t nanosSinceMidnight;
};
int main() {
namespace g = boost::gregorian;
namespace p = boost::posix_time;
using p::time_duration;
std::vector<time_duration> terms{p::seconds(30), p::hours(-168),
p::minutes(-15),
p::nanoseconds(60'000'000'000 * 60 * 24)};
for (auto mydt : {MyDateTime{2021, 2, 17}, MyDateTime{2021, 3, 17}}) {
std::cout << "---- Origin: " << mydt << "\n";
for (time_duration term : terms) {
mydt = ptime(mydt) + term;
std::cout << "Result: " << mydt << "\n";
}
};
}
Prints
---- Origin: 2021/02/17 +0
Result: 2021/02/17 +30000000000
Result: 2021/02/10 +30000000000
Result: 2021/02/09 +85530000000000
Result: 2021/02/10 +85530000000000
---- Origin: 2021/03/17 +0
Result: 2021/03/17 +30000000000
Result: 2021/03/10 +30000000000
Result: 2021/03/09 +85530000000000
Result: 2021/03/10 +85530000000000

Converting 13 decimals to datetime

I have a project about cars with GPS. I need to return the start and the finish moment for each car.
So we have:
time_t x, y;
Because I will use later them for a transformation.
I have a problem. I read from an external file data in this format:
auto1
1439467747492
auto1
1439467748512
...etc.
auto1->name of the car;
1439467747492->the moment in time of the car
I tried to get the first position of the first moment and the last moment for each car. This is the code in C++:
long test = momenti[choice1]/1000;
time_t x = test;
cout << " Momentul initial:\n " << ctime(&x) << endl;
long test1 = momentf[choice1] / 1000;
time_t y = test1;
cout << " Momentul final:\n " << ctime(&y) << endl;
I receive the same date for every car. Is something like momenti[i]=momentf[i]
What did I do wrong?
It is not good. According epoch converter we should get this : GMT: Thu, 13 Aug 2015 12:09:07 GMT
Here is how you can get this output with C++11/14 and using this free, open source date library which extends the C++ <chrono> library to handle dates.
#include "date.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
using namespace std;
using namespace date;
using time_point = std::chrono::time_point<system_clock, milliseconds>;
auto tp = time_point{1439467747492ms};
auto dp = floor<days>(tp);
auto time = make_time(tp - dp);
auto ymd = year_month_day{dp};
cout << "GMT: " << weekday{dp} << ", " << ymd.day() << ' ' << ymd.month()
<< ' ' << ymd.year() << ' ' << time << " GMT\n";
}
Output:
GMT: Thu, 13 Aug 2015 12:09:07.492 GMT
I threw in the fractional seconds for fun, and it seemed a shame to waste them (the C lib won't give them to you). If you really don't want them, it is easy to fix:
auto time = make_time(floor<seconds>(tp) - dp);
Now the output is:
GMT: Thu, 13 Aug 2015 12:09:07 GMT
You need C++14 for the 1439467747492ms above. If you only have C++11 you can sub in this instead: milliseconds{1439467747492}. If you only have C++03, then you are 13 years behind the times and stuck with ctime. ;-)
The chrono solution will offer you greater type safety, more flexibility, and greater performance.
If i can fix and the latitude and longitude problem would be great lol
If you can translate latitude and longitude into an IANA timezone name (and there are tools to do this), I've got a IANA timezone database parser for you which interoperates with <chrono> and "date.h".
#include <iostream>
#include <cstring>
#include <time.h>
using namespace std;
int main()
{
long test = 1439467747492;
time_t x = test;
cout << ctime( &x ) << endl;
return 0;
}
Produces
Tue Sep 18 20:15:32 1990

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

C++: Converting date-time between timezones keeping precision

Consider the input: 2014-04-14T16:28:07.023 (no time-zone, milliseconds precision)
I parsed it and I have the parts as numbers.
The input is always considered to be in UTC
I want to display it as local time
I want to keep the milliseconds precision when displaying
I have C++98 and boost 1.51.
I inspected high_resolution_clock and system_clock, but was unable to produce the final plot for the problem yet.
As requested in the comments to post as an answer, here is how it can be done without Boost:
#include <iostream>
#include <stdlib.h>
#include <time.h>
int main() {
int year, month, day, hour, minute, second, millisecond;
if (std::cin >> year >> month >> day >> hour >> minute >> second >> millisecond) {
struct tm utc;
utc.tm_year = year;
utc.tm_mon = month;
utc.tm_mday = day;
utc.tm_hour = hour;
utc.tm_min = minute;
utc.tm_sec = second;
utc.tm_isdst = 0;
time_t time = timegm(&utc);
if (time == (time_t) -1)
abort();
struct tm *local = localtime(&time);
if (localtime == NULL)
abort();
year = local->tm_year;
month = local->tm_mon;
day = local->tm_mday;
hour = local->tm_hour;
minute = local->tm_min;
second = local->tm_sec;
std::cout << year << ' ' << month << ' ' << day << ' ' << hour << ' ' << minute << ' ' << second << ' ' << millisecond << std::endl;
}
}
Note that the millisecond variable is read from input, and written to output, without any modification.
This uses the non-standard timegm function, but the documentation for that function contains a more portable implementation that you could include, if you want.
I have a solution which will be sufficient for me, but I don't know if it is the best approach in general or not. I'm about to use boost::posix_time::ptime and boost::date_time's c_local_adjustor:
#include <iostream>
#include <boost/date_time.hpp>
#include <boost/date_time/c_local_time_adjustor.hpp>
int main()
{
typedef boost::posix_time::ptime TP;
typedef boost::date_time::c_local_adjustor<TP> local_adj;
TP tUTC(boost::gregorian::date(2014,4,13),boost::posix_time::millisec(23));
TP tLocal(local_adj::utc_to_local(tUTC));
std::cout << boost::posix_time::to_simple_string(tUTC) << std::endl;
std::cout << boost::posix_time::to_simple_string(tLocal) << std::endl;
return 0;
}
Will print:
2014-Apr-13 00:00:00.023000
2014-Apr-13 02:00:00.023000
I did'nt use using namespace to show where is what. The ptime class has accessors to every detail I need. The c_local_adjustor does not have local_to_utc method, but it can be worked around.
(I got nowhere with chrono, I was able to do only circles in the documentation.)

C++ subtract days in string format YYYYMMDD

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