简体   繁体   English

从 C++ 中的给定日期(从头开始)减去天数

[英]Subtract a number of days from given date (from scratch) in C++

I have a function below used to add a number of days to a given date (format: yyyy/mm/dd addDays).我在下面有一个 function 用于向给定日期添加天数(格式: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.我想我将需要一个 for 循环,它也从 addDays_ 中减去天数,但需要递减和重置。 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输入2009/04/05 7 - output 2009 4 12

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 语句,其中一些会导致错误,例如

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如果您在三月之前开始,检查 isLeapYear(year+1) 会让您得到错误的天数

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:给定日期ymd添加(或减去)天数n的操作可以被认为是:

  • Generate a view of the current date ymd plus the following dates (a maximum of 730).生成当前日期ymd加上后面日期(最大 730)的视图。
  • From that view, drop the first n elements.从该视图中,删除前n元素。
  • 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: ranges::views::generate_n代码将包含对给定日期添加(或减去)一天的逻辑:

  • This logic will make use of the days_per_month function.此逻辑将使用days_per_month function。
  • The days_per_month function will make use of the is_leap_year function. days_per_month function 将使用is_leap_year function。

The code below:下面的代码:

  • Defines a struct ymd to hold the values of a given date.定义一个结构ymd来保存给定日期的值。
  • Implements the addition and subtraction of days to a given date as non member operators of the struct ymd .作为 struct ymd的非成员运算符,实现给定日期的天数加法和减法。
  • Uses Eric Niebler's Range-v3 library for the range operations.使用 Eric Niebler 的Range-v3库进行范围操作。

[Demo] [演示]

#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

Another, probably simpler, solution would be to loop until you consume the days you want to add, incrementing the current day whether until the beginning of the next month or until you run out of days to add (or decrementing until the end of the previous month if you are subtracting).另一个可能更简单的解决方案是循环直到你消耗了你想要添加的天数,增加当前日期直到下个月的月初或者直到你用完天数来添加(或者递减直到前一个结束月,如果你正在减去)。 Again, I would make use of daysPerMonth and isLeapYear helper functions:同样,我会使用daysPerMonthisLeapYear辅助函数:

[Demo] [演示]

    void add(int addDays) {
        static int maxAddDays = 730;
        assert(addDays <= maxAddDays);

        while (addDays) {
            auto current_month_days(daysPerMonth(year_, month_));
            auto tmpAddDays = std::min(addDays, current_month_days - day_ + 1);
            day_ += tmpAddDays;
            addDays -= tmpAddDays;
            if (day_ == current_month_days + 1) { day_ = 1; month_++; }
            if (month_ == 13) { month_ = 1; year_++; }
        }
    }

    void subtract(int subtractDays) {
        static int maxSubtractDays = 730;
        assert(subtractDays <= maxSubtractDays);

        while (subtractDays) {
            auto tmpSubtractDays = std::min(subtractDays, day_);
            day_ -= tmpSubtractDays;
            subtractDays -= tmpSubtractDays;
            if (day_ == 0) {
                month_--;
                if (month_ == 0) { month_ = 12; year_--; }
                day_ = daysPerMonth(year_, month_);
            }
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM