Skip to content

Instantly share code, notes, and snippets.

@Anime-pdf
Created November 27, 2024 16:57
Show Gist options
  • Save Anime-pdf/6416b3d1220ad1c220ab27b52ddbffda to your computer and use it in GitHub Desktop.
Save Anime-pdf/6416b3d1220ad1c220ab27b52ddbffda to your computer and use it in GitHub Desktop.
single-file timeperiod data holder
#ifndef BASE_UTILS_TIMEPERIOD_H
#define BASE_UTILS_TIMEPERIOD_H
#include <base/system.h>
#include <base/utils.h>
#include <base/math.h>
#include <regex>
#include <string>
class CTimePeriodData
{
uint64_t m_Days;
uint64_t m_Hours;
uint64_t m_Minutes;
uint64_t m_Seconds;
void validate()
{
m_Seconds = maximum<uint64_t>(0, m_Seconds);
m_Minutes = maximum<uint64_t>(0, m_Minutes);
m_Hours = maximum<uint64_t>(0, m_Hours);
m_Days = maximum<uint64_t>(0, m_Days);
m_Seconds = asUnit(SECONDS);
m_Minutes = m_Seconds / 60;
m_Seconds %= 60;
m_Hours = m_Minutes / 60;
m_Minutes %= 60;
m_Days = m_Hours / 24;
m_Hours %= 24;
}
static uint64_t NumberBeforeChar(const std::string &Str, const char c)
{
std::string tmp;
for(size_t i = Str.find(c); i > 0 && i != std::string::npos; --i)
{
if(isdigit(Str[i - 1]))
tmp.insert(tmp.begin(), Str[i - 1]);
else
{
tmp.insert(tmp.begin(), '0');
break;
}
}
return str_toint64_base(tmp.c_str());
}
public:
explicit CTimePeriodData(const uint64_t Seconds = 0, const uint64_t Minutes = 0, const uint64_t Hours = 0, const uint64_t Days = 0)
{
m_Days = Days;
m_Hours = Hours;
m_Minutes = Minutes;
m_Seconds = Seconds;
validate();
}
explicit CTimePeriodData(const std::string &From)
{
if(!parse(From))
m_Days = m_Hours = m_Minutes = m_Seconds = 0;
}
bool parse(const std::string &From)
{
if(!std::regex_search(From, std::regex("\\d[dhms]")))
{
m_Days = m_Hours = m_Minutes = m_Seconds = 0;
return false;
}
m_Days = NumberBeforeChar(From, 'd');
m_Hours = NumberBeforeChar(From, 'h');
m_Minutes = NumberBeforeChar(From, 'm');
m_Seconds = NumberBeforeChar(From, 's');
validate();
return true;
}
enum EUnit
{
SECONDS = 0,
MINUTES,
HOURS,
DAYS,
};
static const char *UnitString(const EUnit Unit)
{
switch(Unit)
{
case SECONDS:
return "second";
case MINUTES:
return "minute";
case HOURS:
return "hour";
case DAYS:
return "day";
}
return "second";
}
std::string asSqlInterval(const EUnit Unit = SECONDS, const EUnit Precision = SECONDS) const
{
return string_format("interval %s %s", std::to_string(asUnit(Unit, Precision)).c_str(), UnitString(Precision));
}
uint64_t asUnit(const EUnit Unit = SECONDS, const EUnit precision = SECONDS) const
{
uint64_t TotalSeconds = 0;
if(precision == DAYS)
{
TotalSeconds = (m_Days * 24 * 60 * 60);
}
else if(precision == HOURS)
{
TotalSeconds = (m_Days * 24 * 60 * 60) + (m_Hours * 60 * 60);
}
else if(precision == MINUTES)
{
TotalSeconds = (m_Days * 24 * 60 * 60) + (m_Hours * 60 * 60) + (m_Minutes * 60);
}
else if(precision == SECONDS)
{
TotalSeconds = (m_Days * 24 * 60 * 60) + (m_Hours * 60 * 60) + (m_Minutes * 60) + m_Seconds;
}
if(Unit == DAYS)
{
return TotalSeconds / (24 * 60 * 60);
}
if(Unit == HOURS)
{
return TotalSeconds / (60 * 60);
}
if(Unit == MINUTES)
{
return TotalSeconds / 60;
}
if(Unit == SECONDS)
{
return TotalSeconds;
}
return 0;
}
bool isZero() const
{
return (asUnit(SECONDS) == 0);
}
std::string asFancy(const EUnit precision = SECONDS) const
{
std::ostringstream Oss;
bool AddAnd = false;
if (precision <= DAYS && m_Days > 0) {
Oss << m_Days << " day" << (m_Days != 1 ? "s" : "");
AddAnd = true;
}
if (precision <= HOURS && m_Hours > 0) {
if (AddAnd) Oss << (precision == HOURS ? " and " : ", ");
Oss << m_Hours << " hour" << (m_Hours != 1 ? "s" : "");
AddAnd = true;
}
if (precision <= MINUTES && m_Minutes > 0) {
if (AddAnd) Oss << (precision == MINUTES ? " and " : ", ");
Oss << m_Minutes << " minute" << (m_Minutes != 1 ? "s" : "");
AddAnd = true;
}
if (precision <= SECONDS && m_Seconds > 0) {
if (AddAnd) Oss << " and ";
Oss << m_Seconds << " second" << (m_Seconds != 1 ? "s" : "");
}
if(Oss.str().empty())
Oss << string_format("0 %ss", UnitString(precision));
return Oss.str();
}
};
#endif // BASE_UTILS_TIMEPERIOD_H

Hold time period data. Can print as fancy string, as sql interval and return as given time unit with given precision.

I'm too lazy writing examples, here are some unit tests I use that will show expected output:

FancyStr

	CTimePeriodData FromStr = CTimePeriodData("6d49h2m121s");
	ASSERT_EQ(FromStr.asFancy(CTimePeriodData::SECONDS), "8 days, 1 hour, 4 minutes and 1 second");
	ASSERT_EQ(FromStr.asFancy(CTimePeriodData::MINUTES), "8 days, 1 hour and 4 minutes");
	ASSERT_EQ(FromStr.asFancy(CTimePeriodData::HOURS), "8 days and 1 hour");
	ASSERT_EQ(FromStr.asFancy(CTimePeriodData::DAYS), "8 days");

	CTimePeriodData FromStr2 = CTimePeriodData("9h2m121s");
	ASSERT_EQ(FromStr2.asFancy(CTimePeriodData::SECONDS), "9 hours, 4 minutes and 1 second");
	ASSERT_EQ(FromStr2.asFancy(CTimePeriodData::MINUTES), "9 hours and 4 minutes");
	ASSERT_EQ(FromStr2.asFancy(CTimePeriodData::HOURS), "9 hours");
	ASSERT_EQ(FromStr2.asFancy(CTimePeriodData::DAYS), "0 days");

	CTimePeriodData FromStr3 = CTimePeriodData("2m0s");
	ASSERT_EQ(FromStr3.asFancy(CTimePeriodData::SECONDS), "2 minutes");
	ASSERT_EQ(FromStr3.asFancy(CTimePeriodData::MINUTES), "2 minutes");
	ASSERT_EQ(FromStr3.asFancy(CTimePeriodData::HOURS), "0 hours");
	ASSERT_EQ(FromStr3.asFancy(CTimePeriodData::DAYS), "0 days");

AsUnit

	CTimePeriodData FromStr = CTimePeriodData("6d49h2m121s");
	ASSERT_EQ(FromStr.asUnit(CTimePeriodData::SECONDS), (8 * 24 * 60 * 60) + (1 * 60 * 60) + (4 * 60) + 1);
	ASSERT_EQ(FromStr.asUnit(CTimePeriodData::MINUTES), (8 * 24 * 60) + (1 * 60) + 4);
	ASSERT_EQ(FromStr.asUnit(CTimePeriodData::HOURS), (8 * 24) + 1);
	ASSERT_EQ(FromStr.asUnit(CTimePeriodData::DAYS), 8);

	CTimePeriodData FromStr2 = CTimePeriodData("9h2m121s");
	ASSERT_EQ(FromStr2.asUnit(CTimePeriodData::SECONDS), (9 * 60 * 60) + (4 * 60) + 1);
	ASSERT_EQ(FromStr2.asUnit(CTimePeriodData::MINUTES), (9 * 60) + 4);
	ASSERT_EQ(FromStr2.asUnit(CTimePeriodData::HOURS), 9);
	ASSERT_EQ(FromStr2.asUnit(CTimePeriodData::DAYS), 0);

	CTimePeriodData FromStr3 = CTimePeriodData("2m0s");
	ASSERT_EQ(FromStr3.asUnit(CTimePeriodData::SECONDS), 120);
	ASSERT_EQ(FromStr3.asUnit(CTimePeriodData::MINUTES), 2);
	ASSERT_EQ(FromStr3.asUnit(CTimePeriodData::HOURS), 0);
	ASSERT_EQ(FromStr3.asUnit(CTimePeriodData::DAYS), 0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment