mirror of
https://github.com/znc/znc.git
synced 2026-08-09 10:23:14 +02:00
Merge pull request #1857 from DarthGandalf/cctz
Improve handling timezones
This commit is contained in:
+4
-1
@@ -1,9 +1,12 @@
|
||||
[submodule "Csocket"]
|
||||
path = third_party/Csocket
|
||||
url = https://github.com/jimloco/Csocket.git
|
||||
url = https://github.com/znc/Csocket
|
||||
[submodule "third_party/googletest"]
|
||||
path = third_party/googletest
|
||||
url = https://github.com/google/googletest
|
||||
[submodule "docker"]
|
||||
path = docker
|
||||
url = https://github.com/znc/znc-docker
|
||||
[submodule "third_party/cctz"]
|
||||
path = third_party/cctz
|
||||
url = https://github.com/DarthGandalf/cctz
|
||||
|
||||
+23
-5
@@ -269,6 +269,29 @@ else()
|
||||
set(CSOCK_USE_POLL true)
|
||||
endif()
|
||||
|
||||
# TODO: verify that this find_package works; on Gentoo it's not currently (Jan
|
||||
# 2023) packaged, so in my tests it always falls back to submodule
|
||||
find_package(cctz QUIET)
|
||||
set(cctz_cc "")
|
||||
if (NOT cctz_FOUND)
|
||||
set(cctz_cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/civil_time_detail.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_fixed.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_format.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_if.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_impl.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_info.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_libc.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_lookup.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/third_party/cctz/src/zone_info_source.cc
|
||||
)
|
||||
add_library(cctz INTERFACE EXCLUDE_FROM_ALL)
|
||||
add_library(cctz::cctz ALIAS cctz)
|
||||
target_include_directories(cctz INTERFACE
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/third_party/cctz/include>)
|
||||
endif()
|
||||
|
||||
check_cxx_symbol_exists(getopt_long "getopt.h" HAVE_GETOPT_LONG)
|
||||
check_cxx_symbol_exists(lstat "sys/types.h;sys/stat.h;unistd.h" HAVE_LSTAT)
|
||||
check_cxx_symbol_exists(getpassphrase "stdlib.h" HAVE_GETPASSPHRASE)
|
||||
@@ -430,8 +453,3 @@ render_framed_multiline("${summary_lines}")
|
||||
message("")
|
||||
message("Now you can run 'make' to compile ZNC")
|
||||
message("")
|
||||
|
||||
# TODO
|
||||
# ====
|
||||
#
|
||||
# remove old configure.ac and Makefile.in
|
||||
|
||||
Vendored
+2
@@ -3,6 +3,8 @@
|
||||
timestamps {
|
||||
node('freebsd') {
|
||||
// freebsd 13.1 + pkg install git openjdk17 cmake icu pkgconf swig python3 boost-libs gettext-tools qt5-buildtools qt5-network qt5-qmake
|
||||
// Then fill known_hosts with https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
|
||||
// (needed for crowdin cron job in jenkins)
|
||||
timeout(time: 30, unit: 'MINUTES') {
|
||||
def wsdir = pwd()
|
||||
stage('Checkout') {
|
||||
|
||||
@@ -43,6 +43,8 @@ echo "Exporting . to $TMPDIR/$ZNCDIR..."
|
||||
git checkout-index --all --prefix=$TMPDIR/$ZNCDIR/
|
||||
mkdir -p --mode=0755 $TMPDIR/$ZNCDIR/third_party/Csocket
|
||||
cp -p third_party/Csocket/Csocket.cc third_party/Csocket/Csocket.h $TMPDIR/$ZNCDIR/third_party/Csocket/
|
||||
mkdir -p --mode=0755 $TMPDIR/$ZNCDIR/third_party/cctz
|
||||
cp -Rp third_party/cctz/src third_party/cctz/include third_party/cctz/LICENSE.txt $TMPDIR/$ZNCDIR/third_party/cctz/
|
||||
(
|
||||
cd $TMPDIR2
|
||||
cmake $TMPDIR/$ZNCDIR -DWANT_PERL=yes -DWANT_PYTHON=yes
|
||||
|
||||
+8
-6
@@ -25,7 +25,8 @@ set(znc_cpp "ZNCString.cpp" "znc.cpp" "IRCNetwork.cpp" "Translation.cpp"
|
||||
"HTTPSock.cpp" "Template.cpp" "ClientCommand.cpp" "Socket.cpp"
|
||||
"SHA256.cpp" "WebModules.cpp" "Listener.cpp" "Config.cpp" "ZNCDebug.cpp"
|
||||
"Threads.cpp" "Query.cpp" "SSLVerifyHost.cpp" "Message.cpp" "User.cpp")
|
||||
znc_add_library(znclib ${lib_type} ${znc_cpp} "Csocket.cpp" "versionc.cpp")
|
||||
znc_add_library(znclib ${lib_type} ${znc_cpp} "Csocket.cpp" "versionc.cpp"
|
||||
${cctz_cc})
|
||||
znc_add_executable(znc "main.cpp")
|
||||
target_link_libraries(znc PRIVATE znclib)
|
||||
|
||||
@@ -64,23 +65,24 @@ set(znc_include_dirs
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_FULL_INCLUDEDIR}>")
|
||||
target_link_libraries(znclib ${CMAKE_DL_LIBS} Threads::Threads)
|
||||
target_link_libraries(znclib PRIVATE ${CMAKE_DL_LIBS} Threads::Threads)
|
||||
if(OPENSSL_FOUND)
|
||||
target_link_libraries(znclib ${OPENSSL_LIBRARIES})
|
||||
target_link_libraries(znclib PUBLIC ${OPENSSL_LIBRARIES})
|
||||
list(APPEND znc_include_dirs "${OPENSSL_INCLUDE_DIR}")
|
||||
endif()
|
||||
if(ZLIB_FOUND)
|
||||
target_link_libraries(znclib ${ZLIB_LIBRARIES})
|
||||
target_link_libraries(znclib PRIVATE ${ZLIB_LIBRARIES})
|
||||
list(APPEND znc_include_dirs ${ZLIB_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(ICU_FOUND)
|
||||
target_link_libraries(znclib ${ICU_LDFLAGS})
|
||||
target_link_libraries(znclib PUBLIC ${ICU_LDFLAGS})
|
||||
list(APPEND znc_include_dirs ${ICU_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(Boost_FOUND)
|
||||
target_link_libraries(znclib ${Boost_LIBRARIES})
|
||||
target_link_libraries(znclib PRIVATE ${Boost_LIBRARIES})
|
||||
list(APPEND znc_include_dirs ${Boost_INCLUDE_DIRS})
|
||||
endif()
|
||||
target_link_libraries(znclib PRIVATE cctz::cctz)
|
||||
target_include_directories(znc PUBLIC ${znc_include_dirs})
|
||||
target_include_directories(znclib PUBLIC ${znc_include_dirs})
|
||||
|
||||
|
||||
+24
-103
@@ -56,6 +56,9 @@
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
#include "cctz/time_zone.h"
|
||||
|
||||
using std::map;
|
||||
using std::vector;
|
||||
@@ -395,29 +398,6 @@ void CUtils::PrintStatus(bool bSuccess, const CString& sMessage) {
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/* Switch GMT-X and GMT+X
|
||||
*
|
||||
* See https://en.wikipedia.org/wiki/Tz_database#Area
|
||||
*
|
||||
* "In order to conform with the POSIX style, those zone names beginning
|
||||
* with "Etc/GMT" have their sign reversed from what most people expect.
|
||||
* In this style, zones west of GMT have a positive sign and those east
|
||||
* have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours
|
||||
* ahead/east of GMT.)"
|
||||
*/
|
||||
inline CString FixGMT(CString sTZ) {
|
||||
if (sTZ.length() >= 4 && sTZ.StartsWith("GMT")) {
|
||||
if (sTZ[3] == '+') {
|
||||
sTZ[3] = '-';
|
||||
} else if (sTZ[3] == '-') {
|
||||
sTZ[3] = '+';
|
||||
}
|
||||
}
|
||||
return sTZ;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
timeval CUtils::GetTime() {
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
timespec ts;
|
||||
@@ -436,72 +416,27 @@ timeval CUtils::GetTime() {
|
||||
}
|
||||
|
||||
unsigned long long CUtils::GetMillTime() {
|
||||
struct timeval tv = GetTime();
|
||||
unsigned long long iTime = 0;
|
||||
iTime = (unsigned long long)tv.tv_sec * 1000;
|
||||
iTime += ((unsigned long long)tv.tv_usec / 1000);
|
||||
return iTime;
|
||||
std::chrono::time_point<std::chrono::steady_clock> time = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch()).count();
|
||||
}
|
||||
|
||||
CString CUtils::CTime(time_t t, const CString& sTimezone) {
|
||||
char s[30] = {}; // should have at least 26 bytes
|
||||
if (sTimezone.empty()) {
|
||||
ctime_r(&t, s);
|
||||
// ctime() adds a trailing newline
|
||||
return CString(s).Trim_n();
|
||||
}
|
||||
CString sTZ = FixGMT(sTimezone);
|
||||
|
||||
// backup old value
|
||||
char* oldTZ = getenv("TZ");
|
||||
if (oldTZ) oldTZ = strdup(oldTZ);
|
||||
setenv("TZ", sTZ.c_str(), 1);
|
||||
tzset();
|
||||
|
||||
ctime_r(&t, s);
|
||||
|
||||
// restore old value
|
||||
if (oldTZ) {
|
||||
setenv("TZ", oldTZ, 1);
|
||||
free(oldTZ);
|
||||
} else {
|
||||
unsetenv("TZ");
|
||||
}
|
||||
tzset();
|
||||
|
||||
return CString(s).Trim_n();
|
||||
return FormatTime(t, "%c", sTimezone);
|
||||
}
|
||||
|
||||
CString CUtils::FormatTime(time_t t, const CString& sFormat,
|
||||
const CString& sTimezone) {
|
||||
char s[1024] = {};
|
||||
tm m;
|
||||
cctz::time_zone tz;
|
||||
if (sTimezone.empty()) {
|
||||
localtime_r(&t, &m);
|
||||
strftime(s, sizeof(s), sFormat.c_str(), &m);
|
||||
return s;
|
||||
}
|
||||
CString sTZ = FixGMT(sTimezone);
|
||||
|
||||
// backup old value
|
||||
char* oldTZ = getenv("TZ");
|
||||
if (oldTZ) oldTZ = strdup(oldTZ);
|
||||
setenv("TZ", sTZ.c_str(), 1);
|
||||
tzset();
|
||||
|
||||
localtime_r(&t, &m);
|
||||
strftime(s, sizeof(s), sFormat.c_str(), &m);
|
||||
|
||||
// restore old value
|
||||
if (oldTZ) {
|
||||
setenv("TZ", oldTZ, 1);
|
||||
free(oldTZ);
|
||||
tz = cctz::local_time_zone();
|
||||
} else if (sTimezone.StartsWith("GMT")) {
|
||||
int offset = CString(sTimezone.substr(3)).ToInt();
|
||||
tz = cctz::fixed_time_zone(cctz::seconds(offset * 60 * 60));
|
||||
} else {
|
||||
unsetenv("TZ");
|
||||
cctz::load_time_zone(sTimezone, &tz);
|
||||
}
|
||||
tzset();
|
||||
|
||||
return s;
|
||||
return cctz::format(sFormat, std::chrono::system_clock::from_time_t(t), tz);
|
||||
}
|
||||
|
||||
CString CUtils::FormatTime(const timeval& tv, const CString& sFormat,
|
||||
@@ -509,6 +444,7 @@ CString CUtils::FormatTime(const timeval& tv, const CString& sFormat,
|
||||
// Parse additional format specifiers before passing them to
|
||||
// strftime, since the way strftime treats unknown format
|
||||
// specifiers is undefined.
|
||||
// TODO: consider using cctz's %E#f instead.
|
||||
CString sFormat2;
|
||||
|
||||
// Make sure %% is parsed correctly, i.e. %%f is passed through to
|
||||
@@ -563,36 +499,21 @@ CString CUtils::FormatTime(const timeval& tv, const CString& sFormat,
|
||||
}
|
||||
|
||||
CString CUtils::FormatServerTime(const timeval& tv) {
|
||||
CString s_msec(tv.tv_usec / 1000);
|
||||
while (s_msec.length() < 3) {
|
||||
s_msec = "0" + s_msec;
|
||||
}
|
||||
// TODO support leap seconds properly
|
||||
// TODO support message-tags properly
|
||||
struct tm stm;
|
||||
memset(&stm, 0, sizeof(stm));
|
||||
// OpenBSD has tv_sec as int, so explicitly convert it to time_t to make
|
||||
// gmtime_r() happy
|
||||
const time_t secs = tv.tv_sec;
|
||||
gmtime_r(&secs, &stm);
|
||||
char sTime[20] = {};
|
||||
strftime(sTime, sizeof(sTime), "%Y-%m-%dT%H:%M:%S", &stm);
|
||||
return CString(sTime) + "." + s_msec + "Z";
|
||||
using namespace std::chrono;
|
||||
system_clock::time_point time{duration_cast<system_clock::duration>(
|
||||
seconds(tv.tv_sec) + microseconds(tv.tv_usec))};
|
||||
return cctz::format("%Y-%m-%dT%H:%M:%E3SZ", time, cctz::utc_time_zone());
|
||||
}
|
||||
|
||||
timeval CUtils::ParseServerTime(const CString& sTime) {
|
||||
struct tm stm;
|
||||
memset(&stm, 0, sizeof(stm));
|
||||
const char* cp = strptime(sTime.c_str(), "%Y-%m-%dT%H:%M:%S", &stm);
|
||||
using namespace std::chrono;
|
||||
system_clock::time_point tp;
|
||||
cctz::parse("%Y-%m-%dT%H:%M:%E*SZ", sTime, cctz::utc_time_zone(), &tp);
|
||||
struct timeval tv;
|
||||
memset(&tv, 0, sizeof(tv));
|
||||
if (cp) {
|
||||
tv.tv_sec = mktime(&stm);
|
||||
CString s_usec(cp);
|
||||
if (s_usec.TrimPrefix(".") && s_usec.TrimSuffix("Z")) {
|
||||
tv.tv_usec = s_usec.ToULong() * 1000;
|
||||
}
|
||||
}
|
||||
microseconds usec = duration_cast<microseconds>(tp.time_since_epoch());
|
||||
tv.tv_sec = usec.count() / 1000000;
|
||||
tv.tv_usec = usec.count() % 1000000;
|
||||
return tv;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,29 @@ TEST(IRC32, SetMessageTags) {
|
||||
EXPECT_EQ(sLine, R"(@a=\:\s\\\r\n :rest)");
|
||||
}
|
||||
|
||||
TEST(UtilsTest, Timezone) {
|
||||
char* oldTZ = getenv("TZ");
|
||||
if (oldTZ) oldTZ = strdup(oldTZ);
|
||||
setenv("TZ", "Europe/Berlin", 1);
|
||||
tzset();
|
||||
|
||||
EXPECT_EQ(CUtils::FormatTime(1673122230, "%Y-%m-%d %H:%M:%S", "UTC"), "2023-01-07 20:10:30");
|
||||
EXPECT_EQ(CUtils::FormatTime(1673122230, "%Y-%m-%d %H:%M:%S", "GMT"), "2023-01-07 20:10:30");
|
||||
EXPECT_EQ(CUtils::FormatTime(1673122230, "%Y-%m-%d %H:%M:%S", "GMT+7"), "2023-01-08 03:10:30");
|
||||
EXPECT_EQ(CUtils::FormatTime(1673122230, "%Y-%m-%d %H:%M:%S", "GMT-7"), "2023-01-07 13:10:30");
|
||||
EXPECT_EQ(CUtils::FormatTime(1673122230, "%Y-%m-%d %H:%M:%S", "Asia/Vladivostok"), "2023-01-08 06:10:30");
|
||||
// Local TZ, set to Berlin in this test
|
||||
EXPECT_EQ(CUtils::FormatTime(1673122230, "%Y-%m-%d %H:%M:%S", ""), "2023-01-07 21:10:30");
|
||||
|
||||
if (oldTZ) {
|
||||
setenv("TZ", oldTZ, 1);
|
||||
free(oldTZ);
|
||||
} else {
|
||||
unsetenv("TZ");
|
||||
}
|
||||
tzset();
|
||||
}
|
||||
|
||||
TEST(UtilsTest, ServerTime) {
|
||||
char* oldTZ = getenv("TZ");
|
||||
if (oldTZ) oldTZ = strdup(oldTZ);
|
||||
@@ -118,6 +141,26 @@ TEST(UtilsTest, ServerTime) {
|
||||
tzset();
|
||||
}
|
||||
|
||||
TEST(UtilsTest, ParseServerTime) {
|
||||
char* oldTZ = getenv("TZ");
|
||||
if (oldTZ) oldTZ = strdup(oldTZ);
|
||||
setenv("TZ", "America/Montreal", 1);
|
||||
tzset();
|
||||
|
||||
timeval tv4 = CUtils::ParseServerTime("2011-10-19T16:40:51.620Z");
|
||||
CString str4 = CUtils::FormatServerTime(tv4);
|
||||
EXPECT_EQ(str4, "2011-10-19T16:40:51.620Z");
|
||||
|
||||
|
||||
if (oldTZ) {
|
||||
setenv("TZ", oldTZ, 1);
|
||||
free(oldTZ);
|
||||
} else {
|
||||
unsetenv("TZ");
|
||||
}
|
||||
tzset();
|
||||
}
|
||||
|
||||
class TimeTest : public testing::TestWithParam<
|
||||
std::tuple<timeval, CString, CString, CString>> {};
|
||||
|
||||
|
||||
Vendored
+1
-1
Submodule third_party/Csocket updated: e8d9e0bb24...81d27e6137
+1
Submodule third_party/cctz added at cf0cd21f71
Reference in New Issue
Block a user