/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Date range data structure. Based loosely on
* com.google.common.util.DateRange.
*/
goog.provide('goog.date.DateRange');
goog.provide('goog.date.DateRange.Iterator');
goog.provide('goog.date.DateRange.StandardDateRangeKeys');
goog.require('goog.date.Date');
goog.require('goog.date.Interval');
goog.require('goog.iter.Iterator');
goog.require('goog.iter.StopIteration');
/**
* Constructs a date range.
* @constructor
* @struct
* @param {goog.date.Date} startDate The first date in the range.
* @param {goog.date.Date} endDate The last date in the range.
* @final
*/
goog.date.DateRange = function(startDate, endDate) {
'use strict';
/**
* The first date in the range.
* @type {goog.date.Date}
* @private
*/
this.startDate_ = startDate;
/**
* The last date in the range.
* @type {goog.date.Date}
* @private
*/
this.endDate_ = endDate;
};
/**
* The first possible day, as far as this class is concerned.
* @type {goog.date.Date}
*/
goog.date.DateRange.MINIMUM_DATE = new goog.date.Date(0, 0, 1);
/**
* The last possible day, as far as this class is concerned.
* @type {goog.date.Date}
*/
goog.date.DateRange.MAXIMUM_DATE = new goog.date.Date(9999, 11, 31);
/**
* @return {goog.date.Date} The first date in the range.
*/
goog.date.DateRange.prototype.getStartDate = function() {
'use strict';
return this.startDate_;
};
/**
* @return {goog.date.Date} The last date in the range.
*/
goog.date.DateRange.prototype.getEndDate = function() {
'use strict';
return this.endDate_;
};
/**
* Tests if a date falls within this range.
*
* @param {goog.date.Date} date The date to test.
* @return {boolean} Whether the date is in the range.
*/
goog.date.DateRange.prototype.contains = function(date) {
'use strict';
return date.valueOf() >= this.startDate_.valueOf() &&
date.valueOf() <= this.endDate_.valueOf();
};
/**
* @return {!goog.date.DateRange.Iterator} An iterator over the date range.
*/
goog.date.DateRange.prototype.iterator = function() {
'use strict';
return new goog.date.DateRange.Iterator(this);
};
/**
* Tests two {@link goog.date.DateRange} objects for equality.
* @param {goog.date.DateRange} a A date range.
* @param {goog.date.DateRange} b A date range.
* @return {boolean} Whether |a| is the same range as |b|.
*/
goog.date.DateRange.equals = function(a, b) {
'use strict';
// Test for same object reference; type conversion is irrelevant.
if (a === b) {
return true;
}
if (a == null || b == null) {
return false;
}
return a.startDate_.equals(b.startDate_) && a.endDate_.equals(b.endDate_);
};
/**
* Calculates a date that is a number of days after a date. Does not modify its
* input.
* @param {goog.date.Date} date The input date.
* @param {number} offset Number of days.
* @return {!goog.date.Date} The date that is |offset| days after |date|.
* @private
*/
goog.date.DateRange.offsetInDays_ = function(date, offset) {
'use strict';
var newDate = date.clone();
newDate.add(new goog.date.Interval(goog.date.Interval.DAYS, offset));
return newDate;
};
/**
* Calculates a date that is a number of months after the first day in the
* month that contains its input. Does not modify its input.
* @param {goog.date.Date} date The input date.
* @param {number} offset Number of months.
* @return {!goog.date.Date} The date that is |offset| months after the first
* day in the month that contains |date|.
* @private
*/
goog.date.DateRange.offsetInMonths_ = function(date, offset) {
'use strict';
var newDate = date.clone();
newDate.setDate(1);
newDate.add(new goog.date.Interval(goog.date.Interval.MONTHS, offset));
return newDate;
};
/**
* Returns the range from yesterday to yesterday.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that includes only yesterday.
*/
goog.date.DateRange.yesterday = function(opt_today) {
'use strict';
var today = goog.date.DateRange.cloneOrCreate_(opt_today);
var yesterday = goog.date.DateRange.offsetInDays_(today, -1);
return new goog.date.DateRange(yesterday, yesterday.clone());
};
/**
* Returns the range from today to today.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that includes only today.
*/
goog.date.DateRange.today = function(opt_today) {
'use strict';
var today = goog.date.DateRange.cloneOrCreate_(opt_today);
return new goog.date.DateRange(today, today.clone());
};
/**
* Returns the range that includes the seven days that end yesterday.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that includes the seven days that
* end yesterday.
*/
goog.date.DateRange.last7Days = function(opt_today) {
'use strict';
var today = goog.date.DateRange.cloneOrCreate_(opt_today);
var yesterday = goog.date.DateRange.offsetInDays_(today, -1);
return new goog.date.DateRange(
goog.date.DateRange.offsetInDays_(today, -7), yesterday);
};
/**
* Returns the range that starts the first of this month and ends the last day
* of this month.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that starts the first of this month
* and ends the last day of this month.
*/
goog.date.DateRange.thisMonth = function(opt_today) {
'use strict';
var today = goog.date.DateRange.cloneOrCreate_(opt_today);
return new goog.date.DateRange(
goog.date.DateRange.offsetInMonths_(today, 0),
goog.date.DateRange.offsetInDays_(
goog.date.DateRange.offsetInMonths_(today, 1), -1));
};
/**
* Returns the range that starts the first of last month and ends the last day
* of last month.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that starts the first of last month
* and ends the last day of last month.
*/
goog.date.DateRange.lastMonth = function(opt_today) {
'use strict';
var today = goog.date.DateRange.cloneOrCreate_(opt_today);
return new goog.date.DateRange(
goog.date.DateRange.offsetInMonths_(today, -1),
goog.date.DateRange.offsetInDays_(
goog.date.DateRange.offsetInMonths_(today, 0), -1));
};
/**
* Returns the seven-day range that starts on the first day of the week
* (see {@link goog.i18n.DateTimeSymbols.FIRSTDAYOFWEEK}) on or before today.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that starts the Monday on or before
* today and ends the Sunday on or after today.
*/
goog.date.DateRange.thisWeek = function(opt_today) {
'use strict';
var today = goog.date.DateRange.cloneOrCreate_(opt_today);
var iso = today.getIsoWeekday();
var firstDay = today.getFirstDayOfWeek();
var i18nFirstDay = (iso >= firstDay) ? iso - firstDay : iso + (7 - firstDay);
var start = goog.date.DateRange.offsetInDays_(today, -i18nFirstDay);
var end = goog.date.DateRange.offsetInDays_(start, 6);
return new goog.date.DateRange(start, end);
};
/**
* Returns the seven-day range that ends the day before the first day of
* the week (see {@link goog.i18n.DateTimeSymbols.FIRSTDAYOFWEEK}) that
* contains today.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that starts seven days before the
* Monday on or before today and ends the Sunday on or before yesterday.
*/
goog.date.DateRange.lastWeek = function(opt_today) {
'use strict';
var thisWeek = goog.date.DateRange.thisWeek(opt_today);
var start = goog.date.DateRange.offsetInDays_(thisWeek.getStartDate(), -7);
var end = goog.date.DateRange.offsetInDays_(thisWeek.getEndDate(), -7);
return new goog.date.DateRange(start, end);
};
/**
* Returns the range that starts seven days before the Monday on or before
* today and ends the Friday before today.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that starts seven days before the
* Monday on or before today and ends the Friday before today.
*/
goog.date.DateRange.lastBusinessWeek = function(opt_today) {
'use strict';
// TODO(user): should be i18nized.
var today = goog.date.DateRange.cloneOrCreate_(opt_today);
var start =
goog.date.DateRange.offsetInDays_(today, -7 - today.getIsoWeekday());
var end = goog.date.DateRange.offsetInDays_(start, 4);
return new goog.date.DateRange(start, end);
};
/**
* Returns the range that includes all days between January 1, 1900 and
* December 31, 9999.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The range that includes all days between
* January 1, 1900 and December 31, 9999.
*/
goog.date.DateRange.allTime = function(opt_today) {
'use strict';
return new goog.date.DateRange(
goog.date.DateRange.MINIMUM_DATE, goog.date.DateRange.MAXIMUM_DATE);
};
/**
* Standard date range keys. Equivalent to the enum IDs in
* DateRange.java http://go/datarange.java
*
* @enum {string}
*/
goog.date.DateRange.StandardDateRangeKeys = {
YESTERDAY: 'yesterday',
TODAY: 'today',
LAST_7_DAYS: 'last7days',
THIS_MONTH: 'thismonth',
LAST_MONTH: 'lastmonth',
THIS_WEEK: 'thisweek',
LAST_WEEK: 'lastweek',
LAST_BUSINESS_WEEK: 'lastbusinessweek',
ALL_TIME: 'alltime'
};
/**
* @param {string} dateRangeKey A standard date range key.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.DateRange} The date range that corresponds to that key.
* @throws {Error} If no standard date range with that key exists.
*/
goog.date.DateRange.standardDateRange = function(dateRangeKey, opt_today) {
'use strict';
switch (dateRangeKey) {
case goog.date.DateRange.StandardDateRangeKeys.YESTERDAY:
return goog.date.DateRange.yesterday(opt_today);
case goog.date.DateRange.StandardDateRangeKeys.TODAY:
return goog.date.DateRange.today(opt_today);
case goog.date.DateRange.StandardDateRangeKeys.LAST_7_DAYS:
return goog.date.DateRange.last7Days(opt_today);
case goog.date.DateRange.StandardDateRangeKeys.THIS_MONTH:
return goog.date.DateRange.thisMonth(opt_today);
case goog.date.DateRange.StandardDateRangeKeys.LAST_MONTH:
return goog.date.DateRange.lastMonth(opt_today);
case goog.date.DateRange.StandardDateRangeKeys.THIS_WEEK:
return goog.date.DateRange.thisWeek(opt_today);
case goog.date.DateRange.StandardDateRangeKeys.LAST_WEEK:
return goog.date.DateRange.lastWeek(opt_today);
case goog.date.DateRange.StandardDateRangeKeys.LAST_BUSINESS_WEEK:
return goog.date.DateRange.lastBusinessWeek(opt_today);
case goog.date.DateRange.StandardDateRangeKeys.ALL_TIME:
return goog.date.DateRange.allTime(opt_today);
default:
throw new Error('no such date range key: ' + dateRangeKey);
}
};
/**
* Clones or creates new.
* @param {goog.date.Date=} opt_today The date to consider today.
* Defaults to today.
* @return {!goog.date.Date} cloned or new.
* @private
*/
goog.date.DateRange.cloneOrCreate_ = function(opt_today) {
'use strict';
return opt_today ? opt_today.clone() : new goog.date.Date();
};
/**
* Creates an iterator over the dates in a {@link goog.date.DateRange}.
* @constructor
* @struct
* @extends {goog.iter.Iterator<goog.date.Date>}
* @param {goog.date.DateRange} dateRange The date range to iterate.
* @final
*/
goog.date.DateRange.Iterator = function(dateRange) {
'use strict';
/**
* The next date.
* @type {goog.date.Date}
* @private
*/
this.nextDate_ = dateRange.getStartDate().clone();
/**
* The end date, expressed as an integer: YYYYMMDD.
* @type {number}
* @private
*/
this.endDate_ = Number(dateRange.getEndDate().toIsoString());
};
goog.inherits(goog.date.DateRange.Iterator, goog.iter.Iterator);
/** @override */
goog.date.DateRange.Iterator.prototype.nextValueOrThrow = function() {
'use strict';
if (Number(this.nextDate_.toIsoString()) > this.endDate_) {
throw goog.iter.StopIteration;
}
var rv = this.nextDate_.clone();
this.nextDate_.add(new goog.date.Interval(goog.date.Interval.DAYS, 1));
return rv;
};