2

I've got the task to convert some C# code to C++ and I've problems with chrono. Goal is to round a time to a variable time span.

C#:

int iRoundTo = 30; // can be 45 or other variable value, not a const
DateTime dt = Floor(DateTime.Now, new TimeSpan(0, 0, 0, iRoundTo));

I found a solution with const iRoundTo but this is not what I'm looking for. How do I convert this to C++ with using std::chono?

C++:

std::chrono::seconds diff(iRoundTo);
auto dt = std::chrono::floor<diff>(Now);

This is not working due to compile error.

Thanks.

Giacomo Pirinoli
  • 548
  • 6
  • 17
Mighty
  • 33
  • 4

2 Answers2

2

I'm doing a bit of guessing with this answer, but my guess is that you want to truncate the current time to the floor of the current half minute (in case of iRoundTo == 30).

If I'm correct, this is easy to do as long as iRoundTo is a compile-time constant.

#include <chrono>
#include <iostream>

int
main()
{
    using RoundTo = std::chrono::duration<int, std::ratio<30>>;
    auto Now = std::chrono::system_clock::now();
    auto dt = std::chrono::floor<RoundTo>(Now);
    std::cout << dt << '\n';
}

The above creates a new duration type that is 30 seconds long. It then gets the current time and floors it (truncates downwards) to the previous half minute unit. It then prints out the result. Everything before the print works in C++17. The printing (the last line) requires C++20.

Example output for me:

2022-01-18 01:45:30

The above code can also work pre-C++17 but in that case you'll need to find your own floor (e.g. date.h) or use std::chrono::duration_cast which truncates towards zero.

Update

In the comments below, it is explained that iRoundTo is not a compile-time constant, but does always represent a multiple of seconds. In this case I would do this in two steps:

int iRoundTo = 30;
auto Now = std::chrono::system_clock::now();

// First truncate to seconds
auto dt = std::chrono::floor<std::chrono::seconds>(Now);
// Then subtract of the modulus according to iRoundTo
dt -= dt.time_since_epoch() % iRoundTo;

The sub-expression dt.time_since_epoch() % iRoundTo has type seconds and a value between 0s and seconds{iRoundTo} - 1s.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Thank you for your answer. But this is the exact problem, I can't use a compile-time constant. I must decide during runtime, to which value I want to round to. – Mighty Jan 18 '22 at 09:50
  • @Mighty C++ is not C#. It doesn't map 1-to-1. So you have to think differently. You can make multiple types `RoundTo30`, `RoundTo45` etc, and then call the `floor` with that respective type. Maybe a templated function? – JHBonarius Jan 18 '22 at 10:41
  • @JHBonarius Nah, bad idea. I'd rather use something like std::chrono::system_clock::to_time_t and back, doing the rounding on the seconds as normal. – Eugene Ryabtsev Jan 18 '22 at 10:43
  • @EugeneRyabtsev ok, can you put that in an answer? – JHBonarius Jan 18 '22 at 10:45
  • @JHBonarius Not at the moment. I'll see if this is still open in the evenig today. – Eugene Ryabtsev Jan 18 '22 at 10:46
  • @Howard Hinnant That seems to be a solution. Thank you, I'll try it. – Mighty Jan 18 '22 at 14:05
  • @Howard Hinnant I'm absolutely not sure if it works. I tried your code but how do I print the time using std::format? I installed the newest 2022 VS Version and set VC Features to latest but it will result always in printing "%T" and no time. – Mighty Jan 18 '22 at 23:49
  • 1
    I don't have VS to experiment with, and can't locate an online tool to experiment with. So I can't confirm the behavior of VS. C++20 says that `std::cout << dt << '\n';` should output in the format yyyy-mm-dd hh:mm:ss – Howard Hinnant Jan 19 '22 at 00:18
  • @JHBonarius Howard's second solution is good enough. I've posted a complete example as an answer to this. – Eugene Ryabtsev Jan 19 '22 at 06:17
1

Without access to the newest tools (and both std::format and << overload seem to require rather new tools) I've got got it tested only so far (please edit if you can test it further):

#include <chrono>
#include <iostream>
#include <ctime>
#include <iomanip>
//#include <format>

int main() {
    int roundTo = 15;
    auto dt = std::chrono::floor<std::chrono::seconds> (std::chrono::system_clock::now());
    dt -= dt.time_since_epoch() % roundTo;
    std::time_t t = std::chrono::system_clock::to_time_t(dt);
    std::cout << std::ctime(&t); // Using <ctime>
    std::cout << std::put_time(std::localtime(&t), "%Y-%m-%e %H:%M:%S") << '\n'; // Using <iomanip>
    //std::cout << "date: " << std::format("%Y-%m-%e %H:%M:%S", dt) << '\n'; // Using <format>
    //std::cout << "date: " << std::format("{%Y-%m-%e %H:%M:%S}", dt) << '\n'; // Using newer <format>
    //std::cout << dt << '\n'; // Using << overload
}
Eugene Ryabtsev
  • 2,232
  • 1
  • 23
  • 37