mirror of
https://github.com/znc/znc.git
synced 2026-08-10 19:02:51 +02:00
Utils: Add FormatTime overload taking timeval
This overload also supports additional format sequences for formatting the sub-second part of timeval.
This commit is contained in:
@@ -79,6 +79,23 @@ class CUtils {
|
||||
static CString CTime(time_t t, const CString& sTZ);
|
||||
static CString FormatTime(time_t t, const CString& sFormat,
|
||||
const CString& sTZ);
|
||||
/** Supports additional format specifiers for formatting sub-second values:
|
||||
*
|
||||
* - %L - millisecond, 3 digits (ruby extension)
|
||||
* - %N - sub-second fraction (ruby extension)
|
||||
* - %3N - millisecond
|
||||
* - %6N - microsecond
|
||||
* - %9N - nanosecond (default if no digit specified)
|
||||
*
|
||||
* However, note that timeval only supports microsecond precision
|
||||
* (thus, formatting with higher-than-microsecond precision will
|
||||
* always result in trailing zeroes), and IRC server-time is specified
|
||||
* in millisecond precision (thus formatting received timestamps with
|
||||
* higher-than-millisecond precision will always result in trailing
|
||||
* zeroes).
|
||||
*/
|
||||
static CString FormatTime(const timeval& tv, const CString& sFormat,
|
||||
const CString& sTZ);
|
||||
static CString FormatServerTime(const timeval& tv);
|
||||
static timeval ParseServerTime(const CString& sTime);
|
||||
static SCString GetTimezones();
|
||||
|
||||
@@ -499,6 +499,67 @@ CString CUtils::FormatTime(time_t t, const CString& sFormat,
|
||||
return s;
|
||||
}
|
||||
|
||||
CString CUtils::FormatTime(const timeval& tv, const CString& sFormat,
|
||||
const CString& sTimezone) {
|
||||
// Parse additional format specifiers before passing them to
|
||||
// strftime, since the way strftime treats unknown format
|
||||
// specifiers is undefined.
|
||||
CString sFormat2;
|
||||
|
||||
// Make sure %% is parsed correctly, i.e. %%L is passed through to
|
||||
// strftime to become %L, and not 123.
|
||||
bool bInFormat = false;
|
||||
int iDigits;
|
||||
CString::size_type uLastCopied = 0, uFormatStart;
|
||||
|
||||
for (CString::size_type i = 0; i < sFormat.length(); i++) {
|
||||
if (!bInFormat) {
|
||||
if (sFormat[i] == '%') {
|
||||
uFormatStart = i;
|
||||
bInFormat = true;
|
||||
iDigits = 9;
|
||||
}
|
||||
} else {
|
||||
switch (sFormat[i]) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
iDigits = sFormat[i] - '0';
|
||||
break;
|
||||
case 'L':
|
||||
iDigits = 3;
|
||||
// fall-through
|
||||
case 'N': {
|
||||
int iVal = tv.tv_usec;
|
||||
int iDigitDelta = iDigits - 6; // tv_user is in 10^-6 seconds
|
||||
for (; iDigitDelta > 0; iDigitDelta--)
|
||||
iVal *= 10;
|
||||
for (; iDigitDelta < 0; iDigitDelta++)
|
||||
iVal /= 10;
|
||||
sFormat2 += sFormat.substr(uLastCopied,
|
||||
uFormatStart - uLastCopied);
|
||||
CString sVal = CString(iVal);
|
||||
sFormat2 += CString(iDigits - sVal.length(), '0');
|
||||
sFormat2 += sVal;
|
||||
uLastCopied = i + 1;
|
||||
bInFormat = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
bInFormat = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uLastCopied) {
|
||||
sFormat2 += sFormat.substr(uLastCopied);
|
||||
return FormatTime(tv.tv_sec, sFormat2, sTimezone);
|
||||
} else {
|
||||
// If there are no extended format specifiers, avoid doing any
|
||||
// memory allocations entirely.
|
||||
return FormatTime(tv.tv_sec, sFormat, sTimezone);
|
||||
}
|
||||
}
|
||||
|
||||
CString CUtils::FormatServerTime(const timeval& tv) {
|
||||
CString s_msec(tv.tv_usec / 1000);
|
||||
while (s_msec.length() < 3) {
|
||||
|
||||
@@ -117,3 +117,46 @@ TEST(UtilsTest, ServerTime) {
|
||||
}
|
||||
tzset();
|
||||
}
|
||||
|
||||
TEST(UtilsTest, FormatTime) {
|
||||
struct timeTest {
|
||||
int sec, usec;
|
||||
CString sResultL, sResultN, sResult3N;
|
||||
};
|
||||
timeTest aTimeTests[] = {
|
||||
{42, 12345, "42.012", "42.012345000", "42.012"}, // leading zeroes
|
||||
{42, 999999, "42.999", "42.999999000", "42.999"}, // (no) rounding
|
||||
{42, 0, "42.000", "42.000000000", "42.000"}, // no tv_usec part
|
||||
};
|
||||
|
||||
for (const timeTest& t : aTimeTests) {
|
||||
timeval tv;
|
||||
tv.tv_sec = t.sec;
|
||||
tv.tv_usec = t.usec;
|
||||
|
||||
CString strL = CUtils::FormatTime(tv, "%s.%L", "UTC");
|
||||
EXPECT_EQ(t.sResultL, strL);
|
||||
CString strN = CUtils::FormatTime(tv, "%s.%N", "UTC");
|
||||
EXPECT_EQ(t.sResultN, strN);
|
||||
CString str3N = CUtils::FormatTime(tv, "%s.%3N", "UTC");
|
||||
EXPECT_EQ(t.sResult3N, str3N);
|
||||
}
|
||||
|
||||
// Test passthrough
|
||||
timeval tv1;
|
||||
tv1.tv_sec = 42;
|
||||
tv1.tv_usec = 123456;
|
||||
CString str1 = CUtils::FormatTime(tv1, "%s", "UTC");
|
||||
EXPECT_EQ("42", str1);
|
||||
|
||||
// Test escapes
|
||||
timeval tv2;
|
||||
tv2.tv_sec = 42;
|
||||
tv2.tv_usec = 123456;
|
||||
CString str2 = CUtils::FormatTime(tv2, "%%L", "UTC");
|
||||
EXPECT_EQ("%L", str2);
|
||||
|
||||
// Test suffix
|
||||
CString str3 = CUtils::FormatTime(tv2, "a%Lb", "UTC");
|
||||
EXPECT_EQ("a123b", str3);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user