31

What I want to do is convert an epoch time (seconds since midnight 1/1/1970) to "real" time (m/d/y h:m:s)

So far, I have the following algorithm, which to me feels a bit ugly:

void DateTime::splitTicks(time_t time) {
    seconds = time % 60;
    time /= 60;
    minutes = time % 60;
    time /= 60;
    hours = time % 24;
    time /= 24;

    year = DateTime::reduceDaysToYear(time);
    month = DateTime::reduceDaysToMonths(time,year);
    day = int(time);
}

int DateTime::reduceDaysToYear(time_t &days) {
    int year;
    for (year=1970;days>daysInYear(year);year++) {
        days -= daysInYear(year);
    }
    return year;
}

int DateTime::reduceDaysToMonths(time_t &days,int year) {
    int month;
    for (month=0;days>daysInMonth(month,year);month++)
        days -= daysInMonth(month,year);
    return month;
}

you can assume that the members seconds, minutes, hours, month, day, and year all exist.

Using the for loops to modify the original time feels a little off, and I was wondering if there is a "better" solution to this.

casperOne
  • 73,706
  • 19
  • 184
  • 253
Austin Hyde
  • 26,347
  • 28
  • 96
  • 129
  • 2
    OK, so I know you're looking for a procedural solution... but if you haven't seen it already, check out http://www.epochconverter.com. It is a very useful way to check your work :) – Tom Nov 07 '09 at 14:55
  • 2
    What's wrong with just calling to this functionality as exposed by `time.h`, e.g. by [gmtime](http://www.cplusplus.com/reference/clibrary/ctime/gmtime/) if GMT rather than some specific timezone is what you're after? – Alex Martelli Nov 07 '09 at 06:20
  • 1
    Nothing wrong with it, I did not even know it existed. Thanks! But, I am still interested to know how my implementation could be better. – Austin Hyde Nov 07 '09 at 06:27
  • @Austin, "thanks" and no upvote is not compliant with normal SO etiquette!-) – Alex Martelli Nov 07 '09 at 06:31
  • 2
    (Let me haste to indicate that this isn't self-serving, as I'm maxed out for the day anyway: it's about smooth and normal application of SO's basic etiquette -- you upvote answers you find helpful!!!). – Alex Martelli Nov 07 '09 at 06:32
  • @Alex, I just haven't gotten there yet. Don't worry :D – Austin Hyde Nov 07 '09 at 06:38
  • For a non-iterative algorithm on how to do this computation see http://stackoverflow.com/a/32158604/576911 – Howard Hinnant Feb 06 '16 at 04:24

7 Answers7

21

Be careful about leap years in your daysInMonth function.

If you want very high performance, you can precompute the pair to get to month+year in one step, and then calculate the day/hour/min/sec.

A good solution is the one in the gmtime source code:

/*
 * gmtime - convert the calendar time into broken down time
 */
/* $Header: gmtime.c,v 1.4 91/04/22 13:20:27 ceriel Exp $ */

#include        <time.h>
#include        <limits.h>
#include        "loc_time.h"

struct tm *
gmtime(register const time_t *timer)
{
        static struct tm br_time;
        register struct tm *timep = &br_time;
        time_t time = *timer;
        register unsigned long dayclock, dayno;
        int year = EPOCH_YR;

        dayclock = (unsigned long)time % SECS_DAY;
        dayno = (unsigned long)time / SECS_DAY;

        timep->tm_sec = dayclock % 60;
        timep->tm_min = (dayclock % 3600) / 60;
        timep->tm_hour = dayclock / 3600;
        timep->tm_wday = (dayno + 4) % 7;       /* day 0 was a thursday */
        while (dayno >= YEARSIZE(year)) {
                dayno -= YEARSIZE(year);
                year++;
        }
        timep->tm_year = year - YEAR0;
        timep->tm_yday = dayno;
        timep->tm_mon = 0;
        while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
                dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
                timep->tm_mon++;
        }
        timep->tm_mday = dayno + 1;
        timep->tm_isdst = 0;

        return timep;
}
rxin
  • 1,786
  • 16
  • 25
18

The standard library provides functions for doing this. gmtime() or localtime() will convert a time_t (seconds since the epoch, i.e.- Jan 1 1970 00:00:00) into a struct tm. strftime() can then be used to convert a struct tm into a string (char*) based on the format you specify.

see: http://www.cplusplus.com/reference/clibrary/ctime/

Date/time calculations can get tricky. You are much better off using an existing solution rather than trying to roll your own, unless you have a really good reason.

Guy Avraham
  • 3,482
  • 3
  • 38
  • 50
sdtom
  • 890
  • 4
  • 8
  • 3
    This started off as homework a while back, but now I am interested in making it better. Normally, I would go for the library solution, but 1) I didn't know the time functions were built in and 2) I want to perfect my implementation. – Austin Hyde Nov 07 '09 at 06:34
5

An easy way (though different than the format you wanted):

std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));

Output: Wed Sep 21 10:27:52 2011

Notice that the returned result will be automatically concatenated with "\n".. you can remove it using:

std::string::size_type i = res.find("\n");
if (i != std::string::npos)
    res.erase(i, res.length());

Taken from: http://en.cppreference.com/w/cpp/chrono/c/time

brkeyal
  • 1,317
  • 1
  • 16
  • 22
  • time.cpp: In function ‘int main()’: time.cpp:6:36: error: ‘nullptr’ was not declared in this scope std::time_t result = std::time(nullptr); – BlackSwan Mar 30 '18 at 12:02
4
time_t t = unixTime;
cout << ctime(&t) << endl;
4b0
  • 21,981
  • 30
  • 95
  • 142
  • 5
    While this code snippet may be the solution, [including an explanation](https://meta.stackexchange.com/questions/114762/explaining-entirely-%E2%80%8C%E2%80%8Bcode-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Narendra Jadhav Jul 24 '18 at 05:08
1

This code might help you.

#include <iostream>
#include <ctime>

using namespace std;

int main() {
   // current date/time based on current system
   time_t now = time(0);
   
   // convert now to string form
   char* dt = ctime(&now);

   cout << "The local date and time is: " << dt << endl;

   // convert now to tm struct for UTC
   tm *gmtm = gmtime(&now);
   dt = asctime(gmtm);
   cout << "The UTC date and time is:"<< dt << endl;
}
1

To convert a epoch string to UTC

string epoch_to_utc(string epoch) {
  long temp = stol(epoch);
  const time_t old = (time_t)temp;
  struct tm *oldt = gmtime(&old);
  return asctime(oldt);
}

and then it can be called as

  string temp = "245446047";
  cout << epoch_to_utc(temp);

outputs:

Tue Oct 11 19:27:27 1977
lightyagami1
  • 105
  • 3
  • 7
0

If your original time type is time_t, you have to use functions from time.h i.e. gmtime etc. to get portable code. The C/C++ standards do not specify internal format (or even exact type) for the time_t, so you cannot directly convert or manipulate time_t values.

All that is known is that time_t is "arithmetic type", but results of arithmetic operations are not specified - you cannot even add/subtract reliably. In practice, many systems use integer type for time_t with internal format of seconds since epoch, but this is not enforced by standards.

In short, use gmtime (and time.h functionality in general).

eidolon
  • 3,223
  • 2
  • 16
  • 5