Jack Willson
Jack Willson

Reputation: 2104

How to manipulate dates/datetimes in c++11?

This is embarrassing, but i am having a hard time doing simple manipulation of datetimes.

This is the c# version of what i basically try to achive using c++11;

DateTime date1=new DateTime(4,5,2012);
DateTime date2=new DateTIme(7,8,2013);
int day1=date1.Days;
TimeSpan ts=d2-d1;
int diffDays=ts.Days;

What did i try?

    std::tm tm;
    tm.tm_year=113;
    tm.tm_mon=0;
    tm.tm_wday=0;

    std::time_t tt=mktime(&tm);
    std::chrono::system_clock::time_point then = std::chrono::system_clock::from_time_t(tt);
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    auto e1 = std::chrono::duration_cast<std::chrono::hours>(now - then).count();

The value of e1 (379218) makes no sense what so ever.

I took a look at chrono, which is presented as the c++11 standard library for datetime but i just could not find an example of how to create a date having int year=2012, int month=2, int day=14.

PS:Is chrono sufficient for handling date/times/timezones in c++11? Is there a need for time.h?

Upvotes: 5

Views: 6133

Answers (2)

Howard Hinnant
Howard Hinnant

Reputation: 219438

C++20 update

Time keeps marching on. ;-)

In C++20 this is now easy to do entirely within std::chrono:

#include <chrono>
#include <iostream>

int
main()
{
//     DateTime date1=new DateTime(4,5,2012);
//     DateTime date2=new DateTIme(7,8,2013);
//     int day1=date1.Days;
//     TimeSpan ts=d2-d1;
//     int diffDays=ts.Days;

    using namespace std::chrono;
    sys_days date1 = April/5/2012;   // m/d/y is ok
    sys_days date2 = 8d/July/2013;   // d/m/y is ok
                                     // y/m/d is also ok
    auto diffDays = date2 - date1;   // diffDays is a chrono::duration
    std::cout << diffDays << '\n';
}

Demo.

Original Answer:

New answer for an old question.

Except for messing around with the archaic std::tm, C++11 has no good way of dealing with dates or datetime, with the exception of boost Date Time. And there is one other library that I've been working on lately, that is highly tilted towards performance, compile-time type safety, and chrono compatibility. As presented it only works with C++14, but if you back off some of the constexprs, it will work with C++11. It is header-only, and consists of only one header, and is documented here. It is light on its I/O capability. (and now has extensive I/O capability)

But here is what it looks like for your example:

Oh, now that I look closely, and not knowing C#, I don't know whether DateTime(4,5,2012) refers to Apr 5, 2012, or May 4, 2012. Both m/d/y and d/m/y formats are popularly used. This is one of the problems my library addresses. It accepts both formats unambiguously. For this demo I'm going to assume you are writing in m/d/y format. But I'll use both formats to reproduce your example:

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

int
main()
{
//     DateTime date1=new DateTime(4,5,2012);
//     DateTime date2=new DateTIme(7,8,2013);
//     int day1=date1.Days;
//     TimeSpan ts=d2-d1;
//     int diffDays=ts.Days;

    using namespace date;
    auto date1 = sys_days(apr/5/2012);     // m/d/y is ok
    auto date2 = sys_days(8_d/jul/2013);   // d/m/y is ok
                                           // y/m/d is also ok
    auto diffDays = date2 - date1;         // diffDays is a chrono::duration
    std::cout << diffDays.count() << '\n';
}

This will output:

459

And in C++14, this computation can actually be done at compile time:

constexpr auto date1 = sys_days(apr/5/2012);
constexpr auto date2 = sys_days(8_d/jul/2013);
constexpr auto diffDays = date2 - date1;
static_assert(diffDays == days{459}, "");

And that means that "date constants" can be both very readable and very efficient, compiling down to an "immediate load."

See the documentation for a full description, tutorial and implementation.

Upvotes: 7

Daniel Frey
Daniel Frey

Reputation: 56921

You need to initialize all fields from tm, start with

std::tm tm = {0,0,0,0,0,0,0,0,0,0,0};

Without this, the other fields (the ones you don't explicitly set afterwards) will contain arbitrary values. The conversion will also normalize values, which mean that if the field tm_hour contains 123456789, it will add so many hours to the day you specified. This is how those nonsense-values for e1 can be explained. If you initialize all fields explicitly, it will allow your example to return meaningful values, although you might need to set more fields like isdst to make it correct for all cases.

I have to admit that I haven't used chrono so far as I find the required syntax overly verbose and I keep using my own classes to wrap the C-style time functions. That, of course, is not a statement about the quality and power of <chrono>, maybe I should start using it :)

Upvotes: 5

Related Questions