diff --git a/src/ui/public/directives/__tests__/timepicker.js b/src/ui/public/directives/__tests__/timepicker.js index 2ab4350fc5a723..82c964911b3271 100644 --- a/src/ui/public/directives/__tests__/timepicker.js +++ b/src/ui/public/directives/__tests__/timepicker.js @@ -455,5 +455,28 @@ describe('timepicker directive', function () { done(); }); + + describe('datepicker timezone issue', function () { + it('should ignore the timezone from the datepicker because it does not respect dateFormat:tz advanced setting', function (done) { + moment.tz.setDefault('UTC'); + + $scope.pickFromDate(new Date('2012-02-01 12:00-05:00')); + $scope.pickToDate(new Date('2012-02-11 12:00-05:00')); + $scope.$digest(); + + const formattedFrom = inputs.fromInput.val(); + const formattedTo = inputs.toInput.val(); + + expect(formattedFrom).to.be('2012-02-01 00:00:00.000'); + expect(formattedTo).to.be('2012-02-11 23:59:59.999'); + + done(); + }); + + after(function () { + moment.tz.setDefault('Browser'); + }); + }); + }); }); diff --git a/src/ui/public/timepicker/timepicker.js b/src/ui/public/timepicker/timepicker.js index 731117f35b86f2..8520db75884a14 100644 --- a/src/ui/public/timepicker/timepicker.js +++ b/src/ui/public/timepicker/timepicker.js @@ -77,16 +77,49 @@ module.directive('kbnTimepicker', function (quickRanges, timeUnits, refreshInter } }); + // If we always return a new object from the getters below (pickFromDate and pickToDate) we'll create an + // infinite digest loop, so we maintain these copies to return instead. + $scope.$watch('absolute.from', function (newDate) { + _.set($scope, 'browserAbsolute.from', new Date(newDate.year(), newDate.month(), newDate.date())); + }); + + $scope.$watch('absolute.to', function (newDate) { + _.set($scope, 'browserAbsolute.to', new Date(newDate.year(), newDate.month(), newDate.date())); + }); + + // The datepicker directive uses native Javascript Dates, ignoring moment's default timezone. This causes + // the datepicker and the text input above it to get out of sync if the user changed the `dateFormat:tz` config + // in advanced settings. The text input will show the date in the user selected timezone, the datepicker will + // show the date in the local browser timezone. Since we really just want a day, month, year from the datepicker + // instead of a moment in time, we grab those individual values from the native date. $scope.pickFromDate = function (date) { - if (!date) return $scope.absolute.from; - date.setHours(0, 0, 0, 0); // Start of day - return $scope.absolute.from = moment(date); + if (!date) return _.get($scope, 'browserAbsolute.from'); + + const defaultTimeZoneDate = moment({ + year: date.getFullYear(), + month: date.getMonth(), + day: date.getDate(), + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + }); + return $scope.absolute.from = defaultTimeZoneDate; }; $scope.pickToDate = function (date) { - if (!date) return $scope.absolute.to; - date.setHours(23, 59, 59, 999); // End of day - return $scope.absolute.to = moment(date); + if (!date) return _.get($scope, 'browserAbsolute.to'); + + const defaultTimeZoneDate = moment({ + year: date.getFullYear(), + month: date.getMonth(), + day: date.getDate(), + hour: 23, + minute: 59, + second: 59, + millisecond: 999, + }); + return $scope.absolute.to = defaultTimeZoneDate; }; $scope.setMode = function (thisMode) {