mirror of
https://github.com/HowardHinnant/date.git
synced 2025-08-05 13:44:26 +02:00
Updated Examples and Recipes (markdown)
@@ -23,6 +23,7 @@ This page contains examples and recipes contributed by community members. Feel f
|
||||
- [Find all instances when a daylight savings shift is not 1 hour](#tz_search)
|
||||
- [How many timezones are using daylight saving?](#tz_daylight)
|
||||
- [What is the epoch difference between Unix Time and GPS time?](#unix_gps_epoch_diff)
|
||||
- [How to convert to/from C++ Builder's TDate and TDateTime](#TDate)
|
||||
|
||||
***
|
||||
|
||||
@@ -1062,6 +1063,158 @@ Now wait a second, doesn't `gps_time` ignore leap seconds?! Yes, but it does so
|
||||
|
||||
You can discover all kinds of neat subtleties by playing with `sys_time`, `utc_time`, `tai_time`, and `gps_time`.
|
||||
|
||||
<a name="TDate"></a>
|
||||
### How to convert to/from C++ Builder's TDate and TDateTime
|
||||
(by [Howard Hinnant](https://github.com/HowardHinnant))
|
||||
|
||||
If you are using `TDateTime` from [C++ Builder](http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/System__TDateTime.html) you may occasionally need functionality that is not present in that library, but is in this one. This article explains how to convert back and forth between these two libraries, so that you can easily use the functionality of both.
|
||||
|
||||
Two important facts:
|
||||
|
||||
1. The epoch for `TDateTime` is 1899-12-30 00:00:00 UTC.
|
||||
2. The epoch for `std::chrono::system_clock` is (unspecified but de facto): 1970-01-01 00:00:00 UTC.
|
||||
|
||||
`TDateTime` stores a `double`. The integral part of that double counts days since the epoch. The fractional part stores fractions of a day, but in a strange encoding for times prior to its epoch:
|
||||
|
||||
> When working with negative System::TDateTime values, computations must handle time portion separately. The fractional part reflects the fraction of a 24-hour day without regard to the sign of the System::TDateTime value. For example, 6:00 am on 12/29/1899 is –1.25, not –1 + 0.25, which would be –0.75. There are no System::TDateTime values between –1 and 0.
|
||||
|
||||
We'll provide two bidirectional conversions:
|
||||
|
||||
TDate <---> sys_days
|
||||
TDateTime <---> sys_time<D>
|
||||
|
||||
The `TDate`/`sys_days` conversions don't have to worry about the fractional day issue for negative `TDateTime` values, and so they are both easier and more efficient:
|
||||
|
||||
date::sys_days
|
||||
to_sys_days(System::TDate td)
|
||||
{
|
||||
using namespace date;
|
||||
return sys_days(days{static_cast<int>(td)} -
|
||||
(sys_days{1970_y/jan/1} - sys_days{1899_y/dec/30}));
|
||||
}
|
||||
|
||||
The only thing to do here is to extract the integral value from the `TDate`, convert that into `sys_days` and subtract the difference between the two epochs. The reverse conversion is just as easy:
|
||||
|
||||
System::TDate
|
||||
to_TDate(date::sys_days sd)
|
||||
{
|
||||
using namespace date;
|
||||
return System::TDate(static_cast<int>((sd.time_since_epoch() +
|
||||
(sys_days{1970_y/jan/1} - sys_days{1899_y/dec/30})).count()));
|
||||
}
|
||||
|
||||
These can be exercised like this:
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace System;
|
||||
std::cout << to_sys_days(TDate{0}) << '\n';
|
||||
std::cout << to_sys_days(TDate{2}) << '\n';
|
||||
std::cout << to_sys_days(TDate{-1}) << '\n';
|
||||
std::cout << to_sys_days(TDate{35065}) << '\n';
|
||||
|
||||
std::cout << (int)to_TDate(1899_y/dec/30) << '\n';
|
||||
std::cout << (int)to_TDate(1900_y/jan/1) << '\n';
|
||||
std::cout << (int)to_TDate(1899_y/dec/29) << '\n';
|
||||
std::cout << (int)to_TDate(1996_y/jan/1) << '\n';
|
||||
}
|
||||
|
||||
which outputs:
|
||||
|
||||
1899-12-30
|
||||
1900-01-01
|
||||
1899-12-29
|
||||
1996-01-01
|
||||
0
|
||||
2
|
||||
-1
|
||||
35065
|
||||
|
||||
This output is consistent with what is in the `TDateTime` documentation.
|
||||
|
||||
For converting to/from `TDateTime` it is convenient to allow the client to choose the precision of the `sys_time` to convert to or from. For example this converts to a precision of `minutes`:
|
||||
|
||||
to_sys_time<minutes>(TDateTime{2.75})
|
||||
|
||||
while this converts to a precision of `milliseconds`:
|
||||
|
||||
to_sys_time<milliseconds>(TDateTime{2.75})
|
||||
|
||||
Here's the implementation:
|
||||
|
||||
template <class D>
|
||||
date::sys_time<D>
|
||||
to_sys_time(System::TDateTime dt)
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using fdays = duration<double, days::period>;
|
||||
using ftime = time_point<system_clock, fdays>;
|
||||
auto ft = ftime{fdays{static_cast<double>(dt)}};
|
||||
if (ft < ftime{})
|
||||
{
|
||||
auto d = time_point_cast<days>(ft);
|
||||
auto t = d - ft;
|
||||
ft = d + t;
|
||||
}
|
||||
ft -= sys_days{1970_y/jan/1} - sys_days{1899_y/dec/30};
|
||||
return round<D>(ft);
|
||||
}
|
||||
|
||||
For time points not prior to the `TDateTime` epoch, it is quite straightforward. It helps to create a `duration` and chrono `time_point` based on `double` that counts `days`. Then one simply extracts the `double` from the `TDateTime` and converts it to our double-based `time_point`, subtracts the difference in the epochs, and then uses the `round<D>` facility to truncate the result to the requested precision.
|
||||
|
||||
If this is a pre-epoch `TDateTime`, then there's an extra dance to treat the integral and fractional parts of the `double` separately.
|
||||
|
||||
The reverse conversion is similar:
|
||||
|
||||
template <class D>
|
||||
System::TDateTime
|
||||
to_TDateTime(date::sys_time<D> tp)
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using fdays = duration<double, days::period>;
|
||||
using ftime = time_point<system_clock, fdays>;
|
||||
auto ft = ftime{tp} + (sys_days{1970_y/jan/1} - sys_days{1899_y/dec/30});
|
||||
if (ft >= ftime{})
|
||||
return System::TDateTime(ft.time_since_epoch().count());
|
||||
auto d = floor<days>(ft);
|
||||
auto t = d - ft;
|
||||
return System::TDateTime((d + t).time_since_epoch().count());
|
||||
}
|
||||
|
||||
This can all be exercised like this:
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using namespace System;
|
||||
std::cout << to_sys_time<minutes>(TDateTime{0.}) << '\n';
|
||||
std::cout << to_sys_time<minutes>(TDateTime{2.75}) << '\n';
|
||||
std::cout << to_sys_time<minutes>(TDateTime{-1.25}) << '\n';
|
||||
std::cout << to_sys_time<minutes>(TDateTime{35065.}) << '\n';
|
||||
|
||||
std::cout << (double)to_TDateTime(sys_days{1899_y/dec/30} + 0min) << '\n';
|
||||
std::cout << (double)to_TDateTime(sys_days{1900_y/jan/1} + 18h + 0min) << '\n';
|
||||
std::cout << (double)to_TDateTime(sys_days{1899_y/dec/29} + 6h + 0min) << '\n';
|
||||
std::cout << (double)to_TDateTime(sys_days{1996_y/jan/1} + 0min) << '\n';
|
||||
}
|
||||
|
||||
which outputs:
|
||||
|
||||
1899-12-30 00:00
|
||||
1900-01-01 18:00
|
||||
1899-12-29 06:00
|
||||
1996-01-01 00:00
|
||||
0
|
||||
2.75
|
||||
-1.25
|
||||
35065
|
||||
|
||||
***
|
||||
|
||||
 _This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/)._
|
Reference in New Issue
Block a user