Определение разницы между датами - PullRequest
8 голосов
/ 03 апреля 2012

Я пытаюсь найти способ для моей программы взять дату (например, 2 февраля 2003 г.) и показать разницу между ними и другой датой (например, 2 апреля 2012 г.), исключая високосные годы. До сих пор я мог выяснить это только в том случае, если даты в одном месяце, просто вычитая «день». В этой программе я использую 2 набора целых чисел «месяц», «день» и «год». Я в значительной степени в растерянности, откуда идти отсюда. Это совершенно необязательная часть моего задания, но я хотел бы получить представление о том, как заставить его работать. Мне это кажется хлопотным, но, может быть, есть простая математическая формула, о которой я не думаю?

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

Ответы [ 8 ]

14 голосов
/ 03 апреля 2012

Используя только стандартную библиотеку, вы можете преобразовать умеренно безумную структуру даты в число секунд, начиная с произвольной нулевой точки;затем вычтите и конвертируйте в дни:

#include <ctime>

// Make a tm structure representing this date
std::tm make_tm(int year, int month, int day)
{
    std::tm tm = {0};
    tm.tm_year = year - 1900; // years count from 1900
    tm.tm_mon = month - 1;    // months count from January=0
    tm.tm_mday = day;         // days count from 1
    return tm;
}

// Structures representing the two dates
std::tm tm1 = make_tm(2012,4,2);    // April 2nd, 2012
std::tm tm2 = make_tm(2003,2,2);    // February 2nd, 2003

// Arithmetic time values.
// On a posix system, these are seconds since 1970-01-01 00:00:00 UTC
std::time_t time1 = std::mktime(&tm1);
std::time_t time2 = std::mktime(&tm2);

// Divide by the number of seconds in a day
const int seconds_per_day = 60*60*24;
std::time_t difference = (time1 - time2) / seconds_per_day;    

// To be fully portable, we shouldn't assume that these are Unix time;
// instead, we should use "difftime" to give the difference in seconds:
double portable_difference = std::difftime(time1, time2) / seconds_per_day;

Использование Boost.Date_Time немного менее странно:

#include "boost/date_time/gregorian/gregorian_types.hpp"

using namespace boost::gregorian;
date date1(2012, Apr, 2);
date date2(2003, Feb, 2);
long difference = (date1 - date2).days();

Мне кажется, это хлопотно, но, возможно,простая математическая формула, о которой я не думаю?

Это действительно хлопотно, но есть формула , если вы хотите сделать расчет самостоятельно.

7 голосов
/ 03 апреля 2012

Поскольку вы ищете математическую формулу, она поможет вам найти решение вашей проблемы.Пусть Y будет годом, M будет месяцем, а D будет днем.Выполните этот расчет для обеих дат.

Итого = Y * 365 + M * 30 + D, затем найдите разницу между 2 итогами соответствующих дат.

При умножении 30 на значение M, вы должны указать количество дней в этом месяце.Вы можете сделать это с помощью #define value или if loop.Точно так же вы можете сделать это и для високосного года, умножив 366 на Y.

Надеюсь, это поможет вам ....

6 голосов
/ 12 марта 2015

Новый ответ на старый вопрос:

chrono -совместимые алгоритмы даты низкого уровня

имеет формулы для преобразования тройки {год, месяц, день} в последовательное число дней и обратно. Вы можете использовать его для вычисления количества дней между двумя датами, например:

std::cout << days_from_civil(2012, 4, 2) - days_from_civil(2003, 2, 2) << '\n';

который выводит:

3347

Эта статья является руководством, а не библиотекой. Он использует C ++ 14 для демонстрации формул. Каждая формула содержит подробное описание и вывод, которые вам нужно прочитать, только если вы хотите знать, как работает формула.

Формулы очень эффективны и действуют в чрезвычайно широком диапазоне. Например, используя 32-битную арифметику, +/- 5 миллионов лет (более чем достаточно).

Серийный счетчик дней - это количество дней с (или до отрицательных значений) Нового 1970 года, что делает формулы совместимыми с Unix Time и всеми известными реализациями std::chrono::system_clock.

Алгоритм days_from_civil не нов, и он должен выглядеть очень похоже на другие алгоритмы для того же. Но пойти другим путем - от количества дней до тройки {год, месяц, день} - сложнее. Это формула, документированная civil_from_days, и я не видел других составов, столь же компактных, как этот.

В статье приведены примеры использования типичных вычислений , std::chrono совместимости и обширных модульных тестов , демонстрирующих правильность в течение +/- 1 миллиона лет ( используя пролептический григорианский календарь ).

Все формулы и программное обеспечение находятся в свободном доступе.

5 голосов
/ 26 ноября 2015

Вот полный код для вычисления разницы дат в г / м / д.

Предполагая, что до и с равны дата типы, а месяцы и дни начинаются с 1 (аналогично Qt):

static int increment[12] = { 1, -2, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 };

int daysInc = 0;
if (to.day() - from.day() < 0)
{
    int month = to.month() - 2; // -1 from zero, -1 previous month.
    if (month < 0)
        month = 11; // Previous month is December.
    daysInc = increment[month];
    if ( (month == 1) && (to.year()%4 == 0) )
        daysInc++; // Increment days for leap year.
}

int total1 = from.year()*360 + from.month()*30 + from.day();
int total2 = to.year()*360 + to.month()*30 + to.day();
int diff = total2 - total1;
int years = diff/360;
int months = (diff - years*360)/30;
int days = diff - years*360 - months*30 + daysInc;

// Extra calculation when we can pass one month instead of 30 days.
if (from.day() == 1 && to.day() == 31) {
    months--;
    days = 30;
}

Я попробовал этот алгоритм, и он работает нормально.Дайте мне знать, если у вас возникли проблемы с его использованием / пониманием.

2 голосов
/ 11 марта 2015

Есть и другой путь ...

  • Учитывая две даты, принять год более ранней даты за ссылочный год .
  • Затем рассчитайте нет. дней между каждой из указанных дат и 1/1 / <этого года>
  • Сохраните отдельную функцию, которая сообщает количество дней, прошедших до определенного месяца.
  • Абсолютной разницы между этими двумя нет. дней даст разницу между двумя указанными датами.
  • Также не забудьте рассмотреть високосные годы !

код:

#‎include‬<stdio.h>
#include<math.h>
typedef struct
{
    int d, m, y;
} Date;
int isLeap (int y)
{
    return (y % 4 == 0) && ( y % 100 != 0) || (y % 400 == 0);
}
int diff (Date d1, Date d2)                         //logic here!
{
    int dd1 = 0, dd2 = 0, y, yref;                  //dd1 and dd2 store the <i>no. of days</i> between d1, d2 and the reference year
    yref = (d1.y < d2.y)? d1.y: d2.y;               //that <b>reference year</b>
    for (y = yref; y < d1.y; y++)
        if (isLeap(y))                              //check if there is any leap year between the reference year and d1's year (exclusive)
            dd1++;
    if (isLeap(d1.y) && d1.m > 2) dd1++;                //add another day if the date is past a leap year's February
    dd1 += daysTill(d1.m) + d1.d + (d1.y - yref) * 365;     //sum up all the tiny bits (days)
    for (y = yref; y < d2.y; y++)                       //repeat for d2
        if(isLeap(y))
            dd2++;
    if (isLeap(y) && d2.m > 2) dd2++;
    dd2 += daysTill(d2.m) + d2.d + (d2.y - yref) * 365;
    return abs(dd2 - dd1);                          //return the absolute difference between the two <i>no. of days elapsed past the reference year</i>
}
int daysTill (int month)                            //some logic here too!!
{
    int days = 0;
    switch (month)
    {
        case 1: days = 0;
        break;
        case 2: days = 31;
        break;
        case 3: days = 59;
        break;
        case 4: days = 90;      //number of days elapsed before April in a non-leap year
        break;
        case 5: days = 120;
        break;
        case 6: days = 151;
        break;
        case 7: days = 181;
        break;
        case 8: days = 212;
        break;
        case 9: days = 243;
        break;
        case 10:days = 273;
        break;
        case 11:days = 304;
        break;
        case 12:days = 334;
        break;
    }
    return days;
}
main()
{
    int t;          //no. of test cases
    Date d1, d2;    //d1 is the first date, d2 is the second one! obvious, duh!?
    scanf ("%d", &t);
    while (t--)
    {
        scanf ("%d %d %d", &d1.d, &d1.m, &d1.y);
        scanf ("%d %d %d", &d2.d, &d2.m, &d2.y);
        printf ("%d\n", diff(d1, d2));
    }
}

Стандартный ввод:

1
23 9 1960
11 3 2015

Стандартный вывод:

19892

Код в действии: https://ideone.com/RrADFR

Всегда приветствуются лучшие алгоритмы, оптимизации и правки!

2 голосов
/ 03 апреля 2012

Я не уверен, на какой ты платформе? Windows, Linux? Но давайте представим, что вы хотели бы иметь решение, не зависящее от платформы, и язык является стандартным C ++.

Если вы можете использовать библиотеки, вы можете использовать библиотеку Boost :: Date_Time (http://www.boost.org/doc/libs/1_49_0/doc/html/date_time.html)

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

Как dbrank0 указал на это. :)

1 голос
/ 03 апреля 2012

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

0 голосов
/ 03 апреля 2012

Вы должны взглянуть на DateTime класс.

Также ссылка msdn для синтаксиса C ++.

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