0
\$\begingroup\$

Imagine you can't use the standard functions for time calculation or anything alike. You have to start from scratch as it is the case on embedded systems.

Thus I came with the following code to determine the seconds, minutes, hours, days, months and years from unix timestamp. Tested the code, works for everything I threw at it so far, but wonder if there is a better way to do it without using the standard functions

#include <iostream>

int main(int argc, char* argv[])
{
    /* Subtract 2 years to start on leaping year, later add 1972 instead. */
    auto now = std::atoi(argv[1]) - 63072000;
    auto days = (now / 86400);
    auto hours = (((now - days * 86400) / 3600) % 24);
    auto minutes = ((((now - days * 86400) - hours * 3600) / 60) % 60);
    auto seconds = (((((now - days * 86400) - hours * 3600) - minutes * 60)) % 60);

    /* Get number of 4years to accomodate for leap years */
    auto q_years = (days / 1461);

    /* Recalculate the number of years */
    auto years = q_years * 4 + (days - (q_years * 1461)) / 365;

    /* Deterimne no. of days in the current year */
    auto days_last = (days - (q_years * 1461)) % 365;
    static uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    /* Fix for leap year */
    if (years % 4 == 0)
    {
        days_in_month[1] = 29;
        days_last++;
    }

    /* Retrace current month */
    int month = 0;
    while (days_last > days_in_month[month])
    {
        days_last -= days_in_month[month++];
    }

    printf("Year: %d, Month: %d\n", 1972 + years, month + 1);
    printf("Days: %d, Hours: %d, Minutes: %d, Seconds:%d\n", days_last, hours, minutes, seconds);

    return 0;

}

yes, the code is in c++ but I'll be rewriting it to C afterwards - basically letting go of auto.


What I am not looking at is code readability error checking (what if argv does not have 2 el.) et cetra. I am purely looking for a better algo to get the job done.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You take into account leap years. But not the century not being leap years (unless it is also divisible by 4). \$\endgroup\$ – Martin York Apr 13 at 20:04
2
\$\begingroup\$

std::atoi() is a poor choice of conversion function, as its means of reporting errors is indistinguishable from a valid conversion. The necessary header hasn't been included anyway.

uint8_t is not declared - did you mean std::uint8_t from <cstdint>? Similarly, you probably meant to include <cstdio> for std::printf, and <iostream> isn't used.

The repeated calculations during the division of seconds into days, hours, minutes and seconds should be simplified. I'd go with something like

auto seconds = now % 60;
auto minutes = now / 60;
auto hours = minutes / 60;
auto days = hours / 24;
minutes %= 60;
hours %= 24;

Perhaps even consider making a class for mixed base arithmetic.

The calculation of leap years is flawed, because it gives 100 leap years per 400 years, rather than the 97 expected.

Instead of looping over months, it might be better to keep an array of the start of month as Julian day, so we can use a straightforward binary search to identify the month number and day within month.

Calendrical calculations are often easier if we use a year beginning on the 1st of March for computation (so that leap day is the last day in year), then convert January/February dates to the correct year as a final step.

I'd encourage the use of a function (perhaps with the same signature as std::gmtime()) rather than shoving everything directly into main().

return 0; can be omitted from the end of main() .

\$\endgroup\$
3
  • \$\begingroup\$ consider making a class for mixed base arithmetic - it sounds like this is off the table, given OP's intent to switch to C. \$\endgroup\$ – Reinderien Apr 13 at 14:19
  • \$\begingroup\$ @Reinderien, I thought that too, but might be interesting to other readers. TBH, I'm not sure why I reviewed this, given that it's obviously not finished until it's turned into C source... \$\endgroup\$ – Toby Speight Apr 13 at 16:17
  • \$\begingroup\$ I had never thought of starting the calender from March. That seems to make things easier. \$\endgroup\$ – Martin York Apr 13 at 20:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.