summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <guilhem@mysql.com>2004-07-29 23:25:58 +0200
committerunknown <guilhem@mysql.com>2004-07-29 23:25:58 +0200
commit88e3aead85b7136fab3d8cfcfa19174c4c2e2662 (patch)
tree2f01af661dcc90a418853b7a1718fd7a0fe688e1
parent5ef15478cdbaa65c7d037ce5662117b8ed425dba (diff)
downloadmariadb-git-88e3aead85b7136fab3d8cfcfa19174c4c2e2662.tar.gz
WL#1580: --start-datetime, --stop-datetime, --start-position (alias for --position) and --stop-position
options for mysqlbinlog, with a test file. This enables user to say "recover my database to how it was this morning at 10:30" (mysqlbinlog "--stop-datetime=2003-07-29 10:30:00"). Using time functions into client/ made me move them out of sql/ into sql-common/. + (small) fix for BUG#4507 "mysqlbinlog --read-from-remote-server sometimes cannot accept 2 binlogs" (that is, on command line). client/client_priv.h: new options for mysqlbinlog client/mysqlbinlog.cc: WL#1580: --start-datetime, --stop-datetime, --start-position (alias for --position) and --stop-position. (small) fix for BUG#4507 "mysqlbinlog --read-from-remote-server sometimes cannot accept 2 binlogs". include/my_time.h: importing time functions so that client/ files can use them. include/mysql_time.h: importing time types so that client/ files can use them. sql-common/my_time.c: importing time functions so that client/ files can use them. sql/mysql_priv.h: moving time functions out of sql/ into sql-common/ sql/time.cc: moving time functions out of sql/ into sql-common/ sql/tztime.h: moving time functions out of sql/ into sql-common/
-rw-r--r--client/client_priv.h3
-rw-r--r--client/mysqlbinlog.cc248
-rw-r--r--include/my_time.h7
-rw-r--r--include/mysql_time.h9
-rw-r--r--mysql-test/r/mysqlbinlog2.result446
-rw-r--r--mysql-test/t/mysqlbinlog2.test156
-rw-r--r--sql-common/my_time.c155
-rw-r--r--sql/mysql_priv.h5
-rw-r--r--sql/time.cc157
-rw-r--r--sql/tztime.h9
10 files changed, 957 insertions, 238 deletions
diff --git a/client/client_priv.h b/client/client_priv.h
index 854d205e585..ad08484b706 100644
--- a/client/client_priv.h
+++ b/client/client_priv.h
@@ -43,5 +43,6 @@ enum options_client
OPT_PROMPT, OPT_IGN_LINES,OPT_TRANSACTION,OPT_MYSQL_PROTOCOL,
OPT_SHARED_MEMORY_BASE_NAME, OPT_FRM, OPT_SKIP_OPTIMIZATION,
OPT_COMPATIBLE, OPT_RECONNECT, OPT_DELIMITER, OPT_SECURE_AUTH,
- OPT_OPEN_FILES_LIMIT, OPT_SET_CHARSET, OPT_CREATE_OPTIONS
+ OPT_OPEN_FILES_LIMIT, OPT_SET_CHARSET, OPT_CREATE_OPTIONS,
+ OPT_START_POSITION, OPT_STOP_POSITION, OPT_START_DATETIME, OPT_STOP_DATETIME
};
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index ba030379792..97746a52b39 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -17,7 +17,7 @@
#define MYSQL_CLIENT
#undef MYSQL_SERVER
#include "client_priv.h"
-#include <time.h>
+#include <my_time.h>
#include "log_event.h"
#define BIN_LOG_HEADER_SIZE 4
@@ -53,10 +53,18 @@ static int port = MYSQL_PORT;
static const char* sock= 0;
static const char* user = 0;
static char* pass = 0;
-static ulonglong position = 0;
+
+static ulonglong start_position, stop_position;
+#define start_position_mot ((my_off_t)start_position)
+#define stop_position_mot ((my_off_t)stop_position)
+
+static char *start_datetime_str, *stop_datetime_str;
+static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX;
+static ulonglong rec_count= 0;
static short binlog_flags = 0;
static MYSQL* mysql = NULL;
static const char* dirname_for_local_load= 0;
+static bool stop_passed= 0;
static int dump_local_log_entries(const char* logname);
static int dump_remote_log_entries(const char* logname);
@@ -302,15 +310,36 @@ Create_file event for file_id: %u\n",ae->file_id);
Load_log_processor load_processor;
+/*
+ RETURN
+ 0 ok and continue
+ 1 error and terminate
+ -1 ok and terminate
-int process_event(ulonglong *rec_count, char *last_db, Log_event *ev,
- my_off_t pos, int old_format)
+ TODO
+ This function returns 0 even in some error cases. This should be changed.
+*/
+int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
{
char ll_buff[21];
DBUG_ENTER("process_event");
- if ((*rec_count) >= offset)
+ if ((rec_count >= offset) &&
+ ((my_time_t)(ev->when) >= start_datetime))
{
+ /*
+ We have found an event after start_datetime, from now on print
+ everything (in case the binlog has timestamps increasing and decreasing,
+ we do this to avoid cutting the middle).
+ */
+ start_datetime= 0;
+ offset= 0; // print everything and protect against cycling rec_count
+ if (((my_time_t)(ev->when) >= stop_datetime)
+ || (pos >= stop_position_mot))
+ {
+ stop_passed= 1; // skip all next binlogs
+ DBUG_RETURN(-1);
+ }
if (!short_form)
fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
@@ -387,7 +416,7 @@ Create_file event for file_id: %u\n",exv->file_id);
}
end:
- (*rec_count)++;
+ rec_count++;
if (ev)
delete ev;
DBUG_RETURN(0);
@@ -417,13 +446,14 @@ static struct my_option my_long_options[] =
{"port", 'P', "Use port to connect to the remote server.",
(gptr*) &port, (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0,
0, 0, 0},
- {"position", 'j', "Start reading the binlog at position N.",
- (gptr*) &position, (gptr*) &position, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0,
- 0, 0},
+ {"position", 'j', "Deprecated. Use --start-position instead.",
+ (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL,
+ REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE,
+ /* COM_BINLOG_DUMP accepts only 4 bytes for the position */
+ (ulonglong)(~(uint32)0), 0, 0, 0},
{"protocol", OPT_MYSQL_PROTOCOL,
"The protocol of connection (tcp,socket,pipe,memory).",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-
{"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"read-from-remote-server", 'R', "Read binary logs from a MySQL server",
@@ -439,6 +469,35 @@ static struct my_option my_long_options[] =
{"socket", 'S', "Socket file to use for connection.",
(gptr*) &sock, (gptr*) &sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0,
0, 0},
+ {"start-datetime", OPT_START_DATETIME,
+ "Start reading the binlog at first event having a datetime equal or "
+ "posterior to the argument; the argument must be a date and time "
+ "in the local time zone, in any format accepted by the MySQL server "
+ "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 "
+ "(you should probably use quotes for your shell to set it properly).",
+ (gptr*) &start_datetime_str, (gptr*) &start_datetime_str,
+ 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"stop-datetime", OPT_STOP_DATETIME,
+ "Stop reading the binlog at first event having a datetime equal or "
+ "posterior to the argument; the argument must be a date and time "
+ "in the local time zone, in any format accepted by the MySQL server "
+ "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 "
+ "(you should probably use quotes for your shell to set it properly).",
+ (gptr*) &stop_datetime_str, (gptr*) &stop_datetime_str,
+ 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"start-position", OPT_START_POSITION,
+ "Start reading the binlog at position N. Applies to the first binlog "
+ "passed on the command line.",
+ (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL,
+ REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE,
+ /* COM_BINLOG_DUMP accepts only 4 bytes for the position */
+ (ulonglong)(~(uint32)0), 0, 0, 0},
+ {"stop-position", OPT_STOP_POSITION,
+ "Stop reading the binlog at position N. Applies to the last binlog "
+ "passed on the command line.",
+ (gptr*) &stop_position, (gptr*) &stop_position, 0, GET_ULL,
+ REQUIRED_ARG, (ulonglong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE,
+ (ulonglong)(~(my_off_t)0), 0, 0, 0},
{"to-last-log", 't', "Requires -R. Will not stop at the end of the \
requested binlog but rather continue printing until the end of the last \
binlog of the MySQL server. If you send the output to the same MySQL server, \
@@ -513,6 +572,29 @@ the mysql command line client\n\n");
my_print_variables(my_long_options);
}
+
+static my_time_t convert_str_to_timestamp(const char* str)
+{
+ int was_cut;
+ MYSQL_TIME l_time;
+ long dummy_my_timezone;
+ bool dummy_in_dst_time_gap;
+ /* We require a total specification (date AND time) */
+ if (str_to_datetime(str, strlen(str), &l_time, 0, &was_cut) !=
+ MYSQL_TIMESTAMP_DATETIME || was_cut)
+ {
+ fprintf(stderr, "Incorrect date and time argument: %s\n", str);
+ exit(1);
+ }
+ /*
+ Note that Feb 30th, Apr 31st cause no error messages and are mapped to
+ the next existing day, like in mysqld. Maybe this could be changed when
+ mysqld is changed too (with its "strict" mode?).
+ */
+ return
+ my_system_gmt_sec(&l_time, &dummy_my_timezone, &dummy_in_dst_time_gap);
+}
+
#include <help_end.h>
extern "C" my_bool
@@ -559,7 +641,12 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
}
break;
}
- break;
+ case OPT_START_DATETIME:
+ start_datetime= convert_str_to_timestamp(start_datetime_str);
+ break;
+ case OPT_STOP_DATETIME:
+ stop_datetime= convert_str_to_timestamp(stop_datetime_str);
+ break;
case 'V':
print_version();
exit(0);
@@ -604,9 +691,8 @@ static MYSQL* safe_connect()
static int dump_log_entries(const char* logname)
{
- if (remote_opt)
- return dump_remote_log_entries(logname);
- return dump_local_log_entries(logname);
+ return (remote_opt ? dump_remote_log_entries(logname) :
+ dump_local_log_entries(logname));
}
@@ -663,21 +749,27 @@ static int dump_remote_log_entries(const char* logname)
char buf[128];
char last_db[FN_REFLEN+1] = "";
uint len, logname_len;
- NET* net = &mysql->net;
+ NET* net;
int old_format;
+ int error= 0;
+ my_off_t old_off= start_position_mot;
+ char fname[FN_REFLEN+1];
DBUG_ENTER("dump_remote_log_entries");
+ /*
+ Even if we already read one binlog (case of >=2 binlogs on command line),
+ we cannot re-use the same connection as before, because it is now dead
+ (COM_BINLOG_DUMP kills the thread when it finishes).
+ */
+ mysql= safe_connect();
+ net= &mysql->net;
old_format = check_master_version(mysql);
- if (!position)
- position = BIN_LOG_HEADER_SIZE; // protect the innocent from spam
- if (position < BIN_LOG_HEADER_SIZE)
- {
- position = BIN_LOG_HEADER_SIZE;
- // warn the user
- sql_print_error("Warning: The position in the binary log can't be less than %d.\nStarting from position %d\n", BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE);
- }
- int4store(buf, position);
+ /*
+ COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to
+ cast to uint32.
+ */
+ int4store(buf, (uint32)start_position);
int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags);
logname_len = (uint) strlen(logname);
int4store(buf + 6, 0);
@@ -685,33 +777,32 @@ static int dump_remote_log_entries(const char* logname)
if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1))
{
fprintf(stderr,"Got fatal error sending the log dump command\n");
- DBUG_RETURN(1);
+ error= 1;
+ goto err;
}
- my_off_t old_off= position;
- ulonglong rec_count= 0;
- char fname[FN_REFLEN+1];
-
for (;;)
{
- const char *error;
+ const char *error_msg;
len = net_safe_read(mysql);
if (len == packet_error)
{
fprintf(stderr, "Got error reading packet from server: %s\n",
mysql_error(mysql));
- DBUG_RETURN(1);
+ error= 1;
+ goto err;
}
if (len < 8 && net->read_pos[0] == 254)
break; // end of data
DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
len, net->read_pos[5]));
Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 ,
- len - 1, &error, old_format);
+ len - 1, &error_msg, old_format);
if (!ev)
{
fprintf(stderr, "Could not construct log event object\n");
- DBUG_RETURN(1);
+ error= 1;
+ goto err;
}
Log_event_type type= ev->get_type_code();
@@ -735,22 +826,32 @@ static int dump_remote_log_entries(const char* logname)
which are about the binlogs, so which would trigger the end-detection
below.
*/
- if ((rev->when == 0) && !to_last_remote_log)
+ if (rev->when == 0)
{
- if ((rev->ident_len != logname_len) ||
- memcmp(rev->new_log_ident, logname, logname_len))
- DBUG_RETURN(0);
- /*
- Otherwise, this is a fake Rotate for our log, at the very beginning
- for sure. Skip it, because it was not in the original log. If we
- are running with to_last_remote_log, we print it, because it serves
- as a useful marker between binlogs then.
- */
- continue;
+ if (!to_last_remote_log)
+ {
+ if ((rev->ident_len != logname_len) ||
+ memcmp(rev->new_log_ident, logname, logname_len))
+ {
+ error= 0;
+ goto err;
+ }
+ /*
+ Otherwise, this is a fake Rotate for our log, at the very
+ beginning for sure. Skip it, because it was not in the original
+ log. If we are running with to_last_remote_log, we print it,
+ because it serves as a useful marker between binlogs then.
+ */
+ continue;
+ }
+ len= 1; // fake Rotate, so don't increment old_off
}
}
- if (process_event(&rec_count,last_db,ev,old_off,old_format))
- DBUG_RETURN(1);
+ if ((error= process_event(last_db,ev,old_off,old_format)))
+ {
+ error= ((error < 0) ? 0 : 1);
+ goto err;
+ }
}
else
{
@@ -760,29 +861,35 @@ static int dump_remote_log_entries(const char* logname)
File file;
if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0)
- DBUG_RETURN(1);
+ {
+ error= 1;
+ goto err;
+ }
- if (process_event(&rec_count,last_db,ev,old_off,old_format))
+ if ((error= process_event(last_db,ev,old_off,old_format)))
{
my_close(file,MYF(MY_WME));
- DBUG_RETURN(1);
+ error= ((error < 0) ? 0 : 1);
+ goto err;
}
if (load_processor.load_old_format_file(net,old_fname,old_len,file))
{
my_close(file,MYF(MY_WME));
- DBUG_RETURN(1);
+ error= 1;
+ goto err;
}
my_close(file,MYF(MY_WME));
}
/*
Let's adjust offset for remote log as for local log to produce
- similar text. As we don't print the fake Rotate event, all events are
- real so we can simply add the length.
+ similar text.
*/
old_off+= len-1;
}
- DBUG_RETURN(0);
+err:
+ mysql_close(mysql);
+ DBUG_RETURN(error);
}
@@ -817,7 +924,6 @@ static int dump_local_log_entries(const char* logname)
{
File fd = -1;
IO_CACHE cache,*file= &cache;
- ulonglong rec_count = 0;
char last_db[FN_REFLEN+1];
byte tmp_buff[BIN_LOG_HEADER_SIZE];
bool old_format = 0;
@@ -829,7 +935,7 @@ static int dump_local_log_entries(const char* logname)
{
if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
return 1;
- if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0,
+ if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0,
MYF(MY_WME | MY_NABP)))
{
my_close(fd, MYF(MY_WME));
@@ -843,12 +949,12 @@ static int dump_local_log_entries(const char* logname)
0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
return 1;
old_format = check_header(file);
- if (position)
+ if (start_position)
{
- /* skip 'position' characters from stdout */
+ /* skip 'start_position' characters from stdout */
byte buff[IO_SIZE];
my_off_t length,tmp;
- for (length= (my_off_t) position ; length > 0 ; length-=tmp)
+ for (length= start_position_mot ; length > 0 ; length-=tmp)
{
tmp=min(length,sizeof(buff));
if (my_b_read(file, buff, (uint) tmp))
@@ -858,11 +964,11 @@ static int dump_local_log_entries(const char* logname)
}
}
}
- file->pos_in_file=position;
+ file->pos_in_file= start_position_mot;
file->seek_not_done=0;
}
- if (!position)
+ if (!start_position)
{
// Skip header
if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
@@ -891,9 +997,10 @@ static int dump_local_log_entries(const char* logname)
// file->error == 0 means EOF, that's OK, we break in this case
break;
}
- if (process_event(&rec_count,last_db,ev,old_off,false))
+ if ((error= process_event(last_db,ev,old_off,false)))
{
- error= 1;
+ if (error < 0)
+ error= 0;
break;
}
}
@@ -909,11 +1016,14 @@ end:
int main(int argc, char** argv)
{
static char **defaults_argv;
- int exit_value;
+ int exit_value= 0;
+ ulonglong save_stop_position;
MY_INIT(argv[0]);
DBUG_ENTER("main");
DBUG_PROCESS(argv[0]);
+ init_time(); // for time functions
+
parse_args(&argc, (char***)&argv);
defaults_argv=argv;
@@ -925,8 +1035,6 @@ int main(int argc, char** argv)
}
my_set_max_open_files(open_files_limit);
- if (remote_opt)
- mysql = safe_connect();
MY_TMPDIR tmpdir;
tmpdir.list= 0;
@@ -944,24 +1052,26 @@ int main(int argc, char** argv)
else
load_processor.init_by_cur_dir();
- exit_value= 0;
fprintf(result_file,
"/*!40019 SET @@session.max_insert_delayed_threads=0*/;\n");
- while (--argc >= 0)
+ for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ;
+ (--argc >= 0) && !stop_passed ; )
{
+ if (argc == 0) // last log, --stop-position applies
+ stop_position= save_stop_position;
if (dump_log_entries(*(argv++)))
{
exit_value=1;
break;
}
+ // For next log, --start-position does not apply
+ start_position= BIN_LOG_HEADER_SIZE;
}
if (tmpdir.list)
free_tmpdir(&tmpdir);
if (result_file != stdout)
my_fclose(result_file, MYF(0));
- if (remote_opt)
- mysql_close(mysql);
cleanup();
free_defaults(defaults_argv);
my_free_open_file_info();
diff --git a/include/my_time.h b/include/my_time.h
index e42f7e9e402..1212f0533e2 100644
--- a/include/my_time.h
+++ b/include/my_time.h
@@ -41,6 +41,13 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
bool str_to_time(const char *str,uint length, MYSQL_TIME *l_time,
int *was_cut);
+long calc_daynr(uint year,uint month,uint day);
+
+void init_time(void);
+
+my_time_t
+my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap);
+
C_MODE_END
#endif /* _my_time_h_ */
diff --git a/include/mysql_time.h b/include/mysql_time.h
index 943d018fc14..32da27ba33e 100644
--- a/include/mysql_time.h
+++ b/include/mysql_time.h
@@ -34,4 +34,13 @@ typedef struct st_mysql_time
enum enum_mysql_timestamp_type time_type;
} MYSQL_TIME;
+
+/*
+ Portable time_t replacement.
+ Should be signed and hold seconds for 1902-2038 range.
+*/
+typedef long my_time_t;
+#define MY_TIME_T_MAX LONG_MAX
+#define MY_TIME_T_MIN LONG_MIN
+
#endif /* _mysql_time_h_ */
diff --git a/mysql-test/r/mysqlbinlog2.result b/mysql-test/r/mysqlbinlog2.result
new file mode 100644
index 00000000000..3c1b85e05a1
--- /dev/null
+++ b/mysql-test/r/mysqlbinlog2.result
@@ -0,0 +1,446 @@
+drop table if exists t1;
+reset master;
+set @a=UNIX_TIMESTAMP("2020-01-21 15:32:22");
+set timestamp=@a;
+create table t1 (a int auto_increment not null primary key, b char(3));
+insert into t1 values(null, "a");
+insert into t1 values(null, "b");
+set timestamp=@a+2;
+insert into t1 values(null, "c");
+set timestamp=@a+4;
+insert into t1 values(null, "d");
+insert into t1 values(null, "e");
+flush logs;
+set timestamp=@a+1;
+insert into t1 values(null, "f");
+
+--- Local --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+
+--- offset --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+SET INSERT_ID=1;
+use test;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+
+--- start-position --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+
+--- stop-position --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+
+--- start-datetime --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+SET INSERT_ID=3;
+use test;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+
+--- stop-datetime --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+
+--- Local with 2 binlogs on command line --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+use test;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- offset --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+SET INSERT_ID=1;
+use test;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+use test;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- start-position --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+use test;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- stop-position --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+
+--- start-datetime --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+SET INSERT_ID=3;
+use test;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+use test;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- stop-datetime --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+
+--- Remote --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+
+--- offset --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+SET INSERT_ID=1;
+use test;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+
+--- start-position --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+
+--- stop-position --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+
+--- start-datetime --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+SET INSERT_ID=3;
+use test;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+
+--- stop-datetime --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+
+--- Remote with 2 binlogs on command line --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+use test;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- offset --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+SET INSERT_ID=1;
+use test;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+use test;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- start-position --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+use test;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- stop-position --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+
+--- start-datetime --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+SET INSERT_ID=3;
+use test;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+use test;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- stop-datetime --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+
+--- to-last-log --
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+use test;
+SET TIMESTAMP=1579609942;
+create table t1 (a int auto_increment not null primary key, b char(3));
+SET INSERT_ID=1;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "a");
+SET INSERT_ID=2;
+SET TIMESTAMP=1579609942;
+insert into t1 values(null, "b");
+SET INSERT_ID=3;
+SET TIMESTAMP=1579609944;
+insert into t1 values(null, "c");
+SET INSERT_ID=4;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "d");
+SET INSERT_ID=5;
+SET TIMESTAMP=1579609946;
+insert into t1 values(null, "e");
+SET INSERT_ID=6;
+SET TIMESTAMP=1579609943;
+insert into t1 values(null, "f");
+
+--- end of test --
+drop table t1;
diff --git a/mysql-test/t/mysqlbinlog2.test b/mysql-test/t/mysqlbinlog2.test
new file mode 100644
index 00000000000..c6cff7558d4
--- /dev/null
+++ b/mysql-test/t/mysqlbinlog2.test
@@ -0,0 +1,156 @@
+# Test for the new options --start-datetime, stop-datetime,
+# and a few others.
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+reset master;
+
+# We need this for getting fixed timestamps inside of this test.
+# I use a date in the future to keep a growing timestamp along the
+# binlog (including the Start_log_event). This test will work
+# unchanged everywhere, because mysql-test-run has fixed TZ, which it
+# exports (so mysqlbinlog has same fixed TZ).
+set @a=UNIX_TIMESTAMP("2020-01-21 15:32:22");
+set timestamp=@a;
+create table t1 (a int auto_increment not null primary key, b char(3));
+insert into t1 values(null, "a");
+insert into t1 values(null, "b");
+set timestamp=@a+2;
+insert into t1 values(null, "c");
+set timestamp=@a+4;
+insert into t1 values(null, "d");
+insert into t1 values(null, "e");
+
+flush logs;
+set timestamp=@a+1; # this could happen on a slave
+insert into t1 values(null, "f");
+
+# delimiters are for easier debugging in future
+
+--disable_query_log
+select "--- Local --" as "";
+--enable_query_log
+
+#
+# We should use --short-form everywhere because in other case output will
+# be time dependent (the Start events). Better than nothing.
+#
+
+--exec $MYSQL_BINLOG --short-form $MYSQL_TEST_DIR/var/log/master-bin.000001
+
+--disable_query_log
+select "--- offset --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --offset=2 $MYSQL_TEST_DIR/var/log/master-bin.000001
+--disable_query_log
+select "--- start-position --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --start-position=497 $MYSQL_TEST_DIR/var/log/master-bin.000001
+--disable_query_log
+select "--- stop-position --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --stop-position=497 $MYSQL_TEST_DIR/var/log/master-bin.000001
+--disable_query_log
+select "--- start-datetime --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form "--start-datetime=2020-01-21 15:32:24" $MYSQL_TEST_DIR/var/log/master-bin.000001
+--disable_query_log
+select "--- stop-datetime --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form "--stop-datetime=2020-01-21 15:32:24" $MYSQL_TEST_DIR/var/log/master-bin.000001
+
+--disable_query_log
+select "--- Local with 2 binlogs on command line --" as "";
+--enable_query_log
+
+# This is to verify that some options apply only to first, or last binlog
+
+--exec $MYSQL_BINLOG --short-form $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002
+
+--disable_query_log
+select "--- offset --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --offset=2 $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002
+--disable_query_log
+select "--- start-position --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --start-position=497 $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002
+--disable_query_log
+select "--- stop-position --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --stop-position=32 $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002
+--disable_query_log
+select "--- start-datetime --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form "--start-datetime=2020-01-21 15:32:24" $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002
+--disable_query_log
+select "--- stop-datetime --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form "--stop-datetime=2020-01-21 15:32:24" $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002
+
+--disable_query_log
+select "--- Remote --" as "";
+--enable_query_log
+
+--exec $MYSQL_BINLOG --short-form --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001
+
+--disable_query_log
+select "--- offset --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --offset=2 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001
+--disable_query_log
+select "--- start-position --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --start-position=497 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001
+--disable_query_log
+select "--- stop-position --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --stop-position=497 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001
+--disable_query_log
+select "--- start-datetime --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form "--start-datetime=2020-01-21 15:32:24" --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001
+--disable_query_log
+select "--- stop-datetime --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form "--stop-datetime=2020-01-21 15:32:24" --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001
+
+--disable_query_log
+select "--- Remote with 2 binlogs on command line --" as "";
+--enable_query_log
+
+--exec $MYSQL_BINLOG --short-form --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002
+
+--disable_query_log
+select "--- offset --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --offset=2 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002
+--disable_query_log
+select "--- start-position --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --start-position=497 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002
+--disable_query_log
+select "--- stop-position --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form --stop-position=32 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002
+--disable_query_log
+select "--- start-datetime --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form "--start-datetime=20200121153224" --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002
+--disable_query_log
+select "--- stop-datetime --" as "";
+--enable_query_log
+--exec $MYSQL_BINLOG --short-form "--stop-datetime=2020/01/21 15@32@24" --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002
+
+--disable_query_log
+select "--- to-last-log --" as "";
+--enable_query_log
+
+--exec $MYSQL_BINLOG --short-form --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT --to-last-log master-bin.000001
+
+# clean up
+--disable_query_log
+select "--- end of test --" as "";
+--enable_query_log
+drop table t1;
diff --git a/sql-common/my_time.c b/sql-common/my_time.c
index 46c84ac9ba7..24c19be47ba 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -35,6 +35,16 @@ static uchar internal_format_positions[]=
static char time_separator=':';
+static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */
+uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
+
+/*
+ Offset of system time zone from UTC in seconds used to speed up
+ work of my_system_gmt_sec() function.
+*/
+static long my_time_zone=0;
+
+
/*
Convert a timestamp string to a MYSQL_TIME value.
@@ -559,3 +569,148 @@ fractional:
}
+/*
+ Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
+
+ SYNOPSIS
+ init_time()
+*/
+void init_time(void)
+{
+ time_t seconds;
+ struct tm *l_time,tm_tmp;;
+ MYSQL_TIME my_time;
+ bool not_used;
+
+ seconds= (time_t) time((time_t*) 0);
+ localtime_r(&seconds,&tm_tmp);
+ l_time= &tm_tmp;
+ my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
+ my_time.year= (uint) l_time->tm_year+1900;
+ my_time.month= (uint) l_time->tm_mon+1;
+ my_time.day= (uint) l_time->tm_mday;
+ my_time.hour= (uint) l_time->tm_hour;
+ my_time.minute= (uint) l_time->tm_min;
+ my_time.second= (uint) l_time->tm_sec;
+ my_system_gmt_sec(&my_time, &my_time_zone, &not_used); /* Init my_time_zone */
+}
+
+
+ /* Calculate nr of day since year 0 in new date-system (from 1615) */
+
+long calc_daynr(uint year,uint month,uint day)
+{
+ long delsum;
+ int temp;
+ DBUG_ENTER("calc_daynr");
+
+ if (year == 0 && month == 0 && day == 0)
+ DBUG_RETURN(0); /* Skip errors */
+ if (year < 200)
+ {
+ if ((year=year+1900) < 1900+YY_PART_YEAR)
+ year+=100;
+ }
+ delsum= (long) (365L * year+ 31*(month-1) +day);
+ if (month <= 2)
+ year--;
+ else
+ delsum-= (long) (month*4+23)/10;
+ temp=(int) ((year/100+1)*3)/4;
+ DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld",
+ year+(month <= 2),month,day,delsum+year/4-temp));
+ DBUG_RETURN(delsum+(int) year/4-temp);
+} /* calc_daynr */
+
+
+/*
+ Convert time in MYSQL_TIME representation in system time zone to its
+ my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
+
+ SYNOPSIS
+ my_system_gmt_sec()
+ t - time value to be converted
+ my_timezone - pointer to long where offset of system time zone
+ from UTC will be stored for caching
+ in_dst_time_gap - set to true if time falls into spring time-gap
+
+ NOTES
+ The idea is to cache the time zone offset from UTC (including daylight
+ saving time) for the next call to make things faster. But currently we
+ just calculate this offset during startup (by calling init_time()
+ function) and use it all the time.
+ Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
+ is not allowed).
+
+ RETURN VALUE
+ Time in UTC seconds since Unix Epoch representation.
+*/
+my_time_t
+my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap)
+{
+ uint loop;
+ time_t tmp;
+ struct tm *l_time,tm_tmp;
+ long diff, current_timezone;
+
+ /*
+ Calculate the gmt time based on current time and timezone
+ The -1 on the end is to ensure that if have a date that exists twice
+ (like 2002-10-27 02:00:0 MET), we will find the initial date.
+
+ By doing -3600 we will have to call localtime_r() several times, but
+ I couldn't come up with a better way to get a repeatable result :(
+
+ We can't use mktime() as it's buggy on many platforms and not thread safe.
+ */
+ tmp=(time_t) (((calc_daynr((uint) t->year,(uint) t->month,(uint) t->day) -
+ (long) days_at_timestart)*86400L + (long) t->hour*3600L +
+ (long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
+ 3600);
+ current_timezone= my_time_zone;
+
+ localtime_r(&tmp,&tm_tmp);
+ l_time=&tm_tmp;
+ for (loop=0;
+ loop < 2 &&
+ (t->hour != (uint) l_time->tm_hour ||
+ t->minute != (uint) l_time->tm_min);
+ loop++)
+ { /* One check should be enough ? */
+ /* Get difference in days */
+ int days= t->day - l_time->tm_mday;
+ if (days < -1)
+ days= 1; // Month has wrapped
+ else if (days > 1)
+ days= -1;
+ diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
+ (long) (60*((int) t->minute - (int) l_time->tm_min)));
+ current_timezone+= diff+3600; // Compensate for -3600 above
+ tmp+= (time_t) diff;
+ localtime_r(&tmp,&tm_tmp);
+ l_time=&tm_tmp;
+ }
+ /*
+ Fix that if we are in the not existing daylight saving time hour
+ we move the start of the next real hour
+ */
+ if (loop == 2 && t->hour != (uint) l_time->tm_hour)
+ {
+ int days= t->day - l_time->tm_mday;
+ if (days < -1)
+ days=1; // Month has wrapped
+ else if (days > 1)
+ days= -1;
+ diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
+ (long) (60*((int) t->minute - (int) l_time->tm_min)));
+ if (diff == 3600)
+ tmp+=3600 - t->minute*60 - t->second; // Move to next hour
+ else if (diff == -3600)
+ tmp-=t->minute*60 + t->second; // Move to previous hour
+
+ *in_dst_time_gap= 1;
+ }
+ *my_timezone= current_timezone;
+
+ return (my_time_t) tmp;
+} /* my_system_gmt_sec */
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 72ac3af70ff..f68d0951ea1 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -823,7 +823,7 @@ extern Gt_creator gt_creator;
extern Lt_creator lt_creator;
extern Ge_creator ge_creator;
extern Le_creator le_creator;
-extern uchar *days_in_month;
+extern uchar days_in_month[];
extern char language[LIBLEN],reg_ext[FN_EXTLEN];
extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
@@ -989,12 +989,9 @@ void free_blobs(TABLE *table);
int set_zone(int nr,int min_zone,int max_zone);
ulong convert_period_to_month(ulong period);
ulong convert_month_to_period(ulong month);
-long calc_daynr(uint year,uint month,uint day);
uint calc_days_in_year(uint year);
void get_date_from_daynr(long daynr,uint *year, uint *month,
uint *day);
-void init_time(void);
-my_time_t my_system_gmt_sec(const TIME *, long *current_timezone, bool *not_exist);
my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *not_exist);
bool str_to_time_with_warn(const char *str,uint length,TIME *l_time);
timestamp_type str_to_datetime_with_warn(const char *str, uint length,
diff --git a/sql/time.cc b/sql/time.cc
index 132612e53c5..4421b6aa00f 100644
--- a/sql/time.cc
+++ b/sql/time.cc
@@ -20,166 +20,9 @@
#include "mysql_priv.h"
#include <m_ctype.h>
-static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */
-uchar *days_in_month= (uchar*) "\037\034\037\036\037\036\037\037\036\037\036\037";
-
-
-/*
- Offset of system time zone from UTC in seconds used to speed up
- work of my_system_gmt_sec() function.
-*/
-static long my_time_zone=0;
-
-
-/*
- Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
-
- SYNOPSIS
- init_time()
-*/
-void init_time(void)
-{
- time_t seconds;
- struct tm *l_time,tm_tmp;;
- TIME my_time;
- bool not_used;
-
- seconds= (time_t) time((time_t*) 0);
- localtime_r(&seconds,&tm_tmp);
- l_time= &tm_tmp;
- my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
- my_time.year= (uint) l_time->tm_year+1900;
- my_time.month= (uint) l_time->tm_mon+1;
- my_time.day= (uint) l_time->tm_mday;
- my_time.hour= (uint) l_time->tm_hour;
- my_time.minute= (uint) l_time->tm_min;
- my_time.second= (uint) l_time->tm_sec;
- my_system_gmt_sec(&my_time, &my_time_zone, &not_used); /* Init my_time_zone */
-}
-
-
-/*
- Convert time in TIME representation in system time zone to its
- my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
-
- SYNOPSIS
- my_system_gmt_sec()
- t - time value to be converted
- my_timezone - pointer to long where offset of system time zone
- from UTC will be stored for caching
- in_dst_time_gap - set to true if time falls into spring time-gap
-
- NOTES
- The idea is to cache the time zone offset from UTC (including daylight
- saving time) for the next call to make things faster. But currently we
- just calculate this offset during startup (by calling init_time()
- function) and use it all the time.
- Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
- is not allowed).
-
- RETURN VALUE
- Time in UTC seconds since Unix Epoch representation.
-*/
-my_time_t
-my_system_gmt_sec(const TIME *t, long *my_timezone, bool *in_dst_time_gap)
-{
- uint loop;
- time_t tmp;
- struct tm *l_time,tm_tmp;
- long diff, current_timezone;
-
- /*
- Calculate the gmt time based on current time and timezone
- The -1 on the end is to ensure that if have a date that exists twice
- (like 2002-10-27 02:00:0 MET), we will find the initial date.
-
- By doing -3600 we will have to call localtime_r() several times, but
- I couldn't come up with a better way to get a repeatable result :(
-
- We can't use mktime() as it's buggy on many platforms and not thread safe.
- */
- tmp=(time_t) (((calc_daynr((uint) t->year,(uint) t->month,(uint) t->day) -
- (long) days_at_timestart)*86400L + (long) t->hour*3600L +
- (long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
- 3600);
- current_timezone= my_time_zone;
-
- localtime_r(&tmp,&tm_tmp);
- l_time=&tm_tmp;
- for (loop=0;
- loop < 2 &&
- (t->hour != (uint) l_time->tm_hour ||
- t->minute != (uint) l_time->tm_min);
- loop++)
- { /* One check should be enough ? */
- /* Get difference in days */
- int days= t->day - l_time->tm_mday;
- if (days < -1)
- days= 1; // Month has wrapped
- else if (days > 1)
- days= -1;
- diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
- (long) (60*((int) t->minute - (int) l_time->tm_min)));
- current_timezone+= diff+3600; // Compensate for -3600 above
- tmp+= (time_t) diff;
- localtime_r(&tmp,&tm_tmp);
- l_time=&tm_tmp;
- }
- /*
- Fix that if we are in the not existing daylight saving time hour
- we move the start of the next real hour
- */
- if (loop == 2 && t->hour != (uint) l_time->tm_hour)
- {
- int days= t->day - l_time->tm_mday;
- if (days < -1)
- days=1; // Month has wrapped
- else if (days > 1)
- days= -1;
- diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
- (long) (60*((int) t->minute - (int) l_time->tm_min)));
- if (diff == 3600)
- tmp+=3600 - t->minute*60 - t->second; // Move to next hour
- else if (diff == -3600)
- tmp-=t->minute*60 + t->second; // Move to previous hour
-
- *in_dst_time_gap= 1;
- }
- *my_timezone= current_timezone;
-
- return (my_time_t) tmp;
-} /* my_system_gmt_sec */
-
/* Some functions to calculate dates */
- /* Calculate nr of day since year 0 in new date-system (from 1615) */
-
-long calc_daynr(uint year,uint month,uint day)
-{
- long delsum;
- int temp;
- DBUG_ENTER("calc_daynr");
-
- if (year == 0 && month == 0 && day == 0)
- DBUG_RETURN(0); /* Skip errors */
- if (year < 200)
- {
- if ((year=year+1900) < 1900+YY_PART_YEAR)
- year+=100;
- }
- delsum= (long) (365L * year+ 31*(month-1) +day);
- if (month <= 2)
- year--;
- else
- delsum-= (long) (month*4+23)/10;
- temp=(int) ((year/100+1)*3)/4;
- DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld",
- year+(month <= 2),month,day,delsum+year/4-temp));
- DBUG_RETURN(delsum+(int) year/4-temp);
-} /* calc_daynr */
-
-
#ifndef TESTTIME
/* Calc weekday from daynr */
/* Returns 0 for monday, 1 for tuesday .... */
diff --git a/sql/tztime.h b/sql/tztime.h
index 334b14f4fc4..9df5f965f34 100644
--- a/sql/tztime.h
+++ b/sql/tztime.h
@@ -19,15 +19,10 @@
#pragma interface /* gcc class interface */
#endif
-/*
- Portable time_t replacement.
- Should be signed and hold seconds for 1902-2038 range.
-*/
-typedef long my_time_t;
-#define MY_TIME_T_MAX LONG_MAX
-#define MY_TIME_T_MIN LONG_MIN
+#include <mysql_time.h>
#if !defined(TESTTIME) && !defined(TZINFO2SQL)
+
/*
This class represents abstract time zone and provides
basic interface for TIME <-> my_time_t conversion.