Home | History | Annotate | Download | only in fmt
      1 /*
      2  Formatting library for C++ - time formatting
      3 
      4  Copyright (c) 2012 - 2016, Victor Zverovich
      5  All rights reserved.
      6 
      7  For the license information refer to format.h.
      8  */
      9 
     10 #ifndef FMT_TIME_H_
     11 #define FMT_TIME_H_
     12 
     13 #include "format.h"
     14 #include <ctime>
     15 
     16 #ifdef _MSC_VER
     17 # pragma warning(push)
     18 # pragma warning(disable: 4702)  // unreachable code
     19 # pragma warning(disable: 4996)  // "deprecated" functions
     20 #endif
     21 
     22 namespace fmt {
     23 template <typename ArgFormatter>
     24 void format_arg(BasicFormatter<char, ArgFormatter> &f,
     25                 const char *&format_str, const std::tm &tm) {
     26   if (*format_str == ':')
     27     ++format_str;
     28   const char *end = format_str;
     29   while (*end && *end != '}')
     30     ++end;
     31   if (*end != '}')
     32     FMT_THROW(FormatError("missing '}' in format string"));
     33   internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
     34   format.append(format_str, end + 1);
     35   format[format.size() - 1] = '\0';
     36   Buffer<char> &buffer = f.writer().buffer();
     37   std::size_t start = buffer.size();
     38   for (;;) {
     39     std::size_t size = buffer.capacity() - start;
     40     std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
     41     if (count != 0) {
     42       buffer.resize(start + count);
     43       break;
     44     }
     45     if (size >= format.size() * 256) {
     46       // If the buffer is 256 times larger than the format string, assume
     47       // that `strftime` gives an empty result. There doesn't seem to be a
     48       // better way to distinguish the two cases:
     49       // https://github.com/fmtlib/fmt/issues/367
     50       break;
     51     }
     52     const std::size_t MIN_GROWTH = 10;
     53     buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
     54   }
     55   format_str = end + 1;
     56 }
     57 
     58 namespace internal{
     59 inline Null<> localtime_r(...) { return Null<>(); }
     60 inline Null<> localtime_s(...) { return Null<>(); }
     61 inline Null<> gmtime_r(...) { return Null<>(); }
     62 inline Null<> gmtime_s(...) { return Null<>(); }
     63 }
     64 
     65 // Thread-safe replacement for std::localtime
     66 inline std::tm localtime(std::time_t time) {
     67   struct LocalTime {
     68     std::time_t time_;
     69     std::tm tm_;
     70 
     71     LocalTime(std::time_t t): time_(t) {}
     72 
     73     bool run() {
     74       using namespace fmt::internal;
     75       return handle(localtime_r(&time_, &tm_));
     76     }
     77 
     78     bool handle(std::tm *tm) { return tm != FMT_NULL; }
     79 
     80     bool handle(internal::Null<>) {
     81       using namespace fmt::internal;
     82       return fallback(localtime_s(&tm_, &time_));
     83     }
     84 
     85     bool fallback(int res) { return res == 0; }
     86 
     87     bool fallback(internal::Null<>) {
     88       using namespace fmt::internal;
     89       std::tm *tm = std::localtime(&time_);
     90       if (tm) tm_ = *tm;
     91       return tm != FMT_NULL;
     92     }
     93   };
     94   LocalTime lt(time);
     95   if (lt.run())
     96     return lt.tm_;
     97   // Too big time values may be unsupported.
     98   FMT_THROW(fmt::FormatError("time_t value out of range"));
     99   return std::tm();
    100 }
    101 
    102 // Thread-safe replacement for std::gmtime
    103 inline std::tm gmtime(std::time_t time) {
    104   struct GMTime {
    105     std::time_t time_;
    106     std::tm tm_;
    107 
    108     GMTime(std::time_t t): time_(t) {}
    109 
    110     bool run() {
    111       using namespace fmt::internal;
    112       return handle(gmtime_r(&time_, &tm_));
    113     }
    114 
    115     bool handle(std::tm *tm) { return tm != FMT_NULL; }
    116 
    117     bool handle(internal::Null<>) {
    118       using namespace fmt::internal;
    119       return fallback(gmtime_s(&tm_, &time_));
    120     }
    121 
    122     bool fallback(int res) { return res == 0; }
    123 
    124     bool fallback(internal::Null<>) {
    125       std::tm *tm = std::gmtime(&time_);
    126       if (tm != FMT_NULL) tm_ = *tm;
    127       return tm != FMT_NULL;
    128     }
    129   };
    130   GMTime gt(time);
    131   if (gt.run())
    132     return gt.tm_;
    133   // Too big time values may be unsupported.
    134   FMT_THROW(fmt::FormatError("time_t value out of range"));
    135   return std::tm();
    136 }
    137 } //namespace fmt
    138 
    139 #ifdef _MSC_VER
    140 # pragma warning(pop)
    141 #endif
    142 
    143 #endif  // FMT_TIME_H_
    144