Boost parse date/time string and yield .NET-compatible Ticks value - c++

I'd like to use C++/Boost to parse time strings such as 1980.12.06 21:12:04.232 and acquire a ticks value that would correspond to the tick count( used to initialize .NET's System.DateTime). How can I do it?
Update: I do need to use C++; I cannot use C++/CLI for this.

in .Net Date time starts from 01.01.01 00:00:00
in boost ptime starts from 1400.01.01 00.00.00
//c++ code
#include <boost/date_time/posix_time/posix_time.hpp>
int main(int argc, char* argv[])
{
using namespace boost::posix_time;
using namespace boost::gregorian;
//C# offset till 1400.01.01 00:00:00
uint64_t netEpochOffset = 441481536000000000LL;
ptime ptimeEpoch(date(1400,1,1), time_duration(0,0,0));
//note: using different format than yours, you'll need to parse the time in a different way
ptime time = from_iso_string("19801206T211204,232");
time_duration td = time - netEpoch;
uint64_t nano = td.total_microseconds() * 10LL;
std::cout <<"net ticks = " <<nano + netEpochOffset;
return 0;
}
// outputs 624805819242320000
in c# to test
static void Main(string[] args)
{
DateTime date = new DateTime(1400,1,1);
Console.WriteLine(date.Ticks);
DateTime date2 = new DateTime(624805819242320000L); //C++ output
Console.WriteLine(date2);
/*output
* 441481536000000000
* 6/12/1980 21:12:04
* */
return;
}

.Net's "Ticks" is in 100-nanosecond intervals.
http://msdn.microsoft.com/en-us/library/z2xf7zzk.aspx
ticksType: System.Int64 A date and time expressed in the number of
100-nanosecond intervals that have elapsed since January 1, 0001 at
00:00:00.000 in the Gregorian calendar
So you need the tick count of a known epoch (e.g. the Unix epoch), the number of days between the epoch and the desired date/time, and the time of day (the total_nanoseconds accessor may help). Then you can easily calculate the .Net equivalent tick count, with simple addition and multiplication.
You may still have issues to do with the representable range of dates.

Related

Seconds since epoch for a specific date

Currently, I have A solution and another semi-solution. The semi-solution is linked here, Get seconds since epoch in Linux but that gives the time since epoch for the current datetime. I need to have it tell me the seconds since epoch for a specified date instead. I do have a function for this, however I can't find the original post for it,
std::tm t = {};
std::istringstream ss(dateBuf);
ss >> std::get_time(&t, "%Y-%m-%d");
double seconds = (double)(std::mktime(&t));
There are a few issues I have with it though. Currently, dateBuf = "2020-01-01"; and I require it to be able to handle not just days, but hours, minutes, seconds... Also, I am hoping there is another way of doing this without allocating memory every time to make a new std::tm and std::istringstream and then pushing ss into it. I am wondering if there is a way to go straight from char datebuf[] to a double/long double with the seconds as the value. For all intensive purposes, the program will go over this statement many, many times to convert the dates to seconds and this just seems inefficient. Something such as
long double SecondsSinceEpoch(const char* date){
//Taking in a date such as "2020-01-01 00:00:00" (Local Time)
//Will return the value 1577836800 (GMT) and Local time of 1577797200
//And also be able to take in a simpler date such as "2020-01-01"
}
Here's the general purpose solution using the preview C++20 chrono library:
#include "date/tz.h"
#include <iostream>
#include <sstream>
long double
SecondsSinceEpoch(const char* date)
{
//Taking in a date such as "2020-01-01 00:00:00" (Local Time)
//And also be able to take in a simpler date such as "2020-01-01"
//returns -1 on error
using namespace date;
using namespace std;
using namespace std::chrono;
istringstream in{date};
local_days tpd;
in >> parse("%F", tpd);
if (in.fail())
return -1;
seconds s{};
in >> parse(" %T", s);
zoned_time zt{current_zone(), tpd + s};
return zt.get_sys_time().time_since_epoch().count();
}
The expensive parts are calling current_zone() and parsing out of an istringstream (which may create a long string on the heap, depending on the implementation and the input).
The above can be made faster if we can substitute in a constant UTC offset for the time zone, and if we create customized parsing logic that doesn't have to do much error checking.
What this library excels at is taking the {y, m, d, h, M, s} integral values and turning them into a count of seconds. So if you can obtain those integral values more efficiently than strptime (or it's equivalent in this library), including applying the UTC offset to the local time, then this library can translate those 6 fields into a count of seconds with very few cpu clock cycles.
Update
One thing you can do if your time zone is constant is to just look it up once:
static auto tz = current_zone();
zoned_time zt{tz, tpd + s};
or:
static auto tz = locate_zone("Australia/Sydney");
zoned_time zt{tz, tpd + s};
Both of these options are roughly equivalent to each other in terms of performance (assuming your current time zone is always "Australia/Sydney".
If you do your own scanning into integral types, then turning those ints into dates looks more like:
// Return {y, m, d, h, M, s}
std::array<int, 6>
scan(const char* date);
long double
SecondsSinceEpoch(const char* date)
{
//Taking in a date such as "2020-01-01 00:00:00" (Local Time)
//And also be able to take in a simpler date such as "2020-01-01"
//returns -1 on error
using namespace date;
using namespace std::chrono;
auto a = scan(date); // where you write scan
auto tp = local_days{year{a[0]}/a[1]/a[2]} + hours{a[3]}
+ minutes{a[4]} + seconds{a[5]};
static auto tz = locate_zone("Australia/Sydney");
zoned_time zt{tz, tp};
return zt.get_sys_time().time_since_epoch().count();
}
Use of the timezone lib does require some installation.

Converting Epoch time with timezone to Excel format time?

I need to be able to convert Epoch time to Excel time.
Why Excel, because working with the numeric excel time is faster that any parsing done on display formats.
Current time of 2018-06-08 12:46:58 CDTwith UTC 1528480019 should give 0.5326157.
But converted to the New_York time or 2018-06-08 13:46:58 EDT will give 0.574282367.
I only need to convert the time field to Excel style.
Here is my incomplete code:
double GetTime(Datetime currtime, std::string tz = "TZ=America/New_York")
{
std::time_t t = currtime;
//tzset(tz);
std::tm tm = *std::localtime(&t);
return ((tm.tm_hour * 3600 + (tm.tm_min) * 60.0 + tm.tm_sec) / 86400.0);
}
The code works, but only for local time which is "America/Chicago".
I have been unable to use the set the timezone to the one I might need.
Also tm seems to be limited to seconds, but I need to handle milliseconds and microseconds as well.
Furthermore, I need it to be fast and the the current implementation parses the time into separate fields and then I combine it into what I need which seems to do a lot of extra work.
This problem can easily be solved with Howard Hinnant's free, open-source date/time/timezone library, which is very efficient. This library is also in the current C++20 working draft, under namespace std::chrono. So in the future, porting your code to just use the std::lib ought to be as easy as changing a few namespaces.
double
GetTime(std::chrono::system_clock::time_point currtime,
date::time_zone const* tz = date::current_zone())
{
using namespace date;
using namespace std::chrono;
zoned_time<system_clock::duration> zt{tz, currtime};
auto lt = zt.get_local_time();
auto ld = floor<days>(lt);
using ExcelTime = duration<double, days::period>;
ExcelTime tod = lt - ld;
return tod.count();
}
Instead of taking a Datetime it takes a std::chrono::system_clock::time_point, and instead of a std::string, a date::time_zone const*.
On the three big platforms (llvm/gcc/MSVS), the coarsest system_clock::time_point is microseconds, which meets your precision goals.
Step one is to create a zoned_time which is a pairing of a time_point with a time_zone. From this one can get a local_time.
floor<days> truncates the precision of a time_point to days. If one subtracts the day-precision time_point from the finer-precision time_point, one gets the local time-of-day.
If you store this local time-of-day in a chrono::duration that has a double as its representation, and a period of 1 day, then you get the Excel Time-Of-Day format.
This can be used like:
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << std::fixed << std::setprecision(9);
std::cout << GetTime(sys_seconds{1528480019s}, locate_zone("America/Chicago")) << '\n';
zoned_time<system_clock::duration> zt{"America/New_York",
local_days{2018_y/6/8} + 13h + 46min + 58s};
std::cout << GetTime(zt.get_sys_time(), zt.get_time_zone()) << '\n';
}
which outputs:
0.532627315
0.574282407
Above, I struggled to come as close as possible to your existing API. However if you adopt this library, you can make it even simpler, and slightly more efficient, by adopting a "more native" API:
std::chrono::duration<double, date::days::period>
GetTime(const date::zoned_time<std::chrono::system_clock::duration>& zt)
{
using namespace date;
auto lt = zt.get_local_time();
return lt - floor<days>(lt);
}
Now GetTime takes just a single parameter of type zoned_time<system_clock::duration>, and returns a duration<double, days::period>. All that's left for GetTime to do is truncate the local time to days-precision and subtract to get time-of-day.
The demo in main is also simplified:
std::cout << GetTime({"America/Chicago", sys_seconds{1528480019s}}).count() << '\n';
std::cout << GetTime({"America/New_York",
local_days{2018_y/6/8} + 13h + 46min + 58s}).count() << '\n';
And gives the same output as before.

Convert the seconds since "midnight 1904-1-1" to a date-time string

In some multimedia metadata, there may be date-time in seconds since
midnight, Jan. 1, 1904, in UTC time.
As I know, the date time function is normally based on 1970-1-1 midnight in C/C++ standard library, at least in Visual C++, is there a function in C/C++/Win32-API to convert the seconds since "1904-1-1 midnight" to a date time string, like as "hh:mm:ss MM. dd, yyyy" or other format string or a structure like as "struct tm"?
struct tm
{
int tm_sec; // seconds after the minute - [0, 60] including leap second
int tm_min; // minutes after the hour - [0, 59]
int tm_hour; // hours since midnight - [0, 23]
int tm_mday; // day of the month - [1, 31]
int tm_mon; // months since January - [0, 11]
int tm_year; // years since 1900
int tm_wday; // days since Sunday - [0, 6]
int tm_yday; // days since January 1 - [0, 365]
int tm_isdst; // daylight savings time flag
};
Solution#1:
int main()
{
SYSTEMTIME systm;
memset(&systm, 0, sizeof(systm));
systm.wYear = 1904;
systm.wMonth = 1;
systm.wDay = 1;
FILETIME filetm;
if (SystemTimeToFileTime(&systm, &filetm) == FALSE){
printf("Failed to convert system time to file-time.\n");
return 0;
}
ULARGE_INTEGER nanoSeconds;
nanoSeconds.HighPart = filetm.dwHighDateTime;
nanoSeconds.LowPart = filetm.dwLowDateTime;
nanoSeconds.QuadPart += 3600ULL * 10000000; // add 1hour based on 1904/1/1 midnight
filetm.dwHighDateTime = nanoSeconds.HighPart;
filetm.dwLowDateTime = nanoSeconds.LowPart;
if (FileTimeToSystemTime(&filetm, &systm) == FALSE){
printf("Failed to convert file-time to system time.\n");
return 0;
}
printf("New system time by adding 1 hour: %d-%02d-%02d %02d:%02d:%02d.%03d\n",
systm.wYear, systm.wMonth, systm.wDay,
systm.wHour, systm.wMinute, systm.wSecond, systm.wMilliseconds);
return 0;
}
The output is
New system time by adding 1 hour: 1904-01-01 01:00:00.000
Solution#2:
With #Howard Hinnant's date.h, it can also solve this issue, please see the sample code provided by him https://stackoverflow.com/a/49733937/3968307
This would be a good time to use Howard Hinnant's free, open-source date/time library:
#include "date/date.h"
#include <cstdint>
#include <iostream>
#include <string>
std::string
convert(std::int64_t seconds_since_1904)
{
using namespace date;
using namespace std::chrono;
constexpr auto offset = sys_days{January/1/1970} - sys_days{January/1/1904};
return format("%T %m.%d, %Y", sys_seconds{seconds{seconds_since_1904}} - offset);
}
int
main()
{
std::cout << convert(3'606'124'378) << '\n';
}
Output:
13:12:58 04.09, 2018
Update
The above code will port to C++20 (when it ships) by:
Change #include "date/date.h" to #include <chrono>
Change using namespace date; to using namespace std;
Change "%T %m.%d, %Y" to "{:%T %m.%d, %Y}"
As you can easily calculate with any available spreadsheet application in your system, the difference in seconds between those two timestamps (assumed both are in UTC time) the difference in seconds from 1/1/1904 to 1/1/1970 is 2,082,844,800 sec. So the conversion function from a unix timestamp to your time, consists in adding 2082844800 to the unix timestamp you receive from any of the time functions. In case you want to pass back from a timestamp in your time to unix timestamp, then subtract that fixed value from your timescale. Beware that that number does not fit in a signed int so you must use probably a 64bit number to manage properly all those timestamps. Worse if you want to use nanoseconds resolution.
I don't guess the reason of using that strange epoch timestamp, but to illustrate a practical and in use application of such differences, there's a timestamp in internet that uses an epoch close to that, that is the NTP (Network Time Protocol) timestamp, that is based on 1/1/1900 epoch and has a resolution of 1/2**32 sec. that is around 232 ps. for a specification of this protocol, see RFC-5905
The time problem here practically begs you to write your own code for it. The year 1900 is an exception since it is divisible by 4 but still is not a leap year, so by starting in 1904 you can avoid that particular exception and use the fact that there are 1461 days in every four-year period starting with 1904.

How to convert unsigned int 64 into time in C++?

I am converting CLI C++ code to standard C++, and i have a piece of code that gets a UINT64 number (from a remote server - so i can't change to format/precision of the time i get) and converts it into DateTime object and later outputs the following value: myDatetime.ToString("dd/MM/yyyy hh:mm:ss.fffffff tt").
I haven't found a way to convert unsigned int 64 into time in C++.
The following code does nothing for numbers so big (that's the 64bit number i get from the server).
time_t rawtime=131274907755873979
localtime_s(&timeinfo, &rawtime);
I need some help :)
My question wan't answered in the thread Convert Epoch Time string to Time since it doesn't work for numbers as large as i need. For example the number 131274907755873979 which is what i get from the server. The function ctime for that value simply returns NULL.
I need a way to convert between the time i get as a unsigned int64 into standard C++ time object.
std::string LongToString(int64_t longDate) {
char buff[128];
std::chrono::duration<int64_t, std::milli> dur(longDate);
auto tp = std::chrono::system_clock::time_point(
std::chrono::duration_cast<std::chrono::system_clock::duration>(dur));
std::time_t in_time_t = std::chrono::system_clock::to_time_t(tp);
strftime(buff, 128, "%Y-%m-%d %H:%M:%S", localtime(&in_time_t));
std::string resDate(buff);
return resDate;
}
This is a case with bsoncxx::types::b_date get_date().to_int64() MongoDB.
The DateTime saved with int64_t.
You have not told us how the existing code converts that number into a DateTime. Let us suppose that it does so by invoking this constructor: DateTime( long long ticks ).
According to the documentation of that constructor of DateTime,
long long ticks A date and time expressed in the number of 100-nanosecond intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar.
On the other hand, according to the documentation of localtime_s and the documentation of time_t, localtime_s() requires
the number of seconds (not counting leap seconds) since 00:00, Jan 1 1970 UTC.
So, you first need to convert 100-nanosecond intervals to seconds, and then convert from January 1, 0001 to January 1, 1970.
Using Howard Hinnant's datetime library this computation can be done quite easily. It works with VS 2013 and later.
#include "tz.h"
#include <cstdint>
#include <string>
#include <iostream>
std::string
FILETIME_to_string(std::uint64_t i)
{
using namespace std;
using namespace std::chrono;
using namespace date;
using FileTime = duration<int64_t, ratio<1, 10000000>>;
auto const offset = sys_days{jan/1/1970} - sys_days{jan/1/1601};
auto tp = sys_days{jan/1/1970} + (FileTime{static_cast<int64_t>(i)} - offset);
return format("%d/%m/%Y %I:%M:%S %p", make_zoned("Etc/GMT-2", tp));
}
int
main()
{
std::cout << FILETIME_to_string(131274907755873979) << '\n';
}
This skips DateTime and goes straight to the string. I wasn't sure what you are wanting with tt in the format. But whatever it is, it can be handled.
This library builds on the C++11 <chrono> library. So the first thing to do is to create a duration to represent the windows tick size (100 ns). Then just compute the offset between the two epochs and subtract it from the input, and form a std::chrono::time_point. Now you can format that time_point however you want.
The program above outputs:
29/12/2016 03:12:55.5873979 PM
If you use VS 2017 you'll be able to make offset constexpr, making the conversion more efficient.

fully separated date with milliseconds from std::chrono::system_clock

My current pattern (for unix) is to call gettimeofday, cast the tv_sec field to a time_t, pass that through localtime, and combine the results with tv_usec. That gives me a full date (year, month, day, hour, minute, second, nanoseconds).
I'm trying to update my code to C++11 for portability and general good practice. I'm able to do the following:
auto currentTime = std::chrono::system_clock::now( );
const time_t time = std::chrono::system_clock::to_time_t( currentTime );
const tm *values = localtime( &time );
// read values->tm_year, etc.
But I'm stuck on the milliseconds/nanoseconds. For one thing, to_time_t claims that rounding is implementation defined (!) so I don't know if a final reading of 22.6 seconds should actually be 21.6, and for another I don't know how to get the number of milliseconds since the previous second (are seconds guaranteed by the standard to be regular? i.e. could I get the total milliseconds since the epoch and just modulo it? Even if that is OK it feels ugly).
How should I get the current date from std::chrono::system_clock with milliseconds?
I realised that I can use from_time_t to get a "rounded" value, and check which type of rounding occurred. This also doesn't rely on every second being exactly 1000 milliseconds, and works with out-of-the-box C++11:
const auto currentTime = std::chrono::system_clock::now( );
time_t time = std::chrono::system_clock::to_time_t( currentTime );
auto currentTimeRounded = std::chrono::system_clock::from_time_t( time );
if( currentTimeRounded > currentTime ) {
-- time;
currentTimeRounded -= std::chrono::seconds( 1 );
}
const tm *values = localtime( &time );
int year = values->tm_year + 1900;
// etc.
int milliseconds = std::chrono::duration_cast<std::chrono::duration<int,std::milli> >( currentTime - currentTimeRounded ).count( );
Using this free, open-source library you can get the local time with millisecond precision like this:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << make_zoned(current_zone(),
floor<milliseconds>(system_clock::now())) << '\n';
}
This just output for me:
2016-09-06 12:35:09.102 EDT
make_zoned is a factory function that creates a zoned_time<milliseconds>. The factory function deduces the desired precision for you. A zoned_time is a pairing of a time_zone and a local_time. You can get the local time out with:
local_time<milliseconds> lt = zt.get_local_time();
local_time is a chrono::time_point. You can break this down into date and time field types if you want like this:
auto zt = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
auto lt = zt.get_local_time();
local_days ld = floor<days>(lt); // local time truncated to days
year_month_day ymd{ld}; // {year, month, day}
time_of_day<milliseconds> time{lt - ld}; // {hours, minutes, seconds, milliseconds}
// auto time = make_time(lt - ld); // another way to create time_of_day
auto y = ymd.year(); // 2016_y
auto m = ymd.month(); // sep
auto d = ymd.day(); // 6_d
auto h = time.hours(); // 12h
auto min = time.minutes(); // 35min
auto s = time.seconds(); // 9s
auto ms = time.subseconds(); // 102ms
Instead of using to_time_t which rounds off you can instead do like this
auto tp = std::system_clock::now();
auto s = std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
auto t = (time_t)(s.count());
That way you get the seconds without the round-off. It is more effective than checking difference between to_time_t and from_time_t.
I read the standard like this:
It is implementation defined whether the value is rounder or truncated, but naturally the rounding or truncation only occurs on the most detailed part of the resulting time_t. That is: the combined information you get from time_t is never more wrong than 0.5 of its granularity.
If time_t on your system only supported seconds, you would be right that there could be 0.5 seconds systematic uncertainty (unless you find out how things were implemented).
tv_usec is not standard C++, but an accessor of time_t on posix. To conclude, you should not expect any rounding effects bigger than half of the smallest time value difference your system supports, so certainly not more than 0.5 micro seconds.
The most straight forward way is to use boost ptime. It has methods such as fractional_seconds()
http://www.boost.org/doc/libs/1_53_0/doc/html/date_time/posix_time.html#date_time.posix_time.ptime_class
For interop with std::chrono, you can convert as described here: https://stackoverflow.com/a/4918873/1149664
Or, have a look at this question: How to convert std::chrono::time_point to calendar datetime string with fractional seconds?