How can I convert the day of the year (365 days) equivalent date December 31

393 Views Asked by At

how can i use the c++ library such as mktime() to convert day to dates instead of making my own algorithm

3

There are 3 best solutions below

14
Marcus Müller On

Mktime is not C++, but a C library function. And honestly, it's pretty terrible. The fact that you're even mentioning it might point to you reading outdated material. Also, it's serving a different purpose than what to seem to think it does, but that is of no consequence here:

So, a modern C++ solution (all credits to chris, who put this code on godbolt compiler explorer, which I reworked a bit for you to experiment with:

#include <array>
#include <chrono>
#include <iostream>

using namespace std::literals;
namespace chr = std::chrono;

int main() {
    constexpr auto day_of_year = 365;

    // y suffix, to turn 2022 into a year specification in std::literals
    // the overloaded / operator makes specifying Gregorian calendar dates easy
    const auto date = chr::sys_days{2022y / 1 / 0} + chr::days{day_of_year};

    // Year-month-day representation of the resulting date
    const chr::year_month_day ymd(date);

    // Weekday
    const chr::weekday day_of_week(date);
    constexpr std::array<const char*, 7> weekdays{"Weekend, the 2nd",
                                                  "Extended Weekend",
                                                  "Tuesday",
                                                  "Funday",
                                                  "Ultimate Funday",
                                                  "Pre-Weekend",
                                                  "Weekend (first day)"};

    // explicitly cast to int (for years, which exist BC) or unsigned
    // (month of year and day of month are always non-negative)
    std::cout << static_cast<int>(ymd.year()) << "-"
              << static_cast<unsigned>(ymd.month()) << "-"
              << static_cast<unsigned>(ymd.day()) << ",\n"
              << "Which is a " << weekdays[day_of_week.c_encoding()] << "\n";

    // note that the above date calculation is all based on constants, so the
    // compiler does it for you at compile time – it doesn't get more efficient
}

C++ has std::chrono. And that allows you to do something like create a date-representing object that describes the beginning of a year, and then add a time delta, i.e., a std::duration (e.g. 365 days or 10⁶ seconds) to it, then convert it to whatever readable representation you need.

You should have actually stumbled across it! Documentation is ample and readily available.

1
Henrique Bucher On

One example of how to use std::mktime to handle the proposed problem is written below.

The key to this algorithm is to use std::mktime to convert from the year input to a base year epoch, ie the number of seconds from 1970-01-01 until year-01-01.

Then you add manually the number of seconds until the date you want. Remember leap seconds do not count so just adding 86400, the number of seconds in a day, is correct.

The last step is to convert back from epoch time into a full date.

#include <ctime>
#include <cstring>
#include <cstdio>

struct YearDay {
    int year;
    int month;
    int day;
};

YearDay convert( int year, int yday ) {
    // Convert year to epoch
    struct tm tms;
    std::memset(&tms,0,sizeof(tms));
    tms.tm_year = year - 1900;
    std::time_t date = std::mktime(&tms);

    // add days manually
    date += yday * 86400;

    // Synthesize into a full date
    struct tm* newtm = std::gmtime( &date );
    return YearDay{newtm->tm_year+1900, 
                   newtm->tm_mon+1, 
                   newtm->tm_mday};    
}

One example of usage would be:

int main() {
    int year = 2022;
    for ( int yday = 1; yday<=365; ++yday ) {
    YearDay yd = convert( year, yday );
    printf( "%4d %-3d  ==> %4d-%02d-%02d\n",
            year, yday, yd.year, yd.month, yd.day  );
    }
}

Produces when run:

Program stdout
2022 1    ==> 2022-01-01
2022 2    ==> 2022-01-02
2022 3    ==> 2022-01-03
...
2022 363  ==> 2022-12-29
2022 364  ==> 2022-12-30
2022 365  ==> 2022-12-31

Godbolt: https://godbolt.org/z/YvGsoenbK

1
selbie On

For starters, December 31 2022 was a Saturday, not a Sunday as your title suggests.

While you could probably find a std:: library way to get what you want, I ask smaller versions of the "compute the day of the year" type problems in interview loops. So it was rather easy for me to drop this code for you. Here's a function that will take a year and a "day of the year" (a value between 1-365 or 1-366 for leap years) and generate a string for the month, day, and day of week. Enjoy.

The downside of this code is that the strings are hardcoded to English. But it works. If you need to make it work for dates before 301 AD, it will need a minor adjustment.

#include <iostream>
#include <string>
using namespace std;

string MakeDate(const int year, const int dayOfYear) {
    auto isLeapYear = [](int y) {
        return (y % 400 == 0) || ((y % 4 == 0) && (y % 100 != 0));
    };
    static const int days_in_month[13] =    { 0, 31,28,31,30,31,30,31,31,30,31,30,31 };
    static const int days_in_month_ly[13] = { 0, 31,29,31,30,31,30,31,31,30,31,30,31 };
    static const string month_names[13] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    static const string day_names[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursay", "Friday", "Saturday" };

    // January 1, 301 was a Tuesday if the Gregorian calendar went back that far
    // Every 400 years since then, it's been a Tuesday on January 1
    // 0=Sunday, 1=Monday, 2=Tuesday.... 6=Saturday
    int firstDayOfYear = 2;
    int computeYear = 301;
    int diff = (year > computeYear) ? (year - computeYear) : 0;
    computeYear += 400 * (diff / 400);

    while (year > computeYear) {
        int addOn = isLeapYear(computeYear) ? 2 : 1;
        firstDayOfYear = (firstDayOfYear + addOn) % 7;
        computeYear++;
    }

    // assume "jan 1" is "dayOfYear==1". Normalize it to be a zero based offset
    int days = dayOfYear - 1;
    int d = 1;
    int m = 1;
    int y = year;
    int dayOfWeek = (firstDayOfYear + days) % 7;

    const int* dim = isLeapYear(y) ? days_in_month_ly : days_in_month;

    while (days >= dim[m]) {
        days -= dim[m];
        m++;
        if (m > 12) {
            m = 1;
            y++;
            dim = isLeapYear(y) ? days_in_month_ly : days_in_month;
        }
    }
    d = 1 + days;

    string result = month_names[m] + " " + to_string(d) + " " + to_string(y) + ", " + day_names[dayOfWeek];
    return result;
}
int main()
{
    // print out all 365 days of 2022
    for (int i = 1; i <= 365; i++)
    {
        std::cout << MakeDate(2022, i) << "\n";
    }
    return 0;
}