从日期添加或减去天数的算法?

时间:2022-08-25 18:51:00

I'm trying to write a Date class in an attempt to learn C++.

我正在尝试编写一个Date类来尝试学习C ++。

I'm trying to find an algorithm to add or subtract days to a date, where Day starts from 1 and Month starts from 1. It's proving to be very complex, and google doesn't turn up much,

我正在尝试找到一个算法来添加或减去日期到日期,其中Day从1开始,Month从1开始。它被证明是非常复杂的,并且谷歌没有出现太多,

Does anyone know of an algorithm which does this?

有谁知道这样做的算法?

8 个解决方案

#1


19  

The easiest way is to actually write two functions, one which converts the day to a number of days from a given start date, then another which converts back to a date. Once the date is expressed as a number of days, it's trivial to add or subtract to it.

最简单的方法是实际编写两个函数,一个将日期转换为给定开始日期的天数,然后另一个函数转换回日期。一旦日期表示为天数,添加或减去日期是微不足道的。

You can find the algorithms here: http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html

你可以在这里找到算法:http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html

#2


8  

You don't really need an algorithm as such (at least not something worthy of the name), the standard library can do most of the heavy lifting; calender calculations are notoriously tricky. So long as you don't need dates earlier than 1900, then:

你真的不需要这样的算法(至少不是名副其实的东西),标准库可以完成大部分繁重的工作;日历计算是非常棘手的。只要您不需要早于1900年的日期,那么:

#include <ctime>

// Adjust date by a number of days +/-
void DatePlusDays( struct tm* date, int days )
{
    const time_t ONE_DAY = 24 * 60 * 60 ;

    // Seconds since start of epoch
    time_t date_seconds = mktime( date ) + (days * ONE_DAY) ;

    // Update caller's date
    // Use localtime because mktime converts to UTC so may change date
    *date = *localtime( &date_seconds ) ; ;
}

Example usage:

#include <iostream>

int main()
{
    struct tm date = { 0, 0, 12 } ;  // nominal time midday (arbitrary).
    int year = 2010 ;
    int month = 2 ;  // February
    int day = 26 ;   // 26th

    // Set up the date structure
    date.tm_year = year - 1900 ;
    date.tm_mon = month - 1 ;  // note: zero indexed
    date.tm_mday = day ;       // note: not zero indexed

    // Date, less 100 days
    DatePlusDays( &date, -100 ) ; 

    // Show time/date using default formatting
    std::cout << asctime( &date ) << std::endl ;
}

#3


3  

I'm assuming this is for some kind of an exercise, otherwise you would use a time class that's already provided to you.

我假设这是一种练习,否则你会使用已经提供给你的时间表。

You could store your time as the number of milliseconds since a certain date. And then you can add the appropriate value and convert from that to the date upon calling the accessors of your class.

您可以将时间存储为自特定日期以来的毫秒数。然后,您可以添加适当的值,并在调用类的访问器时将其转换为日期。

#4


2  

Here's a sketch of a very simple approach. For simplicity of ideas I will assume that d, the number of days to add, is positive. It is easy to extend the below to cases where d is negative.

这是一个非常简单的方法草图。为了简单起见,我将假设d,即添加的天数是正数。将下面的内容扩展到d为负的情况很容易。

Either d is less than 365 or d is greater than or equal to 365.

d小于365或d大于或等于365。

If d is less than 365:

如果d小于365:

m = 1;
while(d > numberOfDaysInMonth(m, y)) {
    d -= numberOfDaysInMonth(m, y);
    m++;
}
return date with year = y, month = m, day = d;

If d is greater than 365:

如果d大于365:

while(d >= 365) {
    d -= 365;
    if(isLeapYear(y)) {
        d -= 1;
    }
    y++;
}
// now use the case where d is less than 365

Alternatively, you could express the date in, say, Julian form and then merely add to the Julian form and conver to ymd format.

或者,您可以用朱利安形式表示日期,然后仅添加到朱利安形式并转换为ymd格式。

#5


1  

One approach is to map the date to the Julian number of the date, do your integer operations and then transform back.

一种方法是将日期映射到日期的Julian数,执行整数运算然后转换回来。

You will find plenty of resources for the julian functions.

你会发现julian功能的大量资源。

#6


1  

Try this function. It correctly calculates additions or subtractions. dateTime argument must be in UTC format.

试试这个功能。它正确地计算加法或减法。 dateTime参数必须是UTC格式。

tm* dateTimeAdd(const tm* const dateTime, const int& days, const int& hours, const int& mins, const int& secs) {
    tm* newTime = new tm;
    memcpy(newTime, dateTime, sizeof(tm));

    newTime->tm_mday += days;
    newTime->tm_hour += hours;
    newTime->tm_min += mins;
    newTime->tm_sec += secs;        

    time_t nt_seconds = mktime(newTime) - timezone;
    delete newTime;

    return gmtime(&nt_seconds);
}

And there are example of using:

并且有使用的例子:

time_t t = time(NULL);
tm* utc = gmtime(&t);
tm* newUtc = dateTimeAdd(utc, -5, 0, 0, 0); //subtract 5 days

#7


0  

I would suggest writing first a routine which converts year-month-day into a number of days since fixed date, say, since 1.01.01. And a symmetric routine which would convert it back.

我建议先写一个例程,将年 - 月 - 日转换为固定日期后的天数,比如说自1.01.01起。还有一个对称的例程可以将其转换回来。

Don't forget to process leap years correctly!

不要忘记正确处理闰年!

Having those two, your task would be trivial.

拥有这两个,你的任务将是微不足道的。

#8


0  

I know this is a very old question but it's an interesting and some common one when it comes to working with dates and times. So I thought of sharing some code which calculates the new date without using any inbuilt time functionality in C++.

我知道这是一个非常古老的问题,但在处理日期和时间方面,这是一个有趣且常见的问题。所以我想分享一些计算新日期的代码,而不使用C ++中的任何内置时间功能。

#include <iostream>
#include <string>

using namespace std;

class Date {
public:
    Date(size_t year, size_t month, size_t day):m_year(year), m_month(month), m_day(day) {}
    ~Date() {}

    // Add specified number of days to date
    Date operator + (size_t days) const;

    // Subtract specified number of days from date
    Date operator - (size_t days) const;

    size_t Year()  { return m_year; }
    size_t Month() { return m_month; }
    size_t Day()   { return m_day; }

    string DateStr();
private:
    // Leap year check 
    inline bool LeapYear(int year) const
        { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); }

    // Holds all max days in a general year
    static const int MaxDayInMonth[13];

    // Private members
    size_t m_year;
    size_t m_month;
    size_t m_day;   
};

// Define MaxDayInMonth
const int Date::MaxDayInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

//===========================================================================================
/// Add specified number of days to date
Date Date::operator + (size_t days) const {
    // Maximum days in the month
    int nMaxDays(MaxDayInMonth[m_month] + (m_month == 2 && LeapYear(m_year) ? 1 : 0));

    // Initialize the Year, Month, Days
    int nYear(m_year);
    int nMonth(m_month);
    int nDays(m_day + days);

    // Iterate till it becomes a valid day of a month
    while (nDays > nMaxDays) {
        // Subtract the max number of days of current month
        nDays -= nMaxDays;

        // Advance to next month
        ++nMonth;

        // Falls on to next year?
        if (nMonth > 12) {
            nMonth = 1; // January
            ++nYear;    // Next year
        }

        // Update the max days of the new month
        nMaxDays = MaxDayInMonth[nMonth] + (nMonth == 2 && LeapYear(nYear) ? 1 : 0);
    }

    // Construct date
    return Date(nYear, nMonth, nDays);
}

//===========================================================================================
/// Subtract specified number of days from date
Date Date::operator - (size_t days) const {
    // Falls within the same month?
    if (0 < (m_day - days)) {
        return Date(m_year, m_month, m_day - days);
    }

    // Start from this year
    int nYear(m_year);

    // Start from specified days and go back to first day of this month
    int nDays(days);
    nDays -= m_day;

    // Start from previous month and check if it falls on to previous year
    int nMonth(m_month - 1);
    if (nMonth < 1) {
        nMonth = 12; // December
        --nYear;     // Previous year
    }

    // Maximum days in the current month
    int nDaysInMonth = MaxDayInMonth[nMonth] + (nMonth == 2 && LeapYear(nYear) ? 1 : 0);

    // Iterate till it becomes a valid day of a month
    while (nDays >= 0) {
        // Subtract the max number of days of current month
        nDays -= nDaysInMonth;

        // Falls on to previous month?
        if (nDays > 0) {
            // Go to previous month
            --nMonth;

            // Falls on to previous year?
            if (nMonth < 1) {
                nMonth = 12; // December
                --nYear;     // Previous year
            }
        }

        // Update the max days of the new month
        nDaysInMonth = MaxDayInMonth[nMonth] + (nMonth == 2 && LeapYear(nYear) ? 1 : 0);
    }

    // Construct date
    return Date(nYear, nMonth, (0 < nDays ? nDays : -nDays));
}

//===========================================================================================
/// Get the date string in yyyy/mm/dd format
string Date::DateStr() {
    return to_string(m_year) 
        + string("/")
        + string(m_month < 10 ? string("0") + to_string(m_month) : to_string(m_month))
        + string("/")
        + string(m_day < 10 ? string("0") + to_string(m_day) : to_string(m_day)); 
}


int main() {
    // Add n days to a date
    cout << Date(2017, 6, 25).DateStr() << " + 10 days = "
         << (Date(2017, 6, 25) /* Given Date */ + 10 /* Days to add */).DateStr() << endl;

    // Subtract n days from a date
    cout << Date(2017, 6, 25).DateStr() << " - 10 days = "
         << (Date(2017, 6, 25) /* Given Date */ - 10 /* Days to subract */).DateStr() << endl;

    return 0;
}

Output
2017/06/25 + 10 days = 2017/07/05
2017/06/25 - 10 days = 2017/06/15

#1


19  

The easiest way is to actually write two functions, one which converts the day to a number of days from a given start date, then another which converts back to a date. Once the date is expressed as a number of days, it's trivial to add or subtract to it.

最简单的方法是实际编写两个函数,一个将日期转换为给定开始日期的天数,然后另一个函数转换回日期。一旦日期表示为天数,添加或减去日期是微不足道的。

You can find the algorithms here: http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html

你可以在这里找到算法:http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html

#2


8  

You don't really need an algorithm as such (at least not something worthy of the name), the standard library can do most of the heavy lifting; calender calculations are notoriously tricky. So long as you don't need dates earlier than 1900, then:

你真的不需要这样的算法(至少不是名副其实的东西),标准库可以完成大部分繁重的工作;日历计算是非常棘手的。只要您不需要早于1900年的日期,那么:

#include <ctime>

// Adjust date by a number of days +/-
void DatePlusDays( struct tm* date, int days )
{
    const time_t ONE_DAY = 24 * 60 * 60 ;

    // Seconds since start of epoch
    time_t date_seconds = mktime( date ) + (days * ONE_DAY) ;

    // Update caller's date
    // Use localtime because mktime converts to UTC so may change date
    *date = *localtime( &date_seconds ) ; ;
}

Example usage:

#include <iostream>

int main()
{
    struct tm date = { 0, 0, 12 } ;  // nominal time midday (arbitrary).
    int year = 2010 ;
    int month = 2 ;  // February
    int day = 26 ;   // 26th

    // Set up the date structure
    date.tm_year = year - 1900 ;
    date.tm_mon = month - 1 ;  // note: zero indexed
    date.tm_mday = day ;       // note: not zero indexed

    // Date, less 100 days
    DatePlusDays( &date, -100 ) ; 

    // Show time/date using default formatting
    std::cout << asctime( &date ) << std::endl ;
}

#3


3  

I'm assuming this is for some kind of an exercise, otherwise you would use a time class that's already provided to you.

我假设这是一种练习,否则你会使用已经提供给你的时间表。

You could store your time as the number of milliseconds since a certain date. And then you can add the appropriate value and convert from that to the date upon calling the accessors of your class.

您可以将时间存储为自特定日期以来的毫秒数。然后,您可以添加适当的值,并在调用类的访问器时将其转换为日期。

#4


2  

Here's a sketch of a very simple approach. For simplicity of ideas I will assume that d, the number of days to add, is positive. It is easy to extend the below to cases where d is negative.

这是一个非常简单的方法草图。为了简单起见,我将假设d,即添加的天数是正数。将下面的内容扩展到d为负的情况很容易。

Either d is less than 365 or d is greater than or equal to 365.

d小于365或d大于或等于365。

If d is less than 365:

如果d小于365:

m = 1;
while(d > numberOfDaysInMonth(m, y)) {
    d -= numberOfDaysInMonth(m, y);
    m++;
}
return date with year = y, month = m, day = d;

If d is greater than 365:

如果d大于365:

while(d >= 365) {
    d -= 365;
    if(isLeapYear(y)) {
        d -= 1;
    }
    y++;
}
// now use the case where d is less than 365

Alternatively, you could express the date in, say, Julian form and then merely add to the Julian form and conver to ymd format.

或者,您可以用朱利安形式表示日期,然后仅添加到朱利安形式并转换为ymd格式。

#5


1  

One approach is to map the date to the Julian number of the date, do your integer operations and then transform back.

一种方法是将日期映射到日期的Julian数,执行整数运算然后转换回来。

You will find plenty of resources for the julian functions.

你会发现julian功能的大量资源。

#6


1  

Try this function. It correctly calculates additions or subtractions. dateTime argument must be in UTC format.

试试这个功能。它正确地计算加法或减法。 dateTime参数必须是UTC格式。

tm* dateTimeAdd(const tm* const dateTime, const int& days, const int& hours, const int& mins, const int& secs) {
    tm* newTime = new tm;
    memcpy(newTime, dateTime, sizeof(tm));

    newTime->tm_mday += days;
    newTime->tm_hour += hours;
    newTime->tm_min += mins;
    newTime->tm_sec += secs;        

    time_t nt_seconds = mktime(newTime) - timezone;
    delete newTime;

    return gmtime(&nt_seconds);
}

And there are example of using:

并且有使用的例子:

time_t t = time(NULL);
tm* utc = gmtime(&t);
tm* newUtc = dateTimeAdd(utc, -5, 0, 0, 0); //subtract 5 days

#7


0  

I would suggest writing first a routine which converts year-month-day into a number of days since fixed date, say, since 1.01.01. And a symmetric routine which would convert it back.

我建议先写一个例程,将年 - 月 - 日转换为固定日期后的天数,比如说自1.01.01起。还有一个对称的例程可以将其转换回来。

Don't forget to process leap years correctly!

不要忘记正确处理闰年!

Having those two, your task would be trivial.

拥有这两个,你的任务将是微不足道的。

#8


0  

I know this is a very old question but it's an interesting and some common one when it comes to working with dates and times. So I thought of sharing some code which calculates the new date without using any inbuilt time functionality in C++.

我知道这是一个非常古老的问题,但在处理日期和时间方面,这是一个有趣且常见的问题。所以我想分享一些计算新日期的代码,而不使用C ++中的任何内置时间功能。

#include <iostream>
#include <string>

using namespace std;

class Date {
public:
    Date(size_t year, size_t month, size_t day):m_year(year), m_month(month), m_day(day) {}
    ~Date() {}

    // Add specified number of days to date
    Date operator + (size_t days) const;

    // Subtract specified number of days from date
    Date operator - (size_t days) const;

    size_t Year()  { return m_year; }
    size_t Month() { return m_month; }
    size_t Day()   { return m_day; }

    string DateStr();
private:
    // Leap year check 
    inline bool LeapYear(int year) const
        { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); }

    // Holds all max days in a general year
    static const int MaxDayInMonth[13];

    // Private members
    size_t m_year;
    size_t m_month;
    size_t m_day;   
};

// Define MaxDayInMonth
const int Date::MaxDayInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

//===========================================================================================
/// Add specified number of days to date
Date Date::operator + (size_t days) const {
    // Maximum days in the month
    int nMaxDays(MaxDayInMonth[m_month] + (m_month == 2 && LeapYear(m_year) ? 1 : 0));

    // Initialize the Year, Month, Days
    int nYear(m_year);
    int nMonth(m_month);
    int nDays(m_day + days);

    // Iterate till it becomes a valid day of a month
    while (nDays > nMaxDays) {
        // Subtract the max number of days of current month
        nDays -= nMaxDays;

        // Advance to next month
        ++nMonth;

        // Falls on to next year?
        if (nMonth > 12) {
            nMonth = 1; // January
            ++nYear;    // Next year
        }

        // Update the max days of the new month
        nMaxDays = MaxDayInMonth[nMonth] + (nMonth == 2 && LeapYear(nYear) ? 1 : 0);
    }

    // Construct date
    return Date(nYear, nMonth, nDays);
}

//===========================================================================================
/// Subtract specified number of days from date
Date Date::operator - (size_t days) const {
    // Falls within the same month?
    if (0 < (m_day - days)) {
        return Date(m_year, m_month, m_day - days);
    }

    // Start from this year
    int nYear(m_year);

    // Start from specified days and go back to first day of this month
    int nDays(days);
    nDays -= m_day;

    // Start from previous month and check if it falls on to previous year
    int nMonth(m_month - 1);
    if (nMonth < 1) {
        nMonth = 12; // December
        --nYear;     // Previous year
    }

    // Maximum days in the current month
    int nDaysInMonth = MaxDayInMonth[nMonth] + (nMonth == 2 && LeapYear(nYear) ? 1 : 0);

    // Iterate till it becomes a valid day of a month
    while (nDays >= 0) {
        // Subtract the max number of days of current month
        nDays -= nDaysInMonth;

        // Falls on to previous month?
        if (nDays > 0) {
            // Go to previous month
            --nMonth;

            // Falls on to previous year?
            if (nMonth < 1) {
                nMonth = 12; // December
                --nYear;     // Previous year
            }
        }

        // Update the max days of the new month
        nDaysInMonth = MaxDayInMonth[nMonth] + (nMonth == 2 && LeapYear(nYear) ? 1 : 0);
    }

    // Construct date
    return Date(nYear, nMonth, (0 < nDays ? nDays : -nDays));
}

//===========================================================================================
/// Get the date string in yyyy/mm/dd format
string Date::DateStr() {
    return to_string(m_year) 
        + string("/")
        + string(m_month < 10 ? string("0") + to_string(m_month) : to_string(m_month))
        + string("/")
        + string(m_day < 10 ? string("0") + to_string(m_day) : to_string(m_day)); 
}


int main() {
    // Add n days to a date
    cout << Date(2017, 6, 25).DateStr() << " + 10 days = "
         << (Date(2017, 6, 25) /* Given Date */ + 10 /* Days to add */).DateStr() << endl;

    // Subtract n days from a date
    cout << Date(2017, 6, 25).DateStr() << " - 10 days = "
         << (Date(2017, 6, 25) /* Given Date */ - 10 /* Days to subract */).DateStr() << endl;

    return 0;
}

Output
2017/06/25 + 10 days = 2017/07/05
2017/06/25 - 10 days = 2017/06/15