Skip to content

Commit e4f7902

Browse files
BUG#32893161: BACKPORT 28632725 TO 5.7
When using --log-timestamps=system, ISO8601 timestamps in logging would always show the timezone's default offset to UTC, even when DST was active. Note: This is a backport of the fix for Bug#28632725 to 5.7. Change-Id: I488f3468cc320de555cea7bb6f911e9c2f82e225
1 parent efd5018 commit e4f7902

File tree

6 files changed

+170
-9
lines changed

6 files changed

+170
-9
lines changed

config.h.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@
187187
#cmakedefine HAVE_ULONG 1
188188
#cmakedefine HAVE_U_INT32_T 1
189189
#cmakedefine HAVE_STRUCT_TIMESPEC
190+
#cmakedefine HAVE_TM_GMTOFF 1
190191

191192
/* Support for tagging symbols with __attribute__((visibility("hidden"))) */
192193
#cmakedefine HAVE_VISIBILITY_HIDDEN 1

configure.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,9 @@ CHECK_TYPE_SIZE("off_t" SIZEOF_OFF_T)
445445
CHECK_TYPE_SIZE("time_t" SIZEOF_TIME_T)
446446
CHECK_TYPE_SIZE("struct timespec" STRUCT_TIMESPEC)
447447

448+
CHECK_STRUCT_HAS_MEMBER("struct tm"
449+
tm_gmtoff "time.h" HAVE_TM_GMTOFF)
450+
448451
# If finds the size of a type, set SIZEOF_<type> and HAVE_<type>
449452
FUNCTION(MY_CHECK_TYPE_SIZE type defbase)
450453
CHECK_TYPE_SIZE("${type}" SIZEOF_${defbase})

sql/log.cc

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,6 @@ static int log_syslog_facility= 0;
6060
static char *log_syslog_ident = NULL;
6161
static bool log_syslog_enabled = false;
6262

63-
64-
/* 26 for regular timestamp, plus 7 (".123456") when using micro-seconds */
65-
static const int iso8601_size= 33;
66-
6763
enum enum_slow_query_log_table_field
6864
{
6965
SQLT_FIELD_START_TIME = 0,
@@ -467,7 +463,7 @@ static void ull2timeval(ulonglong utime, struct timeval *tv)
467463
@return length of timestamp (excluding \0)
468464
*/
469465

470-
static int make_iso8601_timestamp(char *buf, ulonglong utime= 0)
466+
int make_iso8601_timestamp(char *buf, ulonglong utime)
471467
{
472468
struct tm my_tm;
473469
char tzinfo[7]="Z"; // max 6 chars plus \0
@@ -486,16 +482,24 @@ static int make_iso8601_timestamp(char *buf, ulonglong utime= 0)
486482
{
487483
localtime_r(&seconds, &my_tm);
488484

489-
#ifdef __FreeBSD__
485+
#ifdef HAVE_TM_GMTOFF
490486
/*
491487
The field tm_gmtoff is the offset (in seconds) of the time represented
492488
from UTC, with positive values indicating east of the Prime Meridian.
489+
Originally a BSDism, this is also supported in glibc, so this should
490+
cover the majority of our platforms.
493491
*/
494492
long tim= -my_tm.tm_gmtoff;
495-
#elif _WIN32
496-
long tim = _timezone;
497493
#else
498-
long tim= timezone; // seconds West of UTC.
494+
/*
495+
Work this out "manually".
496+
*/
497+
struct tm my_gm;
498+
long tim, gm;
499+
gmtime_r(&seconds, &my_gm);
500+
gm = (my_gm.tm_sec + 60 * (my_gm.tm_min + 60 * my_gm.tm_hour));
501+
tim = (my_tm.tm_sec + 60 * (my_tm.tm_min + 60 * my_tm.tm_hour));
502+
tim = gm - tim;
499503
#endif
500504
char dir= '-';
501505

sql/log.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,4 +969,18 @@ bool log_syslog_find_facility(char *f, SYSLOG_FACILITY *rsf);
969969
bool log_syslog_init();
970970
void log_syslog_exit();
971971

972+
/* 26 for regular timestamp, plus 7 (".123456") when using micro-seconds */
973+
static const int iso8601_size = 33;
974+
975+
/**
976+
Make and return an ISO 8601 / RFC 3339 compliant timestamp.
977+
Heeds log_timestamps.
978+
979+
@param buf A buffer of at least 26 bytes to store the timestamp in
980+
(19 + tzinfo tail + \0)
981+
@param seconds Seconds since the epoch, or 0 for "now"
982+
983+
@return length of timestamp (excluding \0)
984+
*/
985+
int make_iso8601_timestamp(char *buf, ulonglong utime = 0);
972986
#endif /* LOG_H */

unittest/gunit/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ SET(SERVER_TESTS
331331
json_path
332332
locking_service
333333
log_throttle
334+
log_timestamp
334335
make_sortkey
335336
mdl_sync
336337
my_decimal

unittest/gunit/log_timestamp-t.cc

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
3+
This program is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License as published by
5+
the Free Software Foundation; version 2 of the License.
6+
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
12+
You should have received a copy of the GNU General Public License
13+
along with this program; if not, write to the Free Software
14+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15+
16+
// First include (the generated) my_config.h, to get correct platform defines,
17+
// then gtest.h (before any other MySQL headers), to avoid min() macros etc ...
18+
19+
#ifdef _WIN32_WINNT
20+
#if (_WIN32_WINNT < 0x0602)
21+
#undef _WIN32_WINNT
22+
// We need at least _WIN32_WINNT_WIN8 i.e. 0x0602 for
23+
// EnumDynamicTimeZoneInformation
24+
#define _WIN32_WINNT 0x0602
25+
#endif // (_WIN32_WINNT < 0x0602)
26+
#endif // _WIN32_WINNT
27+
28+
#include "my_config.h"
29+
#include "test_utils.h"
30+
#include <gtest/gtest.h>
31+
#include <stdlib.h>
32+
33+
#include "../sql/log.h"
34+
35+
// CET: 32 bytes
36+
// date (10), 'T', time (8), '.', microseconds (6), timezone offset (6)
37+
#define LEN_MS_CET 32
38+
39+
// UTC: 27 bytes
40+
// date (10), 'T', time (8), '.', microseconds (6), 'Z'
41+
#define LEN_MS_UTC 27
42+
43+
// we use micro-seconds since the epoch
44+
#define MICRO_FAC ((ulonglong)1000000L)
45+
46+
namespace log_timestamp_unittest
47+
{
48+
49+
using my_testing::Server_initializer;
50+
51+
class LogTimestampTest : public ::testing::Test
52+
{
53+
protected:
54+
virtual void SetUp() { initializer.SetUp(); }
55+
virtual void TearDown() { initializer.TearDown(); }
56+
57+
THD *thd() { return initializer.thd(); }
58+
59+
Server_initializer initializer;
60+
};
61+
62+
/*
63+
Test basic functionality - throttling, eligibility, printing of summary of
64+
Slow_log_throttle.
65+
*/
66+
TEST_F(LogTimestampTest, iso8601)
67+
{
68+
char time_buff[iso8601_size];
69+
#ifdef WIN32
70+
DYNAMIC_TIME_ZONE_INFORMATION original_dti = {};
71+
DWORD original_dti_result = GetDynamicTimeZoneInformation(&original_dti);
72+
EXPECT_NE(original_dti_result, TIME_ZONE_ID_INVALID);
73+
74+
if (original_dti.DaylightDate.wMonth == 0)
75+
{
76+
/*
77+
Current system time zone does not support Daylight savings. If the Windows
78+
system time zone has no daylight saving, then attempting to set TZ to a
79+
timezone that does have daylight saving will result in localtime_r
80+
producing inaccurate results. Skipping the test.
81+
Bug#34380460 will be tracking this issue.
82+
*/
83+
return;
84+
}
85+
char tz[] = "TZ=CET-1CES";
86+
#else
87+
char tz[] = "TZ=CET";
88+
#endif
89+
int time_buff_len;
90+
91+
EXPECT_EQ(((iso8601_size)-1), LEN_MS_CET);
92+
EXPECT_EQ(((LEN_MS_CET)-5), LEN_MS_UTC);
93+
94+
// set up timezone (central european time)
95+
putenv(tz);
96+
EXPECT_STREQ(&(tz[3]), getenv("TZ"));
97+
tzset();
98+
99+
/// 1970/01/01 .000001 (1)
100+
101+
// UTC (winter)
102+
opt_log_timestamps = 0; // UTC Timestamp
103+
time_buff_len = make_iso8601_timestamp(time_buff, 1);
104+
EXPECT_EQ(LEN_MS_UTC, time_buff_len);
105+
EXPECT_STREQ("1970-01-01T00:00:00.000001Z", time_buff);
106+
107+
// CET (winter) +1h
108+
opt_log_timestamps = 1; // System Timestamp
109+
time_buff_len = make_iso8601_timestamp(time_buff, 1);
110+
EXPECT_EQ(LEN_MS_CET, time_buff_len);
111+
EXPECT_STREQ("1970-01-01T01:00:00.000001+01:00", time_buff);
112+
113+
/// 2011-07-07 (1309996800)
114+
115+
// UTC (summer)
116+
opt_log_timestamps = 0;
117+
time_buff_len = make_iso8601_timestamp(time_buff, MICRO_FAC * 1309996800);
118+
EXPECT_EQ(LEN_MS_UTC, time_buff_len);
119+
EXPECT_STREQ("2011-07-07T00:00:00.000000Z", time_buff);
120+
121+
// CET (summer) +2h
122+
opt_log_timestamps = 1;
123+
time_buff_len = make_iso8601_timestamp(time_buff, MICRO_FAC * 1309996800);
124+
EXPECT_EQ(LEN_MS_CET, time_buff_len);
125+
EXPECT_STREQ("2011-07-07T02:00:00.000000+02:00", time_buff);
126+
127+
/// 1987-06-05 04:03:02.123456
128+
129+
// UTC
130+
opt_log_timestamps = 0;
131+
time_buff_len = make_iso8601_timestamp(
132+
time_buff, (MICRO_FAC * 549864182) + 123456);
133+
EXPECT_EQ(LEN_MS_UTC, time_buff_len);
134+
EXPECT_STREQ("1987-06-05T04:03:02.123456Z", time_buff);
135+
}
136+
// End of Error_log_timestamp test cases.
137+
138+
} // namespace log_timestamp_unittest

0 commit comments

Comments
 (0)