Converting file_time_type to string.

2,920 views
Skip to first unread message

Bengt Gustafsson

unread,
Jun 7, 2017, 5:42:58 PM6/7/17
to ISO C++ Standard - Future Proposals
Sometimes, such as when presenting a directory listing, there is a need to convert a file time to a string.

This is ridiculously complicated when using the std::filesystem library, and in fact, it seems to be sheer luck that this even compiles:

             auto time = last_write_time(iter->path());

             time_t tt = decltype(time)::clock::to_time_t(time);  // Note: to_time_t not guaranteed to exist.

             tm tmt;

             localtime_s(&tmt, &tt);

 

             cout << std::put_time<wchar_t>(&tmt, L"%F %T") << std::endl;


So the most pressing issue is that (at least according to cppreference.com) the std::filesystem::file_time_type::clock type only has to satisfy the TrivialClock concept, which does not imply that it has a to_time_t method at all (i.e. it doesn't have to be the system_clock). Is this an oversight, an error in cppreference.com's description or am I missing some other way to convert the filesystem::file_time_type object to a printable type? The code above is fairly similar to what replyers come up with on stack overflow: (https://stackoverflow.com/questions/12835577/how-to-convert-stdchronotime-point-to-calendar-datetime-string-with-fraction) so it seems that there is no more straight forward way.


Apart from this specification problem I think that there should be a pure C++ way of formatting time_points that does not involve ancient C functions and multiple steps (getting higher precision than a second makes my code even more complicated).


One way would be to provide an overload of put_time that takes a system_clock::time_point and a similar function that returns the formatted string. A way to get sub-second fractions should be available as (%f is actually free according to strftime documentation at cppreference.com). As put_time is dependant on the locale facet imbued on the stream a string conversion function would probably need to have a locale parameter which defaults to the global locale of the process.


A similar shortcut without C middlemen would be appropriate for scanning time values, I presume.


In C++17 we got the new type timespec and the function to get the current time, timespec_get(). However there does not seem to be a link from this C struct (?) to any of the chrono functionality or filesystem times. Also there does not seem to exist a formatting function which handles formatting including the fractions (see the example here: http://en.cppreference.com/w/cpp/chrono/c/timespec_get).



T. C.

unread,
Jun 7, 2017, 6:30:15 PM6/7/17
to ISO C++ Standard - Future Proposals
On Wednesday, June 7, 2017 at 5:42:58 PM UTC-4, Bengt Gustafsson wrote:

an error in cppreference.com's description 


At cppreference we try pretty hard not to make stuff up. For this particular case, see [fs.filesystem.syn]/1.

More generally, shouldn't you try to confirm whether the standard says X yourself before making a big post about how X is a bad idea on a mailing list about the standard?

Howard Hinnant

unread,
Jun 7, 2017, 8:57:11 PM6/7/17
to std-pr...@isocpp.org
On Jun 7, 2017, at 5:42 PM, Bengt Gustafsson <bengt.gu...@beamways.com> wrote:
>
> Sometimes, such as when presenting a directory listing, there is a need to convert a file time to a string.
>
> This is ridiculously complicated when using the std::filesystem library, and in fact, it seems to be sheer luck that this even compiles:
>
> auto time = last_write_time(iter->path());
>
> time_t tt = decltype(time)::clock::to_time_t(time); // Note: to_time_t not guaranteed to exist.
>
> tm tmt;
>
> localtime_s(&tmt, &tt);
>
>
>
> cout << std::put_time<wchar_t>(&tmt, L"%F %T") << std::endl;


It is sheer luck. std::filesystem::file_time_type::clock is not specified to have the member function to_time_t.

>
>
>
> So the most pressing issue is that (at least according to cppreference.com) the std::filesystem::file_time_type::clock type only has to satisfy the TrivialClock concept, which does not imply that it has a to_time_t method at all (i.e. it doesn't have to be the system_clock). Is this an oversight, an error in cppreference.com's description or am I missing some other way to convert the filesystem::file_time_type object to a printable type? The code above is fairly similar to what replyers come up with on stack overflow: (https://stackoverflow.com/questions/12835577/how-to-convert-stdchronotime-point-to-calendar-datetime-string-with-fraction) so it seems that there is no more straight forward way.
>
>
>
> Apart from this specification problem I think that there should be a pure C++ way of formatting time_points that does not involve ancient C functions and multiple steps (getting higher precision than a second makes my code even more complicated).

I’ve been working on a proposal to format and parse time_point<system_clock, Duration>. Your post has inspired me to include I/O for file_time_type as well.

> One way would be to provide an overload of put_time that takes a system_clock::time_point and a similar function that returns the formatted string. A way to get sub-second fractions should be available as (%f is actually free according to strftime documentation at cppreference.com). As put_time is dependant on the locale facet imbued on the stream a string conversion function would probably need to have a locale parameter which defaults to the global locale of the process.

One of the things I dislike about put_time is that it uses a std::tm to hold the time to be formatted. This inherently limits the precision of the output to seconds. I see almost daily a desire to parse and format at precisions finer than a second. And many modern file systems support subsecond resolution for their file timestamps.

> A similar shortcut without C middlemen would be appropriate for scanning time values, I presume.

Absolutely.

> In C++17 we got the new type timespec and the function to get the current time, timespec_get(). However there does not seem to be a link from this C struct (?) to any of the chrono functionality or filesystem times. Also there does not seem to exist a formatting function which handles formatting including the fractions (see the example here: http://en.cppreference.com/w/cpp/chrono/c/timespec_get).

timespec is simply imported from C. Imho it is born flawed as one doesn’t know whether a timespec is a time point or a time duration, and the C API uses it both ways. I would not encourage the C community to embrace it for that reason alone.

I have been working on this library for a few years now:

https://github.com/HowardHinnant/date

It is fully documented, open-source, and has a small but growing community supporting it. It does not replace <chrono>, and it does not build something in addition to <chrono>. It builds on top of <chrono>, fully embracing it, and extending it into the realm of calendars. It contains formatting and parsing for all reasonable precisions of time_point<sytem_clock, Duration>, as well as simple and efficient conversions between these time_points and calendar types such as year_month_day (a {year, month, day} struct).

Use of it can be as simple as this:

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

int
main()
{
using namespace std::chrono;
using namespace date;
std::cout << system_clock::now() << '\n';
}

which just output for me:

2017-06-08 00:34:40.718556

On my platform, system_clock::time_point has microseconds precision. If on your platform it has nanoseconds precision, that is the precision you’ll see by default on streaming.

I believe I can easily do the same for file_time_type.

If you want custom formatting, that is easy too:

std::cout << format("%a, %b %d, %Y at %I:%M %p", system_clock::now()) << '\n’;

Thu, Jun 08, 2017 at 12:34 AM

This library supports a superset of the strftime formatting flags, including the ability to control the precision of the output:

std::cout << format("%F %T", floor<milliseconds>(system_clock::now())) << '\n’;

2017-06-08 00:34:40.718

And yes, there’s parsing too:

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

int
main()
{
using namespace std::chrono;
using namespace date;
std::istringstream ss{"2017-06-08 00:34:40.718556"};
system_clock::time_point tp;
ss >> parse("%F %T", tp);
assert(tp == sys_days{2017_y/jun/8} + 34min + 40s + 718556us);
}

And as you can see above in the assert, it is easy to build system_clock::time_points out of field information (year, month, day, hour, etc).

I see two ways to go on file_time_type:

1. Provide bidirectional conversions to time_point<system_clock, D>, and then do all of the I/O and calendar stuff with system_clock.

2. Provide I/O and calendar interoperability on file_time_type directly, just like I’ve done for time_point<system_clock, D>.

1 would be easier for the implementor and proposer (me). But I think 2 might be the higher quality solution. I will give this more thought. The latter will introduce more types, such as file_time_days, adding more work for calendar authors (the civil calendar isn’t the only one). Hmm…

3. Provide only I/O for file_time_type, and not calendar interoperability. This would mean you could not easily create a file_time_type for a specific year/month/day hh:mm:ss, nor could you easily extract such fields from a file_time_type, though you could observe them by printing them out.

Howard

signature.asc

Bengt Gustafsson

unread,
Jun 8, 2017, 2:14:10 PM6/8/17
to ISO C++ Standard - Future Proposals
I'm sorry if this came through as belitteling cppreference.com. I rely on it every day and I have never found an error. However, given the rather catastrophic result of not having a standardized way to get from a file_time_type to a string representation so lets say I was rather more hoping that you would be wrong for once...

Bengt Gustafsson

unread,
Jun 8, 2017, 5:54:42 PM6/8/17
to ISO C++ Standard - Future Proposals
Howard,

it is very good that you have this type of functionality in the wings. I don't see how you can make this work for file_time_type if it does not specify a way to convert to wall clock time. Maybe some part of the chrono interface is still eluding me... I understand that you can get a duration from the epoch of the file_time_type::clock to the time point, but I don't see a way to know what the epoch of an unspecified clock actually is. In reality I guess this is not a problem, but specificationwise it may be. However, if the date library gets standardized as part of C++ this would of course be relatively easy to fix in the process.


Here are some comments when reading the date library documentation at: https://howardhinnant.github.io/date/date.html, mostly motivated by its route towards standardization. Admittedly I haven't read the P0355r2 proposal, comments are solely related to the documentation mentioned here.

- It seems that the procedure for creating year_month_day may be a little overworked, involving several other data types with overloaded / operators and user defined literals. Also, if you don't "use" the date namespace and this gets standardized all those types will have long names like std::date::year, or maybe std::chrono::year if this functionality is integrated with chrono. As these names are the same as the usual names you would prefer to use for your variables it can get messy, and with a using statement it downright clashes and you have to give your variables cryptic names.

void MyFunction(int year, int month, int day)
{
    auto dt = std::date::year(year) / month / day;
}

also the use of the slash operator is not that obvious as many parts of the world (and ISO8601) are more at ease with using - between the parts. Using an overloaded operator when the operation is not consistent with the usual meaning of the operator is also generally discouraged (No, I don't like path / path either). Maybe use + as it is neutral between different notations of dates and actually seems to be consistent: You are actually adding years, days and months to make a complete date. On the other hand doing this without first casting all the ints to their corresponding year, day and month types seems strange if you view it as addition.

A library which is more complicated than its task warrants risks not being used as the benefit may not outweigh the learning curve. I would more favor one user defined literal _date which takes a string in ISO-6801 format and returns a date_time object. As you point out we already have one of those in system_clock::time_point. However, this is what you define as a serial-based representation, so why not have a field-based representation of the complete time_point, and ways to convert between the two. Having to do a floor operation, a subtraction and two conversions from serial to field based types seems to be to ask too much of the user.

- The *_last types also seem a bit unnecessary, while a way to inquiry the number of days of a given month is a must. I don't understand how to do that easily with this library. That's part of the learning curve I wrote about above -- simple things should be simple.

- I fail also (sorry) to see the importance of the year_month_weekday type, although it is important to be able to ask what weekday a certain date is. Well, maybe sometimes you may want to get the date of the third sunday of March of 2017 but it is not so easy to find a plausible use case. With this library asking what weekday a date is does not seem particularly simple as each field-based type can only report the fields it has. Here is what I think would be needed:

date::year_month_day date = date::year(2017)/2/4;
date::year_month_weekday wdate = date::sys_days(date);
return wdate.weekday();

In wxWidgets, by contrast, you can do:

wxDate date(2, 4, 2017);
return date.GetDayOfWeek();

This level of simplicity is what we should strive for. Maybe the gist of what I'm trying to say here is that while the internal representation of a datetime may be configurable for optimal performance of certain operations, it would be good if the API of the classes was the same. This could be viewed as fully specialized instances of a template class with an enum template parameter:

enum class datetime_representation {
    linear,
    year_month_day.
    year_month_weekday,
    year_week_day
};

template<datetime_representation = linear> class datetime {
    // Lots of methods to set and get the different parts, with equal behaviour but different performance.
    // Implementations of methods exist only in specializations
};


Now back to more nitpicking:

- The definition of months (the duration) is a bit scary as it relates to an "average month length" which means that it will not accurately represent the current month when used as a duration from a year's start. Is it really needed? I guess the same can be said for years, which tries to subsume the concept of leap years but where presumably the number of years since the start of another year can be off by one on Jan 1 or Dec 31.

- I'm undecided on whether to prefer your to_stream() idea or an overload of the put_time() manipulator. I think both may well be outflanked by a general format() function (see below).

- It seems strange to me that the 12/24 hour format is part of the time_of_day object. Shouldn't this be a formatting issue when converting to a string rather than a member of the object?

- In general I don't like the idea of having different specializations with different API (such as time_of_day<minutes> having a minutes() function while time_of_day<hours> has not). Maybe minutes() could always exist but return 0 for a hour-only specialization? I understand it as your idea being to save implementation bits when not needed.

- format() should take its fmt parameter as a basic_string_view. [this is likely fixed in the standardization proposal]

- Maybe it would be better if format's char types for the returned string and for the fmt parameter were decoupled. I write maybe as this decoupling would require callers to explicitly name the char type they want for the returned string. A name convention as used for basic_string itself could solve this, possibly.

- The name format() could be a bit too generic, especially if the format("{}, is a nice {}", ...) proposal gets standardized. Instead I would suggest integrating the functionality intended here into a general format() method so that the formatting parameters are "strftime like" if the corresponding argument is of a date/time related type as proposed here. This would also work nicely with string translation, so that locale specific formats can be used if needed (without involving a locale parameter).

- A problem with parsing dates entered by users is that persons tend to not type dates in a coherent way. A remedy I have used is to allow multiple format strings which are tried in succession. This could fall outside a standard library but as the standard allows for two distinct output formats per locale it does not seem too far fetched to allow at least two on input. One possible way to define this would be to reserve one character, such as | as a separator in format strings when used for input, with the meaning that each | separated part is one complete format to be tried in order. Obviously it would be up to the programmer to place the formats in a suitable order to avoid ambiguities.

Howard Hinnant

unread,
Jun 8, 2017, 9:05:43 PM6/8/17
to std-pr...@isocpp.org
Hi Bengt,

Thanks for your feedback. Just a few clarifications below:

On Jun 8, 2017, at 5:54 PM, Bengt Gustafsson <bengt.gu...@beamways.com> wrote:

> - It seems that the procedure for creating year_month_day may be a little overworked, involving several other data types with overloaded / operators and user defined literals. Also, if you don't "use" the date namespace and this gets standardized all those types will have long names like std::date::year, or maybe std::chrono::year if this functionality is integrated with chrono. As these names are the same as the usual names you would prefer to use for your variables it can get messy, and with a using statement it downright clashes and you have to give your variables cryptic names.
>
> void MyFunction(int year, int month, int day)
> {
> auto dt = std::date::year(year) / month / day;
> }


The current proposal is to put these into namespace chrono. And my current C++11/14/17 usage of chrono almost always resorts to “using namespace std::chrono” at function scope as I’m already frustrated with:

auto min = std::chrono::time_point_cast<std::chrono::minutes>(std::chrono::system_clock::now());

I write instead:

using namespace std::chrono;
auto min = time_point_cast<minutes>(system_clock::now());

With just one component needing qualification as in your example, I might not bother with the using declaration. But by the time I have to qualify two components, I am so done with that.

You can also write MyFunction like this:

void MyFunction(int y, int m, int d)
{
using namespace std::chrono;
auto dt = year_month_day{year{year}, month{m}, day{d}};
}

The overall picture, as with <chrono>, is to reduce to a minimum the conversions you do between an untyped system (i.e. int) and a typed system where unit mixups are compile-time errors instead of run time errors:

void MyFunction(std::chrono::year y, std::chrono::month m, std::chrono::day d)
{
auto dt = y/m/d;
}

Those conversion points, like reinterpret_cast, are the error-prone parts of large code bases, for example:

void AnotherFunction(int y, int m, int d, int h, int M, int s)
{
...
g(y, M, d, h, m, s); // real easy to accidentally rearrange your units: run time error
...
}

vs

void AnotherFunction(year y, month m, day d, hours h, minutes M, seconds s)
{
...
g(y, M, d, h, m, s); // compile-time error
...
}

Like <chrono> my lib continues the tradition of strong type safety to catch as many errors as possible at compile-time. We force people into such dangerous conversion points today mostly with I/O, and so I’m trying to alleviate that with good I/O support.


> also the use of the slash operator is not that obvious as many parts of the world (and ISO8601) are more at ease with using - between the parts. Using an overloaded operator when the operation is not consistent with the usual meaning of the operator is also generally discouraged (No, I don't like path / path either). Maybe use + as it is neutral between different notations of dates and actually seems to be consistent: You are actually adding years, days and months to make a complete date. On the other hand doing this without first casting all the ints to their corresponding year, day and month types seems strange if you view it as addition.

+ and - have the wrong precedence:

2017_y+jan - 2015_y+sep

vs

2017_y/jan - 2015_y/sep == months{16}

and can easily lead to confusing expressions like this:

2015_y+sep+months{16}

vs

2015_y/sep + months{16} == 2017_y/jan

But if you really just don’t like the ’/‘ syntax, you’re not alone. Others feel the same way. Feel free to use the constructor syntax instead:

year_month{2017_y, jan} - year_month{2015_y, sep} == months{16}

or:

year_month{year{2017}, month{1}} - year_month{year{2015}, month{9}} == months{16}

instead of:

2017_y/jan - 2015_y/sep == months{16}

Just don’t tell everyone else that they _have_ to use the constructor syntax instead of the ‘/‘ syntax (unless you want to enforce that as a coding guideline on a project you’re in charge of). Because there’s lots of people who really like the ‘/‘ syntax. This lib provides both, with no performance, functionality or type-safety penalties for using either.

> A library which is more complicated than its task warrants risks not being used as the benefit may not outweigh the learning curve.

True, that’s why I’ve been field testing this lib for a couple of years now. The field testing is actually going pretty well. This library strives for a simple, self-consistent API. But simple doesn’t always mean “do it the way it has always been done in the past”, or even “don’t have too many types.” My experience from <chrono> is that many people said simple means “don’t use templates.”. And indeed, reading the <chrono> header is a good way to get terrified of that library. But the code people usually write with <chrono> is simple, readable and type-safe. Ditto with this library that extends <chrono> to calendars.

I’m leaning heavily on modern features like auto and user-defined literals which reduce the need to remember so many type names:

auto today = 8_d/jun/2017; // has type year_month_day
auto tomorrow = fri[2]/jun/2017; // has type year_month_weekday

Similarly, auto is of great help with <chrono> itself, in deducing the resultant type of “mixed-mode” arithmetic across time_points and durations of varying precisions.

> date::year_month_day date = date::year(2017)/2/4;
> date::year_month_weekday wdate = date::sys_days(date);
> return wdate.weekday();
>
> In wxWidgets, by contrast, you can do:
>
> wxDate date(2, 4, 2017);
> return date.GetDayOfWeek();

This will do it;

using namespace date::literals;
return date::weekday{feb/4/2017}; // or did you mean 2_d/apr/2017? so hard to tell when your only tool is int

> This level of simplicity is what we should strive for.

The wxWidgets code is actually unreadable to me. I can not read that and know what date you mean without breaking out the wxWidgets documentation. Such ambiguity is not possible in my date lib. Ambiguity is a compile-time error.

> Maybe the gist of what I'm trying to say here is that while the internal representation of a datetime may be configurable for optimal performance of certain operations, it would be good if the API of the classes was the same. This could be viewed as fully specialized instances of a template class with an enum template parameter:
>
> enum class datetime_representation {
> linear,
> year_month_day.
> year_month_weekday,
> year_week_day
> };
>
> template<datetime_representation = linear> class datetime {
> // Lots of methods to set and get the different parts, with equal behaviour but different performance.
> // Implementations of methods exist only in specializations
> };

I feel pretty strongly that this library should build on <chrono>. The central theme of this library is that time_point<system_clock, Duration> _is_ the “linear” datetime representation. Choose any precision you want, it is already in your <chrono> header. Everything else is just helpers for converting to and from calendars. And that very design decision is what will make I/O on file_time_type so convenient, functional and easy.

>
>
> Now back to more nitpicking:
>
> - The definition of months (the duration) is a bit scary as it relates to an "average month length" which means that it will not accurately represent the current month when used as a duration from a year's start. Is it really needed? I guess the same can be said for years, which tries to subsume the concept of leap years but where presumably the number of years since the start of another year can be off by one on Jan 1 or Dec 31.

months can be used both as a chrono::duration (when used with a chrono time_point), getting what you describe above. This is useful for doing chronological computations on physical processes that don’t respect human calendars (e.g. changing weather patterns or mammalian gestation periods). For example:

auto t = system_clock::now() + months{18}; // about 18 months from now

months can also be used as a calendrical duration when used with calendrical types and subtypes such as year_month_day and year_month. This is useful for doing calendrical computations when you require days of the month to match up, for example:

static_assert(2017_y/jan/3 + months{18} == jul/3/2018);

Both chronological and calendrical use cases have value, and are easy to do.

> - It seems strange to me that the 12/24 hour format is part of the time_of_day object. Shouldn't this be a formatting issue when converting to a string rather than a member of the object?

This may well disappear. time_of_day was born as a formatting aid, and predates the more generalized format function.

>
> - In general I don't like the idea of having different specializations with different API (such as time_of_day<minutes> having a minutes() function while time_of_day<hours> has not). Maybe minutes() could always exist but return 0 for a hour-only specialization? I understand it as your idea being to save implementation bits when not needed.

The different types have come in useful in the same manner that different types are useful for durations, instead of just say timespec. But the major client of time_of_day has in practice been format/parse, and so time_of_day may not need to be part of the API. That being said, some clients like to do their own formatting/parsing and time_of_day is useful for them.

>
> - format() should take its fmt parameter as a basic_string_view. [this is likely fixed in the standardization proposal]

Agreed. I haven’t done so in the lib as I get a fair number of requests to support older compilers. I have a small but growing list of such details that should change for the proposal (the next revision of which I need to finish within a week and a half).

>
> - Maybe it would be better if format's char types for the returned string and for the fmt parameter were decoupled. I write maybe as this decoupling would require callers to explicitly name the char type they want for the returned string. A name convention as used for basic_string itself could solve this, possibly.

Nope. That design has already irritated me enough when trying to write generic code with to_string/to_wstring.

>
> - The name format() could be a bit too generic, especially if the format("{}, is a nice {}", ...) proposal gets standardized.

Agreed, though overloading with the type-safe arguments may also address this issue.

> Instead I would suggest integrating the functionality intended here into a general format() method so that the formatting parameters are "strftime like" if the corresponding argument is of a date/time related type as proposed here. This would also work nicely with string translation, so that locale specific formats can be used if needed (without involving a locale parameter).

I anticipate a cooperative integration of these two libraries if they both find their way into the standardization process. I think there could be a strong synergy there.

>
> - A problem with parsing dates entered by users is that persons tend to not type dates in a coherent way. A remedy I have used is to allow multiple format strings which are tried in succession. This could fall outside a standard library but as the standard allows for two distinct output formats per locale it does not seem too far fetched to allow at least two on input. One possible way to define this would be to reserve one character, such as | as a separator in format strings when used for input, with the meaning that each | separated part is one complete format to be tried in order. Obviously it would be up to the programmer to place the formats in a suitable order to avoid ambiguities.

I can’t reserve “|”, but I could reserve “%|”. Or perhaps multiple parse fmt string arguments? Would not be difficult to write as an add-on. Here’s how I did this with two parsing formats:

https://stackoverflow.com/a/38839725/576911

Interesting idea, thanks.

Howard


signature.asc

Bengt Gustafsson

unread,
Jun 10, 2017, 5:58:52 PM6/10/17
to ISO C++ Standard - Future Proposals
> - It seems that the procedure for creating year_month_day may be a little overworked, involving several other data types with overloaded / operators and user defined literals. Also, if you don't "use" the date namespace and this gets standardized all those types will have long names like std::date::year, or maybe std::chrono::year if this functionality is integrated with chrono. As these names are the same as the usual names you would prefer to use for your variables it can get messy, and with a using statement it downright clashes and you have to give your variables cryptic names.

I think we may have to disagree on this. For me <chrono> seems very much too complicated for what it actually offers most users. I can see that to cater for every usage in the universe with utmost storage space efficiency you may have to make everything a template like this, but for a vast majority of uses using double for both time_point and duration would have sufficed. The precision is microseconds in a century which must be well beyond most needs. Alternatively a 64 bit integer representation similar to timespec with suitable functions and operators. As 64 bits gives nanosecond precision for 580 years there seems to be little need for anything else outside very specific physics areas.

While you with your intimate knowledge of <chrono> may find it easy to use as you have the whole history of developing it to fall back on, it surely is not easy to grasp when you start to use it. I think it was Bjarne who stated it so aptly: "Let's not confuse familiarity with simplicity". Everything can't be expected to be done in simple ways, but getting the current time can.

Much of the problem seems to be that the standard does not define: a) a way to know the resolution of a clock object, b) the epoch of a clock object as a date. Without this information it is very hard
to get a wall clock time out of a clock object and know what precision I have. Surely, I can do a duration cast to nanoseconds, but I don't know what precision I actually get. As for the epoch it seems that the only way is to retrieve the time_t (only available on system_clock) which is defined to be relative to 1970-01-01. So to get the number of seconds since this epoch with decimals the minimum is to do:

double GetCurrentTime() {
    system_clock::time_point n = system_clock::now();
    time_t st = system_clock::to_time_t(n);
    int nanoseconds = duration_cast<nanoseconds>(n) - duration_cast<nanoseconds>(seconds(st));
    return st + double(nanoseconds) * 0.000000001;
}

I still fail to call this simple. It took me 15 minutes to figure it out (I have not compiled and tested) and I now have 12 browser history entries (on cppreference.com) of where I have looked around to get this done. Then I have done very similar things before so I'm not a complete newbie to chrono. Still a few pain points: a) The result is of undefined precision, it may not contain any decimals at all for instance. b) I don't feel certain that the number of bits will be enough to convert all the seconds from the epoch to nanoseconds before subtracting them to get only the fraction of the last second. c) I still have to go to an age-old C type to get the time anchored to the wall clock time.

That's why I would much more like <chrono> to get some more convenience functions along the lines of GetCurrentTime() above than to only extend it with even more stuff akin to its current features.

 
>
> void MyFunction(int year, int month, int day)
> {
>     auto dt = std::date::year(year) / month / day;
> }


The current proposal is to put these into namespace chrono.  And my current C++11/14/17 usage of chrono almost always resorts to “using namespace std::chrono” at function scope as I’m already frustrated with:

    auto min = std::chrono::time_point_cast<std::chrono::minutes>(std::chrono::system_clock::now());

I write instead:

    using namespace std::chrono;
    auto min = time_point_cast<minutes>(system_clock::now());

Sure you can use the namespace but  then you end up with "using" most of the names you would want to use as identifiers in the context of working with wall clock time, such as months, year, seconds etc.


With just one component needing qualification as in your example, I might not bother with the using declaration.  But by the time I have to qualify two components, I am so done with that.

You can also write MyFunction like this:

    void MyFunction(int y, int m, int d)
    {
        using namespace std::chrono;
        auto dt = year_month_day{year{year}, month{m}, day{d}};
    }

The overall picture, as with <chrono>, is to reduce to a minimum the conversions you do between an untyped system (i.e. int) and a typed system where unit mixups are compile-time errors instead of run time errors:

    void MyFunction(std::chrono::year y, std::chrono::month m, std::chrono::day d)
    {
        auto dt = y/m/d;
    }

Ok, I see your point here. I was a bit thrown off track by your examples which only involved _literal_ integers rather than variables.


Like <chrono> my lib continues the tradition of strong type safety to catch as many errors as possible at compile-time.

As I made obvious above the problem with chrono _today_ is not so much that it has too many types but that too many things are left unspecified, which makes it hard to relate to reality. What you find yourself
searching for are non-existant methods to ask for epochs, resolutions etc. It is a well eastblished fact that you can waste a lot of time searching for things that don't exist... which I think is a major reason chrono is viewed as so hard to use. One typical example is that the file_time_type type is unrelated to everything else, except that it is on _some_ TrivialClock which in itself is potentially unrelated to time_t, which as of yet is the only way I have found to relate to a _known_ epoch.

 
 We force people into such dangerous conversion points today mostly with I/O, and so I’m trying to alleviate that with good I/O support.

I also dislike untyped data in general, so I will play along here.
 


> also the use of the slash operator is not that obvious as many parts of the world (and ISO8601) are more at ease with using - between the parts. Using an overloaded operator when the operation is not consistent with the usual meaning of the operator is also generally discouraged (No, I don't like path / path either). Maybe use + as it is neutral between different notations of dates and actually seems to be consistent: You are actually adding years, days and months to make a complete date. On the other hand doing this without first casting all the ints to their corresponding year, day and month types seems strange if you view it as addition.

+ and - have the wrong precedence:

I see that now. 


    2017_y+jan - 2015_y+sep

vs

    2017_y/jan - 2015_y/sep == months{16}

and can easily lead to confusing expressions like this:

    2015_y+sep+months{16}

vs

    2015_y/sep + months{16} == 2017_y/jan

But if you really just don’t like the ’/‘ syntax, you’re not alone.  Others feel the same way.  Feel free to use the constructor syntax instead:

    year_month{2017_y, jan} - year_month{2015_y, sep} == months{16}

or:

    year_month{year{2017}, month{1}} - year_month{year{2015}, month{9}} == months{16}

instead of:

    2017_y/jan - 2015_y/sep == months{16}

Just don’t tell everyone else that they _have_ to use the constructor syntax instead of the ‘/‘ syntax (unless you want to enforce that as a coding guideline on a project you’re in charge of).  Because there’s lots of people who really like the ‘/‘ syntax.  This lib provides both, with no performance, functionality or type-safety penalties for using either.  

> A library which is more complicated than its task warrants risks not being used as the benefit may not outweigh the learning curve.

True, that’s why I’ve been field testing this lib for a couple of years now.  The field testing is actually going pretty well.  This library strives for a simple, self-consistent API.  But simple doesn’t always mean “do it the way it has always been done in the past”, or even “don’t have too many types.”  My experience from <chrono> is that many people said simple means “don’t use templates.”.  And indeed, reading the <chrono> header is a good way to get terrified of that library.  But the code people usually write with <chrono> is simple, readable and type-safe.  

Not for all important use cases, as shown above.
 
Ditto with this library that extends <chrono> to calendars.

I’m leaning heavily on modern features like auto and user-defined literals which reduce the need to remember so many type names:

    auto today    = 8_d/jun/2017;     // has type year_month_day
    auto tomorrow = fri[2]/jun/2017;  // has type year_month_weekday 

Similarly, auto is of great help with <chrono> itself, in deducing the resultant type of “mixed-mode” arithmetic across time_points and durations of varying precisions.

> date::year_month_day date = date::year(2017)/2/4;
> date::year_month_weekday wdate = date::sys_days(date);
> return wdate.weekday();
>
> In wxWidgets, by contrast, you can do:
>
> wxDate date(2, 4, 2017);
> return date.GetDayOfWeek();

This will do it;

    using namespace date::literals;
    return date::weekday{feb/4/2017};  // or did you mean 2_d/apr/2017? so hard to tell when your only tool is int

Ok, I was unaware that you can construct a weekday, (and I assume  day, month, etc [upon reading date.h it seems I was assuming wrongly: only weekday has this type of constructor]) a complete date and it will "extract" the relevant information. That seems like a nice feature.

Can you also do:

    auto wd = date::day{system_clock::now()};  // Day in month right now

If not, why?



> This level of simplicity is what we should strive for.

The wxWidgets code is actually unreadable to me.  I can not read that and know what date you mean without breaking out the wxWidgets documentation.  Such ambiguity is not possible in my date lib.  Ambiguity is a compile-time error.

Unless you want to play hide and seek with the compiler you would still need to open the documentation if you are not already a pro. With wxDateTime you have all functionality related to dates and times in one class and thus the documentation is similarly close by when you need it. Or type a dot in the IDE if you forgot the exact method name and it will be shown in a dropdown list, most likely. (No 12 history entries in your browser).



I feel pretty strongly that this library should build on <chrono>.  The central theme of this library is that time_point<system_clock, Duration> _is_ the “linear” datetime representation.  Choose any precision you want, it is already in your <chrono> header.  Everything else is just helpers for converting to and from calendars.  And that very design decision is what will make I/O on file_time_type so convenient, functional and easy.
Lets hope for that. 

>
>
> Now back to more nitpicking:
>
> - The definition of months (the duration) is a bit scary as it relates to an "average month length" which means that it will not accurately represent the current month when used as a duration from a year's start. Is it really needed? I guess the same can be said for years, which tries to subsume the concept of leap years but where presumably the number of years since the start of another year can be off by one on Jan 1 or Dec 31.

months can be used both as a chrono::duration (when used with a chrono time_point), getting what you describe above.  This is useful for doing chronological computations on physical processes that don’t respect human calendars (e.g. changing weather patterns or mammalian gestation periods).  For example:

    auto t = system_clock::now() + months{18};  // about 18 months from now

months can also be used as a calendrical duration when used with calendrical types and subtypes such as year_month_day and year_month.  This is useful for doing calendrical computations when you require days of the month to match up, for example:

    static_assert(2017_y/jan/3 + months{18} == jul/3/2018);

Are you saying that this assert will not fail for any date pairs? Surely 2017_y/feb/1 + month(1) will not be march/1/2017 as february is so short?


Both chronological and calendrical use cases have value, and are easy to do.

> - It seems strange to me that the 12/24 hour format is part of the time_of_day object. Shouldn't this be a formatting issue when converting to a string rather than a member of the object?

This may well disappear.  time_of_day was born as a formatting aid, and predates the more generalized format function.
 
Why the conceptual difference between:

year
year_month
year_month_day

vs

hour
time_of_day

If this is historical reasons it seems that it should be unified. Adding hour_minute and hour_minute_second does not seem to be too much of an addition to the already large type menagerie.

I still also think that there needs to be a datetime thing with fields based representation as you want to be able to pass around complete "time-points" in fields based representation. Maybe a template type like so:

template<class D, class T> class date_time {
    D date_part;
    T time_part;
    date_time(system_clock::time_point);
};

Obviously the D part has to be complete, but you had three such variants, right? For the T part you could have any of the types including hours, as it is ok to loose precision on the right, but not in the middle. However, that could maybe be ignored, allowing any type in this position. 

Is it possible to construct a complete date from year + week number + day within week? If so, what type do I get?


>
> - In general I don't like the idea of having different specializations with different API (such as time_of_day<minutes> having a minutes() function while time_of_day<hours> has not). Maybe minutes() could always exist but return 0 for a hour-only specialization? I understand it as your idea being to save implementation bits when not needed.

The different types have come in useful in the same manner that different types are useful for durations, instead of just say timespec.  But the major client of time_of_day has in practice been format/parse, and so time_of_day may not need to be part of the API.  That being said, some clients like to do their own formatting/parsing and time_of_day is useful for them.

I still think that classes with different APIs should have different names. You seem to adhere to this for dates, why not for times?


>
> - format() should take its fmt parameter as a basic_string_view. [this is likely fixed in the standardization proposal]

Agreed.  I haven’t done so in the lib as I get a fair number of requests to support older compilers.  I have a small but growing list of such details that should change for the proposal (the next revision of which I need to finish within a week and a half).
ok 

>
> - Maybe it would be better if format's char types for the returned string and for the fmt parameter were decoupled. I write maybe as this decoupling would require callers to explicitly name the char type they want for the returned string. A name convention as used for basic_string itself could solve this, possibly.

Nope.  That design has already irritated me enough when trying to write generic code with to_string/to_wstring.

Ok, that's fine then, at least as long as you have a string literal as formatting parameter you can use the L to control whether to get a string or wstring. It is less nice with an incoming basic_string_view though, but on the other hand most programs do tend to use one string width or the other more or less throughout.

>
> - The name format() could be a bit too generic, especially if the format("{}, is a nice {}", ...) proposal gets standardized.

Agreed, though overloading with the type-safe arguments may also address this issue.

> Instead I would suggest integrating the functionality intended here into a general format() method so that the formatting parameters are "strftime like" if the corresponding argument is of a date/time related type as proposed here. This would also work nicely with string translation, so that locale specific formats can be used if needed (without involving a locale parameter).

I anticipate a cooperative integration of these two libraries if they both find their way into the standardization process.  I think there could be a strong synergy there.
Yes, just let it not be forgotten as the dreaded is_const_t thing, where template alias and type_info were both introduced in C++11 and the _t still ended up on the alias because of proposal timing vissues (or so I suppose at least). I guess that's one drawback with the process where proposals are considered mostly one by one.

In the case of the date library there is also a possible prospect of a general units library showing up, which would make it a hard call where to put some of the types, such as hour and year. Maybe the wisest thing in that regard is to use type aliasing to make the same types available in two namespaces (assuming there will be a new units namespace).


>
> - A problem with parsing dates entered by users is that persons tend to not type dates in a coherent way. A remedy I have used is to allow multiple format strings which are tried in succession. This could fall outside a standard library but as the standard allows for two distinct output formats per locale it does not seem too far fetched to allow at least two on input. One possible way to define this would be to reserve one character, such as | as a separator in format strings when used for input, with the meaning that each | separated part is one complete format to be tried in order. Obviously it would be up to the programmer to place the formats in a suitable order to avoid ambiguities.

I can’t reserve “|”, but I could reserve “%|”.  Or perhaps multiple parse fmt string arguments?  Would not be difficult to write as an add-on.  Here’s how I did this with two parsing formats:
I think you can reserve | by applying the rule that || or possibly %| means one vertical bar. Or am I missing something?

A typical format() call would be something like this, from a dir listing function:

format("File: {}, last written: {:%Y %H:%M}", fileName, fileTime)

The leading : before the datetime formatting is the delimiter between the identifier (empty) and the format. So any characters except } would be allowed in the fornat string itself.

Question: I assume that this formatting would work regardless of the type of fileTime, i.e. time_point, year_month_day or year_month_weekday would work. If so, i.e. if a field representation can be converted implicitly to another field representation (possibly via a time_point or time_t type representation) is all of this field representation only for performance, and only when you manually match the type to the required fields. Or is it just for type safety when assembling a date or time from its fields. If so why have more than one representation of a complete date? Indeed, why have any at all beyond system_clock::time_point?

 


https://stackoverflow.com/a/38839725/576911
The length of this function sort of proves the value of having this feature built in.

Interesting idea, thanks.

Howard


T. C.

unread,
Jun 10, 2017, 6:41:38 PM6/10/17
to ISO C++ Standard - Future Proposals


On Saturday, June 10, 2017 at 5:58:52 PM UTC-4, Bengt Gustafsson wrote:

double GetCurrentTime() {
    system_clock::time_point n = system_clock::now();
    time_t st = system_clock::to_time_t(n);
    int nanoseconds = duration_cast<nanoseconds>(n) - duration_cast<nanoseconds>(seconds(st));
    return st + double(nanoseconds) * 0.000000001;
}

system_clock de facto has the same epoch as time_t. If you are willing to rely on that:

double GetCurrentTime(){
   
return duration<double>(system_clock::now().time_since_epoch()).count();
}


Otherwise,

double GetCurrentTime(){
   
return duration<double>(system_clock::now() - system_clock::from_time_t(0)).count();
}

Nicol Bolas

unread,
Jun 10, 2017, 10:08:12 PM6/10/17
to ISO C++ Standard - Future Proposals
On Saturday, June 10, 2017 at 5:58:52 PM UTC-4, Bengt Gustafsson wrote:
> - It seems that the procedure for creating year_month_day may be a little overworked, involving several other data types with overloaded / operators and user defined literals. Also, if you don't "use" the date namespace and this gets standardized all those types will have long names like std::date::year, or maybe std::chrono::year if this functionality is integrated with chrono. As these names are the same as the usual names you would prefer to use for your variables it can get messy, and with a using statement it downright clashes and you have to give your variables cryptic names.

I think we may have to disagree on this. For me <chrono> seems very much too complicated for what it actually offers most users. I can see that to cater for every usage in the universe with utmost storage space efficiency you may have to make everything a template like this, but for a vast majority of uses using double for both time_point and duration would have sufficed. The precision is microseconds in a century which must be well beyond most needs. Alternatively a 64 bit integer representation similar to timespec with suitable functions and operators. As 64 bits gives nanosecond precision for 580 years there seems to be little need for anything else outside very specific physics areas.

While you with your intimate knowledge of <chrono> may find it easy to use as you have the whole history of developing it to fall back on, it surely is not easy to grasp when you start to use it. I think it was Bjarne who stated it so aptly: "Let's not confuse familiarity with simplicity". Everything can't be expected to be done in simple ways, but getting the current time can.

The problem is this: what does "getting the current time" actually mean? Chrono is the first time library that actually tells you what the "current time" actually means. All of the others clock libraries I've seen are based on a bunch of assumptions: time is a floating-point value in units of seconds, time is relative to some assumed base value, etc.

Chrono is a lot like the STL parts of the standard library. STL is not simple. But once you are familiar with its concepts, it's quite easy to use. Just like Chrono.

So long as you're not fighting the library, so long as you're approaching the library on its terms rather than your own, it works quite well.

Much of the problem seems to be that the standard does not define: a) a way to know the resolution of a clock object, b) the epoch of a clock object as a date. Without this information it is very hard
to get a wall clock time out of a clock object and know what precision I have. Surely, I can do a duration cast to nanoseconds, but I don't know what precision I actually get.

The result of `now` is a `time_point`, which contains the `period` typedef, which is a `ratio` describing the tick period of the clock. AKA: the clock's precision. Also, the clock itself has a `period` in it.

So yes, you do know what precision you actually get. If you want to get the number of ticks, you can use the `period` type to get it from the `time_point`.

I don't know what you mean by the difficulty of getting a "wall clock time". `system_clock::now` returns a wall clock time. If your issue is the difficulty in presenting that time to the user via text of some sort, well that's something that Howard's Date library handles, as he has described.

At the end of the day, Chrono is not a complete solution for all time needs. But it was never intended to be; it was always just the first step. The Date library is a much more complete solution, using Chrono as the foundation.

It's also not clear how your suggested `GetCurrentTime` solves these problems. It doesn't provide precision information, which would presumably be implementation-specific. Nor does it specify what the epoch is (the documentation might).

As for the epoch it seems that the only way is to retrieve the time_t (only available on system_clock) which is defined to be relative to 1970-01-01. So to get the number of seconds since this epoch with decimals

Here's a good question: why do you want to do that?

I can imagine things that one might do with such a number. But these would be means to an end, steps in some process. `time_point` contains 100% of the information that this floating-point value would contain. So there is no reason that the process which would consume the result of `GetCurrentTime` couldn't just consume a `time_point`.

<snip>

Ditto with this library that extends <chrono> to calendars.

I’m leaning heavily on modern features like auto and user-defined literals which reduce the need to remember so many type names:

    auto today    = 8_d/jun/2017;     // has type year_month_day
    auto tomorrow = fri[2]/jun/2017;  // has type year_month_weekday 

Similarly, auto is of great help with <chrono> itself, in deducing the resultant type of “mixed-mode” arithmetic across time_points and durations of varying precisions.

> date::year_month_day date = date::year(2017)/2/4;
> date::year_month_weekday wdate = date::sys_days(date);
> return wdate.weekday();
>
> In wxWidgets, by contrast, you can do:
>
> wxDate date(2, 4, 2017);
> return date.GetDayOfWeek();

This will do it;

    using namespace date::literals;
    return date::weekday{feb/4/2017};  // or did you mean 2_d/apr/2017? so hard to tell when your only tool is int

Ok, I was unaware that you can construct a weekday, (and I assume  day, month, etc [upon reading date.h it seems I was assuming wrongly: only weekday has this type of constructor]) a complete date and it will "extract" the relevant information. That seems like a nice feature.

Can you also do:

    auto wd = date::day{system_clock::now()};  // Day in month right now

If not, why?

I don't know exactly what the current library will do, but I don't think that code ought to work. The reason being that you could mean a lot of things:

1) Days since epoch.
2) Days since the start of the year.
3) Days since the start of the month.

It would be far better to make that be a compile error and require that the user convert the time point explicitly to a date before extracting a day or month from it.

> This level of simplicity is what we should strive for.

The wxWidgets code is actually unreadable to me.  I can not read that and know what date you mean without breaking out the wxWidgets documentation.  Such ambiguity is not possible in my date lib.  Ambiguity is a compile-time error.

Unless you want to play hide and seek with the compiler you would still need to open the documentation if you are not already a pro. With wxDateTime you have all functionality related to dates and times in one class and thus the documentation is similarly close by when you need it. Or type a dot in the IDE if you forgot the exact method name and it will be shown in a dropdown list, most likely. (No 12 history entries in your browser).

The difference is this: With Howard's Date library, you have to look up the function signatures. With wxDateTime, you have to look up what the functions do. With the former, a cheat-sheet of the various functions is sufficient. With the latter, you need not just the signatures, but some added textual information to tell you what those parameters mean.

I feel pretty strongly that this library should build on <chrono>.  The central theme of this library is that time_point<system_clock, Duration> _is_ the “linear” datetime representation.  Choose any precision you want, it is already in your <chrono> header.  Everything else is just helpers for converting to and from calendars.  And that very design decision is what will make I/O on file_time_type so convenient, functional and easy.
Lets hope for that. 

Hope? Hope is not a thing you need to use here. This library exists. If you want to see if it makes things "convenient, functional, and easy", just download it and have a go.

>
>
> Now back to more nitpicking:
>
> - The definition of months (the duration) is a bit scary as it relates to an "average month length" which means that it will not accurately represent the current month when used as a duration from a year's start. Is it really needed? I guess the same can be said for years, which tries to subsume the concept of leap years but where presumably the number of years since the start of another year can be off by one on Jan 1 or Dec 31.

months can be used both as a chrono::duration (when used with a chrono time_point), getting what you describe above.  This is useful for doing chronological computations on physical processes that don’t respect human calendars (e.g. changing weather patterns or mammalian gestation periods).  For example:

    auto t = system_clock::now() + months{18};  // about 18 months from now

months can also be used as a calendrical duration when used with calendrical types and subtypes such as year_month_day and year_month.  This is useful for doing calendrical computations when you require days of the month to match up, for example:

    static_assert(2017_y/jan/3 + months{18} == jul/3/2018);

Are you saying that this assert will not fail for any date pairs? Surely 2017_y/feb/1 + month(1) will not be march/1/2017 as february is so short?

Yes, that is exactly what he is saying. If you add one month to a date, it becomes the same day of the next month. A "month" is not a synonym for 30 days or something; it is a month, a typed quantity. So adding a month to a date cause that date to shift forward one month.

Or the closest thing to it. If you're on March 31, and you add a month, you either get April 30 or May 1; I forget which. But if you add two months, you always get May 31.

The library is designed to do exactly what you say. If you wanted to add 30 days, you would add `day(30)`.

Is it possible to construct a complete date from year + week number + day within week? If so, what type do I get?

Yes, there's a type for that: `year_weeknum_weekday`. It is implicitly convertible to `sys_days`, so it can be explicitly converted to/from the other date types. Any other date type. So you can convert from week-numbered dates to the Julian calandar, then back to Year/Month/Day, and any other `sys_days`-compatible type someone wants to create.

Howard Hinnant

unread,
Jun 11, 2017, 9:19:32 AM6/11/17
to std-pr...@isocpp.org
Hopefully helpful resources:

1. A <chrono> tutorial: This is a much easier way to learn about the C++11/14/17 <chrono> library than reading its specification:

https://www.youtube.com/watch?v=P32hvk8b13M

2. An introduction to date.h. Just a couple of things have changed in the 2 years since this video was made:

A. day_point has been renamed to sys_days.
B. The unofficial announcement by Microsoft in the Q/A period that they will change the epoch of their system_clock has been unofficially reversed: They will not change their epoch, and thus it continues to be true that the de-facto standard is system_clock measures Unix Time (https://en.wikipedia.org/wiki/Unix_time). I hope to standardize this de facto standard in the future, and Microsoft has encouraged me to do so.
C. The I/O for types in date.h has been greatly improved with the parse/format functions.

https://www.youtube.com/watch?v=tzyGjOm8AKo

3. Handling time zones and leap seconds:

https://www.youtube.com/watch?v=Vwd3pduVGKY

I recommend watching the videos in this order. Each talk refers to an independent library that builds on top of the previous talk. The first talk is already in your std::lib. The second two talks represent a library which is being proposed, has been ported to Linux, macOS and Windows, and is freely available as open source here:

https://github.com/HowardHinnant/date

The link above also includes a few miscellaneous calendars which interoperate with the above libraries, but are not being proposed for standardization: iso_week.h, julian.h and islamic.h. So (for example), you can easily translate a local datetime specified in the Julian calendar in the Europe/Moscow timezone from the 1800’s into an equivalent local datetime in the Gregorian calendar in the Europe/Paris timezone.

file_time_type can be handled in exactly the same way the third library handles utc_time, tai_time and gps_time, all of which have I/O via the parse/format facilities, and are inter-convertible via the namespace scope to_XXX_time free functions.

Howard

signature.asc

Howard Hinnant

unread,
Jun 16, 2017, 6:31:23 PM6/16/17
to std-pr...@isocpp.org
I’ve updated my proposal to include wording for I/O on file_time_type:

https://howardhinnant.github.io/date/d0355r3.html

Comments welcome.

Howard

signature.asc
Reply all
Reply to author
Forward
0 new messages