2018-04-06 14:49:33 +00:00
|
|
|
#include "Time.hpp"
|
|
|
|
|
2019-09-11 10:13:59 +00:00
|
|
|
#include <iomanip>
|
|
|
|
#include <sstream>
|
|
|
|
#include <chrono>
|
2019-09-24 08:48:24 +00:00
|
|
|
#include <cassert>
|
|
|
|
#include <ctime>
|
|
|
|
#include <cstdio>
|
2019-09-11 10:13:59 +00:00
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#include <map>
|
|
|
|
#endif
|
2019-09-11 10:13:59 +00:00
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
#include "libslic3r/Utils.hpp"
|
2018-04-10 14:27:42 +00:00
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
namespace Slic3r {
|
|
|
|
namespace Utils {
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
// "YYYY-MM-DD at HH:MM::SS [UTC]"
|
|
|
|
// If TimeZone::utc is used with the conversion functions, it will append the
|
|
|
|
// UTC letters to the end.
|
|
|
|
static const constexpr char *const SLICER_UTC_TIME_FMT = "%Y-%m-%d at %T";
|
|
|
|
|
|
|
|
// ISO8601Z representation of time, without time zone info
|
|
|
|
static const constexpr char *const ISO8601Z_TIME_FMT = "%Y%m%dT%H%M%SZ";
|
2019-09-11 10:13:59 +00:00
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
static const char * get_fmtstr(TimeFormat fmt)
|
2019-09-11 10:13:59 +00:00
|
|
|
{
|
2019-09-24 08:48:24 +00:00
|
|
|
switch (fmt) {
|
|
|
|
case TimeFormat::gcode: return SLICER_UTC_TIME_FMT;
|
|
|
|
case TimeFormat::iso8601Z: return ISO8601Z_TIME_FMT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
2019-09-11 10:13:59 +00:00
|
|
|
}
|
2019-09-24 08:48:24 +00:00
|
|
|
|
|
|
|
namespace __get_put_time_emulation {
|
|
|
|
// FIXME: Implementations with the cpp11 put_time and get_time either not
|
|
|
|
// compile or do not pass the tests on the build server. If we switch to newer
|
|
|
|
// compilers, this namespace can be deleted with all its content.
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
// VS2019 implementation fails with ISO8601Z_TIME_FMT.
|
|
|
|
// VS2019 does not have std::strptime either. See bug:
|
|
|
|
// https://developercommunity.visualstudio.com/content/problem/140618/c-stdget-time-not-parsing-correctly.html
|
|
|
|
|
|
|
|
static const std::map<std::string, std::string> sscanf_fmt_map = {
|
|
|
|
{SLICER_UTC_TIME_FMT, "%04d-%02d-%02d at %02d:%02d:%02d"},
|
|
|
|
{std::string(SLICER_UTC_TIME_FMT) + " UTC", "%04d-%02d-%02d at %02d:%02d:%02d UTC"},
|
|
|
|
{ISO8601Z_TIME_FMT, "%04d%02d%02dT%02d%02d%02dZ"}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char * strptime(const char *str, const char *const fmt, std::tm *tms)
|
2019-09-11 10:13:59 +00:00
|
|
|
{
|
2019-09-24 08:48:24 +00:00
|
|
|
auto it = sscanf_fmt_map.find(fmt);
|
|
|
|
if (it == sscanf_fmt_map.end()) return nullptr;
|
|
|
|
|
|
|
|
int y, M, d, h, m, s;
|
|
|
|
if (sscanf(str, it->second.c_str(), &y, &M, &d, &h, &m, &s) != 6)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
tms->tm_year = y - 1900; // Year since 1900
|
|
|
|
tms->tm_mon = M - 1; // 0-11
|
|
|
|
tms->tm_mday = d; // 1-31
|
|
|
|
tms->tm_hour = h; // 0-23
|
|
|
|
tms->tm_min = m; // 0-59
|
|
|
|
tms->tm_sec = s; // 0-61 (0-60 in C++11)
|
|
|
|
|
|
|
|
return str; // WARN strptime return val should point after the parsed string
|
2019-09-11 10:13:59 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
template<class Ttm>
|
|
|
|
struct GetPutTimeReturnT {
|
|
|
|
Ttm *tms;
|
|
|
|
const char *fmt;
|
|
|
|
GetPutTimeReturnT(Ttm *_tms, const char *_fmt): tms(_tms), fmt(_fmt) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
using GetTimeReturnT = GetPutTimeReturnT<std::tm>;
|
|
|
|
using PutTimeReturnT = GetPutTimeReturnT<const std::tm>;
|
|
|
|
|
|
|
|
std::ostream &operator<<(std::ostream &stream, PutTimeReturnT &&pt)
|
|
|
|
{
|
|
|
|
static const constexpr int MAX_CHARS = 200;
|
|
|
|
char _out[MAX_CHARS];
|
|
|
|
strftime(_out, MAX_CHARS, pt.fmt, pt.tms);
|
|
|
|
stream << _out;
|
|
|
|
return stream;
|
2019-09-11 10:13:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
inline PutTimeReturnT put_time(const std::tm *tms, const char *fmt)
|
2018-04-06 14:49:33 +00:00
|
|
|
{
|
2019-09-24 08:48:24 +00:00
|
|
|
return {tms, fmt};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::istream &operator>>(std::istream &stream, GetTimeReturnT &>)
|
|
|
|
{
|
|
|
|
std::string line;
|
|
|
|
std::getline(stream, line);
|
|
|
|
|
|
|
|
if (strptime(line.c_str(), gt.fmt, gt.tms) == nullptr)
|
|
|
|
stream.setstate(std::ios::failbit);
|
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline GetTimeReturnT get_time(std::tm *tms, const char *fmt)
|
|
|
|
{
|
|
|
|
return {tms, fmt};
|
|
|
|
}
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
}
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Platform independent versions of gmtime and localtime. Completely thread
|
|
|
|
// safe only on Linux. MSVC gtime_s and localtime_s sets global errno thus not
|
|
|
|
// thread safe.
|
|
|
|
struct std::tm * _gmtime_r(const time_t *timep, struct tm *result)
|
2018-04-06 14:49:33 +00:00
|
|
|
{
|
2019-09-24 08:48:24 +00:00
|
|
|
assert(timep != nullptr && result != nullptr);
|
2018-04-06 14:49:33 +00:00
|
|
|
#ifdef WIN32
|
2019-09-24 08:48:24 +00:00
|
|
|
time_t t = *timep;
|
|
|
|
gmtime_s(result, &t);
|
|
|
|
return result;
|
2018-04-06 14:49:33 +00:00
|
|
|
#else
|
2019-09-24 08:48:24 +00:00
|
|
|
return gmtime_r(timep, result);
|
2018-04-06 14:49:33 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
struct std::tm * _localtime_r(const time_t *timep, struct tm *result)
|
2018-04-10 14:27:42 +00:00
|
|
|
{
|
2019-09-24 08:48:24 +00:00
|
|
|
assert(timep != nullptr && result != nullptr);
|
2018-04-10 14:27:42 +00:00
|
|
|
#ifdef WIN32
|
2019-09-24 08:48:24 +00:00
|
|
|
// Converts a time_t time value to a tm structure, and corrects for the
|
|
|
|
// local time zone.
|
|
|
|
time_t t = *timep;
|
|
|
|
localtime_s(result, &t);
|
|
|
|
return result;
|
2018-04-10 14:27:42 +00:00
|
|
|
#else
|
2019-09-24 08:48:24 +00:00
|
|
|
return localtime_r(timep, result);
|
2018-04-10 14:27:42 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
time_t _mktime(const struct std::tm *tms)
|
|
|
|
{
|
|
|
|
assert(tms != nullptr);
|
|
|
|
std::tm _tms = *tms;
|
|
|
|
return mktime(&_tms);
|
|
|
|
}
|
|
|
|
|
|
|
|
time_t _timegm(const struct std::tm *tms)
|
|
|
|
{
|
|
|
|
std::tm _tms = *tms;
|
|
|
|
#ifdef WIN32
|
|
|
|
return _mkgmtime(&_tms);
|
|
|
|
#else /* WIN32 */
|
|
|
|
return timegm(&_tms);
|
|
|
|
#endif /* WIN32 */
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string process_format(const char *fmt, TimeZone zone)
|
|
|
|
{
|
|
|
|
std::string fmtstr(fmt);
|
|
|
|
|
|
|
|
if (fmtstr == SLICER_UTC_TIME_FMT && zone == TimeZone::utc)
|
|
|
|
fmtstr += " UTC";
|
|
|
|
|
|
|
|
return fmtstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
time_t get_current_time_utc()
|
2019-09-24 08:48:24 +00:00
|
|
|
{
|
2019-09-11 10:13:59 +00:00
|
|
|
using clk = std::chrono::system_clock;
|
|
|
|
return clk::to_time_t(clk::now());
|
|
|
|
}
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
static std::string tm2str(const std::tm *tms, const char *fmt)
|
2018-04-06 14:49:33 +00:00
|
|
|
{
|
2019-09-11 10:13:59 +00:00
|
|
|
std::stringstream ss;
|
2019-09-24 08:48:24 +00:00
|
|
|
ss.imbue(std::locale("C"));
|
|
|
|
ss << __get_put_time_emulation::put_time(tms, fmt);
|
2019-09-11 10:13:59 +00:00
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
std::string time2str(const time_t &t, TimeZone zone, TimeFormat fmt)
|
2019-09-11 10:13:59 +00:00
|
|
|
{
|
|
|
|
std::string ret;
|
2019-09-24 08:48:24 +00:00
|
|
|
std::tm tms = {};
|
|
|
|
tms.tm_isdst = -1;
|
|
|
|
std::string fmtstr = process_format(get_fmtstr(fmt), zone);
|
|
|
|
|
|
|
|
switch (zone) {
|
|
|
|
case TimeZone::local:
|
|
|
|
ret = tm2str(_localtime_r(&t, &tms), fmtstr.c_str()); break;
|
|
|
|
case TimeZone::utc:
|
|
|
|
ret = tm2str(_gmtime_r(&t, &tms), fmtstr.c_str()); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static time_t str2time(std::istream &stream, TimeZone zone, const char *fmt)
|
|
|
|
{
|
|
|
|
std::tm tms = {};
|
|
|
|
tms.tm_isdst = -1;
|
|
|
|
|
|
|
|
stream >> __get_put_time_emulation::get_time(&tms, fmt);
|
|
|
|
time_t ret = time_t(-1);
|
|
|
|
|
2019-09-11 10:13:59 +00:00
|
|
|
switch (zone) {
|
2019-09-24 08:48:24 +00:00
|
|
|
case TimeZone::local: ret = _mktime(&tms); break;
|
|
|
|
case TimeZone::utc: ret = _timegm(&tms); break;
|
2019-09-11 10:13:59 +00:00
|
|
|
}
|
2019-09-24 08:48:24 +00:00
|
|
|
|
|
|
|
if (stream.fail() || ret < time_t(0)) ret = time_t(-1);
|
|
|
|
|
2019-09-11 10:13:59 +00:00
|
|
|
return ret;
|
2018-04-06 14:49:33 +00:00
|
|
|
}
|
|
|
|
|
2019-09-24 08:48:24 +00:00
|
|
|
time_t str2time(const std::string &str, TimeZone zone, TimeFormat fmt)
|
|
|
|
{
|
|
|
|
std::string fmtstr = process_format(get_fmtstr(fmt), zone).c_str();
|
|
|
|
std::stringstream ss(str);
|
|
|
|
|
|
|
|
ss.imbue(std::locale("C"));
|
|
|
|
return str2time(ss, zone, fmtstr.c_str());
|
|
|
|
}
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
}; // namespace Utils
|
|
|
|
}; // namespace Slic3r
|