Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
6f55cb4b
Commit
6f55cb4b
authored
Sep 15, 2023
by
Sergei Golubchik
Committed by
Rucha Deodhar
Oct 11, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MDEV-31684 post-review changes
parent
94eb8192
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
104 additions
and
133 deletions
+104
-133
mysql-test/main/date_formats.result
mysql-test/main/date_formats.result
+5
-11
mysql-test/main/date_formats.test
mysql-test/main/date_formats.test
+6
-13
mysql-test/main/timezone.result
mysql-test/main/timezone.result
+11
-0
mysql-test/main/timezone.test
mysql-test/main/timezone.test
+9
-0
sql/item_timefunc.cc
sql/item_timefunc.cc
+23
-38
sql/tztime.cc
sql/tztime.cc
+41
-59
sql/tztime.h
sql/tztime.h
+9
-12
No files found.
mysql-test/main/date_formats.result
View file @
6f55cb4b
...
...
@@ -556,9 +556,10 @@ select time_format('2001-01-01 02:02:02', '%T');
time_format('2001-01-01 02:02:02', '%T')
02:02:02
#
#
Beginning of 11.3
test
#
End of 10.2
test
#
# MDEV-31684: Add timezone information to DATE_FORMAT
#
# MDEV-31684 Add timezone information to DATE_FORMAT
#
SET @old_timezone= @@time_zone;
# Using named timezones
...
...
@@ -590,14 +591,7 @@ SET TIME_ZONE='UTC';
SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone;
current_timezone
+0000 UTC
# using system time
#
# output depends on system time information. Hence replacing
# to avoid result diff test failures.
#
SET @@time_zone= default;
SELECT DATE_FORMAT('2009-10+|-HH:MM ABC22:23:00', '%z %Z') AS current_timezone;
current_timezone
+|-HH:MM ABC
SET @@time_zone= @old_timezone;
#
# End of 11.3 test
#
mysql-test/main/date_formats.test
View file @
6f55cb4b
...
...
@@ -258,9 +258,11 @@ select time_format('01 02:02:02', '%T');
select
time_format
(
'2001-01-01 02:02:02'
,
'%T'
);
--
echo
#
--
echo
#
Beginning of 11.3
test
--
echo
#
End of 10.2
test
--
echo
#
--
echo
# MDEV-31684: Add timezone information to DATE_FORMAT
--
echo
#
--
echo
# MDEV-31684 Add timezone information to DATE_FORMAT
--
echo
#
SET
@
old_timezone
=
@@
time_zone
;
...
...
@@ -290,17 +292,8 @@ SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone;
SET
TIME_ZONE
=
'UTC'
;
SELECT
DATE_FORMAT
(
'2009-10-04 22:23:00'
,
'%z %Z'
)
AS
current_timezone
;
--
echo
# using system time
--
echo
#
--
echo
# output depends on system time information. Hence replacing
--
echo
# to avoid result diff test failures.
--
echo
#
SET
@@
time_zone
=
default
;
--
replace_regex
/
[
+-
][
0
-
9
]
*
[
A
-
Z
]
*/+|-
HH
:
MM
ABC
/
SELECT
DATE_FORMAT
(
'2009-10-04 22:23:00'
,
'%z %Z'
)
AS
current_timezone
;
SET
@@
time_zone
=
@
old_timezone
;
--
echo
#
--
echo
# End of 11.3 test
--
echo
#
mysql-test/main/timezone.result
View file @
6f55cb4b
...
...
@@ -75,3 +75,14 @@ alter table mysql.time_zone_transition_type add primary key (time_zone_id,transi
#
# End of 10.8 tests
#
#
# MDEV-31684 Add timezone information to DATE_FORMAT
#
# using system time
SET @@time_zone= default;
SELECT DATE_FORMAT('2009-11-01 22:23:00', '%z %Z') AS current_timezone;
current_timezone
+0100 MET
SELECT DATE_FORMAT('2008-06-04 02:23:00', '%z %Z') AS current_timezone;
current_timezone
+0200 MEST
mysql-test/main/timezone.test
View file @
6f55cb4b
...
...
@@ -74,3 +74,12 @@ alter table mysql.time_zone_transition_type add primary key (time_zone_id,transi
--
echo
#
--
echo
# End of 10.8 tests
--
echo
#
--
echo
#
--
echo
# MDEV-31684 Add timezone information to DATE_FORMAT
--
echo
#
--
echo
# using system time
SET
@@
time_zone
=
default
;
SELECT
DATE_FORMAT
(
'2009-11-01 22:23:00'
,
'%z %Z'
)
AS
current_timezone
;
SELECT
DATE_FORMAT
(
'2008-06-04 02:23:00'
,
'%z %Z'
)
AS
current_timezone
;
sql/item_timefunc.cc
View file @
6f55cb4b
...
...
@@ -482,15 +482,8 @@ static bool make_date_time(THD *thd, const String *format,
uint
weekday
;
ulong
length
;
const
char
*
ptr
,
*
end
;
int
diff_hr
=
0
,
diff_min
=
0
;
char
abbrevation
[
8
];
struct
tz
tmp
;
tmp
.
is_inited
=
false
;
Time_zone
*
curr_timezone
=
my_tz_find
(
thd
,
thd
->
variables
.
time_zone
->
get_name
());
memset
(
abbrevation
,
0
,
sizeof
(
abbrevation
));
struct
tz
curr_tz
;
Time_zone
*
curr_timezone
=
0
;
str
->
length
(
0
);
...
...
@@ -710,41 +703,29 @@ static bool make_date_time(THD *thd, const String *format,
case
'z'
:
{
if
(
!
tmp
.
is_inited
)
if
(
!
curr_timezone
)
{
curr_timezone
->
get_timezone_information
(
&
tmp
,
l_time
)
;
tmp
.
is_inited
=
true
;
curr_timezone
=
thd
->
variables
.
time_zone
;
curr_timezone
->
get_timezone_information
(
&
curr_tz
,
l_time
)
;
}
ulonglong
seconds
=
abs
(
tmp
.
seconds_offset
);
diff_hr
=
(
int
)(
seconds
/
3600L
);
int
temp
=
(
int
)(
seconds
%
3600L
);
diff_min
=
temp
/
60L
;
if
(
tmp
.
is_behind
)
str
->
append
(
"-"
,
1
);
else
str
->
append
(
"+"
,
1
);
if
(
diff_hr
/
10
==
0
)
str
->
append
(
"0"
,
1
);
length
=
(
uint
)
(
int10_to_str
(
diff_hr
,
intbuff
,
10
)
-
intbuff
);
str
->
append
(
intbuff
,
length
);
if
(
diff_min
/
10
==
0
)
str
->
append
(
"0"
,
1
);
length
=
(
uint
)
(
int10_to_str
(
diff_min
,
intbuff
,
10
)
-
intbuff
);
str
->
append
(
intbuff
,
length
);
}
long
minutes
=
labs
(
curr_tz
.
seconds_offset
)
/
60
,
diff_hr
,
diff_min
;
diff_hr
=
minutes
/
60
;
diff_min
=
minutes
%
60
;
str
->
append
(
curr_tz
.
seconds_offset
<
0
?
'-'
:
'+'
);
str
->
append
(
static_cast
<
char
>
(
'0'
+
diff_hr
/
10
));
str
->
append
(
static_cast
<
char
>
(
'0'
+
diff_hr
%
10
));
str
->
append
(
static_cast
<
char
>
(
'0'
+
diff_min
/
10
));
str
->
append
(
static_cast
<
char
>
(
'0'
+
diff_min
%
10
));
break
;
}
case
'Z'
:
{
if
(
!
tmp
.
is_inited
)
if
(
!
curr_timezone
)
{
curr_timezone
->
get_timezone_information
(
&
tmp
,
l_time
)
;
tmp
.
is_inited
=
true
;
curr_timezone
=
thd
->
variables
.
time_zone
;
curr_timezone
->
get_timezone_information
(
&
curr_tz
,
l_time
)
;
}
str
->
append
(
tmp
.
abbrevation
,
strlen
(
tmp
.
abbrevation
));
}
str
->
append
(
curr_tz
.
abbrevation
,
strlen
(
curr_tz
.
abbrevation
));
break
;
default:
str
->
append
(
*
ptr
);
...
...
@@ -1874,6 +1855,7 @@ uint Item_func_date_format::format_length(const String *format)
case
'X'
:
/* Year, used with 'v, where week starts with Monday' */
size
+=
4
;
break
;
case
'Z'
:
/* time zone abbreviation */
case
'a'
:
/* locale's abbreviated weekday name (Sun..Sat) */
case
'b'
:
/* locale's abbreviated month name (Jan.Dec) */
size
+=
32
;
/* large for UTF8 locale data */
...
...
@@ -1912,6 +1894,9 @@ uint Item_func_date_format::format_length(const String *format)
case
'f'
:
/* microseconds */
size
+=
6
;
break
;
case
'z'
:
/* time zone offset */
size
+=
5
;
break
;
case
'w'
:
/* day (of the week), numeric */
case
'%'
:
default:
...
...
sql/tztime.cc
View file @
6f55cb4b
...
...
@@ -52,23 +52,12 @@
#include <mysql/psi/mysql_file.h>
#include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH,
// MYSQL_LOCK_IGNORE_TIMEOUT
/*
Now we don't use abbreviations in server but we will do this in future.
Edit: Started needing abbrevation in server as part of task MDEV-31684.
*/
#ifndef ABBR_ARE_USED
#define ABBR_ARE_USED
#endif
/* defined(TZINFO2SQL) || defined(TESTTIME) */
/* Structure describing local time type (e.g. Moscow summer time (MSD)) */
typedef
struct
ttinfo
{
long
tt_gmtoff
;
// Offset from UTC in seconds
uint
tt_isdst
;
// Is daylight saving time or not. Used to set tm_isdst
#ifdef ABBR_ARE_USED
uint
tt_abbrind
;
// Index of start of abbreviation for this time type.
#endif
/*
We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code
struct since we don't support POSIX-style TZ descriptions in variables.
...
...
@@ -115,10 +104,8 @@ typedef struct st_time_zone_info
my_time_t
*
ats
;
// Times of transitions between time types
uchar
*
types
;
// Local time types for transitions
TRAN_TYPE_INFO
*
ttis
;
// Local time types descriptions
#ifdef ABBR_ARE_USED
/* Storage for local time types abbreviations. They are stored as ASCIIZ */
char
*
chars
;
#endif
/*
Leap seconds corrections descriptions, this array is shared by
all time zones who use leap seconds.
...
...
@@ -174,9 +161,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
struct
tzhead
tzhead
;
uchar
buf
[
sizeof
(
struct
tzhead
)
+
sizeof
(
my_time_t
)
*
TZ_MAX_TIMES
+
TZ_MAX_TIMES
+
sizeof
(
TRAN_TYPE_INFO
)
*
TZ_MAX_TYPES
+
#ifdef ABBR_ARE_USED
MY_MAX
(
TZ_MAX_CHARS
+
1
,
(
2
*
(
MY_TZNAME_MAX
+
1
)))
+
#endif
sizeof
(
LS_INFO
)
*
TZ_MAX_LEAPS
];
}
u
;
uint
ttisstdcnt
;
...
...
@@ -221,9 +206,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
ALIGN_SIZE
(
sp
->
timecnt
)
+
ALIGN_SIZE
(
sp
->
typecnt
*
sizeof
(
TRAN_TYPE_INFO
))
+
#ifdef ABBR_ARE_USED
ALIGN_SIZE
(
sp
->
charcnt
+
1
)
+
#endif
sp
->
leapcnt
*
sizeof
(
LS_INFO
))))
return
1
;
...
...
@@ -233,10 +216,8 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
tzinfo_buf
+=
ALIGN_SIZE
(
sp
->
timecnt
);
sp
->
ttis
=
(
TRAN_TYPE_INFO
*
)
tzinfo_buf
;
tzinfo_buf
+=
ALIGN_SIZE
(
sp
->
typecnt
*
sizeof
(
TRAN_TYPE_INFO
));
#ifdef ABBR_ARE_USED
sp
->
chars
=
tzinfo_buf
;
tzinfo_buf
+=
ALIGN_SIZE
(
sp
->
charcnt
+
1
);
#endif
sp
->
lsis
=
(
LS_INFO
*
)
tzinfo_buf
;
for
(
i
=
0
;
i
<
sp
->
timecnt
;
i
++
,
p
+=
4
)
...
...
@@ -1096,20 +1077,45 @@ Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
void
Time_zone_system
::
get_timezone_information
(
struct
tz
*
curr_tz
,
const
MYSQL_TIME
*
local_TIME
)
const
{
#ifndef _WIN32
uint
error
;
struct
tm
tm_local_time
;
time_t
time_sec
=
this
->
TIME_to_gmt_sec
(
local_TIME
,
&
error
);
time_t
time_sec
=
TIME_to_gmt_sec
(
local_TIME
,
&
error
);
localtime_r
(
&
time_sec
,
&
tm_local_time
);
# ifdef __USE_MISC
int
len
=
strlen
((
tm_local_time
.
tm_zone
));
strmake
(
curr_tz
->
abbrevation
,
tm_local_time
.
tm_zone
,
sizeof
(
tm_local_time
.
tm_zone
)
-
1
);
curr_tz
->
abbrevation
[
len
]
=
'\0'
;
# endif
curr_tz
->
seconds_offset
=
tm_local_time
.
tm_gmtoff
;
curr_tz
->
is_behind
=
tm_local_time
.
tm_gmtoff
<
0
;
strmake_buf
(
curr_tz
->
abbrevation
,
tm_local_time
.
tm_zone
);
curr_tz
->
seconds_offset
=
tm_local_time
.
tm_gmtoff
;
#else
#define mdHMS(mon,day,h,m,s) (((((mon)*100+(day))*100+(h))*100+(m))*100+(s))
TIME_ZONE_INFORMATION
tzi
;
bool
use_dst
=
false
;
GetTimeZoneInformationForYear
(
local_TIME
->
year
,
NULL
,
&
tzi
);
if
(
tzi
.
StandardDate
.
wMonth
)
{
ulonglong
std
=
mdHMS
(
tzi
.
StandardDate
.
wMonth
,
tzi
.
StandardDate
.
wDay
,
tzi
.
StandardDate
.
wHour
,
tzi
.
StandardDate
.
wMinute
,
tzi
.
StandardDate
.
wSecond
);
ulonglong
dst
=
mdHMS
(
tzi
.
DaylightDate
.
wMonth
,
tzi
.
DaylightDate
.
wDay
,
tzi
.
DaylightDate
.
wHour
,
tzi
.
DaylightDate
.
wMinute
,
tzi
.
DaylightDate
.
wSecond
);
ulonglong
cur
=
mdHMS
(
local_TIME
->
month
,
local_TIME
->
day
,
local_TIME
->
hour
,
local_TIME
->
minute
,
local_TIME
->
second
);
use_dst
=
std
<
dst
?
cur
<
std
||
cur
>=
dst
:
cur
>=
dst
&&
cur
<
std
;
}
if
(
use_dst
)
{
size_t
len
;
curr_tz
->
seconds_offset
=
-
60
*
(
tzi
.
Bias
+
tzi
.
DaylightBias
);
wcstombs_s
(
&
len
,
curr_tz
->
abbrevation
,
sizeof
(
curr_tz
->
abbrevation
),
tzi
.
DaylightName
,
_TRUNCATE
);
}
else
{
size_t
len
;
curr_tz
->
seconds_offset
=
-
60
*
(
tzi
.
Bias
+
tzi
.
StandardBias
);
wcstombs_s
(
&
len
,
curr_tz
->
abbrevation
,
sizeof
(
curr_tz
->
abbrevation
),
tzi
.
StandardName
,
_TRUNCATE
);
}
#endif
}
...
...
@@ -1194,17 +1200,8 @@ Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
void
Time_zone_utc
::
get_timezone_information
(
struct
tz
*
curr_tz
,
const
MYSQL_TIME
*
local_TIME
)
const
{
uint
error
;
struct
tm
tm_local_time
;
time_t
time_sec
=
this
->
TIME_to_gmt_sec
(
local_TIME
,
&
error
);
localtime_r
(
&
time_sec
,
&
tm_local_time
);
# ifdef __USE_MISC
strmake
(
curr_tz
->
abbrevation
,
"UTC"
,
3
);
# endif
curr_tz
->
seconds_offset
=
tm_local_time
.
tm_gmtoff
;
curr_tz
->
is_behind
=
tm_local_time
.
tm_gmtoff
<
0
;
strmake_buf
(
curr_tz
->
abbrevation
,
"UTC"
);
curr_tz
->
seconds_offset
=
0
;
}
...
...
@@ -1321,14 +1318,12 @@ Time_zone_db::get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *loc
const
TRAN_TYPE_INFO
*
ttisp
;
/* Get seconds since epoch. */
sec_in_utc
=
this
->
TIME_to_gmt_sec
(
local_TIME
,
&
error
);
sec_in_utc
=
TIME_to_gmt_sec
(
local_TIME
,
&
error
);
/* Get local timezone information. */
ttisp
=
find_transition_type
(
sec_in_utc
,
tz_info
);
curr_tz
->
seconds_offset
=
ttisp
->
tt_gmtoff
;
curr_tz
->
is_behind
=
tz_info
->
revtis
->
rt_offset
<
0
?
true
:
false
;
strmake
(
curr_tz
->
abbrevation
,
&
(
tz_info
->
chars
[
ttisp
->
tt_abbrind
]),
tz_info
->
charcnt
-
1
);
strmake_buf
(
curr_tz
->
abbrevation
,
&
(
tz_info
->
chars
[
ttisp
->
tt_abbrind
]));
}
...
...
@@ -1491,8 +1486,7 @@ Time_zone_offset::get_timezone_information(struct tz* curr_tz, const MYSQL_TIME
{
curr_tz
->
seconds_offset
=
offset
;
const
char
*
name
=
get_name
()
->
ptr
();
curr_tz
->
is_behind
=
name
[
0
]
==
'+'
?
false
:
true
;
strmake
(
curr_tz
->
abbrevation
,
name
,
sizeof
(
curr_tz
->
abbrevation
)
-
1
);
strmake_buf
(
curr_tz
->
abbrevation
,
name
);
}
...
...
@@ -1933,9 +1927,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
my_time_t
ats
[
TZ_MAX_TIMES
];
uchar
types
[
TZ_MAX_TIMES
];
TRAN_TYPE_INFO
ttis
[
TZ_MAX_TYPES
];
#ifdef ABBR_ARE_USED
char
chars
[
MY_MAX
(
TZ_MAX_CHARS
+
1
,
(
2
*
(
MY_TZNAME_MAX
+
1
)))];
#endif
/*
Used as a temporary tz_info until we decide that we actually want to
allocate and keep the tz info and tz name in tz_storage.
...
...
@@ -2041,7 +2033,6 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
ttis
[
ttid
].
tt_gmtoff
=
(
long
)
table
->
field
[
2
]
->
val_int
();
ttis
[
ttid
].
tt_isdst
=
(
table
->
field
[
3
]
->
val_int
()
>
0
);
#ifdef ABBR_ARE_USED
// FIXME should we do something with duplicates here ?
table
->
field
[
4
]
->
val_str
(
&
abbr
,
&
abbr
);
if
(
tmp_tz_info
.
charcnt
+
abbr
.
length
()
+
1
>
sizeof
(
chars
))
...
...
@@ -2061,11 +2052,6 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
(
"time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
"abbr='%s' tt_isdst=%u"
,
tzid
,
ttid
,
ttis
[
ttid
].
tt_gmtoff
,
chars
+
ttis
[
ttid
].
tt_abbrind
,
ttis
[
ttid
].
tt_isdst
));
#else
DBUG_PRINT
(
"info"
,
(
"time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
"tt_isdst=%u"
,
tzid
,
ttid
,
ttis
[
ttid
].
tt_gmtoff
,
ttis
[
ttid
].
tt_isdst
));
#endif
/* ttid is increasing because we are reading using index */
if
(
ttid
<
tmp_tz_info
.
typecnt
)
...
...
@@ -2181,9 +2167,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
ALIGN_SIZE
(
sizeof
(
my_time_t
)
*
tz_info
->
timecnt
)
+
ALIGN_SIZE
(
tz_info
->
timecnt
)
+
#ifdef ABBR_ARE_USED
ALIGN_SIZE
(
tz_info
->
charcnt
)
+
#endif
sizeof
(
TRAN_TYPE_INFO
)
*
tz_info
->
typecnt
)))
{
...
...
@@ -2197,11 +2181,9 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
tz_info
->
types
=
(
uchar
*
)
alloc_buff
;
memcpy
(
tz_info
->
types
,
types
,
tz_info
->
timecnt
);
alloc_buff
+=
ALIGN_SIZE
(
tz_info
->
timecnt
);
#ifdef ABBR_ARE_USED
tz_info
->
chars
=
alloc_buff
;
memcpy
(
tz_info
->
chars
,
chars
,
tz_info
->
charcnt
);
alloc_buff
+=
ALIGN_SIZE
(
tz_info
->
charcnt
);
#endif
tz_info
->
ttis
=
(
TRAN_TYPE_INFO
*
)
alloc_buff
;
memcpy
(
tz_info
->
ttis
,
ttis
,
tz_info
->
typecnt
*
sizeof
(
TRAN_TYPE_INFO
));
...
...
sql/tztime.h
View file @
6f55cb4b
...
...
@@ -32,25 +32,22 @@ class THD;
class
THD
;
/**
This class represents abstract time zone and provides
basic interface for MYSQL_TIME <-> my_time_t conversion.
Actual time zones which are specified by DB, or via offset
or use system functions are its descendants.
*/
/*
Has only offset from UTC, bool value to denote if it is
ahead (+), behind(-) of UTC and abbrevation.
Has only offset from UTC and abbrevation.
*/
struct
tz
{
long
seconds_offset
;
bool
is_behind
;
char
abbrevation
[
8
];
bool
is_inited
;
char
abbrevation
[
64
];
};
/**
This class represents abstract time zone and provides
basic interface for MYSQL_TIME <-> my_time_t conversion.
Actual time zones which are specified by DB, or via offset
or use system functions are its descendants.
*/
class
Time_zone
:
public
Sql_alloc
{
public:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment