diff --git a/src/daterangepicker/daterangepicker.component.html b/src/daterangepicker/daterangepicker.component.html
index 0a14708a..5e5729ba 100644
--- a/src/daterangepicker/daterangepicker.component.html
+++ b/src/daterangepicker/daterangepicker.component.html
@@ -81,6 +81,27 @@
+
diff --git a/src/daterangepicker/daterangepicker.component.scss b/src/daterangepicker/daterangepicker.component.scss
index ee2bbee7..3a59e046 100644
--- a/src/daterangepicker/daterangepicker.component.scss
+++ b/src/daterangepicker/daterangepicker.component.scss
@@ -365,6 +365,18 @@ $input-height: 3rem !default;
margin: 0;
cursor: default;
}
+ &.hourselect,
+ &.minuteselect,
+ &.secondselect,
+ &.ampmselect {
+ width: 50px;
+ margin: 0 auto;
+ background: #eee;
+ border: 1px solid #eee;
+ padding: 2px;
+ outline: 0;
+ font-size: 12px;
+ }
&.monthselect {
margin-right: 2%;
@@ -375,6 +387,17 @@ $input-height: 3rem !default;
width: 40%;
}
}
+ .calendar-time {
+ text-align: center;
+ margin: 4px auto 0 auto;
+ line-height: 30px;
+ position: relative;
+ }
+
+ .calendar-time select.disabled {
+ color: #ccc;
+ cursor: not-allowed;
+ }
.label-input {
border: $md-drppicker-control-border-size solid $md-drppicker-control-border-color;
diff --git a/src/daterangepicker/daterangepicker.component.ts b/src/daterangepicker/daterangepicker.component.ts
index 3340faf6..df5e1356 100644
--- a/src/daterangepicker/daterangepicker.component.ts
+++ b/src/daterangepicker/daterangepicker.component.ts
@@ -28,6 +28,7 @@ export class DaterangepickerComponent implements OnInit {
private _old: {start: any, end: any} = {start: null, end: null};
chosenLabel: string;
calendarVariables: {left: any, right: any} = {left: {}, right: {}};
+ timepickerVariables: {left: any, right: any} = {left: {}, right: {}};
daterangepicker: {start: FormControl, end: FormControl} = {start: new FormControl(), end: new FormControl()};
applyBtn: {disabled: boolean} = {disabled: false};
startDate = moment().startOf('day');
@@ -56,9 +57,17 @@ export class DaterangepickerComponent implements OnInit {
alwaysShowCalendars: Boolean = false;
@Input()
maxSpan: Boolean = false;
+ // timepicker variables
@Input()
timePicker: Boolean = false;
@Input()
+ timePicker24Hour: Boolean = false;
+ @Input()
+ timePickerIncrement: number = 1;
+ @Input()
+ timePickerSeconds: Boolean = false;
+ // end of timepicker variables
+ @Input()
showClearButton: Boolean = false;
@Input()
firstMonthDayClass: string = null;
@@ -127,10 +136,13 @@ export class DaterangepickerComponent implements OnInit {
iterator--;
}
}
- if(this.inline) {
+ if (this.inline) {
this._old.start = this.startDate.clone();
this._old.end = this.endDate.clone();
}
+ if (this.timePicker) {
+ this.locale.format = moment.localeData().longDateFormat('lll');
+ }
this.renderCalendar(SideEnum.left);
this.renderCalendar(SideEnum.right);
this.renderRanges();
@@ -186,10 +198,133 @@ export class DaterangepickerComponent implements OnInit {
this.rangesArray.push(this.locale.customRangeLabel);
}
this.showCalInRanges = (!this.rangesArray.length) || this.alwaysShowCalendars;
+ if (!this.timePicker) {
+ this.startDate = this.startDate.startOf('day');
+ this.endDate = this.endDate.endOf('day');
+ }
+ // can't be used together for now
+ if (this.timePicker && this.autoApply) {
+ this.autoApply = false;
+ }
}
}
- renderCalendar(side: SideEnum) { // site enum
+ renderTimePicker(side: SideEnum) {
+ if (side == SideEnum.right && !this.endDate) {
+ return;
+ }
+ let selected, minDate;
+ let maxDate = this.maxDate
+ if (side === SideEnum.left) {
+ selected = this.startDate.clone(),
+ minDate = this.minDate
+ } else if (side === SideEnum.right) {
+ selected = this.endDate.clone(),
+ minDate = this.startDate
+ }
+ const start = this.timePicker24Hour ? 0 : 1;
+ const end = this.timePicker24Hour ? 23 : 12;
+ this.timepickerVariables[side] = {
+ hours: [],
+ minutes: [],
+ minutesLabel: [],
+ seconds: [],
+ secondsLabel: [],
+ disabledHours: [],
+ disabledMinutes: [],
+ disabledSeconds: [],
+ selectedHour: 0,
+ selectedMinute: 0,
+ selectedSecond: 0,
+ };
+ // generate hours
+ for (let i = start; i <= end; i++) {
+ let i_in_24 = i;
+ if (!this.timePicker24Hour) {
+ i_in_24 = selected.hour() >= 12 ? (i == 12 ? 12 : i + 12) : (i == 12 ? 0 : i);
+ }
+
+ let time = selected.clone().hour(i_in_24);
+ let disabled = false;
+ if (minDate && time.minute(59).isBefore(minDate)) {
+ disabled = true;
+ }
+ if (maxDate && time.minute(0).isAfter(maxDate)) {
+ disabled = true;
+ }
+
+ this.timepickerVariables[side].hours.push(i);
+ if (i_in_24 == selected.hour() && !disabled) {
+ this.timepickerVariables[side].selectedHour = i;
+ } else if (disabled) {
+ this.timepickerVariables[side].disabledHours.push(i);
+ }
+ }
+ // generate minutes
+ for (var i = 0; i < 60; i += this.timePickerIncrement) {
+ var padded = i < 10 ? '0' + i : i;
+ var time = selected.clone().minute(i);
+
+ var disabled = false;
+ if (minDate && time.second(59).isBefore(minDate)) {
+ disabled = true;
+ }
+ if (maxDate && time.second(0).isAfter(maxDate)) {
+ disabled = true;
+ }
+ this.timepickerVariables[side].minutes.push(i);
+ this.timepickerVariables[side].minutesLabel.push(padded);
+ if (selected.minute() == i && !disabled) {
+ this.timepickerVariables[side].selectedMinute = i;
+ } else if (disabled) {
+ this.timepickerVariables[side].disabledMinutes.push(i);
+ }
+ }
+ // generate seconds
+ if (this.timePickerSeconds) {
+ for (var i = 0; i < 60; i++) {
+ var padded = i < 10 ? '0' + i : i;
+ var time = selected.clone().second(i);
+
+ var disabled = false;
+ if (minDate && time.isBefore(minDate)) {
+ disabled = true;
+ }
+ if (maxDate && time.isAfter(maxDate)) {
+ disabled = true;
+ }
+
+ this.timepickerVariables[side].seconds.push(i);
+ this.timepickerVariables[side].secondsLabel.push(padded);
+ if (selected.second() == i && !disabled) {
+ this.timepickerVariables[side].selectedSecond = i;
+ } else if (disabled) {
+ this.timepickerVariables[side].disabledSeconds.push(i);
+ }
+ }
+ }
+ // generate AM/PM
+ if (!this.timePicker24Hour) {
+
+ var am_html = '';
+ var pm_html = '';
+
+ if (minDate && selected.clone().hour(12).minute(0).second(0).isBefore(minDate)) {
+ this.timepickerVariables[side].amDisabled = true;
+ }
+
+ if (maxDate && selected.clone().hour(0).minute(0).second(0).isAfter(maxDate)) {
+ this.timepickerVariables[side].pmDisabled = true;
+ }
+ if (selected.hour() >= 12) {
+ this.timepickerVariables[side].ampmModel = 'PM';
+ } else {
+ this.timepickerVariables[side].ampmModel = 'AM';
+ }
+ }
+ this.timepickerVariables[side].selected = selected;
+ }
+ renderCalendar(side: SideEnum) { // side enum
let mainCalendar: any = ( side === SideEnum.left ) ? this.leftCalendar : this.rightCalendar;
const month = mainCalendar.month.month();
const year = mainCalendar.month.year();
@@ -418,16 +553,28 @@ export class DaterangepickerComponent implements OnInit {
if (typeof startDate === 'object') {
this.startDate = moment(startDate);
}
-
- this.startDate = this.startDate.startOf('day');
+ if (!this.timePicker) {
+ this.startDate = this.startDate.startOf('day');
+ }
+
+ if (this.timePicker && this.timePickerIncrement) {
+ this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
+ }
if (this.minDate && this.startDate.isBefore(this.minDate)) {
this.startDate = this.minDate.clone();
+ if (this.timePicker && this.timePickerIncrement) {
+ this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
+ }
+
}
if (this.maxDate && this.startDate.isAfter(this.maxDate)) {
this.startDate = this.maxDate.clone();
+ if (this.timePicker && this.timePickerIncrement) {
+ this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
+ }
}
if (!this.isShown) {
@@ -445,9 +592,14 @@ export class DaterangepickerComponent implements OnInit {
if (typeof endDate === 'object') {
this.endDate = moment(endDate);
}
+ if (!this.timePicker) {
+ this.endDate = this.endDate.add(1,'d').startOf('day').subtract(1,'second');
+ }
- this.endDate = this.endDate.add(1, 'd').startOf('day').subtract(1, 'second');
-
+ if (this.timePicker && this.timePickerIncrement) {
+ this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
+ }
+
if (this.endDate.isBefore(this.startDate)) {
this.endDate = this.startDate.clone();
@@ -480,6 +632,10 @@ export class DaterangepickerComponent implements OnInit {
}
updateView() {
+ if (this.timePicker) {
+ this.renderTimePicker(SideEnum.left);
+ this.renderTimePicker(SideEnum.right);
+ }
this.updateMonthsInView();
this.updateCalendars();
}
@@ -556,11 +712,23 @@ export class DaterangepickerComponent implements OnInit {
let i = 0;
if (this.rangesArray.length > 0) {
for (const range in this.ranges) {
- if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) {
- customRange = false;
- this.chosenRange = this.rangesArray[i];
- break;
+ if (this.timePicker) {
+ var format = this.timePickerSeconds ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD HH:mm";
+ //ignore times when comparing dates if time picker seconds is not enabled
+ if (this.startDate.format(format) == this.ranges[range][0].format(format) && this.endDate.format(format) == this.ranges[range][1].format(format)) {
+ customRange = false;
+ this.chosenRange = this.rangesArray[i];
+ break;
+ }
+ } else {
+ //ignore times when comparing dates if time picker is not enabled
+ if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) {
+ customRange = false;
+ this.chosenRange = this.rangesArray[i];
+ break;
+ }
}
+
i++;
}
if (customRange) {
@@ -630,6 +798,51 @@ export class DaterangepickerComponent implements OnInit {
const year = parseInt(yearEvent.target.value, 10);
this.monthOrYearChanged(month, year, side);
}
+ /**
+ * called when time is changed
+ * @param timeEvent an event
+ * @param side left or right
+ */
+ timeChanged(timeEvent: any, side: SideEnum) {
+
+ var hour = parseInt(this.timepickerVariables[side].selectedHour, 10);
+ var minute = parseInt(this.timepickerVariables[side].selectedMinute, 10);
+ var second = this.timePickerSeconds ? parseInt(this.timepickerVariables[side].selectedSecond, 10) : 0;
+
+ if (!this.timePicker24Hour) {
+ var ampm = this.timepickerVariables[side].ampmModel;
+ if (ampm === 'PM' && hour < 12)
+ hour += 12;
+ if (ampm === 'AM' && hour === 12)
+ hour = 0;
+ }
+
+ if (side === SideEnum.left) {
+ var start = this.startDate.clone();
+ start.hour(hour);
+ start.minute(minute);
+ start.second(second);
+ this.setStartDate(start);
+ if (this.singleDatePicker) {
+ this.endDate = this.startDate.clone();
+ } else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) {
+ this.setEndDate(start.clone());
+ }
+ } else if (this.endDate) {
+ var end = this.endDate.clone();
+ end.hour(hour);
+ end.minute(minute);
+ end.second(second);
+ this.setEndDate(end);
+ }
+
+ //update the calendars so all clickable dates reflect the new time component
+ this.updateCalendars();
+
+ //re-render the time pickers because changing one selection can affect what's enabled in another
+ this.renderTimePicker(SideEnum.left);
+ this.renderTimePicker(SideEnum.right);
+ }
/**
* call when month or year changed
* @param month month number 0 -11
@@ -723,6 +936,19 @@ export class DaterangepickerComponent implements OnInit {
let date = side === SideEnum.left ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
if (this.endDate || date.isBefore(this.startDate, 'day')) { // picking start
+ if (this.timePicker) {
+ let hour = parseInt(this.timepickerVariables['left'].selectedHour, 10);
+ if (!this.timePicker24Hour) {
+ var ampm = this.timepickerVariables['left'].ampmModel;
+ if (ampm === 'PM' && hour < 12)
+ hour += 12;
+ if (ampm === 'AM' && hour === 12)
+ hour = 0;
+ }
+ var minute = parseInt(this.timepickerVariables['left'].selectedMinute, 10);
+ var second = this.timePickerSeconds ? parseInt(this.timepickerVariables['left'].selectedSecond, 10) : 0;
+ date = date.clone().hour(hour).minute(minute).second(second);
+ }
this.endDate = null;
this.setStartDate(date.clone());
} else if (!this.endDate && date.isBefore(this.startDate)) {
@@ -730,6 +956,19 @@ export class DaterangepickerComponent implements OnInit {
// but the time of the end date is before the start date
this.setEndDate(this.startDate.clone());
} else { // picking end
+ if (this.timePicker) {
+ var hour = parseInt(this.timepickerVariables['right'].selectedHour, 10);
+ if (!this.timePicker24Hour) {
+ var ampm = this.timepickerVariables['right'].ampmModel;
+ if (ampm === 'PM' && hour < 12)
+ hour += 12;
+ if (ampm === 'AM' && hour === 12)
+ hour = 0;
+ }
+ var minute = parseInt(this.timepickerVariables['right'].selectedMinute, 10);
+ var second = this.timePickerSeconds ? parseInt(this.timepickerVariables['right'].selectedSecond, 10) : 0;
+ date = date.clone().hour(hour).minute(minute).second(second);
+ }
this.setEndDate(date.clone());
if (this.autoApply) {
this.calculateChosenLabel();
diff --git a/src/daterangepicker/daterangepicker.directive.ts b/src/daterangepicker/daterangepicker.directive.ts
index b0d6e124..b6dcad88 100644
--- a/src/daterangepicker/daterangepicker.directive.ts
+++ b/src/daterangepicker/daterangepicker.directive.ts
@@ -89,6 +89,15 @@ export class DaterangepickerDirective implements OnInit, OnChanges, DoCheck {
keepCalendarOpeningWithRange: boolean;
@Input()
showRangeLabelOnInput: boolean;
+ // timepicker variables
+ @Input()
+ timePicker: Boolean = false;
+ @Input()
+ timePicker24Hour: Boolean = false;
+ @Input()
+ timePickerIncrement: number = 1;
+ @Input()
+ timePickerSeconds: Boolean = false;
_locale: any = {};
@Input() set locale(value) {
if (value !== null) {