You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by zh...@apache.org on 2016/04/14 14:51:01 UTC

kylin git commit: KYLIN-1441 Display time column as partition column, patch from Dipesh&Jason

Repository: kylin
Updated Branches:
  refs/heads/yang-m1 c37858875 -> 9072724d7


KYLIN-1441 Display time column as partition column, patch from Dipesh&Jason


Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/9072724d
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/9072724d
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/9072724d

Branch: refs/heads/yang-m1
Commit: 9072724d77c6c8c6433ac5bb6518882f707d3b9a
Parents: c378588
Author: Jason <ji...@163.com>
Authored: Thu Apr 14 20:48:52 2016 +0800
Committer: Jason <ji...@163.com>
Committed: Thu Apr 14 20:48:52 2016 +0800

----------------------------------------------------------------------
 pom.xml                                         |   1 +
 webapp/app/index.html                           |   3 +
 webapp/app/js/app.js                            |   2 +-
 webapp/app/js/controllers/modelEdit.js          |   7 +
 webapp/app/js/directives/datetimepicker.js      | 418 +++++++++++++++++++
 webapp/app/js/directives/directives.js          |  40 +-
 webapp/app/js/model/cubeConfig.js               |   5 +
 webapp/app/js/model/metaModel.js                |   8 +-
 .../partials/cubeDesigner/refresh_settings.html |  16 +-
 webapp/app/partials/jobs/job_submit.html        |  24 +-
 .../modelDesigner/conditions_settings.html      |  39 ++
 webapp/bower.json                               |   3 +-
 webapp/grunt.json                               |   1 +
 13 files changed, 553 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 2574b73..36c2138 100644
--- a/pom.xml
+++ b/pom.xml
@@ -785,6 +785,7 @@
                                 <exclude>webapp/app/css/AdminLTE.css</exclude>
                                 <exclude>webapp/app/js/directives/kylin_abn_tree_directive.js</exclude>
                                 <exclude>webapp/app/js/directives/angular-tree-control.js</exclude>
+                                <exclude>webapp/app/js/directives/datetimepicker.js</exclude>
 
                                 <!--configuration file -->
                                 <exclude>webapp/app/routes.json</exclude>

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/index.html
----------------------------------------------------------------------
diff --git a/webapp/app/index.html b/webapp/app/index.html
index b4eb9d7..82cef34 100644
--- a/webapp/app/index.html
+++ b/webapp/app/index.html
@@ -46,6 +46,7 @@
     <link rel="stylesheet" type="text/css" href="components/angular-bootstrap-nav-tree/dist/abn_tree.css">
     <link rel="stylesheet" type="text/css" href="components/angular-toggle-switch/angular-toggle-switch.css">
     <link rel="stylesheet" type="text/css" href="components/angular-ui-select/dist/select.css">
+    <link rel="stylesheet" type="text/css" href="components/angular-bootstrap-datetimepicker/src/css/datetimepicker.css">
 
     <link rel="stylesheet/less" href="less/build.less">
     <!-- endref -->
@@ -114,6 +115,8 @@
 <script src="js/directives/directives.js"></script>
 <script src="js/directives/kylin_abn_tree_directive.js"></script>
 <script src="js/directives/angular-tree-control.js"></script>
+<script src="js/directives/datetimepicker.js"></script>
+
 <script src="js/factories/graph.js"></script>
 <script src="js/services/cache.js"></script>
 <script src="js/services/message.js"></script>

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/js/app.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/app.js b/webapp/app/js/app.js
index 817d24d..b681aca 100644
--- a/webapp/app/js/app.js
+++ b/webapp/app/js/app.js
@@ -17,4 +17,4 @@
 */
 
 //Kylin Application Module
-KylinApp = angular.module('kylin', ['ngRoute', 'ngResource', 'ngGrid', 'ui.bootstrap', 'ui.ace', 'base64', 'angularLocalStorage', 'localytics.directives', 'treeControl', 'nvd3ChartDirectives','ngLoadingRequest','oitozero.ngSweetAlert','ngCookies','angular-underscore', 'ngAnimate', 'ui.sortable','angularBootstrapNavTree','toggle-switch','ngSanitize','ui.select']);
+KylinApp = angular.module('kylin', ['ngRoute', 'ngResource', 'ngGrid', 'ui.bootstrap', 'ui.ace', 'base64', 'angularLocalStorage', 'localytics.directives', 'treeControl', 'nvd3ChartDirectives','ngLoadingRequest','oitozero.ngSweetAlert','ngCookies','angular-underscore', 'ngAnimate', 'ui.sortable','angularBootstrapNavTree','toggle-switch','ngSanitize','ui.select','ui.bootstrap.datetimepicker']);

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/js/controllers/modelEdit.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/modelEdit.js b/webapp/app/js/controllers/modelEdit.js
index 2de251e..903b584 100644
--- a/webapp/app/js/controllers/modelEdit.js
+++ b/webapp/app/js/controllers/modelEdit.js
@@ -40,6 +40,13 @@ KylinApp.controller('ModelEditCtrl', function ($scope, $q, $routeParams, $locati
         return columns;
     };
 
+    $scope.getPartitonTimeColumns = function(tableName){
+        var columns = _.filter($scope.getColumnsByTable(tableName),function(column){
+            return column.datatype==="time"||column.datatype==="timestamp"||column.datatype==="string"||column.datatype.startsWith("varchar");
+        });
+        return columns;
+    };
+
     $scope.getColumnsByTable = function (tableName) {
         var temp = [];
         angular.forEach(TableModel.selectProjectTables, function (table) {

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/js/directives/datetimepicker.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/directives/datetimepicker.js b/webapp/app/js/directives/datetimepicker.js
new file mode 100644
index 0000000..4541836
--- /dev/null
+++ b/webapp/app/js/directives/datetimepicker.js
@@ -0,0 +1,418 @@
+/*globals define, jQuery, module, require */
+/*jslint vars:true */
+
+/**
+ * @license angular-bootstrap-datetimepicker  version: 0.3.15
+ * Copyright 2015 Knight Rider Consulting, Inc. http://www.knightrider.com
+ * License: MIT
+ */
+
+/**
+ *
+ *    @author        Dale "Ducky" Lotts
+ *    @since        2013-Jul-8
+ *    @Modified     Jason,Zhong
+ */
+
+(function (factory) {
+  'use strict';
+  /* istanbul ignore if */
+  if (typeof define === 'function' && /* istanbul ignore next */ define.amd) {
+    define(['angular', 'moment'], factory); // AMD
+    /* istanbul ignore next */
+  } else if (typeof exports === 'object') {
+    module.exports = factory(require('angular'), require('moment')); // CommonJS
+  } else {
+    factory(window.angular, window.moment); // Browser global
+  }
+}(function (angular, moment) {
+  'use strict';
+  angular.module('ui.bootstrap.datetimepicker', [])
+    .constant('dateTimePickerConfig', {
+      dropdownSelector: null,
+      minuteStep: 5,
+      minView: 'minute',
+      startView: 'day'
+    })
+    .directive('datetimepicker', ['$log', 'dateTimePickerConfig', function datetimepickerDirective($log, defaultConfig) {
+
+      function DateObject() {
+
+        var tempDate = new Date();
+        var localOffset = tempDate.getTimezoneOffset() * 60000;
+        this.utcDateValue = tempDate.getTime();
+        this.selectable = true;
+
+        this.localDateValue = function () {
+          return this.utcDateValue + localOffset;
+        };
+
+        var validProperties = ['utcDateValue', 'localDateValue', 'display', 'active', 'selectable', 'past', 'future'];
+
+        for (var prop in arguments[0]) {
+          /* istanbul ignore else */
+          //noinspection JSUnfilteredForInLoop
+          if (validProperties.indexOf(prop) >= 0) {
+            //noinspection JSUnfilteredForInLoop
+            this[prop] = arguments[0][prop];
+          }
+        }
+      }
+
+      var validateConfiguration = function validateConfiguration(configuration) {
+        var validOptions = ['startView', 'minView', 'minuteStep', 'dropdownSelector'];
+
+        for (var prop in configuration) {
+          //noinspection JSUnfilteredForInLoop
+          if (validOptions.indexOf(prop) < 0) {
+            throw ('invalid option: ' + prop);
+          }
+        }
+
+        // Order of the elements in the validViews array is significant.
+        var validViews = ['minute', 'hour', 'day', 'month', 'year'];
+
+        if (validViews.indexOf(configuration.startView) < 0) {
+          throw ('invalid startView value: ' + configuration.startView);
+        }
+
+        if (validViews.indexOf(configuration.minView) < 0) {
+          throw ('invalid minView value: ' + configuration.minView);
+        }
+
+        if (validViews.indexOf(configuration.minView) > validViews.indexOf(configuration.startView)) {
+          throw ('startView must be greater than minView');
+        }
+
+        if (!angular.isNumber(configuration.minuteStep)) {
+          throw ('minuteStep must be numeric');
+        }
+        if (configuration.minuteStep <= 0 || configuration.minuteStep >= 60) {
+          throw ('minuteStep must be greater than zero and less than 60');
+        }
+        if (configuration.dropdownSelector !== null && !angular.isString(configuration.dropdownSelector)) {
+          throw ('dropdownSelector must be a string');
+        }
+
+        /* istanbul ignore next */
+        if (configuration.dropdownSelector !== null && ((typeof jQuery === 'undefined') || (typeof jQuery().dropdown !== 'function'))) {
+          $log.error('Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. ' +
+            'Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \n\n' +
+            'The dropdownSelector configuration option is being removed because it will not function properly.');
+          delete configuration.dropdownSelector;
+        }
+      };
+
+      return {
+        restrict: 'E',
+        require: 'ngModel',
+        template: '<div class="datetimepicker table-responsive">' +
+        '<table class="table table-condensed  {{ data.currentView }}-view">' +
+        '   <thead>' +
+        '       <tr>' +
+        '           <th class="left" data-ng-click="changeView(data.currentView, data.leftDate, $event)" data-ng-show="data.leftDate.selectable"><i class="glyphicon glyphicon-arrow-left"/></th>' +
+        '           <th class="switch" colspan="5" data-ng-show="data.previousViewDate.selectable" data-ng-click="changeView(data.previousView, data.previousViewDate, $event)">{{ data.previousViewDate.display }}</th>' +
+        '           <th class="right" data-ng-click="changeView(data.currentView, data.rightDate, $event)" data-ng-show="data.rightDate.selectable"><i class="glyphicon glyphicon-arrow-right"/></th>' +
+        '       </tr>' +
+        '       <tr>' +
+        '           <th class="dow" data-ng-repeat="day in data.dayNames" >{{ day }}</th>' +
+        '       </tr>' +
+        '   </thead>' +
+        '   <tbody>' +
+        '       <tr data-ng-if="data.currentView !== \'day\'" >' +
+        '           <td colspan="7" >' +
+        '              <span    class="{{ data.currentView }}" ' +
+        '                       data-ng-repeat="dateObject in data.dates"  ' +
+        '                       data-ng-class="{active: dateObject.active, past: dateObject.past, future: dateObject.future, disabled: !dateObject.selectable}" ' +
+        '                       data-ng-click="changeView(data.nextView, dateObject, $event)">{{ dateObject.display }}</span> ' +
+        '           </td>' +
+        '       </tr>' +
+        '       <tr data-ng-if="data.currentView === \'day\'" data-ng-repeat="week in data.weeks">' +
+        '           <td data-ng-repeat="dateObject in week.dates" ' +
+        '               data-ng-click="changeView(data.nextView, dateObject, $event)"' +
+        '               class="day" ' +
+        '               data-ng-class="{active: dateObject.active, past: dateObject.past, future: dateObject.future, disabled: !dateObject.selectable}" >{{ dateObject.display }}</td>' +
+        '       </tr>' +
+        '   </tbody>' +
+        '</table></div>',
+        scope: {
+          onSetTime: '&',
+          beforeRender: '&'
+        },
+        replace: true,
+        link: function link(scope, element, attrs, ngModelController) {
+
+          var directiveConfig = {};
+
+          if (attrs.datetimepickerConfig) {
+            directiveConfig = scope.$parent.$eval(attrs.datetimepickerConfig);
+          }
+
+          var configuration = {};
+
+          angular.extend(configuration, defaultConfig, directiveConfig);
+
+          validateConfiguration(configuration);
+
+          var startOfDecade = function startOfDecade(unixDate) {
+            var startYear = (parseInt(moment.utc(unixDate).year() / 10, 10) * 10);
+            return moment.utc(unixDate).year(startYear).startOf('year');
+          };
+
+          var dataFactory = {
+            year: function year(unixDate) {
+              var selectedDate = moment.utc(unixDate).startOf('year');
+              // View starts one year before the decade starts and ends one year after the decade ends
+              // i.e. passing in a date of 1/1/2013 will give a range of 2009 to 2020
+              // Truncate the last digit from the current year and subtract 1 to get the start of the decade
+              var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10);
+              var startDate = moment.utc(startOfDecade(unixDate)).subtract(1, 'year').startOf('year');
+
+              var activeYear = ngModelController.$modelValue ? moment(ngModelController.$modelValue).year() : 0;
+
+              var result = {
+                'currentView': 'year',
+                'nextView': configuration.minView === 'year' ? 'setTime' : 'month',
+                'previousViewDate': new DateObject({
+                  utcDateValue: null,
+                  display: startDecade + '-' + (startDecade + 9)
+                }),
+                'leftDate': new DateObject({utcDateValue: moment.utc(startDate).subtract(9, 'year').valueOf()}),
+                'rightDate': new DateObject({utcDateValue: moment.utc(startDate).add(11, 'year').valueOf()}),
+                'dates': []
+              };
+
+              for (var i = 0; i < 12; i += 1) {
+                var yearMoment = moment.utc(startDate).add(i, 'years');
+                var dateValue = {
+                  'utcDateValue': yearMoment.valueOf(),
+                  'display': yearMoment.format('YYYY'),
+                  'past': yearMoment.year() < startDecade,
+                  'future': yearMoment.year() > startDecade + 9,
+                  'active': yearMoment.year() === activeYear
+                };
+
+                result.dates.push(new DateObject(dateValue));
+              }
+
+              return result;
+            },
+
+            month: function month(unixDate) {
+
+              var startDate = moment.utc(unixDate).startOf('year');
+              var previousViewDate = startOfDecade(unixDate);
+              var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM') : 0;
+
+              var result = {
+                'previousView': 'year',
+                'currentView': 'month',
+                'nextView': configuration.minView === 'month' ? 'setTime' : 'day',
+                'previousViewDate': new DateObject({
+                  utcDateValue: previousViewDate.valueOf(),
+                  display: startDate.format('YYYY')
+                }),
+                'leftDate': new DateObject({utcDateValue: moment.utc(startDate).subtract(1, 'year').valueOf()}),
+                'rightDate': new DateObject({utcDateValue: moment.utc(startDate).add(1, 'year').valueOf()}),
+                'dates': []
+              };
+
+              for (var i = 0; i < 12; i += 1) {
+                var monthMoment = moment.utc(startDate).add(i, 'months');
+                var dateValue = {
+                  'utcDateValue': monthMoment.valueOf(),
+                  'display': monthMoment.format('MMM'),
+                  'active': monthMoment.format('YYYY-MMM') === activeDate
+                };
+
+                result.dates.push(new DateObject(dateValue));
+              }
+
+              return result;
+            },
+
+            day: function day(unixDate) {
+
+              var selectedDate = moment.utc(unixDate);
+              var startOfMonth = moment.utc(selectedDate).startOf('month');
+              var previousViewDate = moment.utc(selectedDate).startOf('year');
+              var endOfMonth = moment.utc(selectedDate).endOf('month');
+
+              var startDate = moment.utc(startOfMonth).subtract(Math.abs(startOfMonth.weekday()), 'days');
+
+              var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM-DD') : '';
+
+              var result = {
+                'previousView': 'month',
+                'currentView': 'day',
+                'nextView': configuration.minView === 'day' ? 'setTime' : 'hour',
+                'previousViewDate': new DateObject({
+                  utcDateValue: previousViewDate.valueOf(),
+                  display: startOfMonth.format('YYYY-MMM')
+                }),
+                'leftDate': new DateObject({utcDateValue: moment.utc(startOfMonth).subtract(1, 'months').valueOf()}),
+                'rightDate': new DateObject({utcDateValue: moment.utc(startOfMonth).add(1, 'months').valueOf()}),
+                'dayNames': [],
+                'weeks': []
+              };
+
+
+              for (var dayNumber = 0; dayNumber < 7; dayNumber += 1) {
+                result.dayNames.push(moment.utc().weekday(dayNumber).format('dd'));
+              }
+
+              for (var i = 0; i < 6; i += 1) {
+                var week = {dates: []};
+                for (var j = 0; j < 7; j += 1) {
+                  var monthMoment = moment.utc(startDate).add((i * 7) + j, 'days');
+                  var dateValue = {
+                    'utcDateValue': monthMoment.valueOf(),
+                    'display': monthMoment.format('D'),
+                    'active': monthMoment.format('YYYY-MMM-DD') === activeDate,
+                    'past': monthMoment.isBefore(startOfMonth),
+                    'future': monthMoment.isAfter(endOfMonth)
+                  };
+                  week.dates.push(new DateObject(dateValue));
+                }
+                result.weeks.push(week);
+              }
+
+              return result;
+            },
+
+            hour: function hour(unixDate) {
+              var selectedDate = moment.utc(unixDate).startOf('day');
+              var previousViewDate = moment.utc(selectedDate).startOf('month');
+
+              var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H') : '';
+
+              var result = {
+                'previousView': 'day',
+                'currentView': 'hour',
+                'nextView': configuration.minView === 'hour' ? 'setTime' : 'minute',
+                'previousViewDate': new DateObject({
+                  utcDateValue: previousViewDate.valueOf(),
+                  display: selectedDate.format('ll')
+                }),
+                'leftDate': new DateObject({utcDateValue: moment.utc(selectedDate).subtract(1, 'days').valueOf()}),
+                'rightDate': new DateObject({utcDateValue: moment.utc(selectedDate).add(1, 'days').valueOf()}),
+                'dates': []
+              };
+
+              for (var i = 0; i < 24; i += 1) {
+                var hourMoment = moment.utc(selectedDate).add(i, 'hours');
+                var dateValue = {
+                  'utcDateValue': hourMoment.valueOf(),
+                  'display': hourMoment.format('LT'),
+                  'active': hourMoment.format('YYYY-MM-DD H') === activeFormat
+                };
+
+                result.dates.push(new DateObject(dateValue));
+              }
+
+              return result;
+            },
+
+            minute: function minute(unixDate) {
+              var selectedDate = moment.utc(unixDate).startOf('hour');
+              var previousViewDate = moment.utc(selectedDate).startOf('day');
+              var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H:mm') : '';
+
+              var result = {
+                'previousView': 'hour',
+                'currentView': 'minute',
+                'nextView': 'setTime',
+                'previousViewDate': new DateObject({
+                  utcDateValue: previousViewDate.valueOf(),
+                  display: selectedDate.format('lll')
+                }),
+                'leftDate': new DateObject({utcDateValue: moment.utc(selectedDate).subtract(1, 'hours').valueOf()}),
+                'rightDate': new DateObject({utcDateValue: moment.utc(selectedDate).add(1, 'hours').valueOf()}),
+                'dates': []
+              };
+
+              var limit = 60 / configuration.minuteStep;
+
+              for (var i = 0; i < limit; i += 1) {
+                var hourMoment = moment.utc(selectedDate).add(i * configuration.minuteStep, 'minute');
+                var dateValue = {
+                  'utcDateValue': hourMoment.valueOf(),
+                  'display': hourMoment.format('LT'),
+                  'active': hourMoment.format('YYYY-MM-DD H:mm') === activeFormat
+                };
+
+                result.dates.push(new DateObject(dateValue));
+              }
+
+              return result;
+            },
+
+            setTime: function setTime(unixDate) {
+              var tempDate = new Date(unixDate);
+              var newDate = new Date(tempDate.getTime() + (tempDate.getTimezoneOffset() * 60000));
+
+              var oldDate = ngModelController.$modelValue;
+              ngModelController.$setViewValue(unixDate);
+
+              if (configuration.dropdownSelector) {
+                jQuery(configuration.dropdownSelector).dropdown('toggle');
+              }
+
+              scope.onSetTime({newDate: newDate, oldDate: oldDate});
+
+              return dataFactory[configuration.startView](unixDate);
+            }
+          };
+
+          var getUTCTime = function getUTCTime(modelValue) {
+            //var tempDate = (modelValue ? moment(modelValue).toDate() : new Date());
+            //return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
+            var date = new Date();
+            if(!modelValue){
+              var tmp = new Date();
+              date = new Date(tmp.getFullYear(),tmp.getMonth()+1,tmp.getDate());
+            }else{
+              date = new Date(modelValue);
+            }
+            return date.getTime() - (date.getTimezoneOffset() * 60000);
+          };
+
+          scope.changeView = function changeView(viewName, dateObject, event) {
+            if (event) {
+              event.stopPropagation();
+              event.preventDefault();
+            }
+
+            if (viewName && (dateObject.utcDateValue > -Infinity) && dateObject.selectable && dataFactory[viewName]) {
+              var result = dataFactory[viewName](dateObject.utcDateValue);
+
+              var weekDates = [];
+              if (result.weeks) {
+                for (var i = 0; i < result.weeks.length; i += 1) {
+                  var week = result.weeks[i];
+                  for (var j = 0; j < week.dates.length; j += 1) {
+                    var weekDate = week.dates[j];
+                    weekDates.push(weekDate);
+                  }
+                }
+              }
+
+              scope.beforeRender({
+                $view: result.currentView,
+                $dates: result.dates || weekDates,
+                $leftDate: result.leftDate,
+                $upDate: result.previousViewDate,
+                $rightDate: result.rightDate
+              });
+
+              scope.data = result;
+            }
+          };
+
+          ngModelController.$render = function $render() {
+            scope.changeView(configuration.startView, new DateObject({utcDateValue: getUTCTime(ngModelController.$viewValue)}));
+          };
+        }
+      };
+    }]);
+}));

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/js/directives/directives.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/directives/directives.js b/webapp/app/js/directives/directives.js
index 2deb0aa..a3b7bf2 100644
--- a/webapp/app/js/directives/directives.js
+++ b/webapp/app/js/directives/directives.js
@@ -248,7 +248,45 @@ KylinApp.directive('kylinPagination', function ($parse, $q) {
         });
       }
     };
-  }).directive("parametertree", function($compile) {
+  }).directive('dateTimepickerTimezone', function () {
+  // this directive workaround to convert GMT0 timestamp to GMT date for datepicker
+  return {
+    restrict: 'A',
+    priority: 1,
+    require: 'ngModel',
+    link: function (scope, element, attrs, ctrl) {
+      ctrl.$formatters.push(function (value) {
+
+        //set null for 0
+        if(value===0){
+          return '';
+        }
+
+        //return value;
+        var newDate = new Date(value + (60000 * new Date().getTimezoneOffset()));
+
+        var year = newDate.getFullYear();
+        var month = (newDate.getMonth()+1)<10?'0'+(newDate.getMonth()+1):(newDate.getMonth()+1);
+        var date = newDate.getDate()<10?'0'+newDate.getDate():newDate.getDate();
+
+        var hour = newDate.getHours()<10?'0'+newDate.getHours():newDate.getHours();
+        var mins = newDate.getMinutes()<10?'0'+newDate.getMinutes():newDate.getMinutes();
+        var seconds = newDate.getSeconds()<10?'0'+newDate.getSeconds():getSeconds();
+
+        var viewVal = year+"-"+month+"-"+date+" "+hour+":"+mins+":"+seconds;
+        return viewVal;
+      });
+
+      ctrl.$parsers.push(function (value) {
+        if (isNaN(value)||value==null) {
+          return value;
+        }
+        //value = new Date(value.getFullYear(), value.getMonth(), value.getDate(), 0, 0, 0, 0);
+        return value.getTime()-(60000 * value.getTimezoneOffset());
+      });
+    }
+  };
+}).directive("parametertree", function($compile) {
     return {
       restrict: "E",
       transclude: true,

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/js/model/cubeConfig.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/model/cubeConfig.js b/webapp/app/js/model/cubeConfig.js
index 76d9ae9..b5c77f9 100644
--- a/webapp/app/js/model/cubeConfig.js
+++ b/webapp/app/js/model/cubeConfig.js
@@ -89,5 +89,10 @@ KylinApp.constant('cubeConfig', {
   partitionDateFormatOpt:[
     'yyyy-MM-dd',
     'yyyyMMdd'
+  ],
+  partitionTimeFormatOpt:[
+    'HH:mm:ss',
+    'HH:mm',
+    'HH'
   ]
 });

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/js/model/metaModel.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/model/metaModel.js b/webapp/app/js/model/metaModel.js
index 008337f..3f05b3d 100644
--- a/webapp/app/js/model/metaModel.js
+++ b/webapp/app/js/model/metaModel.js
@@ -34,7 +34,9 @@ KylinApp.service('MetaModel',function(){
         "partition_desc" : {
             "partition_date_column" : null,
             "partition_date_start" : null,
-            "partition_type" : 'APPEND'
+            "partition_type" : 'APPEND',
+            "partition_time_column" : null,
+            "partition_time_start" : null
         },
         last_modified:0
     };
@@ -81,7 +83,9 @@ KylinApp.service('MetaModel',function(){
                 "partition_date_column" : null,
                 "partition_date_start" : null,
                 "partition_type" : 'APPEND',
-                "partition_date_format":'yyyy-MM-dd'
+                "partition_date_format":'yyyy-MM-dd',
+                "partition_time_column" : null,
+                "partition_time_start" : null
             },
             last_modified:0
         };

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/partials/cubeDesigner/refresh_settings.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/cubeDesigner/refresh_settings.html b/webapp/app/partials/cubeDesigner/refresh_settings.html
index 1ad294e..814c453 100755
--- a/webapp/app/partials/cubeDesigner/refresh_settings.html
+++ b/webapp/app/partials/cubeDesigner/refresh_settings.html
@@ -123,9 +123,19 @@
                   <div class="col-xs-12 col-sm-6"
                        ng-class="metaModel.model.partition_desc.partition_date_column!=null&& cubeMetaFrame.partition_date_start==null?'has-error':''">
                     <!--edit model will convert &ndash;&gt;-->
-                    <input type="text" class="form-control" datepicker-popup="yyyy-MM-dd" datepicker-timezone
-                           ng-model="cubeMetaFrame.partition_date_start" ng-if="state.mode=='edit'"
-                           placeholder="Click to choose start date..." is-open="opened"/>
+                    <!--<input type="text" class="form-control" datepicker-popup="yyyy-MM-dd" datepicker-timezone-->
+                           <!--ng-model="cubeMetaFrame.partition_date_start" ng-if="state.mode=='edit'"-->
+                           <!--placeholder="Click to choose start date..." is-open="opened"/>-->
+                    <div ng-if="state.mode=='edit'" class="dropdown">
+                      <a class="dropdown-toggle" id="dropdown2" role="button" data-toggle="dropdown" data-target="#" href="#">
+                        <div class="input-group"><input type="text" class="form-control" date-timepicker-timezone data-ng-model="cubeMetaFrame.partition_date_start"><span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
+                        </div>
+                      </a>
+                      <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
+                        <datetimepicker  data-ng-model="cubeMetaFrame.partition_date_start" data-datetimepicker-config="{ dropdownSelector: '#dropdown2' }"/>
+                      </ul>
+                    </div>
+
                     <small class="help-block"
                            ng-show="metaModel.model.partition_desc.partition_date_column!=null&& cubeMetaFrame.partition_date_start==null">
                       Please input start date when partition date column is defined in model.

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/partials/jobs/job_submit.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/jobs/job_submit.html b/webapp/app/partials/jobs/job_submit.html
index 16bca6b..fbf1e4b 100644
--- a/webapp/app/partials/jobs/job_submit.html
+++ b/webapp/app/partials/jobs/job_submit.html
@@ -50,12 +50,24 @@
                             </td>
                         </tr>
                         <tr>
-                            <td>End Date (Exclude)</td>
-                            <td>
-                                <input type="text" datepicker-popup="yyyy-MM-dd" class="input-sm" style="width: 98%" datepicker-timezone
-                                       ng-model="jobBuildRequest.endTime"
-                                       placeholder="Click to choose END date..." is-open="opened" />
-                            </td>
+                          <td width="50%;">End Date (Exclude)</td>
+                          <td width="50%;">
+                            <!--<input type="text" datepicker-popup="yyyy-MM-dd" class="input-sm" style="width: 98%" datepicker-timezone-->
+                            <!--ng-model="jobBuildRequest.endTime"-->
+                            <!--placeholder="Click to choose END date..." is-open="opened" />-->
+
+                            <div class="dropdown" style="width: 98%;">
+                              <a class="dropdown-toggle" id="dropdown2" role="button" data-toggle="dropdown" data-target="#" href="#">
+                                <div class="input-group"><input type="text" class="form-control" date-timepicker-timezone data-ng-model="jobBuildRequest.endTime">
+                                  <span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
+                                </div>
+                              </a>
+                              <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
+                                <datetimepicker  data-ng-model="jobBuildRequest.endTime" data-datetimepicker-config="{ dropdownSelector: '#dropdown2' }"/>
+                              </ul>
+                            </div>
+
+                          </td>
                         </tr>
                         </tbody>
                     </table>

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/app/partials/modelDesigner/conditions_settings.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/modelDesigner/conditions_settings.html b/webapp/app/partials/modelDesigner/conditions_settings.html
index 95dc4b6..2a46698 100644
--- a/webapp/app/partials/modelDesigner/conditions_settings.html
+++ b/webapp/app/partials/modelDesigner/conditions_settings.html
@@ -74,6 +74,45 @@
           </div>
         </div>
 
+        <!--Partition Time Column-->
+        <div class="form-group">
+          <div class="row">
+            <label class="control-label col-xs-12 col-sm-3 no-padding-right font-color-default"><b>Partition Time Column</b></label>
+            <div class="col-xs-12 col-sm-6">
+
+              <select style="width: 100%" chosen data-placeholder="e.g. DEFAULT.TEST_KYLIN_FACT.CAL_DT"
+                      ng-model="modelsManager.selectedModel.partition_desc.partition_time_column"
+                      ng-if="state.mode=='edit'"
+                      data-placement=""
+                      ng-options="modelsManager.selectedModel.fact_table+'.'+columns.name as modelsManager.selectedModel.fact_table+'.'+columns.name for columns in getPartitonTimeColumns(modelsManager.selectedModel.fact_table)" >
+                <option value="">--Select Partition Column--</option>
+              </select>
+              <small class="help-block text-red" ng-show="state.mode=='edit'">(Column Type should be TIME Type)</small>
+
+                    <span ng-if="state.mode=='view'">
+                        {{!!(modelsManager.selectedModel.partition_desc.partition_time_column)?modelsManager.selectedModel.partition_desc.partition_time_column: ''}}</span>
+            </div>
+          </div>
+        </div>
+
+        <!--Time Format-->
+        <div class="form-group">
+          <div class="row">
+            <label class="control-label col-xs-12 col-sm-3 no-padding-right font-color-default"><b>Time Format</b></label>
+            <div class="col-xs-12 col-sm-6">
+              <select style="width: 100%" chosen
+                      ng-required="modelsManager.selectedModel.partition_desc.partition_time_format"
+                      ng-model="modelsManager.selectedModel.partition_desc.partition_time_format"
+                      ng-if="state.mode=='edit'"
+                      data-placement=""
+                      ng-options="ddt as ddt for ddt in cubeConfig.partitionTimeFormatOpt">
+                <option value="">--Select Time Format--</option>
+              </select>
+              <span ng-if="state.mode=='view'&&modelsManager.selectedModel.partition_desc.partition_time_column">{{(modelsManager.selectedModel.partition_desc.partition_time_format)}}</span>
+            </div>
+          </div>
+        </div>
+
           <div class="form-group" ng-show="userService.hasRole('ROLE_ADMIN')">
               <div class="row">
                   <label class="control-label col-xs-12 col-sm-3 no-padding-right font-color-default"><b>Cube Size</b></label>

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/bower.json
----------------------------------------------------------------------
diff --git a/webapp/bower.json b/webapp/bower.json
index bba4a52..21fb7f4 100755
--- a/webapp/bower.json
+++ b/webapp/bower.json
@@ -33,7 +33,8 @@
     "angular-toggle-switch":"1.3.0",
     "angular-ui-select": "0.13.2",
     "angular-sanitize": "1.2.18",
-    "angular-tree-control": "0.2.8"
+    "angular-tree-control": "0.2.8",
+    "angular-bootstrap-datetimepicker": "0.3.15"
   },
   "devDependencies": {
     "less.js": "~1.4.0",

http://git-wip-us.apache.org/repos/asf/kylin/blob/9072724d/webapp/grunt.json
----------------------------------------------------------------------
diff --git a/webapp/grunt.json b/webapp/grunt.json
index 86ad1dc..abf1cb5 100755
--- a/webapp/grunt.json
+++ b/webapp/grunt.json
@@ -65,6 +65,7 @@
                 "app/components/angular-bootstrap-nav-tree/dist/abn_tree.css",
                 "app/components/angular-toggle-switch/angular-toggle-switch.css",
                 "app/components/angular-ui-select/dist/select.css",
+                "app/components/angular-bootstrap-datetimepicker/src/css/datetimepicker.css",
                 "tmp/css/styles.css"
             ],
             "dest": "tmp/css/styles.min.css"