icinga2/lib/base/datetime.cpp
Julian Brost 5404143dee Ensure consistent mktime() DST behavior across different implementations
There are inputs to mktime() where the behavior is not specified and there's
also no single obviously correct behavior. In particular, this affects how
auto-detection of whether DST is in effect is done when tm_isdst = -1 is set
and the time specified does not exist at all or exists twice on that day.

If different implementations are used within an Icinga 2 cluster, that can lead
to inconsistent behavior because different nodes may interpret the same
TimePeriod differently.

This commit introduces a wrapper to mktime(), namely Utility::NormalizeTm()
that implements the behavior provided by glibc. The choice for glibc's behavior
is pretty arbitrary, it was simply picked because most systems that are
officially/fully supported use it (with the only exception being Windows), so
this should give the least possible amount of user-visible changes.

As part of this commit, the closely related helper function mktime_const() is
also moved to Utility::TmToTimestamp() and made a wrapper around the newly
introduced NormalizeTm().
2025-04-28 13:38:55 +02:00

59 lines
1.2 KiB
C++

/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "base/datetime.hpp"
#include "base/datetime-ti.cpp"
#include "base/utility.hpp"
#include "base/primitivetype.hpp"
using namespace icinga;
REGISTER_TYPE_WITH_PROTOTYPE(DateTime, DateTime::GetPrototype());
DateTime::DateTime(double value)
: m_Value(value)
{ }
DateTime::DateTime(const std::vector<Value>& args)
{
if (args.empty())
m_Value = Utility::GetTime();
else if (args.size() == 3 || args.size() == 6) {
struct tm tms;
tms.tm_year = args[0] - 1900;
tms.tm_mon = args[1] - 1;
tms.tm_mday = args[2];
if (args.size() == 6) {
tms.tm_hour = args[3];
tms.tm_min = args[4];
tms.tm_sec = args[5];
} else {
tms.tm_hour = 0;
tms.tm_min = 0;
tms.tm_sec = 0;
}
tms.tm_isdst = -1;
m_Value = Utility::TmToTimestamp(&tms);
} else if (args.size() == 1)
m_Value = args[0];
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid number of arguments for the DateTime constructor."));
}
double DateTime::GetValue() const
{
return m_Value;
}
String DateTime::Format(const String& format) const
{
return Utility::FormatDateTime(format.CStr(), m_Value);
}
String DateTime::ToString() const
{
return Format("%Y-%m-%d %H:%M:%S %z");
}