Алгоритм сложения или вычитания дней из даты? - PullRequest
19 голосов
/ 26 февраля 2010

Я пытаюсь написать класс Date в попытке выучить C ++.

Я пытаюсь найти алгоритм для добавления или вычитания дней к дате, где День начинается с 1, а Месяц начинается с 1. Это оказывается очень сложным, и Google не показывает много,

Кто-нибудь знает алгоритм, который это делает?

Ответы [ 9 ]

19 голосов
/ 26 февраля 2010

Самый простой способ - написать две функции, одна из которых преобразует день в число дней от заданной даты начала, а другая - обратно в дату. Как только дата выражается в количестве дней, добавлять или вычитать ее тривиально.

Вы можете найти алгоритмы здесь: http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html

12 голосов
/ 27 февраля 2010

Вам действительно не нужен алгоритм как таковой (по крайней мере, не то, что заслуживает названия), стандартная библиотека может выполнять большую часть тяжелой работы; расчеты календаря, как известно, хитры. Если вам не нужны даты ранее 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 ) ; ;
}

Пример использования:

#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 голосов
/ 26 февраля 2010

Я предполагаю, что это для какого-то упражнения, в противном случае вы использовали бы уже предоставленный вам класс времени.

Вы можете хранить свое время как количество миллисекунд с определенной даты. А затем вы можете добавить соответствующее значение и преобразовать его в дату после вызова методов доступа вашего класса.

2 голосов
/ 26 февраля 2010

Вот эскиз очень простого подхода. Для простоты идей я предполагаю, что d, количество дней, которое нужно добавить, является положительным. Это легко распространить на случаи, когда d отрицательно.

Либо d меньше 365, либо d больше или равно 365.

Если d меньше 365:

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

Если d больше 365:

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

Кроме того, вы можете выразить дату, скажем, в юлианской форме , а затем просто добавить в юлианскую форму и преобразовать в формат ymd.

1 голос
/ 06 октября 2013

Попробуйте эту функцию. Он правильно рассчитывает сложения или вычитания. Аргумент 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);
}

А есть пример использования:

time_t t = time(NULL);
tm* utc = gmtime(&t);
tm* newUtc = dateTimeAdd(utc, -5, 0, 0, 0); //subtract 5 days
1 голос
/ 26 февраля 2010

Один из подходов - сопоставить дату с юлианским числом даты, выполнить целочисленные операции и затем преобразовать обратно.

Вы найдете множество ресурсов для юлианских функций.

0 голосов
/ 26 апреля 2019

Я знаю, что это старый вопрос, заданный почти десять лет назад. Но за несколько дней до того, как я наткнулся на то же самое для назначения, и вот ответ, как в здесь

// C++ program to find date after adding 
// given number of days. 
#include<bits/stdc++.h> 
using namespace std; 

// Return if year is leap year or not. 
bool isLeap(int y) 
{ 
    if (y%100 != 0 && y%4 == 0 || y %400 == 0) 
        return true; 

    return false; 
} 

// Given a date, returns number of days elapsed 
// from the beginning of the current year (1st 
// jan). 
int offsetDays(int d, int m, int y) 
{ 
    int offset = d; 

    switch (m - 1) 
    { 
    case 11: 
        offset += 30; 
    case 10: 
        offset += 31; 
    case 9: 
        offset += 30; 
    case 8: 
        offset += 31; 
    case 7: 
        offset += 31; 
    case 6: 
        offset += 30; 
    case 5: 
        offset += 31; 
    case 4: 
        offset += 30; 
    case 3: 
        offset += 31; 
    case 2: 
        offset += 28; 
    case 1: 
        offset += 31; 
    } 

    if (isLeap(y) && m > 2) 
        offset += 1; 

    return offset; 
} 

// Given a year and days elapsed in it, finds 
// date by storing results in d and m. 
void revoffsetDays(int offset, int y, int *d, int *m) 
{ 
    int month[13] = { 0, 31, 28, 31, 30, 31, 30, 
                    31, 31, 30, 31, 30, 31 }; 

    if (isLeap(y)) 
        month[2] = 29; 

    int i; 
    for (i = 1; i <= 12; i++) 
    { 
        if (offset <= month[i]) 
            break; 
        offset = offset - month[i]; 
    } 

    *d = offset; 
    *m = i; 
} 

// Add x days to the given date. 
void addDays(int d1, int m1, int y1, int x) 
{ 
    int offset1 = offsetDays(d1, m1, y1); 
    int remDays = isLeap(y1)?(366-offset1):(365-offset1); 

    // y2 is going to store result year and 
    // offset2 is going to store offset days 
    // in result year. 
    int y2, offset2; 
    if (x <= remDays) 
    { 
        y2 = y1; 
        offset2 = offset1 + x; 
    } 

    else
    { 
        // x may store thousands of days. 
        // We find correct year and offset 
        // in the year. 
        x -= remDays; 
        y2 = y1 + 1; 
        int y2days = isLeap(y2)?366:365; 
        while (x >= y2days) 
        { 
            x -= y2days; 
            y2++; 
            y2days = isLeap(y2)?366:365; 
        } 
        offset2 = x; 
    } 

    // Find values of day and month from 
    // offset of result year. 
    int m2, d2; 
    revoffsetDays(offset2, y2, &d2, &m2); 

    cout << "d2 = " << d2 << ", m2 = " << m2 
        << ", y2 = " << y2; 
} 

// Driven Program 
int main() 
{ 
    int d = 14, m = 3, y = 2015; 
    int x = 366; 

    addDays(d, m, y, x); 

    return 0; 
} 
0 голосов
/ 22 июня 2017

Я знаю, что это очень старый вопрос, но он интересный и довольно распространенный, когда речь идет о работе с датами и временем. Поэтому я подумал о совместном использовании некоторого кода, который вычисляет новую дату без использования встроенных функций времени в 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
0 голосов
/ 26 февраля 2010

Я бы предложил сначала написать процедуру, которая преобразует год-месяц-день в число дней с фиксированной даты, скажем, с 1.01.01. И симметричная процедура, которая преобразует его обратно.

Не забудьте правильно обрабатывать високосные годы!

Имея эти два, ваша задача будет тривиальной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...