draconisplusplus/include/rfl/Timestamp.hpp

95 lines
2.5 KiB
C++
Raw Normal View History

2024-05-31 22:59:00 -04:00
#ifndef RFL_TIMESTAMP_HPP_
#define RFL_TIMESTAMP_HPP_
#include <ctime>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <locale>
#include <sstream>
#include <stdexcept>
#include <string>
#include "Result.hpp"
#include "internal/StringLiteral.hpp"
namespace rfl {
2024-06-08 14:10:59 -04:00
/// For serializing and deserializing time stamps.
template <internal::StringLiteral _format>
class Timestamp {
constexpr static const internal::StringLiteral format_ = _format;
public:
using Format = rfl::Literal<_format>;
using ReflectionType = std::string;
Timestamp(const char* _str) : tm_(std::tm {}) {
const auto r = strptime(_str, _format.str().c_str(), &tm_);
if (r == NULL) {
2024-06-08 15:53:06 -04:00
throw std::runtime_error(
2024-06-16 00:13:15 -04:00
"String '" + std::string(_str) + "' did not match format '" + Format().str() + "'."
2024-06-08 15:53:06 -04:00
);
2024-06-08 14:10:59 -04:00
}
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
Timestamp(const std::string& _str) : Timestamp(_str.c_str()) {}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
Timestamp(const std::tm& _tm) : tm_(_tm) {}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
~Timestamp() = default;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Returns a result containing the timestamp when successful or an Error
/// otherwise.
static Result<Timestamp> from_string(const char* _str) noexcept {
try {
return Timestamp(_str);
} catch (std::exception& e) { return Error(e.what()); }
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
/// Returns a result containing the timestamp when successful or an Error
/// otherwise.
static Result<Timestamp> from_string(const std::string& _str) {
return from_string(_str.c_str());
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Necessary for the serialization to work.
ReflectionType reflection() const {
char outstr[200];
strftime(outstr, 200, format_.str().c_str(), &tm_);
return std::string(outstr);
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Expresses the underlying timestamp as a string.
std::string str() const { return reflection(); }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Trivial accessor to the underlying time stamp.
std::tm& tm() { return tm_; }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Trivial (const) accessor to the underlying time stamp.
const std::tm& tm() const { return tm_; }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
private:
2024-05-31 22:59:00 -04:00
#ifdef _MSC_VER
2024-06-08 14:10:59 -04:00
// This workaround is necessary, because MSVC doesn't support strptime.
char* strptime(const char* _s, const char* _f, std::tm* _tm) {
std::istringstream input(_s);
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
input >> std::get_time(_tm, _f);
2024-06-09 18:55:00 -04:00
if (input.fail()) {
return NULL;
}
2024-06-08 14:10:59 -04:00
return (char*)(_s + input.tellg());
2024-05-31 22:59:00 -04:00
}
#endif
2024-06-08 14:10:59 -04:00
private:
/// The underlying time stamp.
std::tm tm_;
};
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
} // namespace rfl
2024-05-31 22:59:00 -04:00
#endif