const DateTime = require('./DateTime');
const DateFormat = require('./DateFormat');
const DateParse = require('./DateParse');
function DateRange(date1, date2) {
if (!date1 || !date2) {
throw('two dates must be specified, date1=' + date1 + ', date2=' + date2)
}
this.start = (date1.compareTo(date2) > 0 ? date2 : date1)
this.end = (date1.compareTo(date2) > 0 ? date1 : date2)
this._days = 0
this._hours = 0
this._minutes = 0
this._valid = true
}
DateRange.emptyRange = () => {
function NullDateRange() {
this.start = null
this.end = null
this.days = () => 0
this.shiftDays = () => {}
this.hasDate = () => false
this.clone = () => DateRange.emptyRange()
this.expandDaysTo = function () { return this }
this.hasEndsOnWeekend = () => false
this.isPermittedRange = () => true
this.hasSelection = () => false
}
return new NullDateRange()
}
DateRange.rangeWithMinimumSize = (oldRange, minimumSize, disableWeekends, outerRange) => {
if (isTooSmallSelection()) {
let newRange = oldRange.expandDaysTo(minimumSize);
if (disableWeekends && newRange.hasEndsOnWeekend()) {
let shiftedDays = newRange.shiftDays(delta(newRange.end.getDay())).shiftInside(outerRange);
while (!shiftedDays.isPermittedRange(minimumSize, disableWeekends, outerRange) || shiftedDays.end.compareTo(outerRange.end) > 0) {
if (!shiftedDays.isPermittedRange(minimumSize, false, outerRange)) {
return DateRange.emptyRange()
}
shiftedDays = shiftedDays.shiftDays(1)
}
newRange = shiftedDays
}
if (!newRange.isPermittedRange(minimumSize, false, outerRange)) {
return DateRange.emptyRange()
}
return newRange
}
return oldRange
function isTooSmallSelection() { return minimumSize && oldRange.days() <= minimumSize }
function delta(x) { return -((x + 1) % 7 + 1) }
}
DateRange.prototype._setDaysHoursAndMinutes = function () {
if (this._hasTimes) {
let ms = parseInt((this.end.getTime() - this.start.getTime()), 10);
this._days = parseInt(ms / DateTime.DAY, 10)
ms = ms - (this._days * DateTime.DAY)
this._hours = parseInt(ms / DateTime.HOUR, 10)
ms = ms - (this._hours * DateTime.HOUR)
this._minutes = parseInt(ms / DateTime.MINUTE, 10)
}
}
DateRange.prototype._dateWithTime = (dateWithoutTime, parsedTime) => dateWithoutTime.withTime(parsedTime[0], parsedTime[1])
DateRange.prototype.hours = function () { return this._hours }
DateRange.prototype.minutes = function () { return this._minutes }
DateRange.prototype.hasDate = function (date) { return date.isBetweenDates(this.start, this.end) }
DateRange.prototype.isValid = function () { return this._valid && this.end.getTime() - this.start.getTime() >= 0 }
DateRange.prototype.days = function () { return this._hasTimes ? this._days : Math.round(this.start.distanceInDays(this.end) + 1) }
DateRange.prototype.shiftDays = function (days) { return new DateRange(this.start.plusDays(days), this.end.plusDays(days)) }
DateRange.prototype.expandTo = function (date) {
let newStart = this.start.clone();
let newEnd = this.end.clone();
if (date.compareTo(this.start) < 0) newStart = date
else if (date.compareTo(this.end) > 0) newEnd = date
return new DateRange(newStart, newEnd)
}
DateRange.prototype.expandDaysTo = function (days) { return new DateRange(this.start, this.start.plusDays(days - 1)) }
DateRange.prototype.hasValidSize = function (minimumDays) { return minimumDays < 0 || this.days() >= minimumDays }
DateRange.prototype.hasValidSizeAndEndsOnWorkWeek = function (minimumDays) {
return this.hasValidSize(minimumDays) && this.hasEndsOnWeekend()
}
DateRange.prototype.and = function (that) {
const latestStart = this.start.compareTo(that.start) > 0 ? this.start : that.start;
const earliestEnd = this.end.compareTo(that.end) > 0 ? that.end : this.end;
return latestStart.compareTo(earliestEnd) < 0 ? new DateRange(latestStart, earliestEnd) : DateRange.emptyRange()
}
DateRange.prototype.isInside = function (outer) {
return this.start.compareTo(outer.start) >= 0 && this.end.compareTo(outer.end) <= 0
}
DateRange.prototype.hasEndsOnWeekend = function () { return this.start.isWeekend() || this.end.isWeekend() }
DateRange.prototype.withTimes = function (startTimeStr, endTimeStr) {
const parsedStartTime = DateParse.parseTime(startTimeStr);
const parsedEndTime = DateParse.parseTime(endTimeStr);
const rangeWithTimes = this.clone();
if (parsedStartTime && parsedEndTime) {
rangeWithTimes._valid = true
rangeWithTimes._hasTimes = true
rangeWithTimes.start = this._dateWithTime(this.start, parsedStartTime)
rangeWithTimes.end = this._dateWithTime(this.end, parsedEndTime)
rangeWithTimes._setDaysHoursAndMinutes()
} else {
rangeWithTimes._valid = false
}
return rangeWithTimes
}
DateRange.prototype.clone = function () { return new DateRange(this.start, this.end) }
DateRange.prototype.toString = function () {
return [
'DateRange:',
this.start.toString(),
'-',
this.end.toString(),
this._days,
'days',
this._hours,
'hours',
this._minutes,
'minutes',
this._valid ? 'valid' : 'invalid'
].join(' ')
}
DateRange.prototype.isPermittedRange = function (minimumSize, disableWeekends, outerRange) {
return this.hasValidSize(minimumSize) && (!(disableWeekends && this.hasEndsOnWeekend())) && this.isInside(outerRange)
}
DateRange.prototype.shiftInside = function (outerRange) {
if (this.days() > outerRange.days()) {
return DateRange.emptyRange()
}
const distanceToOuterRangeStart = this.start.distanceInDays(outerRange.start);
const distanceToOuterRangeEnd = this.end.distanceInDays(outerRange.end);
if (distanceToOuterRangeStart > 0) {
return this.shiftDays(distanceToOuterRangeStart)
}
if (distanceToOuterRangeEnd < 0) {
return this.shiftDays(distanceToOuterRangeEnd)
}
return this
}
DateRange.prototype.hasSelection = function () {
return this.days() > 0
}
module.exports = DateRange