How to represent geological time? (Boost, Date_time?) - c++

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).

Related

Determine if daylight savings applies to a certain date

I'm attempting to modify the creation dates of files to the date they were released. I'm first converting a string such as "2 April 2005" into a std::tm. I then create a SYSTEMTIME as follows:
std::tm dt = from_string("2 April 2005");
SYSTEMTIME st { 0 };
st.wYear = dt.tm_year + 1900; // dt is years from 1900
st.wMonth = dt.tm_mon + 1; // dt is month index 0
st.wDay = dt.tm_mday;
st.wHour = 6; // FILETIME is based on UTC, which is 6 hours ahead
Afterwards I convert the SYSTEMTIME to a FILETIME and use that to apply the changes.
This sets the file time to 2 April 2005 12:00:00 AM which is correct. However, videos after April 2nd were being set to 1:00:00 AM and sure enough, daylight savings happened on April 3rd 2005.
How can I determine if a certain date is before or after daylight savings so I can adjust st.wHour accordingly? The goal is to have all times set to 12:00:00 AM. Preferably this would work on dates dating back to the 60s as well as current.
I tried using TIME_ZONE_INFORMATION and GetTimeZoneInformation but I only get back TIME_ZONE_ID_STANDARD.
A few things:
SYSTEMTIME is just a plain structure. It has separate fields for year, month, day-of-week, day (of month), hour, minute, second and millisecond. It is not either UTC or local time, or anything else by itself. That doesn't come into consideration until you pass it into a function.
FILETIME is another plain structure. It represents the number of 100-nanosecond intervals since midnight of 1601-01-01. Much of the docs would make you think it is always in UTC, but there are functions like FileTimeToLocalFileTime that disprove that. Thus, like SYSTEMTIME, it is up to each individual function to decide how to interpret it.
The SystemTimeToFileTime function accepts a pointer to a SYSTEMTIME that is interpreted as UTC, and returns a pointer to a FILETIME that is also in terms of UTC. There is no local time zone involved.
Don't try to adjust for local time yourself (st.wHour = 6 in your code). The hour will need to vary depending both on time zone and on DST.
You didn't see any response from GetTimeZoneInformation other than TIME_ZONE_STANDARD, because that tells you what is currently in effect - which is disconnected from the dates you might be working with.
You shouldn't be trying to figure out how to adjust for DST on your own. DST is not universally applicable, and it's not always one hour offset. Instead, use a function that converts from the time zone you care about to UTC.
Ultimately it sounds like you are asking about how to set the file time to midnight of the local time zone on a particular date. Thus, I suggest you go through these steps:
Construct a SYSTEMTIME with the date you care about and the time components set to zeros (the default).
Get the local time zone using the GetDynamicTimeZoneInformation function. You want the "dynamic" version such that any historical differences in rules for standard time and DST that Windows knows about are taken into consideration, rather than just the current set of rules.
Pass those two values to the TzSpecificLocalTimeToSystemTimeEx function. It will interpret the input time as being in the input time zone (which was the system's local time zone). The result is a FILETIME that is in terms of UTC.
Pass that value to SetFileTime, which expects the input in terms of UTC.
Also, keep in mind that not all file systems track file times in the same way:
NTFS stores the actual UTC time, so you can move files between computers and the timestamp represents the same point in Universal Time even if the computers have different time zone settings.
FAT and its variants store local time. So when you call SetFileTime, Windows translates from UTC to the local time zone and writes the result. If you then open the file on a system with a different time zone, the date will be interpreted in that time zone, resulting in a different UTC time. (This is often seen on USB sticks, memory cards, etc. when moving files from cameras to computers.)
Lastly, you said:
... Preferably this would work on dates dating back to the 60s as well as current.
Unfortunately, Windows time zones do not track historical dates that far. Microsoft's time zone policy is to track time zone and DST rules for 2010 and forward for all populated places on Earth. Although, several time zones track some historical changes from before 2010 as artifacts from before this policy was formalized. (They are accurate within a given zone, just not uniform in starting years across all zones).
If historical dates are significant to your application, you'll need a very different approach - one that doesn't use the Windows time zone data, but instead uses the IANA time zone database. (More on these in the timezone tag wiki.) Here are some ideas you could explore:
The ICU project has time zone support and a C implementation. It's a bit heavy for this one purpose, but good if you are using it for other localization aspects of an application.
Howard Hinnant (who commented on the question above) has an excellent Date library with IANA time zone support.
It might be possible to get what you need from the Windows.Globalization.Calendar UWP class. I haven't tested to see if it will use historical rules from IANA data or if it uses the Windows data. (If I get a chance to check, I'll come back and update this answer.)
Keep in mind that the IANA database only guarantees from 1970 forward. You said you needed from 1960, and though there are some zones with data for that (and some much older), there is no guarantee of correctness in that period.

number of seconds since start of week?

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.

How to convert time and date to the sentences format in C or C++?

For example the program gets time and date from the system
Date : 17/05/2018
Time: 20:15:55
Output
Date: seventeenth of may two thousand and eighteen
Time: twenty past fifteen
Can i use strftime?
There is not library in c or c++ that does that, but this can be done easily manually. You can create a char array with 31 days, 24 hours, etc. Only problem could be with years, but i suppose that you won't need thousands of years for your application.
You can see basic example with hours and minutes here.

Using boost::chrono, how to calculate milliseconds since epoch UTC from separate year, month, day, hour,min,secs

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.

incorrect value in coledatetime

I'm fighting a few days with COleDateTime in MFC.
I have CTime with correct values. Correct years, days, months, hours, minutes and seconds.
I tried a few ways to convert CTime to COleDateTime:
-1.I put CTime data to constructor of COleDateTime
COleDateTime(int nYear,int nMonth,int nDay, int nHour, int nMin,int nSec );
-2. I formatted CTime to time.Format("%m/%d/%y %H:%M:%S");
and passed to ParseDateTime of COleDateTime.
-3. Also I tried to use SetDateTime of COleDateTime
After that I'm getting incorrect values of minutes 1-2 min. more or less.
I have never seen it before and I couldn't find nothing in internet.Everybody says abot loss precision but this a second, not a minute.
Please advice something for me!
Thank you
I think the problem is that COleDateTime internally uses a float for storage, and the value represents the number of days since 30 December 1899.
As the number of days gets larger, the precision of the smaller fields (like minutes) decreases. For example, a float can accurately store the values 1000000 and 0.0000001, but it CAN'T store 1000000.0000001. It doesn't have enough bits of precision.
This limitation is hinted at in the MSDN documentation:
This type is also used to represent date-only or time-only values. By convention, the date 0 (30 December 1899) is used for time-only values. Similarly, the time 0:00 (midnight) is used for date-only values.
So basically, if you want a precise time, set the date to 30 December 1899.
It seems like Microsoft could have just designed this class to store the "days" portion as an integer, but hey, that would be too EASY.