问题
I am building an application node.js where a user creates a record with a field named tour date and time. I am using daterangepicker plugin to show the date and time dropdown window. Once the user saves the record it is saved in the MySQL table as datetime field.
Then I am using fullcalendar to display all the tour dates for a given month-year.
But here is the problem. Lets say I select 28/11/2018 6:30 PM as the tour date and time. In the daterange picker it gets saved as
2018-11-28 18:30
$('input#tour_date').daterangepicker({
singleDatePicker: true,
showDropdowns: true,
timePicker: true,
timePickerIncrement: 30,
locale: {
format: 'DD/MM/YYYY hh:mm A'
}
}, function (chosendate) {
$('input[name="tour_date"]').val(moment(chosendate).format('YYYY-MM-DD hh:mm'));
//the value for input becomes 2018-11-28 18:30
});
I have not set any timezone for daterangepicker or momentjs. The datetime I get from the form (before saving to the database) is the same. i.e. it comes in as 2018-11-28 18:30.
However on saving to the database, it gets saved as
2018-11-28 01:00:00
It adds +06:30 (IST, Indian Standard Time value) to the incoming datetime field.
So when I open my calendar, the date is correct (28/11/2018) but the time is incorrect. The user saved the field expecting the time to be 6.30 PM but instead sees 01:00
I am saving the datetime as
var tourDate = new Date(req.body.tour_date).toISOString();
I am using Sequelize ORM and I have not given any specific timezone.
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, {
host: process.env.DB_HOST,
port: 3306,
dialect: 'mysql',
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
},
logging: false
});
What I want to do is no matter which timezone the user is present in. I want to save the given date and time as is in the database and retrieve it as is, to be shown in the calendar.
How do I achieve this?
回答1:
If you are storing date via sequelize, then it will store the date as UTC ,
So from the front end just pass the date in UTC , so it will get stored and if you are using date time picker it will automatically convert the UTC into your timezone , just pass the UTC data to your plugin.
Just use UTC while passing and you are good to go.
回答2:
- If you use DATETIME data type in mysql, then value save as is independent on mysql time_zone system variable. See here and here.
mysql return different values for some functions depending on time_zone system varible. For example:
SET time_zone='+00:00'; SELECT now(); //2018-11-30 17-52-00 SET time_zone='+03:00'; SELECT now(); //2018-11-30 20-52-00You can pass timezone option in sequelize to DB connetion. By default it '+00:00'. First of all, Sequelize use this value to set time_zone option on each connection (you can see it, if start Sequelize with DEBUG variable). Also Sequelize (or mysql2 package, i don't know) use timezone option to convert results returned by mysql to js Date objects. So:
const sequelize = new Sequelize('database', 'user', 'password', {timezone: "+00:00"}); sequelize.query('SELECT now() AS now;', {type: sequelize.QueryTypes.SELECT}) //2018-11-30T17:52:00.000Z sequelize.query('SELECT createdAt FROM users WHERE userId = 1;', {type: sequelize.QueryTypes.SELECT}) //2017-09-05T13:43:00.000Z const sequelize = new Sequelize('database', 'user', 'password', {timezone: "+03:00"}); sequelize.query('SELECT now() AS now;', {type: sequelize.QueryTypes.SELECT}) //2018-11-30T17:52:00.000Z sequelize.query('SELECT createdAt FROM users WHERE userId = 1;', {type: sequelize.QueryTypes.SELECT}) //2017-09-05T10:43:00.000Z
Explain:
For now:
+-----------+---------------------+----------------------------+--------------------------+
| time_zone | mysql return | sequelize read result as | sequelize return |
+-----------+---------------------+----------------------------+--------------------------+
| +00:00 | 2018-11-30 17-52-00 | 2018-11-30 17-52-00 +00:00 | 2018-11-30T17:52:00.000Z |
+-----------+---------------------+----------------------------+--------------------------+
| +03:00 | 2018-11-30 20-52-00 | 2018-11-30 20-52-00 +03:00 | 2018-11-30T17:52:00.000Z |
+-----------+---------------------+----------------------------+--------------------------+
For createdAt:
+-----------+---------------------+----------------------------+--------------------------+
| time_zone | mysql return | sequelize read result as | sequelize return |
+-----------+---------------------+----------------------------+--------------------------+
| +00:00 | 2017-09-05 13-43-00 | 2017-09-05 13-43-00 +00:00 | 2017-09-05T13:43:00.000Z |
+-----------+---------------------+----------------------------+--------------------------+
| +03:00 | 2017-09-05 13-43-00 | 2017-09-05 13-43-00 +03:00 | 2017-09-05T10:43:00.000Z |
+-----------+---------------------+----------------------------+--------------------------+
So be carefully when set timezone option, when use DATETIME datatype. If you insert/select your data only with sequelize, then you don't need to change timezone option.
For correct work with datetime in my work, I use next rules:
- For transport datetime between client and server use uniquely determined format: UNIX, ISO 8601 (string like
2017-09-05T13:43:00.000Z); - All DATETIME values in Mysql DB stores with zero timezone (+00:00); So timezone option for Sequelize is '+00:00';
Finally, browsers use time zone setting on your computer. Check the values at all steps and eliminate the uncertainties associated with incorrect interpretation of the time zone.
回答3:
Alright, after much confusion here is how this got solved. I had to convert the date at my client side to utc and then when it comes to my node.js server, I need to convert it to UTC again before saving it to database.
On client side I am setting the date in a hidden input field (which is what I am sending to server) using
$('input#tour_date').daterangepicker({
singleDatePicker: true,
showDropdowns: true,
timePicker: true,
timePickerIncrement: 30,
locale: {
format: 'DD/MM/YYYY hh:mm A'
}
}, function (chosendate) {
//this is the hidden input where the set date is being sent
$('input[name="tour_date"]').val(moment(chosendate).format('YYYY-MM-DDTHH:mm:ss'));
});
So one server it comes in as
2018-12-31T10:30:00.000Z
Which then I have to process again using moment like this:
moment.utc(req.body.tour_date); //this is saved to db
When I read the record and display it to the user I reprocess the date as
moment.utc(act.tour_date).format('DD/MM/YYYY hh:mm A')
So the user sees 31/12/2018 10:30 AM in the datepicker
I also pass moment.utc(act.tour_date).format('YYYY-MM-DDTHH:mm:ss') back to the hidden input, in case the user updates the record
Thank you everyone who helped me figure this out. Appreciate your time and efforts.
来源:https://stackoverflow.com/questions/53553878/how-to-read-and-store-datetime-as-is-from-javascript-to-mysql