Fix #5396 #5393 (time zone)
diff --git a/src/model/globalDefault.js b/src/model/globalDefault.js
index 8b9b6e4..8333f9a 100644
--- a/src/model/globalDefault.js
+++ b/src/model/globalDefault.js
@@ -51,6 +51,9 @@
         // `progressiveThreshold`, otherwise hover will cause restart of progressive,
         // which is unexpected.
         // see example <echarts/test/heatmap-large.html>.
-        hoverLayerThreshold: 3000
+        hoverLayerThreshold: 3000,
+
+        // See: module:echarts/scale/Time
+        useUTC: false
     };
 });
\ No newline at end of file
diff --git a/src/scale/Scale.js b/src/scale/Scale.js
index 1f4a755..1d58e2e 100644
--- a/src/scale/Scale.js
+++ b/src/scale/Scale.js
@@ -6,7 +6,12 @@
 
     var clazzUtil = require('../util/clazz');
 
-    function Scale() {
+    /**
+     * @param {Object} [setting]
+     */
+    function Scale(setting) {
+        this._setting = setting || {};
+
         /**
          * Extent
          * @type {Array.<number>}
@@ -39,6 +44,10 @@
         return val;
     };
 
+    scaleProto.getSetting = function (name) {
+        return this._setting[name];
+    };
+
     scaleProto.contain = function (val) {
         var extent = this._extent;
         return val >= extent[0] && val <= extent[1];
diff --git a/src/scale/Time.js b/src/scale/Time.js
index e83c094..7bd88b2 100644
--- a/src/scale/Time.js
+++ b/src/scale/Time.js
@@ -5,9 +5,20 @@
 
 define(function (require) {
 
+    // [About UTC and local time zone]:
+    // In most cases, `number.parseDate` will treat input data string as local time
+    // (except time zone is specified in time string). And `format.formateTime` returns
+    // local time by default. option.useUTC is false by default. This design have
+    // concidered these common case:
+    // (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
+    // in local time by default.
+    // (2) By default, the input data string (e.g., '2011-01-02') should be displayed
+    // as its original time, without any time difference.
+
     var zrUtil = require('zrender/core/util');
     var numberUtil = require('../util/number');
     var formatUtil = require('../util/format');
+    var scaleHelper = require('./helper');
 
     var IntervalScale = require('./Interval');
 
@@ -47,7 +58,7 @@
 
             var date = new Date(val);
 
-            return formatUtil.formatTime(stepLvl[0], date);
+            return formatUtil.formatTime(stepLvl[0], date, this.getSetting('useUTC'));
         },
 
         // Overwrite
@@ -81,6 +92,8 @@
 
         // Overwrite
         niceTicks: function (approxTickNum) {
+            var timezoneOffset = this.getSetting('useUTC')
+                ? 0 : numberUtil.getTimezoneOffset() * 60 * 1000;
             approxTickNum = approxTickNum || 10;
 
             var extent = this._extent;
@@ -103,10 +116,12 @@
             }
 
             var niceExtent = [
-                mathCeil(extent[0] / interval) * interval,
-                mathFloor(extent[1] / interval) * interval
+                Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset),
+                Math.round(mathFloor((extent[1] - timezoneOffset)/ interval) * interval + timezoneOffset)
             ];
 
+            scaleHelper.fixExtent(niceExtent, extent);
+
             this._stepLvl = level;
             // Interval will be used in getTicks
             this._interval = interval;
@@ -151,10 +166,11 @@
     ];
 
     /**
+     * @param {module:echarts/model/Model}
      * @return {module:echarts/scale/Time}
      */
-    TimeScale.create = function () {
-        return new TimeScale();
+    TimeScale.create = function (model) {
+        return new TimeScale({useUTC: model.ecModel.get('useUTC')});
     };
 
     return TimeScale;
diff --git a/src/scale/helper.js b/src/scale/helper.js
index 4a08390..237e0ef 100644
--- a/src/scale/helper.js
+++ b/src/scale/helper.js
@@ -28,14 +28,7 @@
             roundNumber(Math.floor(extent[1] / interval) * interval, precision)
         ];
 
-        // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
-        !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);
-        !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);
-        clamp(niceTickExtent, 0, extent);
-        clamp(niceTickExtent, 1, extent);
-        if (niceTickExtent[0] > niceTickExtent[1]) {
-            niceTickExtent[0] = niceTickExtent[1];
-        }
+        helper.fixExtent(niceTickExtent, extent);
 
         return result;
     };
@@ -44,6 +37,17 @@
         niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);
     }
 
+    // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
+    helper.fixExtent = function (niceTickExtent, extent) {
+        !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);
+        !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);
+        clamp(niceTickExtent, 0, extent);
+        clamp(niceTickExtent, 1, extent);
+        if (niceTickExtent[0] > niceTickExtent[1]) {
+            niceTickExtent[0] = niceTickExtent[1];
+        }
+    };
+
     helper.intervalScaleGetTicks = function (interval, extent, niceTickExtent, intervalPrecision) {
         var ticks = [];
 
diff --git a/src/util/format.js b/src/util/format.js
index b7ee9d5..3cb507b 100644
--- a/src/util/format.js
+++ b/src/util/format.js
@@ -143,15 +143,12 @@
      * ISO Date format
      * @param {string} tpl
      * @param {number} value
-     * @param {boolean} [isLocal=false] Default use UTC
-     *  Why default UTC? In most case, time provided by user is
-     *  understood in UTC. For example, new Date('2012-01-01')
-     *  or a string '2012-01-01' or a timestamp. So it is
-     *  recommended to format time in UTC.
-     *  (see `echarts/util/number.js#parseDate`);
+     * @param {boolean} [isUTC=false] Default in local time.
+     *           see `module:echarts/scale/Time`
+     *           and `module:echarts/util/number#parseDate`.
      * @inner
      */
-    formatUtil.formatTime = function (tpl, value, isLocal) {
+    formatUtil.formatTime = function (tpl, value, isUTC) {
         if (tpl === 'week'
             || tpl === 'month'
             || tpl === 'quarter'
@@ -162,7 +159,7 @@
         }
 
         var date = numberUtil.parseDate(value);
-        var utc = isLocal ? '' : 'UTC';
+        var utc = isUTC ? 'UTC' : '';
         var y = date['get' + utc + 'FullYear']();
         var M = date['get' + utc + 'Month']() + 1;
         var d = date['get' + utc + 'Date']();
diff --git a/src/util/number.js b/src/util/number.js
index fcb8d4d..23ed770 100644
--- a/src/util/number.js
+++ b/src/util/number.js
@@ -206,8 +206,14 @@
         return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;
     };
 
-    var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(?:Z|([\+\-]\d\d):?\d\d)?)?)?)?)?$/; // jshint ignore:line
-    var TIMEZONE_OFFSET = (new Date()).getTimezoneOffset();
+    var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line
+
+    /**
+     * @return {number} in minutes
+     */
+    number.getTimezoneOffset = function () {
+        return (new Date()).getTimezoneOffset();
+    };
 
     /**
      * @param {string|Date|number} value These values can be accepted:
@@ -216,9 +222,9 @@
      *     + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
      *     + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
      *     + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
-     *     all of which will be treated as they reperent a time in UTC
-     *     if time zone is not specified.
-     *   + Or other string format, including:
+     *     all of which will be treated as local time if time zone is not specified
+     *     (see <https://momentjs.com/>).
+     *   + Or other string format, including (all of which will be treated as loacal time):
      *     '2012', '2012-3-1', '2012/3/1', '2012/03/01',
      *     '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
      *   + a timestamp, which represent a time in UTC.
@@ -241,6 +247,13 @@
                 return new Date(NaN);
             }
 
+            var timezoneOffset = number.getTimezoneOffset();
+            var timeOffset = !match[8]
+                ? 0
+                : match[8].toUpperCase() === 'Z'
+                ? timezoneOffset
+                : +match[8].slice(0, 3) * 60 + timezoneOffset;
+
             // match[n] can only be string or undefined.
             // But take care of '12' + 1 => '121'.
             return new Date(
@@ -248,7 +261,7 @@
                 +(match[2] || 1) - 1,
                 +match[3] || 1,
                 +match[4] || 0,
-                +(match[5] || 0) - (match[8] || 0) * 60 - TIMEZONE_OFFSET,
+                +(match[5] || 0) - timeOffset,
                 +match[6] || 0,
                 +match[7] || 0
             );
diff --git a/test/timeZone.html b/test/timeZone.html
new file mode 100644
index 0000000..a7df234
--- /dev/null
+++ b/test/timeZone.html
@@ -0,0 +1,293 @@
+<html>
+    <head>
+        <meta charset="utf-8">
+        <script src="esl.js"></script>
+        <script src="config.js"></script>
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+        <link rel="stylesheet" href="reset.css" />
+    </head>
+    <body>
+        <style>
+            h1 {
+                line-height: 60px;
+                height: 60px;
+                background: #ddd;
+                text-align: center;
+                font-weight: bold;
+                font-size: 14px;
+            }
+            .chart {
+                height: 350px;
+            }
+        </style>
+
+
+        <h1>time scale label shoule in local time when timestamp (in UTC) input, tick should align with day.</h1>
+        <div class="chart" id="chart0"></div>
+
+        <h1>time scale label shoule in UTC time when option.useUTC is `true`, tick should not align with day.</h1>
+        <div class="chart" id="chart1"></div>
+
+        <h1>useUTC: null, should display '00:00 01-03' in tooltip on the 1st point</h1>
+        <div class="chart" id="chart2"></div>
+
+        <h1>useUTC: true, should display '16:00 01-02' in tooltip on the 1st point</h1>
+        <div class="chart" id="chart3"></div>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+        <script>
+            var dataTimestamp = [
+                [1486656000000, 20000],
+                [1486742400000, 30000],
+                [1486828800000, 10000],
+                [1486915200000, 290000],
+                [1487001600000, 123355],
+                [1487088000000, 198128],
+                [1487174400000, 123124]
+            ];
+
+            var dataTimeString = [
+                ['2012-01-03', 20000],
+                ['2012-01-04', 30000],
+                ['2012-01-05', 10000],
+                ['2012-01-06', 290000]
+            ];
+
+            function makeTimeScaleOption(data, useUTC) {
+                return {
+                    useUTC: useUTC,
+                    tooltip: {
+                        trigger: 'axis'
+                    },
+                    xAxis: [{
+                        type: 'time',
+                        splitNumber: 7,
+                        axisLabel: {
+                            formatter: function (value) {
+                                return echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', value, useUTC);
+                            },
+                            rotate: 10
+                        },
+                        splitLine: {
+                            show: false
+                        }
+                    }],
+                    yAxis: [{
+                        type: 'value',
+                        splitLine: {
+                            show: false
+                        }
+                    }],
+                    series: [{
+                        type: 'line',
+                        smooth: true,
+                        data: data,
+                        label: {
+                            normal: {
+                                show: true,
+                                formatter: function (params) {
+                                    return echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', params.value[0], useUTC);
+                                }
+                            }
+                        }
+                    }]
+                };
+            }
+
+
+        </script>
+
+
+
+
+
+        <script>
+
+            var echarts;
+            var colorTool;
+            var chart;
+            var myChart;
+            var groupCategories = [];
+            var groupColors = [];
+
+            require([
+                'echarts',
+                'zrender/tool/color',
+                'echarts/chart/line',
+                'echarts/component/grid',
+                'echarts/component/legend',
+                'echarts/component/tooltip',
+                'echarts/component/toolbox',
+                'echarts/component/visualMap',
+                'echarts/component/dataZoom'
+            ], function (ec, ct) {
+                echarts = ec;
+                colorTool = ct;
+                var dom = document.getElementById('chart0');
+                if (!dom) {
+                    return;
+                }
+                chart = myChart = echarts.init(dom);
+
+                var option = makeTimeScaleOption(dataTimestamp);
+
+                chart.setOption(option);
+            });
+
+        </script>
+
+
+
+
+
+
+
+
+        <script>
+
+            var echarts;
+            var colorTool;
+            var chart;
+            var myChart;
+            var groupCategories = [];
+            var groupColors = [];
+
+            require([
+                'echarts',
+                'zrender/tool/color',
+                'echarts/chart/line',
+                'echarts/component/grid',
+                'echarts/component/legend',
+                'echarts/component/tooltip',
+                'echarts/component/toolbox',
+                'echarts/component/visualMap',
+                'echarts/component/dataZoom'
+            ], function (ec, ct) {
+                echarts = ec;
+                colorTool = ct;
+                var dom = document.getElementById('chart1');
+                if (!dom) {
+                    return;
+                }
+                chart = myChart = echarts.init(dom);
+
+                var option = makeTimeScaleOption(dataTimestamp, true);
+
+                chart.setOption(option);
+            });
+
+        </script>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+        <script>
+
+            var echarts;
+            var colorTool;
+            var chart;
+            var myChart;
+            var groupCategories = [];
+            var groupColors = [];
+
+            require([
+                'echarts',
+                'zrender/tool/color',
+                'echarts/chart/line',
+                'echarts/component/grid',
+                'echarts/component/legend',
+                'echarts/component/tooltip',
+                'echarts/component/toolbox',
+                'echarts/component/visualMap',
+                'echarts/component/dataZoom'
+            ], function (ec, ct) {
+                echarts = ec;
+                colorTool = ct;
+                var dom = document.getElementById('chart2');
+                if (!dom) {
+                    return;
+                }
+                chart = myChart = echarts.init(dom);
+
+                var option = makeTimeScaleOption(dataTimeString);
+
+                chart.setOption(option);
+            });
+
+        </script>
+
+
+
+
+
+
+
+
+
+
+
+        <script>
+
+            var echarts;
+            var colorTool;
+            var chart;
+            var myChart;
+            var groupCategories = [];
+            var groupColors = [];
+
+            require([
+                'echarts',
+                'zrender/tool/color',
+                'echarts/chart/line',
+                'echarts/component/grid',
+                'echarts/component/legend',
+                'echarts/component/tooltip',
+                'echarts/component/toolbox',
+                'echarts/component/visualMap',
+                'echarts/component/dataZoom'
+            ], function (ec, ct) {
+                echarts = ec;
+                colorTool = ct;
+                var dom = document.getElementById('chart3');
+                if (!dom) {
+                    return;
+                }
+                chart = myChart = echarts.init(dom);
+
+                var option = makeTimeScaleOption(dataTimeString, true);
+
+                chart.setOption(option);
+            });
+
+        </script>
+
+
+
+
+    </body>
+</html>
\ No newline at end of file
diff --git a/test/ut/spec/util/number.js b/test/ut/spec/util/number.js
index 0ef5779..903be81 100755
--- a/test/ut/spec/util/number.js
+++ b/test/ut/spec/util/number.js
@@ -214,18 +214,18 @@
             expect(+numberUtil.parseDate(1330819200000.01)).toEqual(1330819200000);
 
             // ISO string
-            expect(+numberUtil.parseDate('2012-03')).toEqual(1330560000000);
-            expect(+numberUtil.parseDate('2012-03-04')).toEqual(1330819200000);
-            expect(+numberUtil.parseDate('2012-03-04 05')).toEqual(1330837200000);
-            expect(+numberUtil.parseDate('2012-03-04T05')).toEqual(1330837200000);
-            expect(+numberUtil.parseDate('2012-03-04 05:06')).toEqual(1330837560000);
-            expect(+numberUtil.parseDate('2012-03-04T05:06')).toEqual(1330837560000);
-            expect(+numberUtil.parseDate('2012-03-04 05:06:07')).toEqual(1330837567000);
-            expect(+numberUtil.parseDate('2012-03-04T05:06:07')).toEqual(1330837567000);
-            expect(+numberUtil.parseDate('2012-03-04T05:06:07.123')).toEqual(1330837567123);
-            expect(+numberUtil.parseDate('2012-03-04T05:06:07,123')).toEqual(1330837567123);
-            expect(+numberUtil.parseDate('2012-03-04T05:06:07.12')).toEqual(1330837567012);
-            expect(+numberUtil.parseDate('2012-03-04T05:06:07.1')).toEqual(1330837567001);
+            expect(+numberUtil.parseDate('2012-03')).toEqual(1330531200000);
+            expect(+numberUtil.parseDate('2012-03-04')).toEqual(1330790400000);
+            expect(+numberUtil.parseDate('2012-03-04 05')).toEqual(1330808400000);
+            expect(+numberUtil.parseDate('2012-03-04T05')).toEqual(1330808400000);
+            expect(+numberUtil.parseDate('2012-03-04 05:06')).toEqual(1330808760000);
+            expect(+numberUtil.parseDate('2012-03-04T05:06')).toEqual(1330808760000);
+            expect(+numberUtil.parseDate('2012-03-04 05:06:07')).toEqual(1330808767000);
+            expect(+numberUtil.parseDate('2012-03-04T05:06:07')).toEqual(1330808767000);
+            expect(+numberUtil.parseDate('2012-03-04T05:06:07.123')).toEqual(1330808767123);
+            expect(+numberUtil.parseDate('2012-03-04T05:06:07,123')).toEqual(1330808767123);
+            expect(+numberUtil.parseDate('2012-03-04T05:06:07.12')).toEqual(1330808767012);
+            expect(+numberUtil.parseDate('2012-03-04T05:06:07.1')).toEqual(1330808767001);
             expect(+numberUtil.parseDate('2012-03-04T05:06:07,123Z')).toEqual(1330837567123);
             expect(+numberUtil.parseDate('2012-03-04T05:06:07.123+0800')).toEqual(1330808767123);
             expect(+numberUtil.parseDate('2012-03-04T05:06:07.123+08:00')).toEqual(1330808767123);
@@ -233,18 +233,18 @@
             expect(+numberUtil.parseDate('2012-03-04T05:06:07.123-07:00')).toEqual(1330862767123);
 
             // Other string
-            expect(+numberUtil.parseDate('2012')).toEqual(1325376000000);
-            expect(+numberUtil.parseDate('2012/03')).toEqual(1330560000000);
-            expect(+numberUtil.parseDate('2012/03/04')).toEqual(1330819200000);
-            expect(+numberUtil.parseDate('2012-3-4')).toEqual(1330819200000);
-            expect(+numberUtil.parseDate('2012/3')).toEqual(1330560000000);
-            expect(+numberUtil.parseDate('2012/3/4')).toEqual(1330819200000);
-            expect(+numberUtil.parseDate('2012/3/4 2:05')).toEqual(1330826700000);
-            expect(+numberUtil.parseDate('2012/03/04 2:05')).toEqual(1330826700000);
-            expect(+numberUtil.parseDate('2012/3/4 2:05:08')).toEqual(1330826708000);
-            expect(+numberUtil.parseDate('2012/03/04 2:05:08')).toEqual(1330826708000);
-            expect(+numberUtil.parseDate('2012/3/4 2:05:08.123')).toEqual(1330826708123);
-            expect(+numberUtil.parseDate('2012/03/04 2:05:08.123')).toEqual(1330826708123);
+            expect(+numberUtil.parseDate('2012')).toEqual(1325347200000);
+            expect(+numberUtil.parseDate('2012/03')).toEqual(1330531200000);
+            expect(+numberUtil.parseDate('2012/03/04')).toEqual(1330790400000);
+            expect(+numberUtil.parseDate('2012-3-4')).toEqual(1330790400000);
+            expect(+numberUtil.parseDate('2012/3')).toEqual(1330531200000);
+            expect(+numberUtil.parseDate('2012/3/4')).toEqual(1330790400000);
+            expect(+numberUtil.parseDate('2012/3/4 2:05')).toEqual(1330797900000);
+            expect(+numberUtil.parseDate('2012/03/04 2:05')).toEqual(1330797900000);
+            expect(+numberUtil.parseDate('2012/3/4 2:05:08')).toEqual(1330797908000);
+            expect(+numberUtil.parseDate('2012/03/04 2:05:08')).toEqual(1330797908000);
+            expect(+numberUtil.parseDate('2012/3/4 2:05:08.123')).toEqual(1330797908123);
+            expect(+numberUtil.parseDate('2012/03/04 2:05:08.123')).toEqual(1330797908123);
         });
     });