From b8247095d1f4609d7cb92e72871125f5c5695f2c Mon Sep 17 00:00:00 2001 From: Allan Odgaard Date: Sun, 9 Oct 2011 15:48:03 +0200 Subject: [PATCH 1/3] Introduce time zone (string) setting. Presently unused but intended to supersede the current time zone offset. The advantage of a string is that it can be set to something like Europe/Copenhagen and thereby adapt to daylight savings time. --- include/znc/User.h | 3 +++ modules/admin.cpp | 7 +++++++ modules/webadmin.cpp | 2 ++ src/User.cpp | 6 ++++++ 4 files changed, 18 insertions(+) diff --git a/include/znc/User.h b/include/znc/User.h index 1fb9075a..b7ee96be 100644 --- a/include/znc/User.h +++ b/include/znc/User.h @@ -123,6 +123,7 @@ public: void SetTimestampFormat(const CString& s) { m_sTimestampFormat = s; } void SetTimestampAppend(bool b) { m_bAppendTimestamp = b; } void SetTimestampPrepend(bool b) { m_bPrependTimestamp = b; } + void SetTimezone(const CString& s) { m_sTimezone = s; } void SetTimezoneOffset(float b) { m_fTimezoneOffset = b; } void SetJoinTries(unsigned int i) { m_uMaxJoinTries = i; } void SetMaxJoins(unsigned int i) { m_uMaxJoins = i; } @@ -164,6 +165,7 @@ public: unsigned int GetBufferCount() const; bool KeepBuffer() const; bool IsBeingDeleted() const { return m_bBeingDeleted; } + CString GetTimezone() const { return m_sTimezone; } float GetTimezoneOffset() const { return m_fTimezoneOffset; } unsigned long long BytesRead() const { return m_uBytesRead; } unsigned long long BytesWritten() const { return m_uBytesWritten; } @@ -189,6 +191,7 @@ protected: CString m_sQuitMsg; MCString m_mssCTCPReplies; CString m_sTimestampFormat; + CString m_sTimezone; float m_fTimezoneOffset; eHashType m_eHashType; diff --git a/modules/admin.cpp b/modules/admin.cpp index c1954cb4..4a61780b 100644 --- a/modules/admin.cpp +++ b/modules/admin.cpp @@ -59,6 +59,7 @@ class CAdminMod : public CModule { {"Password", str}, {"JoinTries", integer}, {"MaxJoins", integer}, + {"Timezone", str}, {"TimezoneOffset", doublenum}, {"Admin", boolean}, {"AppendTimestamp", boolean}, @@ -158,6 +159,8 @@ class CAdminMod : public CModule { PutModule("MaxJoins = " + CString(pUser->MaxJoins())); else if (sVar == "jointries") PutModule("JoinTries = " + CString(pUser->JoinTries())); + else if (sVar == "timezone") + PutModule("Timezone = " + pUser->GetTimezone()); else if (sVar == "timezoneoffset") PutModule("TimezoneOffset = " + CString(pUser->GetTimezoneOffset())); else if (sVar == "appendtimestamp") @@ -276,6 +279,10 @@ class CAdminMod : public CModule { pUser->SetJoinTries(i); PutModule("JoinTries = " + CString(pUser->JoinTries())); } + else if (sVar == "timezone") { + pUser->SetTimezone(sValue); + PutModule("Timezone = " + pUser->GetTimezone()); + } else if (sVar == "timezoneoffset") { double d = sValue.ToDouble(); pUser->SetTimezoneOffset(d); diff --git a/modules/webadmin.cpp b/modules/webadmin.cpp index c4cce0eb..fb8e820c 100644 --- a/modules/webadmin.cpp +++ b/modules/webadmin.cpp @@ -229,6 +229,7 @@ public: pNewUser->SetMultiClients(WebSock.GetParam("multiclients").ToBool()); pNewUser->SetTimestampAppend(WebSock.GetParam("appendtimestamp").ToBool()); pNewUser->SetTimestampPrepend(WebSock.GetParam("prependtimestamp").ToBool()); + pNewUser->SetTimezone(WebSock.GetParam("timezone")); pNewUser->SetTimezoneOffset(WebSock.GetParam("timezoneoffset").ToDouble()); pNewUser->SetJoinTries(WebSock.GetParam("jointries").ToUInt()); pNewUser->SetMaxJoins(WebSock.GetParam("maxjoins").ToUInt()); @@ -868,6 +869,7 @@ public: Tmpl["DefaultChanModes"] = pUser->GetDefaultChanModes(); Tmpl["BufferCount"] = CString(pUser->GetBufferCount()); Tmpl["TimestampFormat"] = pUser->GetTimestampFormat(); + Tmpl["Timezone"] = pUser->GetTimezone(); Tmpl["TimezoneOffset"] = CString(pUser->GetTimezoneOffset()); Tmpl["JoinTries"] = CString(pUser->JoinTries()); Tmpl["MaxJoins"] = CString(pUser->MaxJoins()); diff --git a/src/User.cpp b/src/User.cpp index 92927909..3852db44 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -61,6 +61,7 @@ CUser::CUser(const CString& sUserName) // set path that depends on the user name: m_sUserPath = CZNC::Get().GetUserPath() + "/" + m_sUserName; + m_sTimezone = "GMT"; m_fTimezoneOffset = 0; m_sNick = m_sCleanUserName; m_sIdent = m_sCleanUserName; @@ -221,6 +222,9 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { return false; } } + if (pConfig->FindStringEntry("timezone", sValue)) { + SetTimezone(sValue); + } if (pConfig->FindStringEntry("timezoneoffset", sValue)) { SetTimezoneOffset(sValue.ToDouble()); } @@ -662,6 +666,7 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneChans) { SetTimestampAppend(User.GetTimestampAppend()); SetTimestampPrepend(User.GetTimestampPrepend()); SetTimestampFormat(User.GetTimestampFormat()); + SetTimezone(User.GetTimezone()); SetTimezoneOffset(User.GetTimezoneOffset()); // !Flags @@ -812,6 +817,7 @@ CConfig CUser::ToConfig() { config.AddKeyValuePair("TimestampFormat", GetTimestampFormat()); config.AddKeyValuePair("AppendTimestamp", CString(GetTimestampAppend())); config.AddKeyValuePair("PrependTimestamp", CString(GetTimestampPrepend())); + config.AddKeyValuePair("Timezone", m_sTimezone); config.AddKeyValuePair("TimezoneOffset", CString(m_fTimezoneOffset)); config.AddKeyValuePair("JoinTries", CString(m_uMaxJoinTries)); config.AddKeyValuePair("MaxJoins", CString(m_uMaxJoins)); From eca0211bb8da0ae3bff1de4acce7149f96100061 Mon Sep 17 00:00:00 2001 From: Allan Odgaard Date: Sun, 9 Oct 2011 16:51:19 +0200 Subject: [PATCH 2/3] Use time zone string instead of offset. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is done by setting the TZ variable prior to calling localtime() or ctime(). There are a few calls to ctime()/localtime() which doesn’t adjust for time zone offset. This patch affects the behavior of these since the TZ variable is global. --- modules/extra/listsockets.cpp | 3 +-- modules/log.cpp | 4 ++-- modules/simple_away.cpp | 2 +- src/User.cpp | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/extra/listsockets.cpp b/modules/extra/listsockets.cpp index c45c8255..625c6ccd 100644 --- a/modules/extra/listsockets.cpp +++ b/modules/extra/listsockets.cpp @@ -248,8 +248,7 @@ public: tm = time(NULL); } - // offset is in hours - tm += (time_t)(m_pUser->GetTimezoneOffset() * 60 * 60); + setenv("TZ", m_pUser->GetTimezone().c_str(), 1); strftime(szTimestamp, sizeof(szTimestamp) / sizeof(char), sFormat.c_str(), localtime(&tm)); diff --git a/modules/log.cpp b/modules/log.cpp index 20065dde..73061484 100644 --- a/modules/log.cpp +++ b/modules/log.cpp @@ -62,8 +62,8 @@ void CLogMod::PutLog(const CString& sLine, const CString& sWindow /*= "Status"*/ char buffer[1024]; time(&curtime); - // Don't forget the user's timezone offset (which is in hours and we want seconds) - curtime += (time_t) (m_pUser->GetTimezoneOffset() * 60 * 60); + // Don't forget the user's timezone offset + setenv("TZ", m_pUser->GetTimezone().c_str(), 1); timeinfo = localtime(&curtime); // Generate file name diff --git a/modules/simple_away.cpp b/modules/simple_away.cpp index bb695c8f..a49447fc 100644 --- a/modules/simple_away.cpp +++ b/modules/simple_away.cpp @@ -189,7 +189,7 @@ private: sReason = SIMPLE_AWAY_DEFAULT_REASON; time_t iTime = time(NULL); - iTime += (time_t)(m_pUser->GetTimezoneOffset() * 60 * 60); // offset is in hours + setenv("TZ", m_pUser->GetTimezone().c_str(), 1); CString sTime = ctime(&iTime); sTime.Trim(); sReason.Replace("%s", sTime); diff --git a/src/User.cpp b/src/User.cpp index 3852db44..e03460f3 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -500,8 +500,8 @@ CString CUser::ExpandString(const CString& sStr) const { } CString& CUser::ExpandString(const CString& sStr, CString& sRet) const { - // offset is in hours, so * 60 * 60 gets us seconds - time_t iUserTime = time(NULL) + (time_t)(m_fTimezoneOffset * 60 * 60); + time_t iUserTime = time(NULL); + setenv("TZ", m_sTimezone.c_str(), 1); char *szTime = ctime(&iUserTime); CString sTime; @@ -543,7 +543,7 @@ CString& CUser::AddTimestamp(const CString& sStr, CString& sRet) const { if (!GetTimestampFormat().empty() && (m_bAppendTimestamp || m_bPrependTimestamp)) { time(&tm); - tm += (time_t)(m_fTimezoneOffset * 60 * 60); // offset is in hours + setenv("TZ", m_sTimezone.c_str(), 1); size_t i = strftime(szTimestamp, sizeof(szTimestamp), GetTimestampFormat().c_str(), localtime(&tm)); // If strftime returns 0, an error occured in format, or result is empty // In both cases just don't prepend/append anything to our string From 355d5feb7aca641dd60307d483a33f0e73edeb86 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Thu, 22 Mar 2012 22:20:41 +0700 Subject: [PATCH 3/3] Move TZ-related code to own place. --- include/znc/Utils.h | 3 ++ modules/extra/listsockets.cpp | 15 +--------- modules/log.cpp | 15 ++-------- modules/simple_away.cpp | 4 +-- src/User.cpp | 27 ++++-------------- src/Utils.cpp | 54 +++++++++++++++++++++++++++++++++++ 6 files changed, 68 insertions(+), 50 deletions(-) diff --git a/include/znc/Utils.h b/include/znc/Utils.h index 88a88ab6..e37dbff6 100644 --- a/include/znc/Utils.h +++ b/include/znc/Utils.h @@ -73,6 +73,9 @@ public: static void GenerateCert(FILE *pOut, const CString& sHost = ""); #endif /* HAVE_LIBSSL */ + static CString CTime(time_t t, const CString& sTZ); + static CString FormatTime(time_t t, const CString& sFormat, const CString& sTZ); + private: protected: }; diff --git a/modules/extra/listsockets.cpp b/modules/extra/listsockets.cpp index 625c6ccd..f7c1330f 100644 --- a/modules/extra/listsockets.cpp +++ b/modules/extra/listsockets.cpp @@ -155,7 +155,7 @@ public: CString GetCreatedTime(Csock* pSocket) { unsigned long long iStartTime = pSocket->GetStartTime(); time_t iTime = iStartTime / 1000; - return FormatTime("%Y-%m-%d %H:%M:%S", iTime); + return CUtils::FormatTime(iTime, "%Y-%m-%d %H:%M:%S", m_pUser->GetTimezone()); } CString GetLocalHost(Csock* pSocket, bool bShowHosts) { @@ -241,19 +241,6 @@ public: virtual ~CListSockets() { } - CString FormatTime(const CString& sFormat, time_t tm = 0) const { - char szTimestamp[1024]; - - if (tm == 0) { - tm = time(NULL); - } - - setenv("TZ", m_pUser->GetTimezone().c_str(), 1); - strftime(szTimestamp, sizeof(szTimestamp) / sizeof(char), - sFormat.c_str(), localtime(&tm)); - - return szTimestamp; - } }; MODULEDEFS(CListSockets, "List active sockets") diff --git a/modules/log.cpp b/modules/log.cpp index 73061484..793002d7 100644 --- a/modules/log.cpp +++ b/modules/log.cpp @@ -58,21 +58,15 @@ void CLogMod::PutLog(const CString& sLine, const CString& sWindow /*= "Status"*/ { CString sPath; time_t curtime; - tm* timeinfo; - char buffer[1024]; time(&curtime); - // Don't forget the user's timezone offset - setenv("TZ", m_pUser->GetTimezone().c_str(), 1); - timeinfo = localtime(&curtime); - // Generate file name - if (!strftime(buffer, sizeof(buffer), m_sLogPath.c_str(), timeinfo)) + sPath = CUtils::FormatTime(curtime, m_sLogPath, m_pUser->GetTimezone()); + if (sPath.empty()) { DEBUG("Could not format log path [" << sPath << "]"); return; } - sPath = buffer; // $WINDOW has to be handled last, since it can contain % sPath.Replace("$NETWORK", (m_pNetwork ? m_pNetwork->GetName() : "znc")); @@ -92,10 +86,7 @@ void CLogMod::PutLog(const CString& sLine, const CString& sWindow /*= "Status"*/ if (!CFile::Exists(sLogDir)) CDir::MakeDir(sLogDir); if (LogFile.Open(O_WRONLY | O_APPEND | O_CREAT)) { - snprintf(buffer, sizeof(buffer), "[%02d:%02d:%02d] ", - timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); - - LogFile.Write(buffer + sLine + "\n"); + LogFile.Write(CUtils::FormatTime(curtime, "[%H:%M:%S] ", m_pUser->GetTimezone()) + sLine + "\n"); } else DEBUG("Could not open log file [" << sPath << "]: " << strerror(errno)); } diff --git a/modules/simple_away.cpp b/modules/simple_away.cpp index a49447fc..a55048be 100644 --- a/modules/simple_away.cpp +++ b/modules/simple_away.cpp @@ -189,9 +189,7 @@ private: sReason = SIMPLE_AWAY_DEFAULT_REASON; time_t iTime = time(NULL); - setenv("TZ", m_pUser->GetTimezone().c_str(), 1); - CString sTime = ctime(&iTime); - sTime.Trim(); + CString sTime = CUtils::CTime(iTime, m_pUser->GetTimezone()); sReason.Replace("%s", sTime); return sReason; diff --git a/src/User.cpp b/src/User.cpp index e03460f3..71198a9b 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -61,7 +61,7 @@ CUser::CUser(const CString& sUserName) // set path that depends on the user name: m_sUserPath = CZNC::Get().GetUserPath() + "/" + m_sUserName; - m_sTimezone = "GMT"; + m_sTimezone = ""; m_fTimezoneOffset = 0; m_sNick = m_sCleanUserName; m_sIdent = m_sCleanUserName; @@ -500,16 +500,7 @@ CString CUser::ExpandString(const CString& sStr) const { } CString& CUser::ExpandString(const CString& sStr, CString& sRet) const { - time_t iUserTime = time(NULL); - setenv("TZ", m_sTimezone.c_str(), 1); - char *szTime = ctime(&iUserTime); - CString sTime; - - if (szTime) { - sTime = szTime; - // ctime() adds a trailing newline - sTime.Trim(); - } + CString sTime = CUtils::CTime(time(NULL), m_sTimezone); sRet = sStr; sRet.Replace("%user%", GetUserName()); @@ -537,27 +528,21 @@ CString CUser::AddTimestamp(const CString& sStr) const { } CString& CUser::AddTimestamp(const CString& sStr, CString& sRet) const { - char szTimestamp[1024]; - time_t tm; sRet = sStr; if (!GetTimestampFormat().empty() && (m_bAppendTimestamp || m_bPrependTimestamp)) { - time(&tm); - setenv("TZ", m_sTimezone.c_str(), 1); - size_t i = strftime(szTimestamp, sizeof(szTimestamp), GetTimestampFormat().c_str(), localtime(&tm)); - // If strftime returns 0, an error occured in format, or result is empty - // In both cases just don't prepend/append anything to our string - if (0 == i) { + CString sTimestamp = CUtils::FormatTime(time(NULL), GetTimestampFormat(), m_sTimezone); + if (sTimestamp.empty()) { return sRet; } if (m_bPrependTimestamp) { - sRet = szTimestamp; + sRet = sTimestamp; sRet += " " + sStr; } if (m_bAppendTimestamp) { sRet += " "; - sRet += szTimestamp; + sRet += sTimestamp; } } return sRet; diff --git a/src/Utils.cpp b/src/Utils.cpp index 70610c9c..69aa9d86 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -338,6 +338,60 @@ void CUtils::PrintStatus(bool bSuccess, const CString& sMessage) { fflush(stdout); } +CString CUtils::CTime(time_t t, const CString& sTZ) { + char s[30] = {}; // should have at least 26 bytes + if (sTZ.empty()) { + ctime_r(&t, s); + // ctime() adds a trailing newline + return CString(s).Trim_n(); + } + + // backup old value + char* oldTZ = getenv("TZ"); + if (oldTZ) oldTZ = strdup(oldTZ); + setenv("TZ", sTZ.c_str(), 1); + + ctime_r(&t, s); + + // restore old value + if (oldTZ) { + setenv("TZ", oldTZ, 1); + free(oldTZ); + } else { + unsetenv("TZ"); + } + + return CString(s).Trim_n(); +} + +CString CUtils::FormatTime(time_t t, const CString& sFormat, const CString& sTZ) { + char s[1024] = {}; + tm m; + if (sTZ.empty()) { + localtime_r(&t, &m); + strftime(s, sizeof(s), sFormat.c_str(), &m); + return s; + } + + // backup old value + char* oldTZ = getenv("TZ"); + if (oldTZ) oldTZ = strdup(oldTZ); + setenv("TZ", sTZ.c_str(), 1); + + localtime_r(&t, &m); + strftime(s, sizeof(s), sFormat.c_str(), &m); + + // restore old value + if (oldTZ) { + setenv("TZ", oldTZ, 1); + free(oldTZ); + } else { + unsetenv("TZ"); + } + + return s; +} + bool CTable::AddColumn(const CString& sName) { for (unsigned int a = 0; a < m_vsHeaders.size(); a++) { if (m_vsHeaders[a].Equals(sName)) {