HowardHinnant / date

A date and time library based on the C++11/14/17 <chrono> header
Other
3.11k stars 673 forks source link

Question: how to extract milliseconds (or nanoseconds) from a hh_mm_ss #549

Open CKD4XXH opened 4 years ago

CKD4XXH commented 4 years ago

By trial and error, i figured out something, but please verify. Is this the proper way to break down a time point? Extract fields, especially the milliseconds (subseconds). Into plain old integer numbers.

$ cat tt.cc
#include "date/tz.h"
#include <iostream>

int main() {
   using namespace date;
   using namespace std::chrono;

   auto z = make_zoned(current_zone(), system_clock::now());
   auto l = z.get_local_time();
   auto mill = floor<milliseconds>(l);
   auto day = floor<days>(mill);

   year_month_day ymd{day};
   hh_mm_ss<milliseconds> hmsms{mill - day}; // Or "make_time(mill - day)"?

   std::cout << "year month day:\n";
   std::cout << (ymd.year() - 0_y).count() << '\n';
   std::cout << (ymd.month() - December).count() << '\n'; // 1-based. 1==January
   std::cout << (ymd.day() - 0_d).count() << '\n'; // Or "static_cast<int>(ymd.day())" ?

   std::cout << "hours minutes seconds weekday:\n";
   std::cout << hmsms.hours().count() << '\n';
   std::cout << hmsms.minutes().count() << '\n';
   std::cout << hmsms.seconds().count() << '\n';
   std::cout << (weekday{day} - Sunday).count() << '\n'; // 1==Monday

   std::cout << "isdst:\n";
   std::cout << std::boolalpha << (z.get_info().save != minutes{0}) << '\n';

   std::cout << "subseconds:\n";
   std::cout << hmsms.subseconds().count() << '\n';
}

$ g++ -Wall -Werror -std=c++14 -I/home/xx/date/include -DINSTALL=/home/xx -DHAS_REMOTE_API=0 -O0 -o tt tt.cc /home/xx/date/src/tz.cpp -lpthread

$ ./tt
year month day:
2020
3
4
hours minutes seconds weekday:
17
41
45
3
isdst:
false
subseconds:
478
HowardHinnant commented 4 years ago

Yes, but there all alternatives you may prefer. I'll intersperse them below:

   auto z = make_zoned(current_zone(), system_clock::now());
   auto l = z.get_local_time();
   auto mill = floor<milliseconds>(l);
   auto day = floor<days>(mill);

or:

   auto day = floor<days>(l);

Either works as well as the other.

   year_month_day ymd{day};
   hh_mm_ss<milliseconds> hmsms{mill - day}; // Or "make_time(mill - day)"?

Yes. In C++17 and higher you can simply say hh_mm_ss hmsms{mill - day};. This is because the language feature CTAD will deduce the <milliseconds> template parameter. And for this reason, make_time, though it still exists, is considered deprecated and was purposefully not put into C++20.

   std::cout << "year month day:\n";
   std::cout << (ymd.year() - 0_y).count() << '\n';
   std::cout << (ymd.month() - December).count() << '\n'; // 1-based. 1==January
   std::cout << (ymd.day() - 0_d).count() << '\n'; // Or "static_cast<int>(ymd.day())" ?

The above could also look like:

   std::cout << int{ymd.year()} << '\n';
   std::cout << unsigned{ymd.month()} << '\n'; // 1-based. 1==January
   std::cout << unsigned{ymd.day()} << '\n'; // Or "static_cast<int>(ymd.day())" ? Yes, this too.

Also each of the above can be streamed out directly:

   std::cout << ymd.year() << '\n';
   std::cout << ymd.month() << '\n';
   std::cout << ymd.day() << '\n';

which outputs:

2020
Mar
04
   std::cout << "hours minutes seconds weekday:\n";
   std::cout << hmsms.hours().count() << '\n';
   std::cout << hmsms.minutes().count() << '\n';
   std::cout << hmsms.seconds().count() << '\n';

The above can also be streamed out direclty:

   std::cout << hmsms.hours() << '\n';
   std::cout << hmsms.minutes() << '\n';
   std::cout << hmsms.seconds() << '\n';

resulting in:

12h
22min
42s
   std::cout << (weekday{day} - Sunday).count() << '\n'; // 1==Monday

This can also be done with:

   std::cout << weekday{day}.c_encoding() << '\n'; // 1==Monday

And there is also an .iso_encoding() which maps Sunday to 7.

   std::cout << "isdst:\n";
   std::cout << std::boolalpha << (z.get_info().save != minutes{0}) << '\n';

In C++14 and later you can use 0min in place of minutes{0}.

   std::cout << "subseconds:\n";
   std::cout << hmsms.subseconds().count() << '\n';

Without the .count() above:

   std::cout << hmsms.subseconds() << '\n';

Then you also get the units:

410ms
CKD4XXH commented 4 years ago

A sub-question:

How to qualify things like 0min or 0_y?

std::chrono::minutes{0} works.

std::chrono::0min does not work. ("expected unqualified-id before numeric constant")

HowardHinnant commented 4 years ago

I don't know of a way to qualify user defined literals. For this reason they are often put in namespaces that only define literals so that you can have a using directive that doesn't pull that much into scope.

For example _y lives in namespace date::literals. You can access it with either using namespace date (which pulls in a lot), or using namespace date::literals (which only pulls in other date literals.

For min the most limited scope is namespace std::chrono_literals. The next largest scope is namespace std::literals (which pulls in all std literals, not just the chrono ones). And you also get them with the huge scope of namespace std.

If you're in a context where you really don't want a using (such as namespace scope in a header), then std::chrono::minutes{0} and date::year{0} is the way to go.

CKD4XXH commented 4 years ago

What is the type of date::make_zoned?

How to pass, for example, a zoned time to a funtion (as parameter)?

In my current environment

date::zoned_time<std::common_type_t<std::chrono::nanoseconds, std::chrono::seconds>> now = date::make_zoned(date::current_zone(), std::chrono::system_clock::now());

seems working, but I am afraid that in other environment, where system_clock has other resolution than nanoseconds, it will fail.

(This question may be independent of date library. I apologize for that.)

HowardHinnant commented 4 years ago

No need to apologize. I'm happy to help.

date::make_zoned should be considered deprecated. It's only function is to deduce the template arguments for zoned_time<Duration>, which is what make_zoned returns. In C++17 and later, the constructor arguments to zoned_time can be automatically reduced.

For example:

auto zt = make_zoned(current_zone(), system_clock::now());
zoned_time<system_clock::duration> zt{current_zone(), system_clock::now()};
zoned_time zt{current_zone(), system_clock::now()};

All three lines above are equivalent. The third one requires C++17 or higher.

And that was a long-winded way of saying that zoned_time<system_clock::duration> is the type you're looking for. The type of system_clock::duration will vary with platform:

CKD4XXH commented 4 years ago

Thank you.

(Issue can be closed.)