Question
I want to parse a date-time given as string (UTC) into seconds since epoch. Example (see EpochConverter):
2019-01-15 10:00:00 -> 1547546400
Problem
The straightforward solution, which is also accepted in a very related question C++ Converting a time string to seconds from the epoch goes std::string -> std::tm -> std::time_t using std::get_time and then std::mktime:
std::tm tm;
std::stringstream ss("2019-01-15 10:00:00");
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
std::time_t epoch = std::mktime(&tm);
// 1547546400 (expected)
// 1547539200 (actual, 2 hours too early)
But std::mktime seems to mess up the hours due to timezone. I am executing the code from UTC+01:00, but we also had DST at that date, so its +2 here.
The tm shows 15 for the hour field after std::get_time. It gets messed up as soon as it enters std::mktime.
So again, the string is to be interpreted as UTC timestamp, no timezones whatsoever should be involved. But all solutions I came up with seem to interpret it as local timestamp and add offsets to it.
Restrictions
I have some restrictions for this:
C++17
platform/compiler independent
no environment variable hacking
no external libraries (like boost)
Feel free to post answers involving those for the sake of Q&A though, but I wont accept them.
Research
I found various attempts to solve this problem, but none met my requirements:
std::mktime (as mentioned above), messes up the time because it assumes local time
strptime, not available on my platform, not part of the standard
timegm (thats exactly what I would need), not platform independent
_mkgmtime, not platform independent
boost::posix_time::from_iso_string, is an external library
std::chrono::date::parse, not available with C++17
clear and reset the timezone variable with tzset, uses environment variable hacking
manually countering the offset with mktime(localtime(×tamp)) - mktime(gmtime(×tamp)), computes the wrong offset since it does not account for DST (1 hour on my platform but it would need to be 2 hours)
Solution prior to C++20: Roll your own.
Given the right documentation, it really is much easier than it sounds, and can even be lightning fast if you don't need much error detection.
The first problem is to parse the numbers without manipulating any of them. You only need to read unsigned values of length 2 and 4 digits, so just do that bare minimum:
int
read2(std::string const& str, int pos)
{
return (str[pos] - '0')*10 + (str[pos+1] - '0');
}
int
read4(std::string const& str, int pos)
{
return (str[pos] - '0')*1000 + (str[pos+1] - '0')*100 +
(str[pos+2] - '0')*10 + (str[pos+3] - '0');
}
Now given a string, it is easy to parse out the different values you will need:
// yyyy-mm-dd hh:MM:ss -> count of non-leap seconds since 1970-01-01 00:00:00 UTC
// 0123456789012345678
long long
EpochConverter(std::string const& str)
{
auto y = read4(str, 0);
auto m = read2(str, 5);
auto d = read2(str, 8);
...
The part that usually trips people up is how to convert the triple {y, m, d} into a count of days since/prior 1970-01-01. Here is a collection of public domain calendrical algorithms that will help you do this. This is not a 3rd party date/time library. It is a tutorial on the algorithms you will need to write your own date/time library. And these algorithms are efficient. No iteration. No large tables. That makes them very pipeline and cache friendly. And they are unit tested over a span of +/- a million years. So you don't have to worry about hitting any correctness boundaries with them. These algorithms also have a very in-depth derivation if you are interested in how they work.
So just go to the collection of public domain calendrical algorithms, pick out the algorithms you need (and customize them however you want), and roll your own converter.
For example:
#include <cstdint>
#include <limits>
#include <string>
int
days_from_civil(int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<int>(doe) - 719468;
}
int
read2(std::string const& str, int pos)
{
return (str[pos] - '0')*10 + (str[pos+1] - '0');
}
int
read4(std::string const& str, int pos)
{
return (str[pos] - '0')*1000 + (str[pos+1] - '0')*100 +
(str[pos+2] - '0')*10 + (str[pos+3] - '0');
}
// yyyy-mm-dd hh:MM:ss -> count of non-leap seconds since 1970-01-01 00:00:00 UTC
// 0123456789012345678
long long
EpochConverter(std::string const& str)
{
auto y = read4(str, 0);
auto m = read2(str, 5);
auto d = read2(str, 8);
auto h = read2(str, 11);
auto M = read2(str, 14);
auto s = read2(str, 17);
return days_from_civil(y, m, d)*86400LL + h*3600 + M*60 + s;
}
#include <iostream>
int
main()
{
std::cout << EpochConverter("2019-01-15 10:00:00") << '\n';
}
This just output for me:
1547546400
Sprinkle in whatever error detection is appropriate for your application.
I had the same requirement recently. I was disappointed to find that the handling of DST and timezones seemed inconsistent between writing timestamps and parsing them.
The code I came up with was this:
void time_point_from_stream(std::istream &is, system_clock::time_point &tp)
{
std::tm tm {};
is >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
// unhappily, mktime thinks it's reading local time with DST adjustments
auto my_time_t = std::mktime(&tm);
my_time_t += tm.tm_gmtoff;
if (tm.tm_isdst == 1)
my_time_t -= 3600;
tp = system_clock::from_time_t(my_time_t);
if (not is)
return;
auto ch = is.peek();
if (std::isspace(ch))
return;
if (ch == '.')
{
double zz;
is >> zz;
auto zseconds = std::chrono::duration< double >(zz);
tp += chrono::duration_cast< system_clock::duration >(zseconds);
if (not is)
return;
ch = is.peek();
}
if (ch == 'Z')
is.get();
else if (not isspace(ch))
{
is.setstate(std::ios::failbit);
}
}
Essentially, the steps are:
Use std::get_time to fill a tm
use std::mktime to convert that to a time_t
reverse out timezone and DST adjustments
convert to a std::chrono::system_clock::time_point
Parse the fractional seconds and adjust the result.
I believe c++20 improves on the situation.
Howard Hinnant has also written an improved date/time library. There is also boost::posix_time which I have always found easier to use than the std offering.
Related
How to convert timestamp string, e.g. "1997-07-16T19:20:30.45+01:00" into UTC time. The result of conversion should be timespec structure as in utimensat input arguments.
// sorry, should be get_utc_time
timespec get_local_time(const char* ts);
P.S. I need solution using either standard Linux/C/C++ facilities (whatever that means) or Boost C++ library.
Assumption: You want the "+01:00" to be subtracted from the "1997-07-16T19:20:30.45" to get a UTC timestamp and then convert that into a timespec.
Here is a C++20 solution that will automatically handle the centisecond precision and the [+/-]hh:mm UTC offset for you:
#include <chrono>
#include <ctime>
#include <sstream>
std::timespec
get_local_time(const char* ts)
{
using namespace std;
using namespace chrono;
istringstream in{ts};
in.exceptions(ios::failbit);
sys_time<nanoseconds> tp;
in >> parse("%FT%T%Ez", tp);
auto tps = floor<seconds>(tp);
return {.tv_sec = tps.time_since_epoch().count(),
.tv_nsec = (tp - tps).count()};
}
When used like this:
auto r = get_local_time("1997-07-16T19:20:30.45+01:00");
std::cout << '{' << r.tv_sec << ", " << r.tv_nsec << "}\n";
The result is:
{869077230, 450000000}
std::chrono::parse will subtract the +/-hh:mm UTC offset from the parsed local value to obtain a UTC timestamp (to up to nanosecond precision).
If the input has precision seconds, this code will handle it. If the precision is as fine as nanoseconds, this code will handle it.
If the input does not conform to this syntax, an exception will be thrown. If this is not desired, remove in.exceptions(ios::failbit);, and then you must check in.fail() to see if the parse failed.
This code will also handle dates prior to the UTC epoch of 1970-01-01 by putting a negative value into .tv_sec, and a positive value ([0, 999'999'999]) into .tv_nsec. Note that handling pre-epoch dates is normally outside of the timespec specification, and so most C utilities will not handle such a timespec value.
If you can not use C++20, or if your vendor has yet to implement this part of C++20, there exists a header-only library which implements this part of C++20, and works with C++11/14/17. I have not linked to it here as it is not in the set: "standard Linux/C/C++ facilities (whatever that means) or Boost C++ library". I'm happy to add a link if requested.
For comparison, here's how you could do this in mostly-standard C. It's somewhat cumbersome, because C's date/time support is still rather fragmented, unlike the much more complete support which C++ has, as illustrated in Howard Hinnant's answer. (Also, two of the functions I'm going to use are not specified by the C Standard, although they're present on many/most systems.)
If you have the semistandard strptime function, and if you didn't care about subseconds and explicit time zones, it would be relatively straightforward. strptime is a (partial) inverse of strftime, parsing a time string under control of a format specifier, and constructing a struct tm. Then you can call mktime to turn that struct tm into a time_t. Then you can use the time_t to populate a struct timespec.
char *inpstr = "1997-07-16T19:20:30.45+01:00";
struct tm tm;
memset(&tm, 0, sizeof(tm));
char *p = strptime(inpstr, "%Y-%m-%dT%H:%M:%S", &tm);
if(p == NULL) {
printf("strptime failed\n");
exit(1);
}
tm.tm_isdst = -1;
time_t t = mktime(&tm);
if(t == -1) {
printf("mktime failed\n");
exit(1);
}
struct timespec ts;
ts.tv_sec = t;
ts.tv_nsec = 0;
printf("%ld %ld\n", ts.tv_sec, ts.tv_nsec);
printf("%s", ctime(&ts.tv_sec));
printf("rest = %s\n", p);
In my time zone, currently UTC+4, this prints
869095230 0
Wed Jul 16 19:20:30 1997
rest = .45+01:00
But you did have subsecond information, and you did have an explicit time zone, and there's no built-in support for those in any of the basic C time-conversion functions, so you have to do things "by hand". Here's one way to do it. I'm going to use sscanf to separate out the year, month, day, hour, minute, second, and other components. I'm going to use those components to populate a struct tm, then use the semistandard timegm function to convert them straight to a UTC time. (That is, I temporarily assume that the HH:MM:SS part was UTC.) Then I'm going to manually correct for the time zone. Finally, I'm going to populate the tv_nsec field of the struct timesec with the subsecond information I extracted back in the beginning.
int y, m, d;
int H, M, S;
int ss; /* subsec */
char zs; /* zone sign */
int zh, zm; /* zone hours, minutes */
int r = sscanf(inpstr, "%d-%d-%dT%d:%d:%d.%2d%c%d:%d",
&y, &m, &d, &H, &M, &S, &ss, &zs, &zh, &zm);
if(r != 10 || (zs != '+' && zs != '-')) {
printf("parse failed\n");
exit(1);
}
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = y - 1900;
tm.tm_mon = m - 1;
tm.tm_mday = d;
tm.tm_hour = H;
tm.tm_min = M;
tm.tm_sec = S;
time_t t = timegm(&tm);
if(t == -1) {
printf("timegm failed\n");
exit(1);
}
long int z = ((zh * 60L) + zm) * 60;
if(zs == '+') /* East of Greenwich */
t -= z;
else t += z;
struct timespec ts;
ts.tv_sec = t;
ts.tv_nsec = ss * (1000000000 / 100);
printf("%ld %ld\n", ts.tv_sec, ts.tv_nsec);
printf("%s", ctime(&ts.tv_sec));
printf(".%02ld\n", ts.tv_nsec / (1000000000 / 100));
For me this prints
869077230 450000000
Wed Jul 16 14:20:30 1997
.45
The time zone and subsecond information have been honored.
This code makes no special provision for dates prior to 1970. I think it will work if mktime/timegm work.
As mentioned, two of these functions — strptime and timegm — are not specified by the ANSI/ISO C Standard and are therefore not guaranteed to be available everywhere.
I am writing current GMT time as string as follow :
const std::time_t now = std::time(nullptr);
std::stringstream ss;
ss << std::put_time(std::gmtime(&now), "%Y-%m-%d %H:%M:%S");
Later I want to do the reverse operation, reading time from the stringstream as GMT, and compare it to current timestamp :
std::tm tm = {};
ssTimestamp >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
const std::time_t&& time = std::mktime(&tm);
const double timestampDiff((std::difftime(std::time(nullptr), time)));
Something is missing in the code below, because the decoded time is never converted to GMT, thus I end up with 1 hour time difference due to my local timezone
P.S : Can use only standard libraries, and can' t change date string format
The C++20 spec has a convenient way to do this:
using namespace std::chrono;
sys_seconds tp;
ssTimestamp >> parse("%Y-%m-%d %H:%M:%S", tp);
std::time_t time = system_clock::to_time_t(tp);
No vendor has yet implemented this part of C++20, but there is an example implementation here in namespace date.
There is no library support to do this operation in C++ prior to C++20.
The best you can do using only standard libraries is to parse the fields into a tm using std::get_time (as your question shows), and then convert that {y, m, d, h, M, s} structure to a time_t using your own math, and the assumption (which is generally true) that std::time_t is Unix Time with a precision of seconds.
Here is a collection of public domain calendrical algorithms to help you do that. This is not a 3rd party library. It is a cookbook for writing your own date library.
For example:
#include <ctime>
std::time_t
to_time_t(std::tm const& tm)
{
int y = tm.tm_year + 1900;
unsigned m = tm.tm_mon + 1;
unsigned d = tm.tm_mday;
y -= m <= 2;
const int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return (era * 146097 + static_cast<int>(doe) - 719468)*86400 +
tm.tm_hour*3600 + tm.tm_min*60 + tm.tm_sec;
}
The link above has a very in-depth description of this algorithm and unit tests to make sure it works over a range of +/- millions of years.
The above to_time_t is essentially a portable version of timegm that ships on linux and bsd platforms. This function is also called _mkgmtime on Windows.
The tm struct doesn't store the timezone information, it's mktime that by default uses the local timezone.
Following this thread, the best option would be to use:
#include "time.h"
timestamp = mktime(&tm) - timezone; //or _timezone
if timezone or _timezone is available to your compiler. A comment in the linked answer warns that it may raise issues with daylight saving time, but it should not apply to GMT.
I recently tried to solve a very similar problem. I was trying to convert a string to a specific timezone regardless what is the current timezone of a computer. Here is the solution that I came up with and works as expected:
std::time_t from_time_str(std::string time_str) {
std::stringstream ss;
std::tm tm = {};
ss << time_str;
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
std::time_t t = std::mktime(&tm);
std::tm* gm_tm = std::gmtime(&t);
gm_tm->tm_isdst = false;
std::time_t gm_t = std::mktime(gm_tm);
std::time_t gm_offset = (gm_t - t);
std::time_t real_gm_t = t - gm_offset;
return real_gm_t;
}
The idea is use the function gmtime to get the gmtime of the timestamp so we could calculate the offset of the target computer's timezone. We then subtract the offset to get the GM time.
Note that the line gm_tm->tm_isdst = false; is required for any timezone that has daylight saving is enable, otherwise the gmtime is calculated with daylight saving offset (1 hour off) and this should not be the desired effect of calculating the actual GM time.
How do I parse a string equivalent RFC 3339 to any type of regular DateTime structure? The RFC 3339 date-time format is used in a number of specifications such as the Atom Syndication Format.
Here is example of date time in ATOM(RFC 3339) format:
2005-08-15T15:52:01+04:00
Here is a complete, but unfortunately unsatisfactory, and yet portable program among the very latest versions of libc++, libstdc++, VS implementations, which parses the string in the format you show into a std::chrono::system_clock::time_point.
I could not find a DateTime to which you refer. However std::chrono::system_clock::time_point is a "DateTime" structure. std::chrono::system_clock::time_point is a count of some time duration (seconds, microseconds, nanoseconds, whatever) since some unspecified epoch. And you can query std::chrono::system_clock::time_point to find out what its time duration is. And as it turns out, every implementation measures time since New Years 1970 neglecting leap seconds.
#include <chrono>
#include <iostream>
#include <limits>
#include <locale>
#include <sstream>
template <class Int>
// constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const Int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<Int>(doe) - 719468;
}
using days = std::chrono::duration
<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
namespace std
{
namespace chrono
{
template<class charT, class traits>
std::basic_istream<charT,traits>&
operator >>(std::basic_istream<charT,traits>& is, system_clock::time_point& item)
{
typename std::basic_istream<charT,traits>::sentry ok(is);
if (ok)
{
std::ios_base::iostate err = std::ios_base::goodbit;
try
{
const std::time_get<charT>& tg = std::use_facet<std::time_get<charT> >
(is.getloc());
std::tm t = {};
const charT pattern[] = "%Y-%m-%dT%H:%M:%S";
tg.get(is, 0, is, err, &t, begin(pattern), end(pattern)-1);
if (err == std::ios_base::goodbit)
{
charT sign = {};
is.get(sign);
err = is.rdstate();
if (err == std::ios_base::goodbit)
{
if (sign == charT('+') || sign == charT('-'))
{
std::tm t2 = {};
const charT pattern2[] = "%H:%M";
tg.get(is, 0, is, err, &t2, begin(pattern2), end(pattern2)-1);
if (!(err & std::ios_base::failbit))
{
auto offset = (sign == charT('+') ? 1 : -1) *
(hours{t2.tm_hour} + minutes{t2.tm_min});
item = system_clock::time_point{
days{days_from_civil(t.tm_year+1900, t.tm_mon+1,
t.tm_mday)} +
hours{t.tm_hour} + minutes{t.tm_min} + seconds{t.tm_sec} -
offset};
}
else
{
err |= ios_base::failbit;
}
}
else
{
err |= ios_base::failbit;
}
}
else
{
err |= ios_base::failbit;
}
}
else
{
err |= ios_base::failbit;
}
}
catch (...)
{
err |= std::ios_base::badbit | std::ios_base::failbit;
}
is.setstate(err);
}
return is;
}
} // namespace chrono
} // namespace std
int
main()
{
std::istringstream infile("2005-08-15T15:52:01+04:00");
std::chrono::system_clock::time_point tp;
infile >> tp;
std::cout << tp.time_since_epoch().count() << '\n';
}
This has been tested against libc++, libstdc++-5.0 and VS-2015 and produces respectively:
1124106721000000
1124106721000000000
11241067210000000
On libc++ this is a count of microseconds since New Years 1970, neglecting leap seconds. On libstdc++-5.0 it is a count of nanoseconds, and on VS-2015 it is a count of 100 nanoseconds.
The problem with this solution is that it involves inserting a function into the std namespace. In the future the C++ committee may decide to insert this same function into the same namespace which could invalidate your code.
Another problem with this code is that it is horribly complicated. It is a shame that the standard does not provide a simpler solution.
Another problem with this code is that it does not use the simpler "%F", "%T", and "%z" parsing patterns documented in the C standard (though documented as formatting patterns). I experimentally discovered their use was not portable.
Another problem with this code is that it will require gcc-5.0. If you're running gcc-4.9, you're out of luck. You'll have to parse things yourself. I was not able to test VS implementations prior to VS-2015. libc++ should be ok (though even libc++ does not support "%z").
You can convert the std::chrono::system_clock::time_point back into a "broken down" structure if desired via the formulas here. However if that is your ultimate goal, it would be more efficient to modify the code above to parse directly into your "broken down" structure instead of into a std::chrono::system_clock::time_point.
Disclaimer: Only very lightly tested. I'm happy to update this answer with any bug reports.
Update
In the years since I first gave this answer I have written a library which does all the computations above with a far more concise syntax.
#include "date/date.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
std::istringstream infile{"2005-08-15T15:52:01+04:00"};
sys_seconds tp; // This is a system_clock time_point with seconds precision
infile >> parse("%FT%T%Ez", tp);
std::cout << tp.time_since_epoch() << " is " << tp << '\n';
}
You can find "date.h" here. It is a free, open-source, header only library. At the this link there are also links to full documentation, and for "date.h" even a video tutorial. Though the video tutorial was created prior to the implementation of the parse function.
The output of the above program is:
1124106721s is 2005-08-15 11:52:01
which gives both the seconds since epoch (1970-01-01 00:00:00 UTC), and the date/time in UTC (taking the offset into account).
On the off chance that you need to count leap seconds since the epoch, another library at this same GitHub link is available, but is not header only and requires a small amount of installation. But using it is a simple modification of the program above:
#include "date/tz.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
std::istringstream infile{"2005-08-15T15:52:01+04:00"};
utc_seconds tp; // This is a utc_clock time_point with seconds precision
infile >> parse("%FT%T%Ez", tp);
std::cout << tp.time_since_epoch() << " is " << tp << '\n';
}
And the output is now:
1124106743s is 2005-08-15 11:52:01
The difference in code is that "tz.h" is now included instead of "date.h", and utc_seconds is parsed instead of sys_seconds. utc_seconds is still a std::chrono::time_point, but now based on a leap-second-aware clock. The program outputs the same date/time, but the number of seconds since the epoch is now 22s greater as this is the number of leap seconds inserted between 1970-01-01 and 2005-08-15.
I am looking for a nice way to convert an int into a time format. For example, I take in the integer 460 and it returns 5:00, or the integer 1432 and it returns 14:32. The only way I could think of would be tediously turning it into a string, breaking it into two strings, and checking both strings for correctness.
Thank you.
As I pointed out in my comment, I think your representation is highly problematic. I propose that you represent everything as seconds, and use some simple calculations for parsing the minutes/hours.
class PlayTime {
size_t sec;
public:
PlayTime(size_t hours = 0, size_t minutes = 0, size_t seconds = 0)
: sec(hours*60*60 + minutes*60 + seconds) {}
size_t hours() const { return sec/(60*60); }
size_t minutes() const { return (sec/60)%60; }
size_t seconds() const { return sec%60; }
PlayTime & operator +=(const PlayTime &that) {
this-> sec += that.sec;
return *this;
}
};
PlayTime operator+(PlayTime a, Playtime b) { return a+=b; }
For easy compatibility and portability, you might want to look at the standard C runtime library functions for managing a time_t. It represents time in seconds since 1970-01-01T00:00:00 +0000 UTC.
Now that you have posted your intent (4 minutes 32 seconds stored as 432), be aware that simple calculations using this format are not straightforward. For example, what is 30 seconds later from 4:32? It would appear to be 462.
Since your examples aren't quite precise, it's hard to give a straight answer. You haven't said how would you want to store the time after conversion. Do you have some kind of class, or just wanna store it in a string? If the latter, you'll probably use a stringstream:
unsigned int number = 460;
std::stringstream parser;
if((number/100)+(number%100)/60<10) parser << "0"; // trailing 0 if minutes < 10
parser << (number/100)+(number%100)/60 // minutes
<< ':'; // separator
if((number%100)%60<10) parser << "0"; // trailing 0 if seconds < 10
parser << (number%100)%60; // seconds
std::string time = parser.str();
Note hovewer that that's not the best way to do it. C++ provides a <ctime> header which includes the tm struct, and it would be better if you used that instead.
I have a trace file that each transaction time represented in Windows filetime format. These time numbers are something like this:
128166372003061629
128166372016382155
128166372026382245
Would you please let me know if there are any C/C++ library in Unix/Linux to extract actual time (specially second) from these numbers ? May I write my own extraction function ?
it's quite simple: the windows epoch starts 1601-01-01T00:00:00Z. It's 11644473600 seconds before the UNIX/Linux epoch (1970-01-01T00:00:00Z). The Windows ticks are in 100 nanoseconds. Thus, a function to get seconds from the UNIX epoch will be as follows:
#define WINDOWS_TICK 10000000
#define SEC_TO_UNIX_EPOCH 11644473600LL
unsigned WindowsTickToUnixSeconds(long long windowsTicks)
{
return (unsigned)(windowsTicks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
}
FILETIME type is is the number 100 ns increments since January 1 1601.
To convert this into a unix time_t you can use the following.
#define TICKS_PER_SECOND 10000000
#define EPOCH_DIFFERENCE 11644473600LL
time_t convertWindowsTimeToUnixTime(long long int input){
long long int temp;
temp = input / TICKS_PER_SECOND; //convert from 100ns intervals to seconds;
temp = temp - EPOCH_DIFFERENCE; //subtract number of seconds between epochs
return (time_t) temp;
}
you may then use the ctime functions to manipulate it.
(I discovered I can't enter readable code in a comment, so...)
Note that Windows can represent times outside the range of POSIX epoch times, and thus a conversion routine should return an "out-of-range" indication as appropriate. The simplest method is:
... (as above)
long long secs;
time_t t;
secs = (windowsTicks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
t = (time_t) secs;
if (secs != (long long) t) // checks for truncation/overflow/underflow
return (time_t) -1; // value not representable as a POSIX time
return t;
New answer for old question.
Using C++11's <chrono> plus this free, open-source library:
https://github.com/HowardHinnant/date
One can very easily convert these timestamps to std::chrono::system_clock::time_point, and also convert these timestamps to human-readable format in the Gregorian calendar:
#include "date.h"
#include <iostream>
std::chrono::system_clock::time_point
from_windows_filetime(long long t)
{
using namespace std::chrono;
using namespace date;
using wfs = duration<long long, std::ratio<1, 10'000'000>>;
return system_clock::time_point{floor<system_clock::duration>(wfs{t} -
(sys_days{1970_y/jan/1} - sys_days{1601_y/jan/1}))};
}
int
main()
{
using namespace date;
std::cout << from_windows_filetime(128166372003061629) << '\n';
std::cout << from_windows_filetime(128166372016382155) << '\n';
std::cout << from_windows_filetime(128166372026382245) << '\n';
}
For me this outputs:
2007-02-22 17:00:00.306162
2007-02-22 17:00:01.638215
2007-02-22 17:00:02.638224
On Windows, you can actually skip the floor, and get that last decimal digit of precision:
return system_clock::time_point{wfs{t} -
(sys_days{1970_y/jan/1} - sys_days{1601_y/jan/1})};
2007-02-22 17:00:00.3061629
2007-02-22 17:00:01.6382155
2007-02-22 17:00:02.6382245
With optimizations on, the sub-expression (sys_days{1970_y/jan/1} - sys_days{1601_y/jan/1}) will translate at compile time to days{134774} which will further compile-time-convert to whatever units the full-expression requires (seconds, 100-nanoseconds, whatever). Bottom line: This is both very readable and very efficient.
The solution that divides and adds will not work correctly with daylight savings.
Here is a snippet that works, but it is for windows.
time_t FileTime_to_POSIX(FILETIME ft)
{
FILETIME localFileTime;
FileTimeToLocalFileTime(&ft,&localFileTime);
SYSTEMTIME sysTime;
FileTimeToSystemTime(&localFileTime,&sysTime);
struct tm tmtime = {0};
tmtime.tm_year = sysTime.wYear - 1900;
tmtime.tm_mon = sysTime.wMonth - 1;
tmtime.tm_mday = sysTime.wDay;
tmtime.tm_hour = sysTime.wHour;
tmtime.tm_min = sysTime.wMinute;
tmtime.tm_sec = sysTime.wSecond;
tmtime.tm_wday = 0;
tmtime.tm_yday = 0;
tmtime.tm_isdst = -1;
time_t ret = mktime(&tmtime);
return ret;
}
Assuming you are asking about the FILETIME Structure, then FileTimeToSystemTime does what you want, you can get the seconds from the SYSTEMTIME structure it produces.
Here's essentially the same solution except this one encodes negative numbers from Ldap properly and lops off the last 7 digits before conversion.
public static int LdapValueAsUnixTimestamp(SearchResult searchResult, string fieldName)
{
var strValue = LdapValue(searchResult, fieldName);
if (strValue == "0") return 0;
if (strValue == "9223372036854775807") return -1;
return (int)(long.Parse(strValue.Substring(0, strValue.Length - 7)) - 11644473600);
}
If somebody need convert it in MySQL
SELECT timestamp,
FROM_UNIXTIME(ROUND((((timestamp) / CAST(10000000 AS UNSIGNED INTEGER)))
- CAST(11644473600 AS UNSIGNED INTEGER),0))
AS Converted FROM events LIMIT 100
Also here's a pure C#ian way to do it.
(Int32)(DateTime.FromFileTimeUtc(129477880901875000).Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
Here's the result of both methods in my immediate window:
(Int32)(DateTime.FromFileTimeUtc(long.Parse(strValue)).Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
1303314490
(int)(long.Parse(strValue.Substring(0, strValue.Length - 7)) - 11644473600)
1303314490
DateTime.FromFileTimeUtc(long.Parse(strValue))
{2011-04-20 3:48:10 PM}
Date: {2011-04-20 12:00:00 AM}
Day: 20
DayOfWeek: Wednesday
DayOfYear: 110
Hour: 15
InternalKind: 4611686018427387904
InternalTicks: 634389112901875000
Kind: Utc
Millisecond: 187
Minute: 48
Month: 4
Second: 10
Ticks: 634389112901875000
TimeOfDay: {System.TimeSpan}
Year: 2011
dateData: 5246075131329262904