问题
Is there a definition somewhere in the standard namespace that sets forward:
- Months in a year
- Days in a week
- Hours in a day
- Minutes in an hour
- Seconds in a minute
The struct tm has contains member variables that must be in these ranges, but I can't find the defined limits anywhere.
I'm not even sure if there are locales defined where these wouldn't match the conventional set (12/7/24/60/60).
Even if there aren't potential users with other range limits, I'd sure like to use a define from the standard namespace rather than arbitrarily defining my own.
EDIT:
It looks like I'm not the first to ask for such a thing: http://david.tribble.com/text/c0xcalendar.html
I notice in this proposal there is mention of the struct calendarinfo
which does exactly what I'm looking for.
It looks like the last change on this was 2009. I guess nothing's happened since then? I guess that also means this stuff is not readily available to me?
More info, boost::locale::calendar::maximum seems to accomplish exactly what I'm looking for. I can't use Boost, but I'm certain that the code in Boost is the defacto standard on how to come up with these limits. Unfortuantely I can't seem to get at the implementation of maximum
. Maybe someone else here knows how?
回答1:
The C and C++ standards say absolutely nothing about any calendar except the Gregorian calendar, and not a lot about that one.
1.
Months in a year
The only thing you'll find is a comment in the C standard beside the tm_mon
member of tm
:
int tm_mon; // months since January -- [0, 11]
Well, almost only. You'll also find %b
and %B
specifiers in strftime
which correspond to the current locale's abbreviated and full month names corresponding to tm_mon
.
2.
Days in a week
You've got:
int tm_wday; // days since Sunday -- [0, 6]
and %a
, %A
for strftime
.
3.
Hours in a day
You've got:
int tm_hour; // hours since midnight -- [0, 23]
There's also strftime
specifiers, a few of which are sensitive to the current locale.
4.
Minutes in an hour
int tm_min; // minutes after the hour -- [0, 59]
Also in this case you've got some help from the new C++11 <chrono>
library:
std::cout << std::chrono::hours{1}/std::chrono::minutes{1} << '\n';
This will portably (and consistently) output 60
. If your compiler supports constexpr
and you are worried about efficiency, this quantity can be a compile-time integral constant:
constexpr auto minutes_per_hour = std::chrono::hours{1}/std::chrono::minutes{1};
The type of minutes_per_hour
is guaranteed to be signed integral and at least 29 bits.
5.
Seconds in a minute
The C spec is a little interesting on this one:
int tm_sec; // seconds after the minutes -- [0, 60]
The range is not documented as [0, 59]
so as to allow for the addition of a positive leap second. That being said, no OS that I'm aware of actually implements an accurate accounting of leap seconds. Everyone appears to follow Unix Time which tracks UTC except ignoring leap seconds. When a leap second occurs, all OS's I'm aware of simply treat it as an ordinary clock correction. Google famously treats it as a smear of corrections over some window of time.
Additionally you can consistently and portably write:
std::cout << std::chrono::minutes{1}/std::chrono::seconds{1} << '\n';
and get 60
.
While not defined by the C or C++ standards, every OS appears to be measuring time since New Years 1970 (neglecting leap seconds). In C++11 this quantity is available via:
auto tp = std::chrono::system_clock::now();
where tp
will have type std::chrono::system_clock::time_point
. This time_point
has an unspecified precision (fortnights, seconds, femtoseconds, whatever). The precision is programmatically discoverable at compile time.
In case it is helpful, this link contains code which can translate tp
into year/month/day hour:minute:second and even fractions of a second (and vice-versa). Oh, day-of-week if you want it too (and several other calendrical tricks). This public domain code depends on the non-standard but de-facto portable epoch of New Years 1970. It could easily be adopted to other epochs if the need ever arises.
回答2:
I believe that I can conclusively say that there is not a define for any of these numbers in namespace std
.
I am basing this statement on the fact that Boost's gregorian.cpp hard codes all of these values in get_value
If you do have Boost, you are able to leverage get_value
to find "Months in a year", "Days in a week", and "Hours in a day" by doing:
locale::global(boost::locale::generator()(""));
cout.imbue(locale());
boost::locale::date_time now;
cout << "Months in a year: " << now.maximum(boost::locale::period::period_type(boost::locale::period::marks::month)) << endl;
cout << "Days in a week: " << now.maximum(boost::locale::period::period_type(boost::locale::period::marks::day_of_week)) << endl;
cout << "Hours in a day: " << now.maximum(boost::locale::period::period_type(boost::locale::period::marks::hour)) << endl;
Which outputs:
Months in a year: 11
Days in a week: 7
Hours in a day: 23
Note the 0-based months and hours and the 1-based days. This is consistant with put_time's %u
flag.
If you do not have Boost you'll need to define these yourself.
Howard Hinnant's solution for finding "Minutes in an hour" and "Seconds in a minute" is actually based in defines from namespace std
:
cout << "Minutes in an hour: " << chrono::hours{1} / chrono::minutes{1} << endl;
cout << "Seconds in a minute: " << chrono::minutes{1} / chrono::seconds{1} << endl;
Which outputs:
Minutes in an hour: 60
Seconds in a minute: 60
来源:https://stackoverflow.com/questions/28548634/is-there-an-upper-bound-in-my-locale-for-time-related-information