Calculate past date using Months value - c++

I want to calculate past date from today based on Months input.
Like today is 29-08-2019, 6 months before it was be 29-02-2019.
User inputs would be number of months. It could be 6, 8, 18, 30, 60....
I want to calculate exact complete date. I have tried below code which helps me to get Date for current and past year, but I am looking some solution to get date for months value which is much higher.
time_t now = time( NULL);
struct tm now_tm = *localtime( &now);
int inDuration = 0;
std::cout << "Add Duration..." << std::endl;
std::cin >> inDuration; //month value. looking for solution when mnth value is more then month in current and previous year.
int crnMonth = now_tm.tm_mon+1;
int pastDay = now_tm.tm_mday;
int pastMonth = 0;
int pastYear = now_tm.tm_year + 1900;
if(inDuration > crnMonth)
{
pastMonth = (12-(inDuration-crnMonth));
pastYear = (now_tm.tm_year + 1900)-1;
}
else
{
pastMonth = crnMonth-inDuration;
}
printf("%d-%d-%d", pastDay, pastMonth, pastYear);

This is very easy using Howard Hinnant's free, open-source date/time library:
#include "date/tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
// Get current local date
auto now_utc = floor<seconds>(system_clock::now());
auto now_local = zoned_seconds{current_zone(), now_utc}.get_local_time();
year_month_day crnDate{floor<days>(now_local)};
// Get number of months
int inDuration = 0;
std::cout << "Add Duration..." << std::endl;
std::cin >> inDuration;
// Compute past date
auto pastDate = crnDate - months{inDuration};
if (!pastDate.ok())
pastDate = pastDate.year()/pastDate.month()/last;
std::cout << pastDate << '\n';
}
Example output:
35
2016-09-29
The first few lines get the current time (in UTC), and then convert that to your local time, and then converts the local time to a {year, month, day} structure.
Then the number of desired months is obtained from the user as in your question.
Finally, the input is converted to a months duration and that is subtracted from the current date. If the current date is near the end of a month, it is possible that the past date won't be a valid date (e.g. September 31). If this happens, one has to decide on a policy of what to do. The above example chooses to "snap" to the end of the month. Other policies such as overflowing into the next month are also possible.
Some installation is required for the local time zone support.
If getting the current date in UTC is good enough, then no time zone support is required and you could use the header-only library "date/date.h" (requires no installation). Just #include "date/date.h" instead of #include "date/tz.h", and change the first 3 lines to:
// Get current UTC date
year_month_day crnDate{floor<days>(system_clock::now())};
And the rest of the program works with the same code.

Related

Why calendar week number updates on Monday at 3 a.m., but not at midnight?

I am using iso_week.h from howardhinnant.github.io/iso_week.html to calculate the week number for a given date. However, it looks that it updates the week number on Monday at 3 a.m., instead of midnight.
For example code like this:
#include <iostream>
#include "iso_week.h"
int main() {
using namespace iso_week;
using namespace std::chrono;
/* Assume we have two time points:
* tp1 corresponds: Monday July 15 02:50:00 2019
* tp2 corresponds: Monday July 15 03:00:00 2019
*/
// Floor time points to convert to the sys_days:
auto tp1_sys = floor<days>(tp1);
auto tp2_sys = floor<days>(tp2);
// Convert from sys_days to iso_week::year_weeknum_weekday format
auto yww1 = year_weeknum_weekday{tp1_sys};
auto yww2 = year_weeknum_weekday{tp2_sys};
// Print corresponding week number of the year
std::cout << "Week of yww1 is: " << yww1.weeknum() << std::endl;
std::cout << "Week of yww2 is: " << yww2.weeknum() << std::endl;
}
The output is:
Week of yww1 is: W28
Week of yww2 is: W29
Why is this being done?
At first I didn't notice your comment:
// Floor time points to convert to the sys_days:
This means that tp1 and tp2 are based on system_clock. And system_clock models UTC.
You can use the tz.h header (tz.cpp source) to get your current time zone, convert the UTC time points to local time points, and then feed them to year_weeknum_weekday. This will define the start of the day as your local midnight instead of midnight UTC.
This would look something like:
#include "date/iso_week.h"
#include "date/tz.h"
#include <iostream>
int
main()
{
using namespace iso_week;
using namespace date;
using namespace std::chrono;
auto tp1 = floor<seconds>(system_clock::now());
zoned_seconds zt{current_zone(), tp1};
auto tp1_local = floor<days>(zt.get_local_time());
auto yww1 = year_weeknum_weekday{tp1_local};
std::cout << "Week of yww1 is: " << yww1.weeknum() << std::endl;
}
current_zone() queries your computer for its current local time zone setting. If you prefer some other time zone, you can just replace current_zone() with the time zone name:
zoned_seconds zt{"Europe/Athens", tp1};
If you want to work in a precision finer than seconds, zoned_seconds is just a type alias for zoned_time<seconds>. So use whatever precision you need (e.g. zoned_time<milliseconds>).
Use of tz.h does require some installation. It is not header only.
Could this have something to do with your time zone? I know a lot of businesses are located on the east coast and "iso_week.h" could be based on that time, meaning it could be running at midnight and it just tells you that it is running at 3am. If this is not the case would it be wrong to just run the program at 9pm?

Convert the seconds since "midnight 1904-1-1" to a date-time string

In some multimedia metadata, there may be date-time in seconds since
midnight, Jan. 1, 1904, in UTC time.
As I know, the date time function is normally based on 1970-1-1 midnight in C/C++ standard library, at least in Visual C++, is there a function in C/C++/Win32-API to convert the seconds since "1904-1-1 midnight" to a date time string, like as "hh:mm:ss MM. dd, yyyy" or other format string or a structure like as "struct tm"?
struct tm
{
int tm_sec; // seconds after the minute - [0, 60] including leap second
int tm_min; // minutes after the hour - [0, 59]
int tm_hour; // hours since midnight - [0, 23]
int tm_mday; // day of the month - [1, 31]
int tm_mon; // months since January - [0, 11]
int tm_year; // years since 1900
int tm_wday; // days since Sunday - [0, 6]
int tm_yday; // days since January 1 - [0, 365]
int tm_isdst; // daylight savings time flag
};
Solution#1:
int main()
{
SYSTEMTIME systm;
memset(&systm, 0, sizeof(systm));
systm.wYear = 1904;
systm.wMonth = 1;
systm.wDay = 1;
FILETIME filetm;
if (SystemTimeToFileTime(&systm, &filetm) == FALSE){
printf("Failed to convert system time to file-time.\n");
return 0;
}
ULARGE_INTEGER nanoSeconds;
nanoSeconds.HighPart = filetm.dwHighDateTime;
nanoSeconds.LowPart = filetm.dwLowDateTime;
nanoSeconds.QuadPart += 3600ULL * 10000000; // add 1hour based on 1904/1/1 midnight
filetm.dwHighDateTime = nanoSeconds.HighPart;
filetm.dwLowDateTime = nanoSeconds.LowPart;
if (FileTimeToSystemTime(&filetm, &systm) == FALSE){
printf("Failed to convert file-time to system time.\n");
return 0;
}
printf("New system time by adding 1 hour: %d-%02d-%02d %02d:%02d:%02d.%03d\n",
systm.wYear, systm.wMonth, systm.wDay,
systm.wHour, systm.wMinute, systm.wSecond, systm.wMilliseconds);
return 0;
}
The output is
New system time by adding 1 hour: 1904-01-01 01:00:00.000
Solution#2:
With #Howard Hinnant's date.h, it can also solve this issue, please see the sample code provided by him https://stackoverflow.com/a/49733937/3968307
This would be a good time to use Howard Hinnant's free, open-source date/time library:
#include "date/date.h"
#include <cstdint>
#include <iostream>
#include <string>
std::string
convert(std::int64_t seconds_since_1904)
{
using namespace date;
using namespace std::chrono;
constexpr auto offset = sys_days{January/1/1970} - sys_days{January/1/1904};
return format("%T %m.%d, %Y", sys_seconds{seconds{seconds_since_1904}} - offset);
}
int
main()
{
std::cout << convert(3'606'124'378) << '\n';
}
Output:
13:12:58 04.09, 2018
Update
The above code will port to C++20 (when it ships) by:
Change #include "date/date.h" to #include <chrono>
Change using namespace date; to using namespace std;
Change "%T %m.%d, %Y" to "{:%T %m.%d, %Y}"
As you can easily calculate with any available spreadsheet application in your system, the difference in seconds between those two timestamps (assumed both are in UTC time) the difference in seconds from 1/1/1904 to 1/1/1970 is 2,082,844,800 sec. So the conversion function from a unix timestamp to your time, consists in adding 2082844800 to the unix timestamp you receive from any of the time functions. In case you want to pass back from a timestamp in your time to unix timestamp, then subtract that fixed value from your timescale. Beware that that number does not fit in a signed int so you must use probably a 64bit number to manage properly all those timestamps. Worse if you want to use nanoseconds resolution.
I don't guess the reason of using that strange epoch timestamp, but to illustrate a practical and in use application of such differences, there's a timestamp in internet that uses an epoch close to that, that is the NTP (Network Time Protocol) timestamp, that is based on 1/1/1900 epoch and has a resolution of 1/2**32 sec. that is around 232 ps. for a specification of this protocol, see RFC-5905
The time problem here practically begs you to write your own code for it. The year 1900 is an exception since it is divisible by 4 but still is not a leap year, so by starting in 1904 you can avoid that particular exception and use the fact that there are 1461 days in every four-year period starting with 1904.

boost::locale::date_time: How to get data from date_time object in Boost C++?

I'm trying to handle dates and times in my code, and have been pointed in the direction of the boost library - specifically, boost::locale::date_time (in part because this lets me avoid Daylight Saving Time weirdness that was making my previous implementation difficult).
However, I'm getting inconsistent results. When I store a date in the date_time object and later try to get data from it, it is incorrect. Here's an example:
#include <boost\\asio\\error.hpp>
#include <boost\\locale.hpp>
using namespace std;
int main()
{
// Necessary to avoid bad_cast exception - system default should be fine
boost::locale::generator gen;
std::locale::global(gen(""));
// Create date_time of 12/19/2016
boost::locale::date_time dt = boost::locale::period::year(2016) + boost::locale::period::month(12) + boost::locale::period::day(19);
unsigned int month = dt.get(boost::locale::period::month());
unsigned int day = dt.get(boost::locale::period::day());
unsigned int year = dt.get(boost::locale::period::year());
cout << month << "/" << day << "/" << year << endl;
// Expected output: 12/19/2016
// Actual output: 0/19/2017
}
What am I doing wrong? I just want to extract the saved days, months, years, hours, etc.
Thank you.
EDIT: It's possible I'm initially setting the date_time in an incorrect manner. Is there a better way to explicitly set a date time (for instance, to 12-19-2016), assuming I have all the relevant data in integer (not string) format?
2016-04-05 + 12 months = 2017-04-05. This makes sense, since 12 months is a whole year.
Try adding 11 months instead, then increment to adjust from a 0-based month to a 1-based month.
boost::locale::date_time dt = boost::locale::period::year(2016) + boost::locale::period::month(11) + boost::locale::period::day(19);
uint month = dt.get(boost::locale::period::month()) + 1;
uint day = dt.get(boost::locale::period::day());
uint year = dt.get(boost::locale::period::year());
cout << month << "/" << day << "/" << year << endl;

localtime_s fails where gmtime_s succeeds with dates before 1-1-1970

I'm trying to get the current year stored in a date from before 1970 using an std::chrono::time_point<std::chrono::system_clock>, however I've run into an issue regarding the reading from its contents into a std::tm struct.
I convert the time_point to a time_t first, after which I read its values to get the tm_year value. However, when trying to do so, the code fails when using localtime_s, however it succeeds when I'm using gmtime_s. This is only for dates before 1-1-1970, dates after that work fine using both functions.
The code below reproduces the error. If terstGmTimeVsLocalTime is called with utc=true it works, if it is called with utc=false it doesn't produce the correct output.
#include <iomanip>
#include <time.h>
#include <iostream>
void testGmTimeVsLocaltime(const bool& utc) {
// Create time
std::tm timeInfoWrite = std::tm();
timeInfoWrite.tm_year = 1969 - 1900; // Year to parse, here it is 1969
timeInfoWrite.tm_mon = 0;
timeInfoWrite.tm_mday = 1;
timeInfoWrite.tm_hour = 1;
timeInfoWrite.tm_min = 0;
timeInfoWrite.tm_sec = 0;
timeInfoWrite.tm_isdst = -1;
std::chrono::time_point<std::chrono::system_clock> timePoint = std::chrono::system_clock::from_time_t(utc ? _mkgmtime(&timeInfoWrite) : std::mktime(&timeInfoWrite));
// Convert to time_t
std::time_t timeT = std::chrono::system_clock::to_time_t(timePoint);
// Read values
std::tm timeInfoRead;
if (utc) {
gmtime_s(&timeInfoRead, &timeT);
} else {
localtime_s(&timeInfoRead, &timeT);
}
// Output result
std::cout << (timeInfoRead.tm_year + 1900) << '\n';
// Wait for input
std::getchar();
}
int main() {
testGmTimeVsLocaltime(true); // Set to false to show bug
return 0;
}
utc=true outputs 1969, as would be expected. However, utc=false outputs 1899 (presumably since an error occurs and tm_year gets set to -1).
Is there anything I'm missing? The documentation doesn't specifically specify that localtime_s should fail for dates before 1-1-1970.
I'm on Windows 10 x64 if it makes a difference.
Using Howard Hinnant's free, open-source date lib, you can completely side-step the clumsy, error and bug-prone C api, and work directly with a modern <chrono>-based system:
#include "chrono_io.h"
#include "date.h"
#include <iostream>
void
testGmTimeVsLocaltime()
{
using namespace date;
// Create time
auto timeInfoWrite = 1969_y/jan/1;
sys_days timePoint = timeInfoWrite; // this is a chrono::time_point
std::cout << timePoint.time_since_epoch() << '\n'; // -365 days
// Convert to time_t
// no need
// Read values
year_month_day timeInfoRead = timePoint;
// Output result
std::cout << timeInfoRead.year() << '\n';
}
int
main()
{
testGmTimeVsLocaltime();
}
Output:
-365[86400]s
1969
There are literals to make it easy to fill in a year_month_day struct which is the analog to the year, month and day parts of a tm. You can easily convert this to a std::chrono::time_point<system_clock, days> (sys_days). This is the same as a system_clock::time_point, but with a precision of days instead. It itself will implicitly convert to a seconds-precision time_point (typedef'd to sys_seconds), or to a system_clock::time_point.
Above I just output its time_since_epoch() which shows that it is -365 days prior to the epoch.
There's never really any need to convert to C API data structures, but it is easy if you want to. For example, assuming time_t is seconds since 1970-01-01:
std::time_t timeT = sys_seconds{timePoint}.time_since_epoch().count();
std::cout << timeT << '\n';
which outputs:
-31536000
The reverse conversion (back to year_month_day) is just as easy. If you want to convert from timeT it is just slightly more involved:
year_month_day timeInfoRead = floor<days>(sys_seconds{seconds{timeT}});
This first converts time_t to chrono::seconds, and then to a seconds-precsion time_point, and then to a days-precsion time_point, finally to the year_month_day field type (tm-like).
Finally year_month_day has a year() getter member function which is streamable. You can explicitly convert year to int if desired:
int{timeInfoRead.year()}
But I think it best to keep things like years, months and days as distinct types so that the compiler can help you catch when you accidentally mix them up.
Finally, if you really meant that you wanted 1969-01-01 00:00:00 in your computer's local timezone, there's a library to do that as well. And it is just a minor modification of the simple program above.
#include "tz.h"
#include <iostream>
void
testGmTimeVsLocaltime()
{
using namespace date;
using namespace std::chrono;
// Create time
auto timeInfoWrite = 1969_y/jan/1;
auto timePoint = make_zoned(current_zone(), local_days{timeInfoWrite});
// Convert to time_t
std::time_t timeT = timePoint.get_sys_time().time_since_epoch().count();
std::cout << timeT << '\n';
// Read values
timePoint = sys_seconds{seconds{timeT}};
year_month_day timeInfoRead{floor<days>(timePoint.get_local_time())};
// Output result
std::cout << timeInfoRead.year() << '\n';
}
int
main()
{
testGmTimeVsLocaltime();
}
Output:
-31518000
1969
Now you create a zoned_seconds using the computer's current_zone() timezone, and converting your timeInfoWrite to local_days instead of to sys_days.
You can get the local time or the system time out of timePoint. For converting to time_t, system time makes the most sense:
std::time_t timeT = timePoint.get_sys_time().time_since_epoch().count();
And now the output (for me) is 5h later (18000s).
-31518000
You can get back either the local year, or the system (UTC) year, by either using .get_local_time() or .get_sys_time(). For me it makes no difference ("America/New_York"). But if you're in "Australia/Sydney", you'll get 1968 if you request the UTC year instead of 1969. And that's all very easy to simulate by simply substituting "Australia/Sydney" or "America/New_York" for current_zone() in the program above.
Yes, it works on Windows, VS-2013 and later. There is some installation required for the timezone lib: https://howardhinnant.github.io/date/tz.html#Installation

c++ Add or subtract values to the date

I couldn't figüre out how to add values to the date. Imagine, I have a date like 2012-5-15. After I parsed it, I obtained the year,month and day seperately and I tried to convert to my exact date to the days by using time_t and mktime. Do I have a chance to convert it back to the date from the days or seconds I found? Here is part of my code trying to do this;
if(operation="+"){
tm tm1=make_tm(year,mon,day);
time_t time1=mktime(&tm1);
int seconds_per_day=60*60*24;
time_t second=time1/seconds_per_day;
int sum=second //Trying to convert to the int from my time_t, don't know
//if it is neccesary
sum=sum+value //the value I want to add
So, basically if I give an input like 2012/5/15. It calculates how many days it passed by using the method but I just need to convert it to date and make it shown. Do you have any idea?
If you are asking for a way to add days to a particular date, here is a way to do it in similar terms to your attempt:
tm addDays(tm t1, int days)
{
time_t epoch = mktime(&t1);
epoch += (60*60*24 * days);
return *localtime(&epoch);
}
Then use the returned tm structure and access the tm_year, tm_mon and tm_mday members to get the yyyymmdd required output. A very rough and ready example (no proper formatting):
tm t1;
std::fill((char*)&t1, (char*)&t1 + sizeof(t1), 0);
t1.tm_year = 2016;
t1.tm_mon = 8;
t1.tm_mday = 2;
auto result = addDays(t1, 30);
std::cout << result.tm_year << '/' << result.tm_mon << '/' << result.tm_mday << std::endl;
Here is a free, open-source C++11/14 library to do what you want with very nice syntax, and unparalleled performance.
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
year_month_day x = sys_days{2012_y/may/15} + days{55};
std::cout << x << '\n';
}
This outputs:
2012-07-09
If you would prefer to write this yourself, here are the algorithms used by this date library. But the date library wraps everything up in a type-safe manner which catches many errors at compile time.
The date library is also nothing more than an extension of the <chrono> library, and so is very compatible with it:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
auto x = sys_days{2012_y/may/15} + days{55} + 7h + 30min;
std::cout << x << '\n';
}
2012-07-09 07:30
thanks everybody for your attention but I figured out the problem in another way. Here is my way;
tm tm1=make_tm(year,mon,day);//of course I had an make_tm as struct
time_t time1=mktime(&tm1);
time1=time1+value*86400-86400 //because it was a second, I needed to convert the day to second. Then, I don't know why but it showed extra one day. So, again I subtracted 86400 then the problem solved.
char *cb=ctime(&time1);
cout<<cb<<endl;