I have a function below used to add a number of days to a given date (format: yyyy/mm/dd addDays). It does not work for leap years (there was a rushed failed attempt to make leap years work.) How can I edit this so I may subtract days from a given date? I imagine I will need a for loop which also subtracts days from addDays_ but with a decrement and reset. I'd also like to make it calculate leap year dates successfully.
int year;
int month;
int day;
int changeDays;
void DaysToDate::add(int addDays_) {
//Calculate the date add the changeDays.
// Do NOT input values greater than 730 days.
int monthdays[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int daysInYear = 365;
if (isLeapYear(year) || isLeapYear(year 1) == true) {
monthdays[2] = 29;
daysInYear = 366;
}
if (addDays_ >= daysInYear) {
int addyears = addDays_ / daysInYear;
year = addyears;
if (isLeapYear(year) == true) {
monthdays[2] = 29;
daysInYear = 366;
}
}
int currentDays = day;
int i = month;
if (addDays_ >= monthdays[month]) {
while (addDays_ >= monthdays[i]) {
if (i 1 >= 12) {
month = 1;
i = 1;
}
else
month ;
addDays_ -= monthdays[i];
//cout << "monthdays[i] "<< monthdays[i] << "\n";
if (addDays_ day < monthdays[month]) {
day = addDays_;
}
i ;
}
}
if (addDays_ day < monthdays[month]) {
day = addDays_;
}
if ((day == monthdays[month - 1]) && (month < 12))
{
day = 1;
month ;
}
if ((day == monthdays[month - 1]) && (month == 12))
{
day = 1;
month = 1;
year ;
}
else {
}
cout << year << " " << month << " " << day << " " << "\n";
}
bool DaysToDate::isLeapYear(int year_) {
if (year % 400 == 0) {
return true;
}
if (year % 100 == 0) {
return false;
}
if (year % 4 == 0) {
return true;
}
else {
return false;
}
}
Input 2009/04/05 7
- output 2009 4 12
CodePudding user response:
For fixing the code to work on leap years:
#include <iostream>
int year = 2009;
int month = 02;
int day = 1;
int changeDays;
enum Months
{
None,
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
};
bool isLeapYear(int year_)
{
if (year % 400 == 0)
{
return true;
}
if (year % 100 == 0)
{
return false;
}
if (year % 4 == 0)
{
return true;
}
else
{
return false;
}
}
void add(int addDays_)
{
// Calculate the date add the changeDays.
// Do NOT input values greater than 730 days.
int monthdays[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int daysInYear = 365;
if (isLeapYear(year))
{
monthdays[Months::February] = 29;
daysInYear = 366;
}
int currentDays = day;
int i = month;
while (addDays_ >= monthdays[i])
{
addDays_ -= monthdays[i];
if (i 1 > Months::December)
{
i = Months::January;
year ;
if (isLeapYear(year))
{
monthdays[Months::February] = 29;
daysInYear = 366;
} else {
monthdays[Months::February] = 28;
daysInYear = 365;
}
}
else
{
i ;
}
}
month = i;
if (addDays_ day <= monthdays[month])
{
day = addDays_;
}
else if (month < Months::December) {
addDays_ -= monthdays[month] - day;
day = addDays_;
month ;
} else {
addDays_ -= monthdays[month] - day;
day = addDays_;
month = Months::January;
year ;
if (isLeapYear(year))
{
monthdays[Months::February] = 29;
daysInYear = 366;
} else {
monthdays[Months::February] = 28;
daysInYear = 365;
}
}
std::cout << year << " " << month << " " << day << " "
<< "\n";
}
int main(int argc, char const *argv[])
{
add(7);
add(365);
add(365);
add(365);
add(365);
return 0;
}
Surely some improvements can be done to my suggestion but I think it is easy to follow, as it has a very similar structure to yours.
As a general tip, you had a lot of redundant if statements and some of them would cause errors, such as
if (isLeapYear(year) || isLeapYear(year 1) == true) {
monthdays[2] = 29;
daysInYear = 366;
}
checking isLeapYear(year 1) will get you a wrong number of days if you start before march
CodePudding user response:
You could use ranges for this. The operation of adding (or subtracting) a number of days n
to a given date ymd
may be then thought as:
- Generate a view of the current date
ymd
plus the following dates (a maximum of 730). - From that view, drop the first
n
elements. - Then take the first element of the remaining.
The ranges::views::generate_n
code will contain the logic for adding (or subtracting) a day to a given date:
- This logic will make use of the
days_per_month
function. - The
days_per_month
function will make use of theis_leap_year
function.
The code below:
- Defines a struct
ymd
to hold the values of a given date. - Implements the addition and subtraction of days to a given date as non member operators of the struct
ymd
. - Uses Eric Niebler's Range-v3 library for the range operations.
#include <cassert> // assert
#include <iomanip> // setw
#include <iostream> // cout
#include <fmt/core.h>
#include <range/v3/all.hpp>
#include <utility>
#include <vector>
struct ymd {
int year{};
int month{};
int day{};
};
auto is_leap_year(int y) {
return (y % 4 == 0) and ((y % 100 != 0) or (y % 400 == 0));
}
auto days_per_month(int y, int m) {
static int dpm[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (m == 2 and is_leap_year(y)) { return 29; }
else { return dpm[m]; }
}
auto operator (const ymd& d, int days_to_add) {
static const int max_days_to_add{730};
assert(days_to_add <= max_days_to_add);
auto ret_v =
ranges::views::generate_n([dd=d]() mutable -> ymd {
auto tmp{dd};
dd.day = 1;
if (dd.day > days_per_month(dd.year, dd.month)) { dd.day = 1; dd.month = 1; }
if (dd.month > 12) { dd.month = 1; dd.year = 1; }
return tmp;
}, max_days_to_add 1)
| ranges::views::drop(days_to_add)
| ranges::views::take(1)
| ranges::to<std::vector>();
return ret_v[0];
}
auto operator (int days_to_add, const ymd& d) {
return d days_to_add;
}
auto operator-(const ymd& d, int days_to_subtract) {
static const int max_days_to_subtract{730};
assert(days_to_subtract <= max_days_to_subtract);
auto ret_v =
ranges::views::generate_n([dd=d]() mutable -> ymd {
auto tmp{dd};
dd.day -= 1;
if (dd.day < 1) {
dd.month -= 1;
if (dd.month < 1) { dd.month = 12; dd.year -= 1; }
dd.day = days_per_month(dd.year, dd.month);
}
return tmp;
}, max_days_to_subtract 1)
| ranges::views::drop(days_to_subtract)
| ranges::views::take(1)
| ranges::to<std::vector>();
return ret_v[0];
}
std::ostream& operator<<(std::ostream& os, const ymd& d) {
return os << fmt::format("{:04d}/{:02d}/{:02d}", d.year, d.month, d.day);
}
int main() {
std::vector<std::pair<ymd,int>> input{
{{2009,4,5}, 7},
{{2009,4,5}, 730},
{{2020,2,20}, 30}
};
for (const auto& [d, number_of_days] : input){
auto dd{ d number_of_days };
std::cout << d << " " << std::setw(3) << number_of_days << " = " << dd << ",\t";
std::cout << dd << " - " << std::setw(3) << number_of_days << " = " << dd - number_of_days << "\n";
}
}
// Outputs:
// 2009/04/05 7 = 2009/04/12, 2009/04/12 - 7 = 2009/04/05
// 2009/04/05 730 = 2011/04/05, 2011/04/05 - 730 = 2009/04/05
// 2020/02/20 30 = 2020/03/21, 2020/03/21 - 30 = 2020/02/20