Skip to content

Instantly share code, notes, and snippets.

@HowardHinnant
Created August 1, 2018 21:40
Show Gist options
  • Save HowardHinnant/3c560d747dea6c91b4081da331f640cb to your computer and use it in GitHub Desktop.
Save HowardHinnant/3c560d747dea6c91b4081da331f640cb to your computer and use it in GitHub Desktop.
Example implementation of filesystem::file_clock which covers the range and precision of ext4 (https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout)
#include "date/tz.h"
#include <ostream>
#include <istream>
namespace filesystem
{
struct file_clock
{
using duration = std::chrono::nanoseconds;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<file_clock>;
static constexpr bool is_steady = false;
static time_point now();
template<typename Duration>
static
std::chrono::time_point<std::chrono::system_clock, Duration>
to_sys(const std::chrono::time_point<file_clock, Duration>& t) noexcept;
template<typename Duration>
static
std::chrono::time_point<file_clock, Duration>
from_sys(const std::chrono::time_point<std::chrono::system_clock, Duration>& t) noexcept;
template<typename Duration>
static
std::chrono::time_point<date::local_t, Duration>
to_local(const std::chrono::time_point<file_clock, Duration>& t) noexcept;
template<typename Duration>
static
std::chrono::time_point<file_clock, Duration>
from_local(const std::chrono::time_point<date::local_t, Duration>& t) noexcept;
// private helpers
static
timespec
to_timespec(const time_point& t) noexcept;
static
time_point
from_timespec(const timespec& t) noexcept;
};
template <class Duration>
using file_time = std::chrono::time_point<file_clock, Duration>;
using file_time_type = file_clock::time_point;
template <class Duration>
inline
std::chrono::time_point<std::chrono::system_clock, Duration>
file_clock::to_sys(const std::chrono::time_point<file_clock, Duration>& t) noexcept
{
using namespace date;
return sys_time<Duration>{t.time_since_epoch()} +
(sys_days{2174_y/1/1} - sys_days{1970_y/1/1});
}
template <class Duration>
inline
std::chrono::time_point<file_clock, Duration>
file_clock::from_sys(const std::chrono::time_point<std::chrono::system_clock, Duration>& t) noexcept
{
using namespace date;
return file_time<Duration>{t.time_since_epoch()} -
(sys_days{2174_y/1/1} - sys_days{1970_y/1/1});
}
template <class Duration>
inline
std::chrono::time_point<date::local_t, Duration>
file_clock::to_local(const std::chrono::time_point<file_clock, Duration>& t) noexcept
{
using namespace date;
return local_time<Duration>{to_sys(t).time_since_epoch()};
}
template <class Duration>
inline
std::chrono::time_point<file_clock, Duration>
file_clock::from_local(const std::chrono::time_point<date::local_t, Duration>& t) noexcept
{
using namespace date;
return file_time<Duration>{from_sys(sys_time<Duration>{t.time_since_epoch()})};
}
file_clock::time_point
file_clock::now()
{
return from_sys(std::chrono::system_clock::now());
}
template <class CharT, class Traits, class Duration>
std::basic_ostream<CharT, Traits>&
to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
const file_time<Duration>& t)
{
using namespace std::chrono;
const std::string abbrev("UTC");
constepxr std::chrono::seconds offset{0};
using D128 = duration<__int128, typename Duration::period>;
return date::to_stream(os, fmt, file_clock::to_local(time_point_cast<D128>(t)),
&abbrev, &offset);
}
template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>>
std::basic_istream<CharT, Traits>&
from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
file_time<Duration>& tp,
std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
std::chrono::minutes* offset = nullptr)
{
using namespace date;
using namespace std::chrono;
using D128 = duration<__int128, typename Duration::period>;
local_time<D128> lp;
from_stream(is, fmt, lp, abbrev, offset);
if (!is.fail())
tp = file_clock::from_local(lp);
return is;
}
template <class CharT, class Traits, class Duration>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const file_time<Duration>& t)
{
const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}};
return to_stream(os, fmt, t);
}
inline
timespec
file_clock::to_timespec(const time_point& t) noexcept
{
using namespace date;
using namespace std::chrono;
auto tp = to_sys(time_point_cast<std::chrono::duration<__int128, std::nano>>(t));
auto s = floor<seconds>(tp);
timespec ts;
ts.tv_sec = static_cast<decltype(ts.tv_sec)>(s.time_since_epoch().count());
ts.tv_nsec = static_cast<decltype(ts.tv_nsec)>((tp - s).count());
return ts;
}
inline
file_clock::time_point
file_clock::from_timespec(const timespec& t) noexcept
{
using namespace date;
using namespace std::chrono;
auto d = std::chrono::duration<__int128>{t.tv_sec} + nanoseconds{t.tv_nsec};
return time_point_cast<duration>(from_sys(sys_time<decltype(d)>{d}));
}
} // namespace filesystem
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
std::cout << filesystem::file_clock::time_point::min() << '\n';
std::cout << filesystem::file_clock::now() << '\n';
std::cout << filesystem::file_clock::time_point::max() << '\n';
std::istringstream in{"2466-04-11 23:47:16.854775807"};
filesystem::file_clock::time_point tp;
in >> date::parse("%F %T", tp);
cout << tp << '\n';
in.clear();
in.str("1881-09-22 00:12:43.145224192");
in >> date::parse("%F %T", tp);
cout << tp << '\n';
timespec ts = {15661036036, 854775807}; // or {-2785708037, 145224192}
tp = filesystem::file_clock::from_timespec(ts);
cout << tp << '\n';
ts = filesystem::file_clock::to_timespec(tp);
cout << "{" << ts.tv_sec << ", " << ts.tv_nsec << "}\n";
}
@HowardHinnant
Copy link
Author

Example output:

1881-09-22 00:12:43.145224192
2018-08-01 21:32:51.875962000
2466-04-11 23:47:16.854775807
2466-04-11 23:47:16.854775807
1881-09-22 00:12:43.145224192
2466-04-11 23:47:16.854775807
{15661036036, 854775807}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment