Angular Strap Datepicker: Invalid timestamps returned, UTC +0 vs UTC +12 hours
Situation:
- We are using Angular -Strap datepicker and want to return UTC timestamps to the server.
- When picking a date (no time picking), we found out that some computers return a timestamp from 0:00, and some from 12:00.
- It was a browser independent browser, but it was different between computers.
Problem:
- Some computers return 0:00, some 12:00.
What I have tried:
- While debugging, I figured out that the problem occurred on line 379 of angular-strap / dist / modules / datepicker.js # L379 , $ dateValue controller returns' Sat Mar 28 1987 12:00:00 GMT + 0100 (W. Europe Standard Time) 'instead of' Sat Mar 28 1987 00:00:00 GMT + 0100 (W. Europe standard time) ', the value returned by other computers.
Question:
- How to return time 0:00 in all situations?
source to share
I also faced the same problem. What I did was written by directive to convert date to utc date. check the sample code. This solves my problem.
(function (angular) {
angular.module('myApp').directive('datePicker', ['$parse', 'dateFilter', function ($parse, dateFilter) {
var inputDateFormat = 'dd/MM/yyyy';
var utcDateGeneretor = function (dateString) {
if ('undefined' === typeof dateString || '' === dateString) {
return null;
}
var parts = dateString.split('/');
if (3 !== parts.length) {
return null;
}
var year = parseInt(parts[2], 10);
var month = parseInt(parts[1], 10);
var day = parseInt(parts[0], 10);
if (year.toString().length < 4) {
return null;
}
if (month < 0 || year < 0 || day < 1) {
return null;
}
//Javascript month is zero based. thats why always reduce 1 form selected month.
return new Date(Date.UTC(year, (month - 1), day));
};
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ctrls) {
var mindatecalvalue = parseInt(attrs["mindatecalvalue"], 10);
var maxdatecalvalue = parseInt(attrs["maxdatecalvalue"], 10);
var twoDigitCutoffSpecified = attrs["twoDigitCutoff"];
var twoDigitYear1900cutoff = -1; //If a 2-digityear is less than this number it is treated as being 2000+, otherwise 1900+.
if (twoDigitCutoffSpecified) {
twoDigitYear1900cutoff = parseInt(attrs["twoDigitCutoff"], 10);
if (isNaN(twoDigitYear1900cutoff)) {
twoDigitYear1900cutoff = -1;
} else if (twoDigitYear1900cutoff <= 0) {
twoDigitYear1900cutoff += (new Date().getFullYear() - 2000); //A negative number is interpreted as a number of years before the current year.
}
}
var updateModel = function (dateText) {
var canonicalisedDateText = ensureFullYear(dateText, twoDigitYear1900cutoff);
// call $apply to bring stuff to angular model
scope.$apply(function () {
ctrls.$setViewValue(canonicalisedDateText);
});
ctrls.$render();
};
var ensureFullYear = function (dateText, twoDigitYear1900cutoff) {
var findYearRegex = /(.+[^0-9])([0-9]+)\s*$/;
var dateParts = findYearRegex.exec(dateText);
if ((!dateParts) || (dateParts.length != 3)) {
return dateText; //can't find a year in this date.
}
var originalYearAsStr = dateParts[2];
if (originalYearAsStr.length > 2) {
return dateText; //Not a 2-digit year.
}
var originalYear = parseInt(originalYearAsStr, 10);
var fullYear = 0;
if (originalYear <= twoDigitYear1900cutoff) {
fullYear = 2000 + originalYear;
} else {
fullYear = 1900 + originalYear;
}
var restOfDate = dateParts[1];
return restOfDate + fullYear;
}
ctrls.$formatters.push(function (modelValue) {
return dateFilter(modelValue, inputDateFormat);
});
ctrls.$parsers.push(function (dateString) {
return utcDateGeneretor(dateString);
});
}
};
}]);
})(angular);
source to share
I too faced a similar problem regarding date-only selection ... I solved it with ng-model $ parsers.
ngModelCtrl.$parsers.push(function (datepickerValue) {
return moment(datepickerValue).format("YYYY-MM-DD");
});
I am using moment.js to handle date. but above solution can work even without moment.js like below. (note: I have not tested the code below, so you may need to tweak it a bit)
ngModelCtrl.$parsers.push(function (datepickerValue) {
//return new Date(datepickerValue).toISOString().substring(0,10);
return new Date(datepickerValue).format("YYYY-MM-DD");
});
above will give you a date without a time stamp. But if your requirement says that you always need 0:00 at the end, this can also be achieved very easily ...
ngModelCtrl.$parsers.push(function (datepickerValue) {
return moment(datepickerValue).format("YYYY-MM-DD") + " 0:00";
});
complete directive code I used for this:
shared.directive("dateToIso", function () {
var linkFunction = function (scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$parsers.push(function (datepickerValue) {
return moment(datepickerValue).format("YYYY-MM-DD");
});
};
return {
restrict: "A",
require: "ngModel",
link: linkFunction
};
});
I've defined it as an attribute so you can use it with your other directives. you can use it like below.
<input type="text" ng-model="myDate" name="date" bs-datepicker date-to-iso>
source to share
Outside of fixing the bug in your local copy of AngularStrap, you can create a directive to wrap the DatePicker directive. I grabbed the plunker from angular-strap and wrote a decorator directive to always return 0:00 of the time for all situations. This is nice because you can write all the logic you need in a directive. Note: This directive creates a clock on the model, then makes any changes needed after the update and returns it to the application controller via a callback. Note. I also switched to moment.js for ease of use.
HTML:
<date-wrapper></date-wrapper>
Directive
app.directive('dateWrapper', function($compile) {
return {
restrict:'AE',
link:function(scope, element, attrs, ctrl) {
var html = '<input type="text" class="form-control" name="date" ng-model="selectedDate" bs-datepicker>';
var e = $compile(html)(scope);
element.replaceWith(e);
scope.$watch('selectedDate', function(date) {
var updatedDate = moment.utc(date).hour(0).minute(0).second(0).format("YYYY-MM-DDTHH:mm:ss Z");
scope.dateChanged(updatedDate);
});
}
}
});
Application controller example:
// call back for updated date
$scope.dateChanged = function(date) {
$scope.selectedZeroHourUtcDate = date;
};
source to share