How to create arbitrary date and add days to it - c++ - c++

I am trying to write an application for an assignment and I am new to c++. A small portion of the application requires me to store a date and add an arbitrary number of days as an offset from the date. I know how I would accomplish this with Java or C# but I have been unable to find anything for c++. My professor alluded to ctime but after many searches all the examples I found had to do with the current system time. How do I create a ctime::tm struct and set it to an arbitrary date? Is it possible to add a number of days using ctime to obtain another date? For example, if I added 40 days to January 1, 2001 I would expect February 10, 2001 not January 41, 2001.

To be an example of usage
#include <stdio.h>
#include <time.h>
int main ()
{
time_t currentTime;
time(&currentTime);
struct tm * tmDate;
int day, month, year;
tmDate = localtime (&currentTime);
tmDate->tm_year = 99;
tmDate->tm_mon = 11;
tmDate->tm_mday = 10;
mktime ( tmDate );
printf("now: %d-%d-%d %d:%d:%d\n", tmDate->tm_year + 1900, tmDate->tm_mon + 1, tmDate->tm_mday, tmDate->tm_hour, tmDate->tm_min, tmDate->tm_sec);
return 0;
}
as you can see on
tmDate->tm_year = 99;
tmDate->tm_mon = 11;
tmDate->tm_mday = 10;
you can set, sub, add months, years, days .. to date.
For example simply you can add 1 month to date with
tmDate->tm_mon++;

Related

strftime - convert week number ISO8601 to Gregorian standard C++

I get week number in ISO 8601 format (%V) using strftime and like to convert to week number shown in my outlook (guess it is gregorian).
Outlook calendar setting is:
First Day of the week (Sunday)
First week of the year starts 1st Jan
For certain years, strftime matches with weeknumber shown in outlook, but for some it doesn't.
A simple code check:
#include <stdio.h>
#include <time.h>
int main ()
{
time_t rawtime;
struct tm *tmDate;
char buffer [80];
time (&rawtime);
tmDate = localtime(&rawtime);
tmDate->tm_hour = 12;
tmDate->tm_mday = 1; //Day of the Month
tmDate->tm_mon = 8; //Month (0=Jan...8=Sep)
tmDate->tm_year = 2018-1900; //Year
mktime(tmDate);
strftime (buffer,80,"%Y-W%V",tmDate);
puts (buffer);
return 0;
}
For above code with it's input, the output will be 2018-W35, which matches with my outlook calendar(https://www.calendar-365.com/2018-calendar.html).
Whereas, if you change year to 2019, the output will be 2019-W35, but in my outlook it falls on 2019-W36 (https://www.calendar-365.com/2019-calendar.html).
Is it possible to map ISO8601 week number to gregorian style?
Any suggestions or code sample will be helpful!
Thank you!
The website you reference seems to use a very unique week numbering system. Its definition of week number appears to mirror the ISO definition with the exception that the first day of the week is Sunday instead of Monday. This means that the first day of the year is the Sunday prior to the first Thursday of the year.
There is no strftime flag to give a week number with this definition. But you can easily compute it with the C++20 <chrono> tools. Unfortunately they aren't shipping yet, but you can use this free, open-source C++20 chrono preview library which works with C++11/14/17.
In addition to computing week number, you'll also need to compute the year, as sometimes the gregorian year does not match the year associated with the week. For example according to https://www.calendar-365.com/2019-calendar.html, December 31, 2018 falls on week 1 of 2019.
So here is a function that computes both the year and the week number, given a date:
#include "date/date.h"
#include <chrono>
#include <iostream>
#include <utility>
// {year, week number}
std::pair<int, int>
outlook_weeknum(date::sys_days sd)
{
using namespace date;
auto y = year_month_day{sd + (Thursday - Sunday)}.year();
auto year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);
if (sd < year_start)
{
--y;
year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);
}
return {int{y}, (sd - year_start)/weeks{1} + 1};
}
The logic is a little tricky. The hard part is finding the first day of the year of the date, which is the Sunday prior to the first Thursday of the year. This is nominally:
auto year_start = sys_days{Thursday[1]/January/y} - (Thursday - Sunday);
where y is the year in the week numbering system (usually, but not always the same as the gregorian year). When the date is very late in the year i.e. December 28 - 31, it may fall in the first week of the next year. To catch that possibility, first bump the date by 4 days (the difference between Sunday and Thursday), and then compute the current year.
S M T W T F S
y-1 WL 21 22 23 24 25 26 27
y W1 28 29 30 31 1 2 3
After doing this, compute the start of the year. And if the start of the year happens to be after the date, then you are in the situation that your date belongs in the previous year. In this case, the week number year may be one less than the gregorian year. This can happen when Jan 1 is Friday or Saturday.
S M T W T F S
y-1 WL 27 28 29 30 31 1 2
y W1 3 4 5 6 7 8 9
In summary, the dates 12/28 - 12/31 can have a week year number either equal to their gregorian year, or one greater. And the dates 01/01 and 01/02 can have a week year number either equal to their gregorian year, or one lesser. -- All depending on what day of the month the first Thursday of January falls on [1 - 7].
Once the week number year (y) is figured out, then the week number is simply the difference between the date and the first of the year divided by 7 days (1 week), plus one to bias the first week to 1 instead of 0.
This can be exercised like this:
int
main()
{
using namespace date;
auto [i, w] = outlook_weeknum(2019_y/9/1);
std::cout << i << "-W" << w << '\n';
}
which outputs:
2019-W36
To port this code to C++20:
Drop the #include "date/date.h"
Change namespace date to namespace std::chrono
Change 2019_y to 2019y

Solution doesn't work for number of days between two dates

I know this question has been asked a few times, and I'm asking again because I've got issues with existing solutions on SO.
My goal is to find number of days between 1900-01-01 and a given date. The date will be in the format as yyyy-mm-dd and the type is std::string.
The solution I've followed is https://stackoverflow.com/a/14219008/2633803
And below is my version:
std::string numberOfDaysSince1900v2(std::string aDate)
{
string year, month, day;
year = aDate.substr(0, 4);
month = aDate.substr(5, 2);
day = aDate.substr(8, 2);
struct std::tm a = { 0,0,0,1,1,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month),std::stoi(year) - 1900 };
std::time_t x = std::mktime(&a);
std::time_t y = std::mktime(&b);
double difference;
if (x != (std::time_t)(-1) && y != (std::time_t)(-1))
{
difference = std::difftime(y, x) / (60 * 60 * 24) + 36526; //36526 is number of days between 1900-01-01 and 2000-01-01
}
return std::to_string(difference);
}
It worked fine until the given date comes to 2019-01-29 and 2019-02-01. In both cases, the output is 43494. And for the whole Feb, the output is 3 days less than expected. Then, when it comes to March 2019, the output is back to normal again.
Another case is 2019-09-03, the output is 43710, whilst the expected output is 43711.
Why would this happen to these specific dates? I ran the solution step by step and closely watched the variables in the memory but couldn't explain it.
Any suggestion is appreciated. Thanks.
The month should be represented as an integer between 0 and 11, not 1 and 12.
So
struct std::tm a = { 0,0,0,1,0,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month)-1,std::stoi(year) - 1900 };
I would say there are other problems with your code. You cannot reliably initialise a tm like that (the order of fields within the struct is not guaranteed). Neither does difftime necessarily return a number of seconds (which you are assuming).

Locale dependent Week of Year in C/C++

I am trying to get Week of Year. For this I am using tm * __CRTDECL localtime(const time_t * _Time) but I am not getting desired result which should be locale dependent. So I was looking for solution and some more information. I have found that there is API in JAVA Calendar.getInstance(Locale.GERMAN);.So I am just wondering whether there is such API in c/c++ as I did not find any ( not sure ). If not can anybody give me some time pointers to get Week of Year locale dependant.
The code I am using
int MyClass::getCalendarWeek(time_t time, int * p_year)
{
// Get tm structure of time parameter
tm* pCurrentTm = localtime(&time);
// determine Thursday in that week
LONG offSet = 4 - pCurrentTm->tm_wday;
if (offSet >= 4) {
offSet = -3; // Sunday
}
time += offSet * 86400L;
pCurrentTm = localtime(&time);
if (p_year) {
*p_year = pCurrentTm->tm_year + 1900;// year of current calendar week
}
return (pCurrentTm->tm_yday + 7) / 7; // current calendar week
}
Output:
11 July is in week 28 when running most locales that have monday as first day of the week, but it is week 29 for sunday as first day of week.

Find the date given the year, the month and the "nth" occurrance of day within the month C/C++

In order that a device (with limited memory) is able to manage its own timezone and daylight savings, I'm trying to calculate daylight savings triggers for 85 time zones based on a simplified description of each timezone. I have access to minimal C and C++ libraries within the device. The format of the timezone (inc. DST) description for each time zone is as follows:
UTC - the base time and date from system clock
GMTOffsetMinutes - offset from GMT with DST inactive
DSTDeltaMinutes - modifier to above with DST active (as applicable to TZ)
DSTStartMonth - month in which DST becomes active
DSTStartNthOccurranceOfDay - the nth occurrence of the day name in month
DSTDayOfWeek - Sun = 0 through to Sat = 6
DSTStartHour - hour at which DST becomes active
DSTStartMinute - minute at which DST becomes active
and corresponding EndMonth, EndNth..., EndHour, EndMinute
I have found numerous examples going the other way, i.e. starting with the date, but they involve using the modulus, keeping the remainder and dropping the quotient hence I have been unable to transpose the formula to suit my needs.
I also tried to reuse the standard "Jan = 6, Feb = 2, Mar = 2, Apr = 5, May = 0, etc. modifier table and year modifiers from the "tell me what day the 25th of June, 2067 is?" party trick and developed the following algorithm.
Date = DayOfWeek + ((NthOccuranceOfDay - 1) x 7 ) - MonthCode - YearCode
This worked for the first 6 random test dates I selected but then I started to see dates for which it failed. Is it possible that the basic algorithm is sound but I'm missing a further modifier or maybe that I'm applying the modifiers incorrectly?
Is there another solution I could utilize?
Using this open source, cross platform date library, one can write:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
year_month_day us_daylight_starts = sys_days(sun[2]/mar/2015);
year_month_day us_daylight_ends = sys_days(sun[1]/nov/2015);
std::cout << us_daylight_starts << '\n';
std::cout << us_daylight_ends << '\n';
}
which will output:
2015-03-08
2015-11-01
The formulas this library is based on are in the public domain and documented here.
The algorithms paper has very complete unit tests validating the date algorithms over a range of millions of years (a far larger range than is necessary).
Sometimes daylight savings rules are written in terms of the last weekday of a month. That is just as easily handled:
year_month_day ymd = sys_days(sun[last]/nov/2015);
std::cout << ymd << '\n'; // 2015-11-29
That formula will be off by one week (or even two weeks) if MonthCode + YearCode is greater than or equal to DayOfWeek, because in that case you will be counting NthOccurenceOfDay from a negative date.
As an alternative, with no tables, you can compute the day of week of the first of the month using, for example, Zeller's algorithm:
int NthOccurrence(int year, int month, int n, int dayOfWeek) {
// year is the current year (eg. 2015)
// month is the target month (January == 1...December == 12)
// Finds the date of the nth dayOfWeek (Sun == 0...Sat == 6)
// Adjust month and year
if (month < 3) { --year, month += 12; }
// The gregorian calendar is a 400-year cycle
year = year % 400;
// There are no leap years in years 100, 200 and 300 of the cycle.
int century = year / 100;
int leaps = year / 4 - century;
// A normal year is 52 weeks and 1 day, so the calendar advances one day.
// In a leap year, it advances two days.
int advances = year + leaps;
// This is either magic or carefully contrived,
// depending on how you look at it:
int month_offset = (13 * (month + 1)) / 5;
// From which, we can compute the day of week of the first of the month:
int first = (month_offset + advances) % 7;
// If the dayOfWeek we're looking for is at least the day we just
// computed, we just add the difference. Otherwise, we need to add 7.
// Then we just add the desired number of weeks.
int offset = dayOfWeek - first;
if (offset < 0) offset += 7;
return 1 + offset + (n - 1) * 7;
}

C++ add 1 year to date

This is how im generating my date i want to add 1 year to it. Thanks in advance.
char tmpbuf[128];
time_t ltime;
struct tm *today;
stringstream reD;
string todayDate;
time( &ltime );
today = localtime( &ltime );
strftime( tmpbuf, 128,"%Y-%m-%d_%H:%M:%S", today );
reD << tmpbuf;
reD >> todayDate;
boost::replace_all(todayDate, "_", " ");
cout << todayDate << endl;
OK ive decided to go with boost since it will be easier to add days, so 2 examples i need one to add 1 year, and one to add 14 days, heres what i have so fare
#include "boost/date_time.hpp"
#include "boost/date_time/local_time/local_time.hpp"
using namespace boost::posix_time;
using namespace boost::local_time;
int main(){
local_date_time t = local_sec_clock::local_time(time_zone_ptr());
local_time_facet* lf(new local_time_facet("%Y-%m-%d_%H:%M:%S"));
std::cout.imbue(std::locale(std::cout.getloc(), lf));
std::cout << t << std::endl;
return 0;
}
Edit putting time into string
stringstream reD;
reD.imbue(locale(reD.getloc(), lf));
reD << t;
bthis = reD.str();
cout << bthis << endl;
If you're using C++, I highly recommend boost::date_time to take the leg-work out of this.
There is no such thing as "adding a year".
Let us suppose that you go into incrementing the year by 1, after all, that is what you are aiming for.
Unfortunately, there are some inconsistencies in the way we deal with the time:
leap years: if you are on February 29th, 2008, what does adding a year mean ? February 28th, 2009 or March 1st, 2009 ? (Hint: changing the month is very confusing for the user of a calendar)
leap seconds: on June 30th or December 31th, a second or two might be added to the last minute, making that minute 61 or 62 seconds. (Hint: once again, changing the day is confusing)
special events: like the calendar re-adjustment that occurred in 1582, where Thursday, October 4, 1582 was followed by Friday, October 15, 1582, resulting in a full 10 days loss.
The problem here, is not really in "adding" a year, you can always choose to round down (preferably when end-users are involved) or up. The real problem is that if you follow this reasoning, you unfortunately lose the symmetry between adding and removing a year:
original: February 29th, 2008
+ 1 year: March 1st, 2009 (rounding up)
- 1 year: March 1st, 2008
or by adding 4 years in several consecutive leaps:
original: February 29th, 2008
+ 2 years: February 28th, 2010 (rounding down)
+ 2 years: February 28th, 2012
Oups!
The mathematical solution to this is to simply evaluate the year duration in terms of seconds, Let's ask Wolfram about it: 3.154 × 10^7 seconds.
However, it may be quite confusing for the user.
And finally, the last solution is that whenever you make computations based on dates and duration, you save the original date away, compute the durations on their own, and then adjust the "displayed" date.
This way, you will be both mathematically correct (ie, respect symmetry and associativity) and behave intuitively for end users.
class MyTime {
...
private:
tm _origin;
tm _deviation;
};
However it is more work... so you have to decide on your scheme by yourself, depending on your application needs.
I agree about using boost::date_time, however, the solution here is quite easy.
today->tm_year++;
Although, if you happen to call localtime again, the value will be overwritten, so you should make a copy. Make today an instance instead of a pointer, and dereference the return value of localtime like this:
today = *localtime( &ltime );
You'll have to take into account certain anomalies, like incrementing a year from February 29th on a leap year.
Edit: I see you've decided to use boost::date_time after all. This makes things much simpler. Here's how you add a year:
t += boost::gregorian::years(1);
And here's how you add 14 days:
t += boost::gregorian::days(14);
Or
t += boost::gregorian::weeks(2);
Oh good grief, you C++ people :)
// compare June 15 2018 - 2017 to 2017 - 2016
struct tm y1_tm, y2_tm, y3_tm;
time_t y1716, y1817; // differences
y1_tm.tm_sec = 0; // 2016
y1_tm.tm_min = 0;
y1_tm.tm_hour = 0;
y1_tm.tm_mon = 6;
y1_tm.tm_mday = 15;
y1_tm.tm_year = 2016 - 1900;
y1_tm.tm_mday = 1;
y2_tm.tm_sec = 0; // 2017
y2_tm.tm_min = 0;
y2_tm.tm_hour = 0;
y2_tm.tm_mon = 6;
y2_tm.tm_mday = 15;
y2_tm.tm_year = 2017 - 1900;
y2_tm.tm_mday = 1;
y3_tm.tm_sec = 0; // 2018
y3_tm.tm_min = 0;
y3_tm.tm_hour = 0;
y3_tm.tm_mon = 6;
y3_tm.tm_mday = 15;
y3_tm.tm_year = 2018 - 1900;
y3_tm.tm_mday = 1;
y1716 = mktime(&y2_tm) - mktime(&y1_tm); // 2017 - 2016
y1817 = mktime(&y3_tm) - mktime(&y2_tm); // 2018 - 2017
Both subtractions yield 31536000 seconds. Add that to a time_t for 1 year.