2010-02-26 9 views
15

Я пытаюсь написать класс Date, пытаясь изучить C++.Алгоритм добавления или вычитания дней с даты?

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

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

+0

Я удивлен, что этот вопрос существует без сопроводительного ответа «Использовать Boost» со ссылкой на документацию. – jww

ответ

16

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

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

+0

Спасибо, это именно то, что я искал, по какой-то причине я не мог найти алгоритм при поиске в сети! – bcoughlan

+0

, к сожалению, эти функции не очень точны ... или, по крайней мере, когда я сравнивал результаты с вольфрамом альфа, я ушел на день или около того. – aimango

+0

Здесь: http://home.roadrunner.com/~hinnant/date_algorithms.html - это алгоритмы, которые точны. Их обоснованность была проверена на точность в пролептическом григорианском календаре в диапазоне +/- 5,8 млн. Лет с использованием 32-разрядной арифметики. Они считают дни до или после 1970-01-01. –

3

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

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

+0

Почему миллисекунды? Кажется, ему нужны даты, а не времена и, конечно, не миллисекундная точность. Это даже намекает на учет прыжковых секунд. – Steve314

1

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

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

0

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

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

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

2

Вот эскиз очень простого подхода. Для простоты идей я предполагаю, что 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 

В качестве альтернативы вы можете указать дату, например, Julian form, а затем просто добавьте в форму Julian и конвертируйте в формат ymd.

+0

работает для меня, спасибо! – aimango

7

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

Спасибо, что опубликовали это. Очень полезно. – ForeverLearning

+0

Будет ли скачок секунд испортить этот расчет? – vargonian

+0

@ vargonian: Хороший вопрос; Временная эпоха UNIX - с 1 января 1970 года и не учитывает прыжковые секунды. Однако установка номинального времени дня в полдень позволит избежать любой потенциальной проблемы в течение нескольких десятков тысяч лет. – Clifford

1

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

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