1

I have a double containing seconds. I would like to convert this into a struct tm.

I can't find a standard function which accomplishes this. Do I have to fill out the struct tm by hand?

I just accidentally asked this about converting to a time_t and http://www.StackOverflow.com will not let me post unless I link it.

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288

3 Answers3

4

Well, you accidentally asked the right question before. Convert double to time_t, and then convert that to a struct tm. There's no subsecond field in struct tm anyway.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Is there a function to do that conversion? Looks like I need to choose between [`localtime`](http://en.cppreference.com/w/cpp/chrono/c/localtime) and [`gmtime`](http://en.cppreference.com/w/cpp/chrono/c/gmtime). Which one should I prefer? – Jonathan Mee Jun 23 '15 at 10:50
  • 3
    @JonathanMee: `localtime` if you want local time and `gmtime` if you want GMT (UTC). Fairly obvious, really. – MSalters Jun 23 '15 at 10:53
  • To complete this answer, you may want to mention that the function that does the conversion of `time_t` to `struct tm` is [`mktime`](http://www.cplusplus.com/reference/ctime/mktime/). – Sergey Kalinichenko Jun 23 '15 at 10:59
  • @MSalters So `struct tm` and `time_t` both use the same epoch? I assume that's true but I can't seem to find support for it anywhere. – Jonathan Mee Jun 23 '15 at 11:03
  • 1
    @JonathanMee: No, `struct tm` uses `1900` as its epoch whereas the epoch of `time_t` is unspecified. Why do you care? – MSalters Jun 23 '15 at 11:31
  • @MSalters Well I just have an arbitrary number of hours, minutes, and seconds that I want to manipulate using `put_time` (for which I need my seconds in a `struct tm`.) If they don't share the same epoch and I just dump 320 into a `time_t` and then use `localtime` to put that into a `struct tm`; it the `struct tm` going to contain 5 minutes and 20 seconds? I just don't want the epoch discrepancy because I just dumped an arbitrary number into the `time_t` to give me unreliable results in my `struct tm`. – Jonathan Mee Jun 23 '15 at 11:40
  • @JonathanMee: That won't work - `struct tm` is a time **point** not a time **duration**. 13 hours duration is **not** "1:00 PM" – MSalters Jun 23 '15 at 12:10
  • @MSalters Ummm... seems like you are arguing the difference between a point and a vector. If it takes me 13 hrs to emotionally recover from a down-vote, that is a duration, but it is also a point in time given an arbitrary start time. What's more, I can measure that duration starting at the `struct tm` epoch and then I'll have a point which also indicates duration. – Jonathan Mee Jun 23 '15 at 12:21
  • @JonathanMee: The new stuff explicitly names it `std::chrono::time_point` and `std::chrono::duration`. I'm indeed arguing the difference because that's how C++ models time. As for the `struct tm` epoch, be aware that it probably isn't expressible as a `time_t` since its usual epoch is 1-1-1970 00:00 UTC. – MSalters Jun 23 '15 at 12:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/81388/discussion-between-jonathan-mee-and-msalters). – Jonathan Mee Jun 24 '15 at 10:23
2

For grins, using this chrono-based header-only library:

#include "date.h"
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace date;
    auto recovery_time = 320.023s;  // Requires C++14 for the literal 's'
    std::cout << make_time(duration_cast<milliseconds>(recovery_time)) << '\n';
}

outputs:

00:05:20.023

The object returned by make_time has getters if you want to query each field:

constexpr std::chrono::hours hours() const noexcept {return h_;}
constexpr std::chrono::minutes minutes() const noexcept {return m_;}
constexpr std::chrono::seconds seconds() const noexcept {return s_;}
constexpr precision subseconds() const noexcept {return sub_s_;}

You don't need to choose milliseconds. You could choose any precision you want from hours to picoseconds (if you also supply a type alias for picoseconds). For example:

std::cout << make_time(duration_cast<seconds>(recovery_time)) << '\n';

Outputs:

00:05:20
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • It's not exactly an answer to the question but it does so much more! This is so pretty I might cry. I know this is your code that you've worked hard on but please tell me you've put this up for standardization? – Jonathan Mee Jun 23 '15 at 15:56
  • No, but you are welcome to use it in your code. It might eventually be put up for standardization, but before that can happen, you and 1000 other programmers need to not only use it, but vocally support it. – Howard Hinnant Jun 23 '15 at 16:00
  • I've been keeping my eye on [P0355](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0355r0.html) I did have a question: "`puttime` ignores `iomanip` formatters, do your library's stream operators respect formatters?" (http://stackoverflow.com/q/40132424/2642059) – Jonathan Mee Oct 20 '16 at 11:21
  • 1
    @JonathanMee: Yes, `std::chrono::format` returns a `string` and thus when you stream it, `setw` and `left`/`right` are respected. Fwiw, there will be an update to p0355r0 appearing any day now. Here is a draft preview of that update: https://howardhinnant.github.io/date/d0355r1.html – Howard Hinnant Oct 20 '16 at 15:19
  • Exciting! I think I'll get as much use of this as I'm going to get out of `filesystem` (which is to say a lot of use.) Would you be interested in answering [my linked question](http://stackoverflow.com/q/40132424/2642059) about how your library would solve my issue? I think that would mean more than my recommendation of your library. – Jonathan Mee Oct 20 '16 at 15:24
0

MSalters answer is correct, but I thought I'd add a bit of detail on how you should convert to time_t and how you should convert to tm.

So given a number of seconds in double input you can use the implementation dependent method of casting:

const auto temp = static_cast<time_t>(input);

But since time_t is implementation defined there is no way to know that this is a primitive that can simply be cast to. So the guaranteed method would be to use the chrono library's implementation independent method of converting:

const auto temp = chrono::system_clock::to_time_t(chrono::system_clock::time_point(chrono::duration_cast<chrono::seconds>(chrono::duration<double>(input))));

The conversion options are discussed in more detail here: https://stackoverflow.com/a/50495821/2642059 but once you have obtained your time_t through one of these methods you can simply use localtime to convert temp to a struct tm.

const auto output = *localtime(&temp);

Note the dereference is important. It will use the default copy assignment operator so output is captured by value, which is essential because:

The structure may be shared between std::gmtime, std::localtime, and std::ctime, and may be overwritten on each invocation.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288