daterange.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Date range data structure. Based loosely on
  16. * com.google.common.util.DateRange.
  17. *
  18. * @author dpb@google.com (David P. Baker)
  19. */
  20. goog.provide('goog.date.DateRange');
  21. goog.provide('goog.date.DateRange.Iterator');
  22. goog.provide('goog.date.DateRange.StandardDateRangeKeys');
  23. goog.require('goog.date.Date');
  24. goog.require('goog.date.Interval');
  25. goog.require('goog.iter.Iterator');
  26. goog.require('goog.iter.StopIteration');
  27. /**
  28. * Constructs a date range.
  29. * @constructor
  30. * @struct
  31. * @param {goog.date.Date} startDate The first date in the range.
  32. * @param {goog.date.Date} endDate The last date in the range.
  33. * @final
  34. */
  35. goog.date.DateRange = function(startDate, endDate) {
  36. /**
  37. * The first date in the range.
  38. * @type {goog.date.Date}
  39. * @private
  40. */
  41. this.startDate_ = startDate;
  42. /**
  43. * The last date in the range.
  44. * @type {goog.date.Date}
  45. * @private
  46. */
  47. this.endDate_ = endDate;
  48. };
  49. /**
  50. * The first possible day, as far as this class is concerned.
  51. * @type {goog.date.Date}
  52. */
  53. goog.date.DateRange.MINIMUM_DATE = new goog.date.Date(0, 0, 1);
  54. /**
  55. * The last possible day, as far as this class is concerned.
  56. * @type {goog.date.Date}
  57. */
  58. goog.date.DateRange.MAXIMUM_DATE = new goog.date.Date(9999, 11, 31);
  59. /**
  60. * @return {goog.date.Date} The first date in the range.
  61. */
  62. goog.date.DateRange.prototype.getStartDate = function() {
  63. return this.startDate_;
  64. };
  65. /**
  66. * @return {goog.date.Date} The last date in the range.
  67. */
  68. goog.date.DateRange.prototype.getEndDate = function() {
  69. return this.endDate_;
  70. };
  71. /**
  72. * Tests if a date falls within this range.
  73. *
  74. * @param {goog.date.Date} date The date to test.
  75. * @return {boolean} Whether the date is in the range.
  76. */
  77. goog.date.DateRange.prototype.contains = function(date) {
  78. return date.valueOf() >= this.startDate_.valueOf() &&
  79. date.valueOf() <= this.endDate_.valueOf();
  80. };
  81. /**
  82. * @return {!goog.date.DateRange.Iterator} An iterator over the date range.
  83. */
  84. goog.date.DateRange.prototype.iterator = function() {
  85. return new goog.date.DateRange.Iterator(this);
  86. };
  87. /**
  88. * Tests two {@link goog.date.DateRange} objects for equality.
  89. * @param {goog.date.DateRange} a A date range.
  90. * @param {goog.date.DateRange} b A date range.
  91. * @return {boolean} Whether |a| is the same range as |b|.
  92. */
  93. goog.date.DateRange.equals = function(a, b) {
  94. // Test for same object reference; type conversion is irrelevant.
  95. if (a === b) {
  96. return true;
  97. }
  98. if (a == null || b == null) {
  99. return false;
  100. }
  101. return a.startDate_.equals(b.startDate_) && a.endDate_.equals(b.endDate_);
  102. };
  103. /**
  104. * Calculates a date that is a number of days after a date. Does not modify its
  105. * input.
  106. * @param {goog.date.Date} date The input date.
  107. * @param {number} offset Number of days.
  108. * @return {!goog.date.Date} The date that is |offset| days after |date|.
  109. * @private
  110. */
  111. goog.date.DateRange.offsetInDays_ = function(date, offset) {
  112. var newDate = date.clone();
  113. newDate.add(new goog.date.Interval(goog.date.Interval.DAYS, offset));
  114. return newDate;
  115. };
  116. /**
  117. * Calculates a date that is a number of months after the first day in the
  118. * month that contains its input. Does not modify its input.
  119. * @param {goog.date.Date} date The input date.
  120. * @param {number} offset Number of months.
  121. * @return {!goog.date.Date} The date that is |offset| months after the first
  122. * day in the month that contains |date|.
  123. * @private
  124. */
  125. goog.date.DateRange.offsetInMonths_ = function(date, offset) {
  126. var newDate = date.clone();
  127. newDate.setDate(1);
  128. newDate.add(new goog.date.Interval(goog.date.Interval.MONTHS, offset));
  129. return newDate;
  130. };
  131. /**
  132. * Returns the range from yesterday to yesterday.
  133. * @param {goog.date.Date=} opt_today The date to consider today.
  134. * Defaults to today.
  135. * @return {!goog.date.DateRange} The range that includes only yesterday.
  136. */
  137. goog.date.DateRange.yesterday = function(opt_today) {
  138. var today = goog.date.DateRange.cloneOrCreate_(opt_today);
  139. var yesterday = goog.date.DateRange.offsetInDays_(today, -1);
  140. return new goog.date.DateRange(yesterday, yesterday.clone());
  141. };
  142. /**
  143. * Returns the range from today to today.
  144. * @param {goog.date.Date=} opt_today The date to consider today.
  145. * Defaults to today.
  146. * @return {!goog.date.DateRange} The range that includes only today.
  147. */
  148. goog.date.DateRange.today = function(opt_today) {
  149. var today = goog.date.DateRange.cloneOrCreate_(opt_today);
  150. return new goog.date.DateRange(today, today.clone());
  151. };
  152. /**
  153. * Returns the range that includes the seven days that end yesterday.
  154. * @param {goog.date.Date=} opt_today The date to consider today.
  155. * Defaults to today.
  156. * @return {!goog.date.DateRange} The range that includes the seven days that
  157. * end yesterday.
  158. */
  159. goog.date.DateRange.last7Days = function(opt_today) {
  160. var today = goog.date.DateRange.cloneOrCreate_(opt_today);
  161. var yesterday = goog.date.DateRange.offsetInDays_(today, -1);
  162. return new goog.date.DateRange(
  163. goog.date.DateRange.offsetInDays_(today, -7), yesterday);
  164. };
  165. /**
  166. * Returns the range that starts the first of this month and ends the last day
  167. * of this month.
  168. * @param {goog.date.Date=} opt_today The date to consider today.
  169. * Defaults to today.
  170. * @return {!goog.date.DateRange} The range that starts the first of this month
  171. * and ends the last day of this month.
  172. */
  173. goog.date.DateRange.thisMonth = function(opt_today) {
  174. var today = goog.date.DateRange.cloneOrCreate_(opt_today);
  175. return new goog.date.DateRange(
  176. goog.date.DateRange.offsetInMonths_(today, 0),
  177. goog.date.DateRange.offsetInDays_(
  178. goog.date.DateRange.offsetInMonths_(today, 1), -1));
  179. };
  180. /**
  181. * Returns the range that starts the first of last month and ends the last day
  182. * of last month.
  183. * @param {goog.date.Date=} opt_today The date to consider today.
  184. * Defaults to today.
  185. * @return {!goog.date.DateRange} The range that starts the first of last month
  186. * and ends the last day of last month.
  187. */
  188. goog.date.DateRange.lastMonth = function(opt_today) {
  189. var today = goog.date.DateRange.cloneOrCreate_(opt_today);
  190. return new goog.date.DateRange(
  191. goog.date.DateRange.offsetInMonths_(today, -1),
  192. goog.date.DateRange.offsetInDays_(
  193. goog.date.DateRange.offsetInMonths_(today, 0), -1));
  194. };
  195. /**
  196. * Returns the seven-day range that starts on the first day of the week
  197. * (see {@link goog.i18n.DateTimeSymbols.FIRSTDAYOFWEEK}) on or before today.
  198. * @param {goog.date.Date=} opt_today The date to consider today.
  199. * Defaults to today.
  200. * @return {!goog.date.DateRange} The range that starts the Monday on or before
  201. * today and ends the Sunday on or after today.
  202. */
  203. goog.date.DateRange.thisWeek = function(opt_today) {
  204. var today = goog.date.DateRange.cloneOrCreate_(opt_today);
  205. var iso = today.getIsoWeekday();
  206. var firstDay = today.getFirstDayOfWeek();
  207. var i18nFirstDay = (iso >= firstDay) ? iso - firstDay : iso + (7 - firstDay);
  208. var start = goog.date.DateRange.offsetInDays_(today, -i18nFirstDay);
  209. var end = goog.date.DateRange.offsetInDays_(start, 6);
  210. return new goog.date.DateRange(start, end);
  211. };
  212. /**
  213. * Returns the seven-day range that ends the day before the first day of
  214. * the week (see {@link goog.i18n.DateTimeSymbols.FIRSTDAYOFWEEK}) that
  215. * contains today.
  216. * @param {goog.date.Date=} opt_today The date to consider today.
  217. * Defaults to today.
  218. * @return {!goog.date.DateRange} The range that starts seven days before the
  219. * Monday on or before today and ends the Sunday on or before yesterday.
  220. */
  221. goog.date.DateRange.lastWeek = function(opt_today) {
  222. var thisWeek = goog.date.DateRange.thisWeek(opt_today);
  223. var start = goog.date.DateRange.offsetInDays_(thisWeek.getStartDate(), -7);
  224. var end = goog.date.DateRange.offsetInDays_(thisWeek.getEndDate(), -7);
  225. return new goog.date.DateRange(start, end);
  226. };
  227. /**
  228. * Returns the range that starts seven days before the Monday on or before
  229. * today and ends the Friday before today.
  230. * @param {goog.date.Date=} opt_today The date to consider today.
  231. * Defaults to today.
  232. * @return {!goog.date.DateRange} The range that starts seven days before the
  233. * Monday on or before today and ends the Friday before today.
  234. */
  235. goog.date.DateRange.lastBusinessWeek = function(opt_today) {
  236. // TODO(user): should be i18nized.
  237. var today = goog.date.DateRange.cloneOrCreate_(opt_today);
  238. var start =
  239. goog.date.DateRange.offsetInDays_(today, -7 - today.getIsoWeekday());
  240. var end = goog.date.DateRange.offsetInDays_(start, 4);
  241. return new goog.date.DateRange(start, end);
  242. };
  243. /**
  244. * Returns the range that includes all days between January 1, 1900 and
  245. * December 31, 9999.
  246. * @param {goog.date.Date=} opt_today The date to consider today.
  247. * Defaults to today.
  248. * @return {!goog.date.DateRange} The range that includes all days between
  249. * January 1, 1900 and December 31, 9999.
  250. */
  251. goog.date.DateRange.allTime = function(opt_today) {
  252. return new goog.date.DateRange(
  253. goog.date.DateRange.MINIMUM_DATE, goog.date.DateRange.MAXIMUM_DATE);
  254. };
  255. /**
  256. * Standard date range keys. Equivalent to the enum IDs in
  257. * DateRange.java http://go/datarange.java
  258. *
  259. * @enum {string}
  260. */
  261. goog.date.DateRange.StandardDateRangeKeys = {
  262. YESTERDAY: 'yesterday',
  263. TODAY: 'today',
  264. LAST_7_DAYS: 'last7days',
  265. THIS_MONTH: 'thismonth',
  266. LAST_MONTH: 'lastmonth',
  267. THIS_WEEK: 'thisweek',
  268. LAST_WEEK: 'lastweek',
  269. LAST_BUSINESS_WEEK: 'lastbusinessweek',
  270. ALL_TIME: 'alltime'
  271. };
  272. /**
  273. * @param {string} dateRangeKey A standard date range key.
  274. * @param {goog.date.Date=} opt_today The date to consider today.
  275. * Defaults to today.
  276. * @return {!goog.date.DateRange} The date range that corresponds to that key.
  277. * @throws {Error} If no standard date range with that key exists.
  278. */
  279. goog.date.DateRange.standardDateRange = function(dateRangeKey, opt_today) {
  280. switch (dateRangeKey) {
  281. case goog.date.DateRange.StandardDateRangeKeys.YESTERDAY:
  282. return goog.date.DateRange.yesterday(opt_today);
  283. case goog.date.DateRange.StandardDateRangeKeys.TODAY:
  284. return goog.date.DateRange.today(opt_today);
  285. case goog.date.DateRange.StandardDateRangeKeys.LAST_7_DAYS:
  286. return goog.date.DateRange.last7Days(opt_today);
  287. case goog.date.DateRange.StandardDateRangeKeys.THIS_MONTH:
  288. return goog.date.DateRange.thisMonth(opt_today);
  289. case goog.date.DateRange.StandardDateRangeKeys.LAST_MONTH:
  290. return goog.date.DateRange.lastMonth(opt_today);
  291. case goog.date.DateRange.StandardDateRangeKeys.THIS_WEEK:
  292. return goog.date.DateRange.thisWeek(opt_today);
  293. case goog.date.DateRange.StandardDateRangeKeys.LAST_WEEK:
  294. return goog.date.DateRange.lastWeek(opt_today);
  295. case goog.date.DateRange.StandardDateRangeKeys.LAST_BUSINESS_WEEK:
  296. return goog.date.DateRange.lastBusinessWeek(opt_today);
  297. case goog.date.DateRange.StandardDateRangeKeys.ALL_TIME:
  298. return goog.date.DateRange.allTime(opt_today);
  299. default:
  300. throw Error('no such date range key: ' + dateRangeKey);
  301. }
  302. };
  303. /**
  304. * Clones or creates new.
  305. * @param {goog.date.Date=} opt_today The date to consider today.
  306. * Defaults to today.
  307. * @return {!goog.date.Date} cloned or new.
  308. * @private
  309. */
  310. goog.date.DateRange.cloneOrCreate_ = function(opt_today) {
  311. return opt_today ? opt_today.clone() : new goog.date.Date();
  312. };
  313. /**
  314. * Creates an iterator over the dates in a {@link goog.date.DateRange}.
  315. * @constructor
  316. * @struct
  317. * @extends {goog.iter.Iterator<goog.date.Date>}
  318. * @param {goog.date.DateRange} dateRange The date range to iterate.
  319. * @final
  320. */
  321. goog.date.DateRange.Iterator = function(dateRange) {
  322. /**
  323. * The next date.
  324. * @type {goog.date.Date}
  325. * @private
  326. */
  327. this.nextDate_ = dateRange.getStartDate().clone();
  328. /**
  329. * The end date, expressed as an integer: YYYYMMDD.
  330. * @type {number}
  331. * @private
  332. */
  333. this.endDate_ = Number(dateRange.getEndDate().toIsoString());
  334. };
  335. goog.inherits(goog.date.DateRange.Iterator, goog.iter.Iterator);
  336. /** @override */
  337. goog.date.DateRange.Iterator.prototype.next = function() {
  338. if (Number(this.nextDate_.toIsoString()) > this.endDate_) {
  339. throw goog.iter.StopIteration;
  340. }
  341. var rv = this.nextDate_.clone();
  342. this.nextDate_.add(new goog.date.Interval(goog.date.Interval.DAYS, 1));
  343. return rv;
  344. };