diff options
Diffstat (limited to 'butl/timestamp.cxx')
-rw-r--r-- | butl/timestamp.cxx | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/butl/timestamp.cxx b/butl/timestamp.cxx new file mode 100644 index 0000000..2d02416 --- /dev/null +++ b/butl/timestamp.cxx @@ -0,0 +1,177 @@ +// file : butl/timestamp.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <butl/timestamp> + +#include <unistd.h> // stat +#include <sys/types.h> // stat +#include <sys/stat.h> // stat + +#include <time.h> // localtime, gmtime, strftime + +#include <ostream> +#include <system_error> + +using namespace std; + +namespace butl +{ + // Figuring out whether we have the nanoseconds in some form. + // + template <typename S> + constexpr auto nsec (const S* s) -> decltype(s->st_mtim.tv_nsec) + { + return s->st_mtim.tv_nsec; // POSIX (GNU/Linux, Solaris). + } + + template <typename S> + constexpr auto nsec (const S* s) -> decltype(s->st_mtimespec.tv_nsec) + { + return s->st_mtimespec.tv_nsec; // MacOS X. + } + + template <typename S> + constexpr auto nsec (const S* s) -> decltype(s->st_mtime_n) + { + return s->st_mtime_n; // AIX 5.2 and later. + } + + template <typename S> + constexpr int nsec (...) {return 0;} + + timestamp + path_mtime (const path& p) + { + struct stat s; + if (stat (p.string ().c_str (), &s) != 0) + { + if (errno == ENOENT || errno == ENOTDIR) + return timestamp_nonexistent; + else + throw system_error (errno, system_category ()); + } + + return system_clock::from_time_t (s.st_mtime) + + chrono::duration_cast<duration> ( + chrono::nanoseconds (nsec<struct stat> (&s))); + } + + ostream& + operator<< (ostream& os, const timestamp& ts) + { + // @@ replace with put_time() + // + + time_t t (system_clock::to_time_t (ts)); + + if (t == 0) + return os << "<nonexistent>"; + + std::tm tm; + if (localtime_r (&t, &tm) == nullptr) + throw system_error (errno, system_category ()); + + // If year is greater than 9999, we will overflow. + // + char buf[20]; // YYYY-MM-DD HH:MM:SS\0 + if (strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S", &tm) == 0) + return os << "<beyond year 9999>"; + + os << buf; + + using namespace chrono; + + timestamp sec (system_clock::from_time_t (t)); + nanoseconds ns (duration_cast<nanoseconds> (ts - sec)); + + if (ns != nanoseconds::zero ()) + { + os << '.'; + os.width (9); + os.fill ('0'); + os << ns.count (); + } + + return os; + } + + ostream& + operator<< (ostream& os, const duration& d) + { + // @@ replace with put_time() + // + + timestamp ts; // Epoch. + ts += d; + + time_t t (system_clock::to_time_t (ts)); + + const char* fmt (nullptr); + const char* unt ("nanoseconds"); + if (t >= 365 * 12 * 24 * 60 * 60) + { + fmt = "%Y-%m-%d %H:%M:%S"; + unt = "years"; + } + else if (t >= 12 * 24 * 60* 60) + { + fmt = "%m-%d %H:%M:%S"; + unt = "months"; + } + else if (t >= 24 * 60* 60) + { + fmt = "%d %H:%M:%S"; + unt = "days"; + } + else if (t >= 60 * 60) + { + fmt = "%H:%M:%S"; + unt = "hours"; + } + else if (t >= 60) + { + fmt = "%M:%S"; + unt = "minutes"; + } + else if (t >= 1) + { + fmt = "%S"; + unt = "seconds"; + } + + if (fmt != nullptr) + { + std::tm tm; + if (gmtime_r (&t, &tm) == nullptr) + throw system_error (errno, system_category ()); + + char buf[20]; // YYYY-MM-DD HH:MM:SS\0 + if (strftime (buf, sizeof (buf), fmt, &tm) == 0) + return os << "<beyond 9999>"; + + os << buf; + } + + using namespace chrono; + + timestamp sec (system_clock::from_time_t (t)); + nanoseconds ns (duration_cast<nanoseconds> (ts - sec)); + + if (ns != nanoseconds::zero ()) + { + if (fmt != nullptr) + { + os << '.'; + os.width (9); + os.fill ('0'); + } + + os << ns.count () << ' ' << unt; + } + else if (fmt == 0) + os << '0'; + + return os; + } +} |