I've been trying out Howard's date library. There are many examples for getting the number of seconds since midnight of a certain day, but not since the start of week to which the day belongs. Following the examples I produced this code:
using timepoint_t = std::chrono::time_point<
std::chrono::system_clock,
std::chrono::nanoseconds
>;
timepoint_t tp; // input
auto dp = std::chrono::floor<date::weeks>(tp); // output, should point to start of week?
But the results are wrong. For example, tp:
2018-10-25T11:26:00.397836134Z
will produce dp:
2018-10-25T00:00:00.000000000Z
which is midnight of the 25th, not midnight of the 21th. What is the most direct way to produce the correct output?
I've upvoted user1095108's self answer as it gets the correct answer. But I wanted to add more information that wouldn't all fit in a comment.
The "start of the week" is not universally agreed upon. Some countries obverse Monday as the start of the week, as does the ISO standard. And other countries observe Sunday as the start of the week. This library tries to stay as neutral as possible on this issue.
Even once you nail down which weekday you're aiming for, you also need to nail down where on the planet you want to consider when finding the second the week starts.
Judging from your code, I'm going to assume:
The week starts on Sunday.
We want to find the start of the week according to UTC.
For the following code, assume:
using namespace date;
using namespace std::chrono;
just to keep things from getting overly verbose.
The first thing I would do is transform the input into a time_point with a precision of seconds:
auto tp = floor<seconds>(system_clock::now());
Then, in order to do weekday computations, such as finding the weekday of the input tp, one is going to need another time_point with a precision of days:
auto dp = floor<days>(tp);
You can construct a weekday directly from a day-precision time_point (dp), instead of going through the more expensive computation of forming a year_month_weekday:
weekday{dp}
then (as you show) subtract off the number of days since the start of the week:
dp -= weekday{dp} - Sunday;
Now you have two time_points: Your input, and the start of the week. You can simply subtract them to get the number of seconds into the week:
std::cout << tp - dp << '\n';
Now the reason that floor<weeks>(tp) doesn't give you the desired result is that system_clock is Unix Time. This measure counts time since 1970-01-01 00:00:00 UTC (neglecting leap seconds). And 1970-01-01 was a Thursday. So truncating a system_clock::time_point to a precision of weeks defines the "start of the week" as Thursday.
For even more fun, compute the number of seconds since the start of the week in a local time, which may involve daylight savings adjustments. In this case the computation has more than one right answer: Do you count physical seconds, which makes some weeks not have exactly 604800s, or do you count "calendrical seconds"? For example is Nov/4/2018 1:00 EST (America/New_York) 1 or 2 hours into the week? Once you decide which is the right answer, this library can compute it.
Most of Howard's calendar calculations seem to be based off of date:days. So you're out of luck with date::weeks, it seems. One possible answer is:
auto dp(std::chrono::floor<date::days>(tp));
dp -= date::year_month_weekday(dp).weekday() - date::Sunday;
The number of seconds is then obtained as the customary std::chrono::duration_cast<std::chrono::seconds>(tp - dp).count(). Could it be the lack of support for date::weeks in std::chrono, that the date::weeks cast does not work? Howard's date::floor() gives the same output as std::chrono::floor() though.
Related
How can I add months to a chrono::system_clock::time_point value?
Thank you!
Overview
This is a very interesting question with several answers. The "correct" answer is something you must decide for your specific application.
With months, you can choose to do either chronological computations or calendrical computations. A chronological computation deals with regular units of time points and time durations, such as hours, minutes and seconds. A calendrical computation deals with irregular calendars that mainly serve to give days memorable names.
The Chronological Computation
If the question is about some physical process months in the future, physics doesn't care that different months have different lengths, and so a chronological computation is sufficient:
The baby is due in 9 months.
What will the weather be like here 6 months from now?
In order to model these things, it may be sufficient to work in terms of the average month. One can create a std::chrono::duration that has precisely the length of an average Gregorian (civil) month. It is easiest to do this by defining a series of durations starting with days:
days is 24 hours:
using days = std::chrono::duration
<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
years is 365.2425 days, or 146097/400 days:
using years = std::chrono::duration
<int, std::ratio_multiply<std::ratio<146097, 400>, days::period>>;
And finally months is 1/12 of years:
using months = std::chrono::duration
<int, std::ratio_divide<years::period, std::ratio<12>>>;
Now you can easily compute 8 months from now:
auto t = system_clock::now() + months{8};
Important note: This computation does not preserve the time of day, or even the day of the month.
The Calendrical Computation
It is also possible to add months while preserving time of day and day of month. Such computations are calendrical computations as opposed to chronological computations.
After choosing a calendar (such as the Gregorian (civil) calendar, the Julian calendar, or perhaps the Islamic, Coptic or Ethiopic calendars — they all have months, but they are not all the same months), the process is:
Convert the system_clock::time_point to the calendar.
Perform the months computation in the calendrical system.
Convert the new calendar time back into system_clock::time_point.
You can use Howard Hinnant's free, open-source date/time library to do this for a few calendars. Here is what it looks like for the civil calendar:
#include "date.h"
int
main()
{
using namespace date;
using namespace std::chrono;
// Get the current time
auto now = system_clock::now();
// Get a days-precision chrono::time_point
auto sd = floor<days>(now);
// Record the time of day
auto time_of_day = now - sd;
// Convert to a y/m/d calendar data structure
year_month_day ymd = sd;
// Add the months
ymd += months{8};
// Add some policy for overflowing the day-of-month if desired
if (!ymd.ok())
ymd = ymd.year()/ymd.month()/last;
// Convert back to system_clock::time_point
system_clock::time_point later = sys_days{ymd} + time_of_day;
}
For grins I just ran this, and compared it with now + months{8} and got:
now is 2017-03-25 15:17:14.467080
later is 2017-11-25 15:17:14.467080 // calendrical computation
now + months{8} is 2017-11-24 03:10:02.467080 // chronological computation
This gives a rough "feel" for how the calendrical computation differs from the chronological computation. The latter is perfectly accurate on average; it just has a deviation from the calendrical on the order of a few days. And sometimes the simpler (latter) solution is close enough, and sometimes it is not. Only you can answer that question.
The Calendrical Computation — Now with timezones
Finally, you might want to perform your calendrical computation in a specific timezone. The previous computation was UTC.
Side note: system_clock is not specified to be UTC, but the de facto standard is that it is Unix Time which is a very close approximation to UTC.
You can use Howard Hinnant's free, open-source timezone library to do this computation. This is an extension of the previously mentioned datetime library.
The code is very similar, you just need to convert to local time from UTC, then to a local calendar, do the computation then back to local time, and finally back to system_clock::time_point (UTC):
#include "tz.h"
int
main()
{
using namespace date;
using namespace std::chrono;
// Get the current local time
auto lt = make_zoned(current_zone(), system_clock::now());
// Get a days-precision chrono::time_point
auto ld = floor<days>(lt.get_local_time());
// Record the local time of day
auto time_of_day = lt.get_local_time() - ld;
// Convert to a y/m/d calendar data structure
year_month_day ymd{ld};
// Add the months
ymd += months{8};
// Add some policy for overflowing the day-of-month if desired
if (!ymd.ok())
ymd = ymd.year()/ymd.month()/last;
// Convert back to local time
lt = local_days{ymd} + time_of_day;
// Convert back to system_clock::time_point
auto later = lt.get_sys_time();
}
Updating our results I get:
now is 2017-03-25 15:17:14.467080
later is 2017-11-25 15:17:14.467080 // calendrical: UTC
later is 2017-11-25 16:17:14.467080 // calendrical: America/New_York
now + months{8} is 2017-11-24 03:10:02.467080 // chronological computation
The time is an hour later (UTC) because I preserved the local time (11:17am) but the computation started in daylight saving time, and ended in standard time, and so the UTC equivalent is later by 1 hour.
I used current_zone() to pick up my current location, but I could have also used a specific time zone (e.g. "Asia/Tokyo").
C++20 Update
As I write this update, technical work has ceased on C++20, and it looks like we will have a new C++ standard later this year (just administrative work left to do to complete C++20).
The advice in this answer translates well to C++20:
For the chronological computation, std::chrono::months is supplied by <chrono> so you don't have to compute it yourself.
For the UTC calendrical computation, loose #include "date.h" and use instead #include <chrono>, and drop using namespace date, and things will just work.
For the time zone sensitive calendrical computation, loose #include "tz.h" and use instead #include <chrono>, drop using namespace date, and replace make_zoned with zoned_time, and you're good to go.
I'm trying to process timestep interval data. The data is of two formats:
1) each interval is explicitly set (e.g., 1982-12-31, 1988-01-01T00:00:00);
or
2) a start date is set followed by offsets of seconds, minutes, hours, days, months, or years
I've been using a combination of boost::gregorian::date and boost::posix_time::ptime to manage this, and use the facilities to get nicely formatted strings. However, I've now been presented with data that covers 1.9 million years, with each timestep being approximately 10 years. The start date is 0 and the last interval is 7e8. Obviously, I've hit the limits.
Is there a way using Boost to represent such scale? My searching has led to the conclusion 'no' in which case we'll just write our own class.
This is a very interesting question. But reaching the limits of boost in this area requires careful thinking about the risk of going beyond the astronomical limits of today.
Calendars and dates are very relative:
Posix time is defined as time elapsed from January 1st, 1970, not counting the leap seconds. boost allows you to choose between the microsecond or the nanosecond resolution at build time.
The gregorian calendar is defined since October 15th, 1582. Note that before 1930, some countries used the gregorian calendar and some still the julian one, the transition resulting in some interesting facts, such as the absence of 13 september 1752 in England and America.
Before it was the Julian calendar, defined by J.Caesar in 45 BC. Note that while the format, the number of month and length of month is the same than in the gregorian calendar, there are 13 days of difference between both, that take into account accumulated differences over the years.
before 45BC, was there the old roman calendar which had 355 days/year.
And longer before, until begin of mankind there were certainly all sorts of other calendars. But a days wasn't always 24 hours long. The variations of 1 to 3 microsecond per day of the solar day add up if you go in the millions of years. For instance, 600 millions of years ago, the averge length of the day was only 22 hours.
If you're working on both geological and narrow scales the easiest approach coud be to use a class or a union combining a long long (for geological scale in years BC) and boost::gregorian::date (for years AC, if you can afford the imprecision julian/gregorian). The nice formating would then be relatively easy to organize.
Alternatively you could consider use of chrono with the longest integer type and a ratio indicating that you'r counting the years:
typedef chrono::duration<long long, ratio<31556926, 1>> duration_in_year;
duration_in_year d2(1900000); // 1,9M years
chrono::time_point<chrono::system_clock> t1 = chrono::system_clock::now() - d2;
but nice printout will not be so evident as with boost. And you'll have to define your own clock class (The example above will work with 1,9Mio years but not much more, due to the parameters used to instantiate the system_clock class).
First time poster so excuse me if I make any sort of mistake.
I am fairly new to the whole programming in C++ but I was wondering if it is possible to print out a calendar of a certain month (the current one) e.g. today it is June 2015 so I want to print out the monthly calendar of June 2015 in C++
If anyone has any suggestion how to make this possible that would be extremely helpful. I know how to post the current month with user input yet I want my program to look at the system date.
Thanks in advance.
Use time(0) to get a time_t that is also somehow the number of seconds since xxxoopsxxxxx => epoch: Wed Dec 31 19:00:00 1969, so 1/1/1970
time_t linearTime = time(0);
Use localtime_r to convert the linearTime to
struct tm timeinfo = *localtime_r(&linearTime, &timeinfo);
Use struct tm to decode broken-down-time (in Linux, at /usr/include/time.h) and produce your calendar as you want.
Have fun.
Edit 2
Looked into chrono. Figured out how to measure duration in us.
BUT, the following chrono stuff is suspiciously similar to previous stuff.
std::time_t t0 = std::time(nullptr);
std::tm* localtime(const std::time_t* t0); // std::tm has the fields you expect
// convenient to text
std::cout << std::asctime(std::localtime(&t0)); // prints the calendar info for 'now'
I suspect not much different at all.
edit 3
I have doubts. I think the previous suspiciously familiar bit came from ctime, not chrono. So I plan digging around in chrono some more.
I am given the values for year, month,day,hour,minute and second in UTC. I need to calculate the milliseconds since the epoch (UTC).
How can this be achieved?
Thanks
If you can neglect leap seconds, this is quite easy with the right tools.
You will need:
days_from_civil located here.
A custom duration type representing days == 24 hours (shown below).
C++11's std::chrono or boost::chrono library (I'm showing it with std::chrono).
Here is how you create a custom chrono::duration that represents 24 hours:
typedef std::chrono::duration
<
std::int32_t, std::ratio_multiply<std::chrono::hours::period, std::ratio<24>>
> days;
Now you are ready to write your function:
std::chrono::milliseconds
ms_since_UTC(int year, int month, int day, std::chrono::hours h,
std::chrono::minutes m, std::chrono::seconds s)
{
return days(days_from_civil(year, month, day)) + h + m + s;
}
days_from_civil will convert your year/month/day into the number of days since New Years 1970 (the UTC epoch). Your custom duration days will seamlessly interoperate with the "pre-defined" chrono durations: hours, minutes, seconds, and milliseconds. All of these durations will implicitly convert to milliseconds with no truncation error.
If you need to take leap seconds into account, then you will need to build a table of {days, chrono::seconds}, where the second part of the entry is the cumulative number of leap seconds you need to add to the duration if the current value of days is on or after the days entry in the table.
static constexpr std::pair<days, std::chrono::seconds> leap_seconds[25] =
{
std::make_pair(days(days_from_civil(1972, 7, 1)), std::chrono::seconds(1)),
// ...
};
Then you will need to search that table (using std::find_if, or std::lower_bound) to see if the return from days_from_civil lies prior to the table, within the table, or beyond the table. If prior to the table you add 0 leap seconds. If within the table, you add the appropriate number of leap seconds according to the table entry. If beyond the table you add (currently) 25 leap seconds.
return d + h + m + s + ls;
This is a "high maintenance" solution in that the table in this code will need to be updated every time they decide to add a leap second to UTC. It should also be noted that this solution can't be used with high confidence more than about 6 months in the future because one simply does not know if a leap second will be added (or subtracted) further in the future than that.
The days_from_civil code is in the public domain, so feel free to use it. It is efficient. And it has a +/- range of about 5.8 million years when given 32 bit signed inputs (so you don't really have to worry about breaching its range of validity). The paper goes into the gnarly details of how the algorithm works if you are interested.
As title, how to calculate time duration from epoch 1900 to now use boost?
Edit: Sorry about too short question at previous. I will describe my question again.
I have problem about save birthday to database as integer number. I have create four functions use as following:
ptime to integer (by day):
int ti=time_to_int(ptime(date(2012,1,1),0));
=> ti = 15430;//number of day from 1970 to 2012
integer to ptime:
ptime pt = int_to_time(15430);
ptime to string:
ptime pt(date(2012,1,1), 0);
string s = time_to_string(pt, "%d.%m.%Y");
string to ptime:
ptime pt = string_to_time(%d.%m.%Y);
PROBLEM:
Above, I have used epoch from 1970 and All work very well. So how can I convert 01/01/1945 to integer? I think that need use epoch from such as 1900.
However, when convert 01/01/2012 to int from 1900 it returns negative number, because "I think" tick count in miliseconds overflow (32bit).
Use some ways manual to calculate date to int is ok, but convert int back to date seems to bad. I want to use boost to do this.
Any suggestion?
Thanks!
Integer (int) is not big enough to hold the number of seconds since 1900. It can only hold about 68 years. You need to use a long (64 bit). Boost's time_duration will give you access to the number of seconds as a long. You could also use the number of days as mentioned by #jogojapan. However, day lengths are not always the same (23-25 hours), so sometimes keeping track of things in days is more prone to error (if you are careful it should be fine though).
Take a look at some examples from the documentation. You can subtract two ptime objects to get a time_duration:
time_duration td = ptime1 - ptime2;
long secElapsed = td.total_seconds();