8

I have been given a date, Which I am taking as an input like (day, month, year): 12, 03, 87.

Now I need to find out the date after n days.

I have written code for this, But its not efficient. Can you please tell me any good logic which works faster and have less complexity.

#include <stdio.h>

static int days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day, month, year;

unsigned short day_counter;

int is_leap(int y) {
    return ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0);
}

next_day()
{
    day += 1; day_counter++;
    if (day > days_in_month[month]) {
        day = 1;
        month += 1;
        if (month > 12) {
            month = 1;
            year += 1;
            if (is_leap(year)) {
                days_in_month[2] = 29;
            } else {
                days_in_month[2] = 28;
            }
        }
    }
}

set_date(int d, int m, int y) 
{
    m < 1 ? m = 1 : 0;
    m > 12 ? m = 12 : 0;
    d < 1 ? d = 1 : 0;
    d > days_in_month[m] ? d = days_in_month[m] : 0;
    if (is_leap(y)){
        days_in_month[2] = 29;
    } 
    else {
        days_in_month[2] = 28;
    }
    day = d;
    month = m;
    year = y;
}

skip_days(int x)
{
    int i;
    for (i=0;i<x;i++) next_day();
}

print_date()
{
    printf ("day: %d month: %d year: %d\n", day, month, year);
}

int main(int argc, char **argv)
{
    int i;

    set_date(5, 2, 1980);
    skip_days(40);
    day_counter = 0;
    /* after this call next_day each day */

    print_date();
    return 0;
}
J...S
  • 5,079
  • 1
  • 20
  • 35
vivek jain
  • 591
  • 4
  • 13
  • 28

8 Answers8

17

Can you please tell me any good logic which works faster and have less complexity.

If this exact thing is indeed a performance critical part of your application, you're likely doing something wrong. For the sake of clarity and correctness, you should stick to the existing solutions. Select the one that is most appropriate to your development environment.


The C approach:

#include <stdio.h>
#include <time.h>

int main()
{        
    /* initialize */
    int y=1980, m=2, d=5;    
    struct tm t = { .tm_year=y-1900, .tm_mon=m-1, .tm_mday=d };
    /* modify */
    t.tm_mday += 40;
    mktime(&t);
    /* show result */
    printf("%s", asctime(&t)); /* prints: Sun Mar 16 00:00:00 1980 */
    return 0;
}

The C++ without using Boost approach:

#include <ctime>
#include <iostream>

int main()
{        
    // initialize
    int y=1980, m=2, d=5;
    std::tm t = {};
    t.tm_year = y-1900;
    t.tm_mon  = m-1;
    t.tm_mday = d;
    // modify
    t.tm_mday += 40;
    std::mktime(&t);
    // show result
    std::cout << std::asctime(&t); // prints: Sun Mar 16 00:00:00 1980
}

The Boost.Date_Time approach:

#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>

int main()
{
    using namespace boost::gregorian;
    // initialize
    date d(1980,2,5);
    // modify
    d += days(40);
    // show result
    std::cout << d << '\n'; // prints: 1980-Mar-16
}
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
  • How do you compare different times? (less than, greater than, equal, etc) – David G May 05 '19 at 05:09
  • 1
    @0x499602D2 You can use the standard [difftime](https://en.cppreference.com/w/c/chrono/difftime) function. – strupo Jun 05 '19 at 17:29
  • But to have the correct date later is necessary to re-add 1900 years to t.tm_year and 1 mo to t.tm_mon, right? – AndreaGobs Aug 03 '22 at 09:41
  • @AndreaGobs When you want to extract the info from the `std::tm` attributes later, then you have to consider its data specification: https://en.cppreference.com/w/cpp/chrono/c/tm – moooeeeep Aug 14 '23 at 07:42
5

The standard library mktime function includes a trick to make it easy to add a number of months or days into a given date: you can give it a date such as "45th of February" or "2nd day of the 40th month" and mktime will normalize it into a proper date. Example:

#include <time.h>
#include <stdio.h>

int main() {
    int y = 1980;
    int m = 2;
    int d = 5;
    int skip = 40;

    // Represent the date as struct tm.                                                           
    // The subtractions are necessary for historical reasons.
    struct tm  t = { 0 };
    t.tm_mday = d;
    t.tm_mon = m-1;
    t.tm_year = y-1900;

    // Add 'skip' days to the date.                                                               
    t.tm_mday += skip;
    mktime(&t);

    // Print the date in ISO-8601 format.                                                         
    char buffer[30];
    strftime(buffer, 30, "%Y-%m-%d", &t);
    puts(buffer);
}

Compared to doing the arithmetic in seconds using time_t, this approach has the advantage that daylight savings transitions do not cause any problems.

han
  • 755
  • 4
  • 7
  • This approach has advantage only when adding months or years. If we add days there's no difference. Daylight savings are anyway eliminated by mktime. Why care about something we're gonna throw away? Adding n*86400 to timestamp is much easier and faster (assuming you already have initial time as unix timestamp). – Mike Siomkin Dec 25 '18 at 10:09
1

Here's what the solution looks like using algorithms published here by Howard Hinnant.

int main() {
    int day_count = days_from_civil(1980, 5, 2);
    auto [year, month, day] = civil_from_days(day_count + 40);
    printf("%04i-%02i-%02-i\n", (int)year, (int)month, (int)day);
}

New date functionality has been approved for inclusion in C++20, though with a different API. A C++20 solution will probably look something like:

#include <chrono>

int main() {
    using namespace std::chrono;
    sys_days in_days = year_month_day{1980y, may, 2d};
    std::cout << year_month_day(in_days + days(40)) << '\n';
}
bames53
  • 86,085
  • 15
  • 179
  • 244
0

the easiest trick is to use time_t type and corresponding functions.

mktime will convert tm structure to time_t. which is an integer value counting the seconds starting 01-Jan-1970.

After you have a time_t value just add the seconds count you need (86400 per day).

To convert back, use gmtime or localtime

eyalm
  • 3,366
  • 19
  • 21
  • 2
    Counting 86400 seconds as a day is not the correct thing to do because 'a day' is not a fixed unit of time like seconds are; some days are 86401 seconds long, some are 82800, some are 90000. These differences may be reflected by the C time functions depending on the platform and timezone data (for example, a timezone may be compiled with an accurate leap second table). – bames53 Mar 29 '13 at 16:56
  • @bames53 And some days are half a year... Seriously, don't you understand the actual problem here? Why are you overcomplicating things? – hkBattousai Nov 16 '15 at 12:27
  • 1
    @hkBattousai I don't believe it's overcomplicating things to notice that this solution can provide incorrect answers. The correct, simple solution is to use correct date arithmetic. E.g. assuming an appropriate [library](http://howardhinnant.github.io/date_algorithms.html) just do: `civil_from_days(10 + days_from_civil(2015, 11, 16))`. – bames53 Nov 16 '15 at 19:35
0

Just add to a time_t object for how many days you want.

#define SECOND              1
#define MINUTE              60 * SECOND
#define HOUR                60 * MINUTE
#define DAY                 24 * HOUR





time_t curTime;
time_t futureTime;

time( & curTime );

futureTime = curTime + (5 * DAY);

struct tm * futureDate = gmtime(&futureTime);
std::cout<<"5 days from now will be: "<<futureDate->tm_mday<<"/"<<futureDate->tm_mon+1<<"/"<<futureDate->tm_year+1900<<std::endl;
lcs
  • 4,227
  • 17
  • 36
  • 2
    Days aren't really fixed units of time like seconds and hours so this sort of thing is not correct, though in practice the inaccuracy isn't usually very noticeable. But consider for example if the above code were run on June 29th, 2012 at 00:00:00. Adding 5 days to June 29th should result in the date July 4th, however there was a leap second on June 30th, 2012. Assuming the timezone contains correct leap-second data then the output of this code will indicate that 5 days from June 29th is July 3rd. Additionally, transitions to or from daylight savings can cause similar problems. – bames53 Mar 27 '13 at 21:56
  • Rather than use 24*60*60 as a substitute for 'one day' you should instead use a date type that knows how to work in terms of real dates. One example is Howard Hinnant's [`chrono::date`](http://home.roadrunner.com/~hinnant/bloomington/date.html) library. – bames53 Mar 27 '13 at 21:56
  • 2
    @bames53: Again, `time_t` does not count leap seconds. – Lightness Races in Orbit Feb 14 '18 at 15:51
0

Can be implemented using C++ operators and in a quite OOP way by representing date as a class.

#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;

    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);
}

/// 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;

    return 0;
}

Output
2017/06/25 + 10 days = 2017/07/05
SajithP
  • 592
  • 8
  • 19
-1

It might be easier to do the math using seconds since the epoch instead of manipulating the date fields directly.

For example, this program prints the date 7 days from now:

#include <stdio.h>
#include <time.h>

main()
{
    time_t t;
    struct tm *tmp;

    time(&t);

    /* add a week to today */
    t += 7 * 24 * 60 * 60;

    tmp = localtime(&t);
    printf("%02d/%02d/%02d\n", tmp->tm_mon+1, tmp->tm_mday,
        tmp->tm_year % 100);
}
Jayesh Goyani
  • 11,008
  • 11
  • 30
  • 50
andy
  • 6,888
  • 1
  • 19
  • 17
  • 2
    Using seconds this way is not the correct thing to do because 'a day' is not a fixed unit of time like seconds are; some days are 86401 seconds long, some are 82800, some are 90000. These differences may be reflected by the C time functions depending on the platform and timezone data (for example, a timezone may be compiled with an accurate leap second table). – bames53 Mar 29 '13 at 16:57
  • @bames53: Not so. `time_t` is not UTC. All its days are 86400 seconds long. Leap seconds are ignored/skipped/slewed-over in POSIX. – Lightness Races in Orbit Feb 14 '18 at 15:51
  • @LightnessRacesinOrbit That depends on how the system is configured. For example consider the following documentation: ["The asctime(), ctime(), difftime(), gmtime(), localtime(), and mktime() functions conform to ISO/IEC 9899:1990 ("ISO C89''), and conform to ISO/IEC 9945-1:1996 ("POSIX.1'') **provided the selected local timezone does not contain a leap-second table** (see zic(8) )."](http://www.manpages.info/macosx/localtime.3.html) [emp. added] – bames53 Feb 28 '18 at 23:22
  • @bames53: That note relates to the way POSIX time is converted into not-POSIX time (the various human-readable timestampes, structs with broken-out year/month/day/hour etc fields, and so forth), hence why leap seconds are important. But `time_t` itself is _always_ just a count of seconds since 1st Jan 1970 not counting leap seconds. A UNIX timestamp is defined that way, always. So, you're correct in saying that "the differences may be reflected by C time functions", but you can never trigger said differences by "using seconds this way". Andy's method will always add a week to today, period. – Lightness Races in Orbit Feb 28 '18 at 23:46
  • @LightnessRacesinOrbit No, systems can be configured such that time_t includes leap seconds. Here's another example ["If the time package is configured with leap-second support enabled \[...\] time_t values continue to increase over leap events"](https://www.freebsd.org/cgi/man.cgi?query=time2posix&apropos=0&sektion=0&manpath=FreeBSD+5.0-current&format=html) – bames53 Mar 01 '18 at 14:28
-3

This code will print the date coming after 10 days. Change the value to N for a user-defined number.

#include< iostream.h>

#include< conio.h>

struct date{int d,m,y;};

void main()

    {

    date d1;

    void later (date);

    cout<<"ENTER A VALID DATE (dd/mm/yy)";

    cin>>d1.d>>d1.m>>d1.y;

    later(d1);

    getch();

    }

    void later(date d1)

    {

    int mdays[12]={31,28,31,30,31,30,31,31,30,31,30,31};

    if(((d1.y%4==0) && (d1.y%100!=0))||(d1.y%400==0))
    mdays[1]=29;
    d1.d=d1.d+10;

    if(d1.d>mdays[d1.m-1])

    {

    d1.d=d1.d-mdays[d1.m-1];

    d1.m++;

    if(d1.m>12)

    {d1.m=1;

    d1.y++;

    }

    }
    cout<<"DATE AFTER TEN DAYS IS "<<d1.d<<"\t"<<d1.m<<"\t"<<d1.y;

    getch();

    }
ARJUN
  • 1
  • 4