Oprea.Rocks Logo

Get updates on new articles, and other opportunities

How to fix Bootstrap Datetimepicker reset to 12/31/1899

broken-watch

jQuery is not dead! I'd love to say it is, but it is alive and kicking. Unfortunately, its existence hit the team I'm currently working with, and it hit pretty hard.

We had this admin panel built on an older version of the Metronic theme, and the bootstrap-datetimepicker the theme used was giving us headaches. It would randomly reset to 12/31/1899 when losing focus. We were using UTC time and so our dates had this format DD/MM/YYYY HH:ii UTC.

We had to live with this bug for about three weeks, and we were always going back-and-forth with it between QA and Dev. For every fix we would put in, our QA would find a different combo that would confuse bootstrap-datetimepicker. It became this never-ending DEV-QA ping-pong match.

One afternoon, I shoulder-surfed one of my colleagues while she was trying to knock it out of the park for good. She was fiddling with moment.js formatting, trying to find the appropriate format that would not upset the datepicker. Seeing her having to do that, to circumvent the library, started to rub me the wrong way, so I asked her to pop open the plugin's source code. While looking through the source, I remembered there was something about jQuery plugins I used to like. You could always find them on jQuery.prototype or $.prototype or $.fn for short!

That was all I needed. I asked her to switch to DevTools and we started drilling down into jQuery.prototype. As it turns out, the plugin is located at jQuery.prototype.datetimepicker. This was the first step. The plugin also seems to expose a nice object, jQuery.prototype.datetimepicker.DPGlobal which has this neat property called nonpunctuation. This contains a regular expression that's applied to the input's value to properly separate it into pieces (parts). It is used by the plugin's internal parser to properly separate day from month from year, from time and from timezone.

The original regular expression looks like this:

[...]
nonpunctuation: /[^ -\/:[email protected]\[-`{-~\t\n\rTZ]+/g,
[...]

You can check line 1568, in the plugin's source code.

There's no problem with the expression. It is well written, and does its job. The problem is that it expects to parse ISO 8601-compatible date strings. This means that it will try to split the string by characters such as "T" or "Z". Guess what!? If you apply the original regular expression on this date string "02/02/2018 22:44 UTC" you will get the following results: 02 02 2018 22 44 U C. See for yourself by testing the regular expression.

Because of the way the parts are parsed — line 1619 in the source code — and because of the comparison on line 1682 the code goes berserk. The number of resulting parts, after applying the regular expression to the datetime string is greater than the number of items that are present in the format you provide to the plugin's initialization logic. That's bad, really bad! So bad it can send you to December 31st 1899!

Finally, our solution was to override the regular expression by removing T altogether, from the regular expression. What we did, was to place the plugin's initialization logic in a separate JavaScript file, and structure it like below:

$.prototype.datetimepicker.DPGlobal.nonpunctuation = /[^ -\/:[email protected]\[-`{-~\t\n\rZ]+/g;
var BootstrapDatetimepicker = function () {
  var currentDate = new Date();
  currentDate.setMinutes(0,0,0);
  currentDate.setHours(currentDate.getHours()+currentDate.getTimezoneOffset()/60);

  var datetimepickerOptions = {
    todayHighlight: true,
    weekStart: 1,
    minView: 0,
    autoclose: true,
    pickerPosition: 'bottom-left',
    format: 'dd/mm/yyyy hh:ii Z',
    timezone: 'UTC',
    startDate: currentDate,
  };

  var initializePickers = function () {
    $("input[name^='m_datetimepicker'],input[id^='m_datetimepicker']").datetimepicker(datetimepickerOptions);
  };

  return {
    // public functions
    init: function() {
      initializePickers();
    }
  };
}();

jQuery(document).ready(function() {
  BootstrapDatetimepicker.init();
});

We could have also removed Z, since we're not using it, but we were to lazy.

If there's anything you should get from this post, other than the solution to your bootstrap-datetimepicker problem, then here's a list:

  1. Always look for the GitHub repository of the project.
  2. Look for issues similar to yours.
  3. Read through people's comments.
  4. Read the source code.
  5. Give back if you found a solution. Write an article, write comments on issues, open a PR. Anything is valuable!

Cheers!

Photo credits: col_adamsonBrokenness