1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.commons.lang.time;
  18.  
  19. import java.text.ParseException;
  20. import java.text.ParsePosition;
  21. import java.text.SimpleDateFormat;
  22. import java.util.Calendar;
  23. import java.util.Date;
  24. import java.util.Iterator;
  25. import java.util.NoSuchElementException;
  26. import java.util.TimeZone;
  27.  
  28. import org.apache.commons.lang.StringUtils;
  29.  
  30. /**
  31. * <p>A suite of utilities surrounding the use of the
  32. * {@link java.util.Calendar} and {@link java.util.Date} object.</p>
  33. *
  34. * <p>DateUtils contains a lot of common methods considering manipulations
  35. * of Dates or Calendars. Some methods require some extra explanation.
  36. * The truncate, ceiling and round methods could be considered the Math.floor(),
  37. * Math.ceil() or Math.round versions for dates
  38. * This way date-fields will be ignored in bottom-up order.
  39. * As a complement to these methods we've introduced some fragment-methods.
  40. * With these methods the Date-fields will be ignored in top-down order.
  41. * Since a date without a year is not a valid date, you have to decide in what
  42. * kind of date-field you want your result, for instance milliseconds or days.
  43. * </p>
  44. *
  45. *
  46. *
  47. * @author Apache Software Foundation
  48. * @author <a href="mailto:sergek@lokitech.com">Serge Knystautas</a>
  49. * @author Janek Bogucki
  50. * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
  51. * @author Phil Steitz
  52. * @author Robert Scholte
  53. * @since 2.0
  54. * @version $Id: DateUtils.java 1056840 2011-01-09 00:12:23Z niallp $
  55. */
  56. public class DateUtils {
  57.  
  58. /**
  59. * The UTC time zone (often referred to as GMT).
  60. */
  61. public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("GMT");
  62. /**
  63. * Number of milliseconds in a standard second.
  64. * @since 2.1
  65. */
  66. public static final long MILLIS_PER_SECOND = 1000;
  67. /**
  68. * Number of milliseconds in a standard minute.
  69. * @since 2.1
  70. */
  71. public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
  72. /**
  73. * Number of milliseconds in a standard hour.
  74. * @since 2.1
  75. */
  76. public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
  77. /**
  78. * Number of milliseconds in a standard day.
  79. * @since 2.1
  80. */
  81. public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
  82.  
  83. /**
  84. * This is half a month, so this represents whether a date is in the top
  85. * or bottom half of the month.
  86. */
  87. public final static int SEMI_MONTH = 1001;
  88.  
  89. private static final int[][] fields = {
  90. {Calendar.MILLISECOND},
  91. {Calendar.SECOND},
  92. {Calendar.MINUTE},
  93. {Calendar.HOUR_OF_DAY, Calendar.HOUR},
  94. {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM
  95. /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
  96. },
  97. {Calendar.MONTH, DateUtils.SEMI_MONTH},
  98. {Calendar.YEAR},
  99. {Calendar.ERA}};
  100.  
  101. /**
  102. * A week range, starting on Sunday.
  103. */
  104. public final static int RANGE_WEEK_SUNDAY = 1;
  105.  
  106. /**
  107. * A week range, starting on Monday.
  108. */
  109. public final static int RANGE_WEEK_MONDAY = 2;
  110.  
  111. /**
  112. * A week range, starting on the day focused.
  113. */
  114. public final static int RANGE_WEEK_RELATIVE = 3;
  115.  
  116. /**
  117. * A week range, centered around the day focused.
  118. */
  119. public final static int RANGE_WEEK_CENTER = 4;
  120.  
  121. /**
  122. * A month range, the week starting on Sunday.
  123. */
  124. public final static int RANGE_MONTH_SUNDAY = 5;
  125.  
  126. /**
  127. * A month range, the week starting on Monday.
  128. */
  129. public final static int RANGE_MONTH_MONDAY = 6;
  130.  
  131. /**
  132. * Constant marker for truncating
  133. */
  134. private final static int MODIFY_TRUNCATE = 0;
  135.  
  136. /**
  137. * Constant marker for rounding
  138. */
  139. private final static int MODIFY_ROUND = 1;
  140.  
  141. /**
  142. * Constant marker for ceiling
  143. */
  144. private final static int MODIFY_CEILING= 2;
  145.  
  146. /**
  147. * <p><code>DateUtils</code> instances should NOT be constructed in
  148. * standard programming. Instead, the class should be used as
  149. * <code>DateUtils.parse(str);</code>.</p>
  150. *
  151. * <p>This constructor is public to permit tools that require a JavaBean
  152. * instance to operate.</p>
  153. */
  154. public DateUtils() {
  155. super();
  156. }
  157.  
  158. //-----------------------------------------------------------------------
  159. /**
  160. * <p>Checks if two date objects are on the same day ignoring time.</p>
  161. *
  162. * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
  163. * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
  164. * </p>
  165. *
  166. * @param date1 the first date, not altered, not null
  167. * @param date2 the second date, not altered, not null
  168. * @return true if they represent the same day
  169. * @throws IllegalArgumentException if either date is <code>null</code>
  170. * @since 2.1
  171. */
  172. public static boolean isSameDay(Date date1, Date date2) {
  173. if (date1 == null || date2 == null) {
  174. throw new IllegalArgumentException("The date must not be null");
  175. }
  176. Calendar cal1 = Calendar.getInstance();
  177. cal1.setTime(date1);
  178. Calendar cal2 = Calendar.getInstance();
  179. cal2.setTime(date2);
  180. return isSameDay(cal1, cal2);
  181. }
  182.  
  183. /**
  184. * <p>Checks if two calendar objects are on the same day ignoring time.</p>
  185. *
  186. * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
  187. * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
  188. * </p>
  189. *
  190. * @param cal1 the first calendar, not altered, not null
  191. * @param cal2 the second calendar, not altered, not null
  192. * @return true if they represent the same day
  193. * @throws IllegalArgumentException if either calendar is <code>null</code>
  194. * @since 2.1
  195. */
  196. public static boolean isSameDay(Calendar cal1, Calendar cal2) {
  197. if (cal1 == null || cal2 == null) {
  198. throw new IllegalArgumentException("The date must not be null");
  199. }
  200. return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
  201. cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
  202. cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
  203. }
  204.  
  205. //-----------------------------------------------------------------------
  206. /**
  207. * <p>Checks if two date objects represent the same instant in time.</p>
  208. *
  209. * <p>This method compares the long millisecond time of the two objects.</p>
  210. *
  211. * @param date1 the first date, not altered, not null
  212. * @param date2 the second date, not altered, not null
  213. * @return true if they represent the same millisecond instant
  214. * @throws IllegalArgumentException if either date is <code>null</code>
  215. * @since 2.1
  216. */
  217. public static boolean isSameInstant(Date date1, Date date2) {
  218. if (date1 == null || date2 == null) {
  219. throw new IllegalArgumentException("The date must not be null");
  220. }
  221. return date1.getTime() == date2.getTime();
  222. }
  223.  
  224. /**
  225. * <p>Checks if two calendar objects represent the same instant in time.</p>
  226. *
  227. * <p>This method compares the long millisecond time of the two objects.</p>
  228. *
  229. * @param cal1 the first calendar, not altered, not null
  230. * @param cal2 the second calendar, not altered, not null
  231. * @return true if they represent the same millisecond instant
  232. * @throws IllegalArgumentException if either date is <code>null</code>
  233. * @since 2.1
  234. */
  235. public static boolean isSameInstant(Calendar cal1, Calendar cal2) {
  236. if (cal1 == null || cal2 == null) {
  237. throw new IllegalArgumentException("The date must not be null");
  238. }
  239. return cal1.getTime().getTime() == cal2.getTime().getTime();
  240. }
  241.  
  242. //-----------------------------------------------------------------------
  243. /**
  244. * <p>Checks if two calendar objects represent the same local time.</p>
  245. *
  246. * <p>This method compares the values of the fields of the two objects.
  247. * In addition, both calendars must be the same of the same type.</p>
  248. *
  249. * @param cal1 the first calendar, not altered, not null
  250. * @param cal2 the second calendar, not altered, not null
  251. * @return true if they represent the same millisecond instant
  252. * @throws IllegalArgumentException if either date is <code>null</code>
  253. * @since 2.1
  254. */
  255. public static boolean isSameLocalTime(Calendar cal1, Calendar cal2) {
  256. if (cal1 == null || cal2 == null) {
  257. throw new IllegalArgumentException("The date must not be null");
  258. }
  259. return (cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) &&
  260. cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) &&
  261. cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) &&
  262. cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR) &&
  263. cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
  264. cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
  265. cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
  266. cal1.getClass() == cal2.getClass());
  267. }
  268.  
  269. //-----------------------------------------------------------------------
  270. /**
  271. * <p>Parses a string representing a date by trying a variety of different parsers.</p>
  272. *
  273. * <p>The parse will try each parse pattern in turn.
  274. * A parse is only deemed successful if it parses the whole of the input string.
  275. * If no parse patterns match, a ParseException is thrown.</p>
  276. * The parser will be lenient toward the parsed date.
  277. *
  278. * @param str the date to parse, not null
  279. * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null
  280. * @return the parsed date
  281. * @throws IllegalArgumentException if the date string or pattern array is null
  282. * @throws ParseException if none of the date patterns were suitable (or there were none)
  283. */
  284. public static Date parseDate(String str, String[] parsePatterns) throws ParseException {
  285. return parseDateWithLeniency(str, parsePatterns, true);
  286. }
  287.  
  288. //-----------------------------------------------------------------------
  289. /**
  290. * <p>Parses a string representing a date by trying a variety of different parsers.</p>
  291. *
  292. * <p>The parse will try each parse pattern in turn.
  293. * A parse is only deemed successful if it parses the whole of the input string.
  294. * If no parse patterns match, a ParseException is thrown.</p>
  295. * The parser parses strictly - it does not allow for dates such as "February 942, 1996".
  296. *
  297. * @param str the date to parse, not null
  298. * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null
  299. * @return the parsed date
  300. * @throws IllegalArgumentException if the date string or pattern array is null
  301. * @throws ParseException if none of the date patterns were suitable
  302. * @since 2.5
  303. */
  304. public static Date parseDateStrictly(String str, String[] parsePatterns) throws ParseException {
  305. return parseDateWithLeniency(str, parsePatterns, false);
  306. }
  307.  
  308. /**
  309. * <p>Parses a string representing a date by trying a variety of different parsers.</p>
  310. *
  311. * <p>The parse will try each parse pattern in turn.
  312. * A parse is only deemed successful if it parses the whole of the input string.
  313. * If no parse patterns match, a ParseException is thrown.</p>
  314. *
  315. * @param str the date to parse, not null
  316. * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null
  317. * @param lenient Specify whether or not date/time parsing is to be lenient.
  318. * @return the parsed date
  319. * @throws IllegalArgumentException if the date string or pattern array is null
  320. * @throws ParseException if none of the date patterns were suitable
  321. * @see java.util.Calender#isLenient()
  322. */
  323. private static Date parseDateWithLeniency(String str, String[] parsePatterns,
  324. boolean lenient) throws ParseException {
  325. if (str == null || parsePatterns == null) {
  326. throw new IllegalArgumentException("Date and Patterns must not be null");
  327. }
  328.  
  329. SimpleDateFormat parser = new SimpleDateFormat();
  330. parser.setLenient(lenient);
  331. ParsePosition pos = new ParsePosition(0);
  332. for (int i = 0; i < parsePatterns.length; i++) {
  333.  
  334. String pattern = parsePatterns[i];
  335.  
  336. // LANG-530 - need to make sure 'ZZ' output doesn't get passed to SimpleDateFormat
  337. if (parsePatterns[i].endsWith("ZZ")) {
  338. pattern = pattern.substring(0, pattern.length() - 1);
  339. }
  340.  
  341. parser.applyPattern(pattern);
  342. pos.setIndex(0);
  343.  
  344. String str2 = str;
  345. // LANG-530 - need to make sure 'ZZ' output doesn't hit SimpleDateFormat as it will ParseException
  346. if (parsePatterns[i].endsWith("ZZ")) {
  347. int signIdx = indexOfSignChars(str2, 0);
  348. while (signIdx >=0) {
  349. str2 = reformatTimezone(str2, signIdx);
  350. signIdx = indexOfSignChars(str2, ++signIdx);
  351. }
  352. }
  353.  
  354. Date date = parser.parse(str2, pos);
  355. if (date != null && pos.getIndex() == str2.length()) {
  356. return date;
  357. }
  358. }
  359. throw new ParseException("Unable to parse the date: " + str, -1);
  360. }
  361.  
  362. /**
  363. * Index of sign charaters (i.e. '+' or '-').
  364. *
  365. * @param str The string to search
  366. * @param startPos The start position
  367. * @return the index of the first sign character or -1 if not found
  368. */
  369. private static int indexOfSignChars(String str, int startPos) {
  370. int idx = StringUtils.indexOf(str, '+', startPos);
  371. if (idx < 0) {
  372. idx = StringUtils.indexOf(str, '-', startPos);
  373. }
  374. return idx;
  375. }
  376.  
  377. /**
  378. * Reformat the timezone in a date string.
  379. *
  380. * @param str The input string
  381. * @param signIdx The index position of the sign characters
  382. * @return The reformatted string
  383. */
  384. private static String reformatTimezone(String str, int signIdx) {
  385. String str2 = str;
  386. if (signIdx >= 0 &&
  387. signIdx + 5 < str.length() &&
  388. Character.isDigit(str.charAt(signIdx + 1)) &&
  389. Character.isDigit(str.charAt(signIdx + 2)) &&
  390. str.charAt(signIdx + 3) == ':' &&
  391. Character.isDigit(str.charAt(signIdx + 4)) &&
  392. Character.isDigit(str.charAt(signIdx + 5))) {
  393. str2 = str.substring(0, signIdx + 3) + str.substring(signIdx + 4);
  394. }
  395. return str2;
  396. }
  397.  
  398. //-----------------------------------------------------------------------
  399. /**
  400. * Adds a number of years to a date returning a new object.
  401. * The original date object is unchanged.
  402. *
  403. * @param date the date, not null
  404. * @param amount the amount to add, may be negative
  405. * @return the new date object with the amount added
  406. * @throws IllegalArgumentException if the date is null
  407. */
  408. public static Date addYears(Date date, int amount) {
  409. return add(date, Calendar.YEAR, amount);
  410. }
  411.  
  412. //-----------------------------------------------------------------------
  413. /**
  414. * Adds a number of months to a date returning a new object.
  415. * The original date object is unchanged.
  416. *
  417. * @param date the date, not null
  418. * @param amount the amount to add, may be negative
  419. * @return the new date object with the amount added
  420. * @throws IllegalArgumentException if the date is null
  421. */
  422. public static Date addMonths(Date date, int amount) {
  423. return add(date, Calendar.MONTH, amount);
  424. }
  425.  
  426. //-----------------------------------------------------------------------
  427. /**
  428. * Adds a number of weeks to a date returning a new object.
  429. * The original date object is unchanged.
  430. *
  431. * @param date the date, not null
  432. * @param amount the amount to add, may be negative
  433. * @return the new date object with the amount added
  434. * @throws IllegalArgumentException if the date is null
  435. */
  436. public static Date addWeeks(Date date, int amount) {
  437. return add(date, Calendar.WEEK_OF_YEAR, amount);
  438. }
  439.  
  440. //-----------------------------------------------------------------------
  441. /**
  442. * Adds a number of days to a date returning a new object.
  443. * The original date object is unchanged.
  444. *
  445. * @param date the date, not null
  446. * @param amount the amount to add, may be negative
  447. * @return the new date object with the amount added
  448. * @throws IllegalArgumentException if the date is null
  449. */
  450. public static Date addDays(Date date, int amount) {
  451. return add(date, Calendar.DAY_OF_MONTH, amount);
  452. }
  453.  
  454. //-----------------------------------------------------------------------
  455. /**
  456. * Adds a number of hours to a date returning a new object.
  457. * The original date object is unchanged.
  458. *
  459. * @param date the date, not null
  460. * @param amount the amount to add, may be negative
  461. * @return the new date object with the amount added
  462. * @throws IllegalArgumentException if the date is null
  463. */
  464. public static Date addHours(Date date, int amount) {
  465. return add(date, Calendar.HOUR_OF_DAY, amount);
  466. }
  467.  
  468. //-----------------------------------------------------------------------
  469. /**
  470. * Adds a number of minutes to a date returning a new object.
  471. * The original date object is unchanged.
  472. *
  473. * @param date the date, not null
  474. * @param amount the amount to add, may be negative
  475. * @return the new date object with the amount added
  476. * @throws IllegalArgumentException if the date is null
  477. */
  478. public static Date addMinutes(Date date, int amount) {
  479. return add(date, Calendar.MINUTE, amount);
  480. }
  481.  
  482. //-----------------------------------------------------------------------
  483. /**
  484. * Adds a number of seconds to a date returning a new object.
  485. * The original date object is unchanged.
  486. *
  487. * @param date the date, not null
  488. * @param amount the amount to add, may be negative
  489. * @return the new date object with the amount added
  490. * @throws IllegalArgumentException if the date is null
  491. */
  492. public static Date addSeconds(Date date, int amount) {
  493. return add(date, Calendar.SECOND, amount);
  494. }
  495.  
  496. //-----------------------------------------------------------------------
  497. /**
  498. * Adds a number of milliseconds to a date returning a new object.
  499. * The original date object is unchanged.
  500. *
  501. * @param date the date, not null
  502. * @param amount the amount to add, may be negative
  503. * @return the new date object with the amount added
  504. * @throws IllegalArgumentException if the date is null
  505. */
  506. public static Date addMilliseconds(Date date, int amount) {
  507. return add(date, Calendar.MILLISECOND, amount);
  508. }
  509.  
  510. //-----------------------------------------------------------------------
  511. /**
  512. * Adds to a date returning a new object.
  513. * The original date object is unchanged.
  514. *
  515. * @param date the date, not null
  516. * @param calendarField the calendar field to add to
  517. * @param amount the amount to add, may be negative
  518. * @return the new date object with the amount added
  519. * @throws IllegalArgumentException if the date is null
  520. * @deprecated Will become privately scoped in 3.0
  521. */
  522. public static Date add(Date date, int calendarField, int amount) {
  523. if (date == null) {
  524. throw new IllegalArgumentException("The date must not be null");
  525. }
  526. Calendar c = Calendar.getInstance();
  527. c.setTime(date);
  528. c.add(calendarField, amount);
  529. return c.getTime();
  530. }
  531.  
  532. //-----------------------------------------------------------------------
  533. /**
  534. * Sets the years field to a date returning a new object.
  535. * The original date object is unchanged.
  536. *
  537. * @param date the date, not null
  538. * @param amount the amount to set
  539. * @return a new Date object set with the specified value
  540. * @throws IllegalArgumentException if the date is null
  541. * @since 2.4
  542. */
  543. public static Date setYears(Date date, int amount) {
  544. return set(date, Calendar.YEAR, amount);
  545. }
  546.  
  547. //-----------------------------------------------------------------------
  548. /**
  549. * Sets the months field to a date returning a new object.
  550. * The original date object is unchanged.
  551. *
  552. * @param date the date, not null
  553. * @param amount the amount to set
  554. * @return a new Date object set with the specified value
  555. * @throws IllegalArgumentException if the date is null
  556. * @since 2.4
  557. */
  558. public static Date setMonths(Date date, int amount) {
  559. return set(date, Calendar.MONTH, amount);
  560. }
  561.  
  562. //-----------------------------------------------------------------------
  563. /**
  564. * Sets the day of month field to a date returning a new object.
  565. * The original date object is unchanged.
  566. *
  567. * @param date the date, not null
  568. * @param amount the amount to set
  569. * @return a new Date object set with the specified value
  570. * @throws IllegalArgumentException if the date is null
  571. * @since 2.4
  572. */
  573. public static Date setDays(Date date, int amount) {
  574. return set(date, Calendar.DAY_OF_MONTH, amount);
  575. }
  576.  
  577. //-----------------------------------------------------------------------
  578. /**
  579. * Sets the hours field to a date returning a new object. Hours range
  580. * from 0-23.
  581. * The original date object is unchanged.
  582. *
  583. * @param date the date, not null
  584. * @param amount the amount to set
  585. * @return a new Date object set with the specified value
  586. * @throws IllegalArgumentException if the date is null
  587. * @since 2.4
  588. */
  589. public static Date setHours(Date date, int amount) {
  590. return set(date, Calendar.HOUR_OF_DAY, amount);
  591. }
  592.  
  593. //-----------------------------------------------------------------------
  594. /**
  595. * Sets the minute field to a date returning a new object.
  596. * The original date object is unchanged.
  597. *
  598. * @param date the date, not null
  599. * @param amount the amount to set
  600. * @return a new Date object set with the specified value
  601. * @throws IllegalArgumentException if the date is null
  602. * @since 2.4
  603. */
  604. public static Date setMinutes(Date date, int amount) {
  605. return set(date, Calendar.MINUTE, amount);
  606. }
  607.  
  608. //-----------------------------------------------------------------------
  609. /**
  610. * Sets the seconds field to a date returning a new object.
  611. * The original date object is unchanged.
  612. *
  613. * @param date the date, not null
  614. * @param amount the amount to set
  615. * @return a new Date object set with the specified value
  616. * @throws IllegalArgumentException if the date is null
  617. * @since 2.4
  618. */
  619. public static Date setSeconds(Date date, int amount) {
  620. return set(date, Calendar.SECOND, amount);
  621. }
  622.  
  623. //-----------------------------------------------------------------------
  624. /**
  625. * Sets the miliseconds field to a date returning a new object.
  626. * The original date object is unchanged.
  627. *
  628. * @param date the date, not null
  629. * @param amount the amount to set
  630. * @return a new Date object set with the specified value
  631. * @throws IllegalArgumentException if the date is null
  632. * @since 2.4
  633. */
  634. public static Date setMilliseconds(Date date, int amount) {
  635. return set(date, Calendar.MILLISECOND, amount);
  636. }
  637.  
  638. //-----------------------------------------------------------------------
  639. /**
  640. * Sets the specified field to a date returning a new object.
  641. * This does not use a lenient calendar.
  642. * The original date object is unchanged.
  643. *
  644. * @param date the date, not null
  645. * @param calendarField the calendar field to set the amount to
  646. * @param amount the amount to set
  647. * @return a new Date object set with the specified value
  648. * @throws IllegalArgumentException if the date is null
  649. * @since 2.4
  650. */
  651. private static Date set(Date date, int calendarField, int amount) {
  652. if (date == null) {
  653. throw new IllegalArgumentException("The date must not be null");
  654. }
  655. // getInstance() returns a new object, so this method is thread safe.
  656. Calendar c = Calendar.getInstance();
  657. c.setLenient(false);
  658. c.setTime(date);
  659. c.set(calendarField, amount);
  660. return c.getTime();
  661. }
  662.  
  663. //-----------------------------------------------------------------------
  664. /**
  665. * Convert a Date into a Calendar object.
  666. *
  667. * @param date the date to convert to a Calendar
  668. * @return the created Calendar
  669. * @throws NullPointerException if null is passed in
  670. * @since 2.6
  671. */
  672. public static Calendar toCalendar(Date date) {
  673. Calendar c = Calendar.getInstance();
  674. c.setTime(date);
  675. return c;
  676. }
  677.  
  678. //-----------------------------------------------------------------------
  679. /**
  680. * <p>Round this date, leaving the field specified as the most
  681. * significant field.</p>
  682. *
  683. * <p>For example, if you had the datetime of 28 Mar 2002
  684. * 13:45:01.231, if this was passed with HOUR, it would return
  685. * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
  686. * would return 1 April 2002 0:00:00.000.</p>
  687. *
  688. * <p>For a date in a timezone that handles the change to daylight
  689. * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
  690. * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
  691. * date that crosses this time would produce the following values:
  692. * <ul>
  693. * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
  694. * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
  695. * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
  696. * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
  697. * </ul>
  698. * </p>
  699. *
  700. * @param date the date to work with
  701. * @param field the field from <code>Calendar</code>
  702. * or <code>SEMI_MONTH</code>
  703. * @return the rounded date
  704. * @throws IllegalArgumentException if the date is <code>null</code>
  705. * @throws ArithmeticException if the year is over 280 million
  706. */
  707. public static Date round(Date date, int field) {
  708. if (date == null) {
  709. throw new IllegalArgumentException("The date must not be null");
  710. }
  711. Calendar gval = Calendar.getInstance();
  712. gval.setTime(date);
  713. modify(gval, field, MODIFY_ROUND);
  714. return gval.getTime();
  715. }
  716.  
  717. /**
  718. * <p>Round this date, leaving the field specified as the most
  719. * significant field.</p>
  720. *
  721. * <p>For example, if you had the datetime of 28 Mar 2002
  722. * 13:45:01.231, if this was passed with HOUR, it would return
  723. * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
  724. * would return 1 April 2002 0:00:00.000.</p>
  725. *
  726. * <p>For a date in a timezone that handles the change to daylight
  727. * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
  728. * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
  729. * date that crosses this time would produce the following values:
  730. * <ul>
  731. * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
  732. * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
  733. * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
  734. * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
  735. * </ul>
  736. * </p>
  737. *
  738. * @param date the date to work with
  739. * @param field the field from <code>Calendar</code>
  740. * or <code>SEMI_MONTH</code>
  741. * @return the rounded date (a different object)
  742. * @throws IllegalArgumentException if the date is <code>null</code>
  743. * @throws ArithmeticException if the year is over 280 million
  744. */
  745. public static Calendar round(Calendar date, int field) {
  746. if (date == null) {
  747. throw new IllegalArgumentException("The date must not be null");
  748. }
  749. Calendar rounded = (Calendar) date.clone();
  750. modify(rounded, field, MODIFY_ROUND);
  751. return rounded;
  752. }
  753.  
  754. /**
  755. * <p>Round this date, leaving the field specified as the most
  756. * significant field.</p>
  757. *
  758. * <p>For example, if you had the datetime of 28 Mar 2002
  759. * 13:45:01.231, if this was passed with HOUR, it would return
  760. * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
  761. * would return 1 April 2002 0:00:00.000.</p>
  762. *
  763. * <p>For a date in a timezone that handles the change to daylight
  764. * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
  765. * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
  766. * date that crosses this time would produce the following values:
  767. * <ul>
  768. * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
  769. * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
  770. * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
  771. * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
  772. * </ul>
  773. * </p>
  774. *
  775. * @param date the date to work with, either Date or Calendar
  776. * @param field the field from <code>Calendar</code>
  777. * or <code>SEMI_MONTH</code>
  778. * @return the rounded date
  779. * @throws IllegalArgumentException if the date is <code>null</code>
  780. * @throws ClassCastException if the object type is not a <code>Date</code>
  781. * or <code>Calendar</code>
  782. * @throws ArithmeticException if the year is over 280 million
  783. */
  784. public static Date round(Object date, int field) {
  785. if (date == null) {
  786. throw new IllegalArgumentException("The date must not be null");
  787. }
  788. if (date instanceof Date) {
  789. return round((Date) date, field);
  790. } else if (date instanceof Calendar) {
  791. return round((Calendar) date, field).getTime();
  792. } else {
  793. throw new ClassCastException("Could not round " + date);
  794. }
  795. }
  796.  
  797. //-----------------------------------------------------------------------
  798. /**
  799. * <p>Truncate this date, leaving the field specified as the most
  800. * significant field.</p>
  801. *
  802. * <p>For example, if you had the datetime of 28 Mar 2002
  803. * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
  804. * 2002 13:00:00.000. If this was passed with MONTH, it would
  805. * return 1 Mar 2002 0:00:00.000.</p>
  806. *
  807. * @param date the date to work with
  808. * @param field the field from <code>Calendar</code>
  809. * or <code>SEMI_MONTH</code>
  810. * @return the rounded date
  811. * @throws IllegalArgumentException if the date is <code>null</code>
  812. * @throws ArithmeticException if the year is over 280 million
  813. */
  814. public static Date truncate(Date date, int field) {
  815. if (date == null) {
  816. throw new IllegalArgumentException("The date must not be null");
  817. }
  818. Calendar gval = Calendar.getInstance();
  819. gval.setTime(date);
  820. modify(gval, field, MODIFY_TRUNCATE);
  821. return gval.getTime();
  822. }
  823.  
  824. /**
  825. * <p>Truncate this date, leaving the field specified as the most
  826. * significant field.</p>
  827. *
  828. * <p>For example, if you had the datetime of 28 Mar 2002
  829. * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
  830. * 2002 13:00:00.000. If this was passed with MONTH, it would
  831. * return 1 Mar 2002 0:00:00.000.</p>
  832. *
  833. * @param date the date to work with
  834. * @param field the field from <code>Calendar</code>
  835. * or <code>SEMI_MONTH</code>
  836. * @return the rounded date (a different object)
  837. * @throws IllegalArgumentException if the date is <code>null</code>
  838. * @throws ArithmeticException if the year is over 280 million
  839. */
  840. public static Calendar truncate(Calendar date, int field) {
  841. if (date == null) {
  842. throw new IllegalArgumentException("The date must not be null");
  843. }
  844. Calendar truncated = (Calendar) date.clone();
  845. modify(truncated, field, MODIFY_TRUNCATE);
  846. return truncated;
  847. }
  848.  
  849. /**
  850. * <p>Truncate this date, leaving the field specified as the most
  851. * significant field.</p>
  852. *
  853. * <p>For example, if you had the datetime of 28 Mar 2002
  854. * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
  855. * 2002 13:00:00.000. If this was passed with MONTH, it would
  856. * return 1 Mar 2002 0:00:00.000.</p>
  857. *
  858. * @param date the date to work with, either <code>Date</code>
  859. * or <code>Calendar</code>
  860. * @param field the field from <code>Calendar</code>
  861. * or <code>SEMI_MONTH</code>
  862. * @return the rounded date
  863. * @throws IllegalArgumentException if the date
  864. * is <code>null</code>
  865. * @throws ClassCastException if the object type is not a
  866. * <code>Date</code> or <code>Calendar</code>
  867. * @throws ArithmeticException if the year is over 280 million
  868. */
  869. public static Date truncate(Object date, int field) {
  870. if (date == null) {
  871. throw new IllegalArgumentException("The date must not be null");
  872. }
  873. if (date instanceof Date) {
  874. return truncate((Date) date, field);
  875. } else if (date instanceof Calendar) {
  876. return truncate((Calendar) date, field).getTime();
  877. } else {
  878. throw new ClassCastException("Could not truncate " + date);
  879. }
  880. }
  881.  
  882. //-----------------------------------------------------------------------
  883. /**
  884. * <p>Ceil this date, leaving the field specified as the most
  885. * significant field.</p>
  886. *
  887. * <p>For example, if you had the datetime of 28 Mar 2002
  888. * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
  889. * 2002 13:00:00.000. If this was passed with MONTH, it would
  890. * return 1 Mar 2002 0:00:00.000.</p>
  891. *
  892. * @param date the date to work with
  893. * @param field the field from <code>Calendar</code>
  894. * or <code>SEMI_MONTH</code>
  895. * @return the rounded date
  896. * @throws IllegalArgumentException if the date is <code>null</code>
  897. * @throws ArithmeticException if the year is over 280 million
  898. * @since 2.5
  899. */
  900. public static Date ceiling(Date date, int field) {
  901. if (date == null) {
  902. throw new IllegalArgumentException("The date must not be null");
  903. }
  904. Calendar gval = Calendar.getInstance();
  905. gval.setTime(date);
  906. modify(gval, field, MODIFY_CEILING);
  907. return gval.getTime();
  908. }
  909.  
  910. /**
  911. * <p>Ceil this date, leaving the field specified as the most
  912. * significant field.</p>
  913. *
  914. * <p>For example, if you had the datetime of 28 Mar 2002
  915. * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
  916. * 2002 13:00:00.000. If this was passed with MONTH, it would
  917. * return 1 Mar 2002 0:00:00.000.</p>
  918. *
  919. * @param date the date to work with
  920. * @param field the field from <code>Calendar</code>
  921. * or <code>SEMI_MONTH</code>
  922. * @return the rounded date (a different object)
  923. * @throws IllegalArgumentException if the date is <code>null</code>
  924. * @throws ArithmeticException if the year is over 280 million
  925. * @since 2.5
  926. */
  927. public static Calendar ceiling(Calendar date, int field) {
  928. if (date == null) {
  929. throw new IllegalArgumentException("The date must not be null");
  930. }
  931. Calendar ceiled = (Calendar) date.clone();
  932. modify(ceiled, field, MODIFY_CEILING);
  933. return ceiled;
  934. }
  935.  
  936. /**
  937. * <p>Ceil this date, leaving the field specified as the most
  938. * significant field.</p>
  939. *
  940. * <p>For example, if you had the datetime of 28 Mar 2002
  941. * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
  942. * 2002 13:00:00.000. If this was passed with MONTH, it would
  943. * return 1 Mar 2002 0:00:00.000.</p>
  944. *
  945. * @param date the date to work with, either <code>Date</code>
  946. * or <code>Calendar</code>
  947. * @param field the field from <code>Calendar</code>
  948. * or <code>SEMI_MONTH</code>
  949. * @return the rounded date
  950. * @throws IllegalArgumentException if the date
  951. * is <code>null</code>
  952. * @throws ClassCastException if the object type is not a
  953. * <code>Date</code> or <code>Calendar</code>
  954. * @throws ArithmeticException if the year is over 280 million
  955. * @since 2.5
  956. */
  957. public static Date ceiling(Object date, int field) {
  958. if (date == null) {
  959. throw new IllegalArgumentException("The date must not be null");
  960. }
  961. if (date instanceof Date) {
  962. return ceiling((Date) date, field);
  963. } else if (date instanceof Calendar) {
  964. return ceiling((Calendar) date, field).getTime();
  965. } else {
  966. throw new ClassCastException("Could not find ceiling of for type: " + date.getClass());
  967. }
  968. }
  969.  
  970. //-----------------------------------------------------------------------
  971. /**
  972. * <p>Internal calculation method.</p>
  973. *
  974. * @param val the calendar
  975. * @param field the field constant
  976. * @param modType type to truncate, round or ceiling
  977. * @throws ArithmeticException if the year is over 280 million
  978. */
  979. private static void modify(Calendar val, int field, int modType) {
  980. if (val.get(Calendar.YEAR) > 280000000) {
  981. throw new ArithmeticException("Calendar value too large for accurate calculations");
  982. }
  983.  
  984. if (field == Calendar.MILLISECOND) {
  985. return;
  986. }
  987.  
  988. // ----------------- Fix for LANG-59 ---------------------- START ---------------
  989. // see http://issues.apache.org/jira/browse/LANG-59
  990. //
  991. // Manually truncate milliseconds, seconds and minutes, rather than using
  992. // Calendar methods.
  993.  
  994. Date date = val.getTime();
  995. long time = date.getTime();
  996. boolean done = false;
  997.  
  998. // truncate milliseconds
  999. int millisecs = val.get(Calendar.MILLISECOND);
  1000. if (MODIFY_TRUNCATE == modType || millisecs < 500) {
  1001. time = time - millisecs;
  1002. }
  1003. if (field == Calendar.SECOND) {
  1004. done = true;
  1005. }
  1006.  
  1007. // truncate seconds
  1008. int seconds = val.get(Calendar.SECOND);
  1009. if (!done && (MODIFY_TRUNCATE == modType || seconds < 30)) {
  1010. time = time - (seconds * 1000L);
  1011. }
  1012. if (field == Calendar.MINUTE) {
  1013. done = true;
  1014. }
  1015.  
  1016. // truncate minutes
  1017. int minutes = val.get(Calendar.MINUTE);
  1018. if (!done && (MODIFY_TRUNCATE == modType || minutes < 30)) {
  1019. time = time - (minutes * 60000L);
  1020. }
  1021.  
  1022. // reset time
  1023. if (date.getTime() != time) {
  1024. date.setTime(time);
  1025. val.setTime(date);
  1026. }
  1027. // ----------------- Fix for LANG-59 ----------------------- END ----------------
  1028.  
  1029. boolean roundUp = false;
  1030. for (int i = 0; i < fields.length; i++) {
  1031. for (int j = 0; j < fields[i].length; j++) {
  1032. if (fields[i][j] == field) {
  1033. //This is our field... we stop looping
  1034. if (modType == MODIFY_CEILING || (modType == MODIFY_ROUND && roundUp)) {
  1035. if (field == DateUtils.SEMI_MONTH) {
  1036. //This is a special case that's hard to generalize
  1037. //If the date is 1, we round up to 16, otherwise
  1038. // we subtract 15 days and add 1 month
  1039. if (val.get(Calendar.DATE) == 1) {
  1040. val.add(Calendar.DATE, 15);
  1041. } else {
  1042. val.add(Calendar.DATE, -15);
  1043. val.add(Calendar.MONTH, 1);
  1044. }
  1045. // ----------------- Fix for LANG-440 ---------------------- START ---------------
  1046. } else if (field == Calendar.AM_PM) {
  1047. // This is a special case
  1048. // If the time is 0, we round up to 12, otherwise
  1049. // we subtract 12 hours and add 1 day
  1050. if (val.get(Calendar.HOUR_OF_DAY) == 0) {
  1051. val.add(Calendar.HOUR_OF_DAY, 12);
  1052. } else {
  1053. val.add(Calendar.HOUR_OF_DAY, -12);
  1054. val.add(Calendar.DATE, 1);
  1055. }
  1056. // ----------------- Fix for LANG-440 ---------------------- END ---------------
  1057. } else {
  1058. //We need at add one to this field since the
  1059. // last number causes us to round up
  1060. val.add(fields[i][0], 1);
  1061. }
  1062. }
  1063. return;
  1064. }
  1065. }
  1066. //We have various fields that are not easy roundings
  1067. int offset = 0;
  1068. boolean offsetSet = false;
  1069. //These are special types of fields that require different rounding rules
  1070. switch (field) {
  1071. case DateUtils.SEMI_MONTH:
  1072. if (fields[i][0] == Calendar.DATE) {
  1073. //If we're going to drop the DATE field's value,
  1074. // we want to do this our own way.
  1075. //We need to subtrace 1 since the date has a minimum of 1
  1076. offset = val.get(Calendar.DATE) - 1;
  1077. //If we're above 15 days adjustment, that means we're in the
  1078. // bottom half of the month and should stay accordingly.
  1079. if (offset >= 15) {
  1080. offset -= 15;
  1081. }
  1082. //Record whether we're in the top or bottom half of that range
  1083. roundUp = offset > 7;
  1084. offsetSet = true;
  1085. }
  1086. break;
  1087. case Calendar.AM_PM:
  1088. if (fields[i][0] == Calendar.HOUR_OF_DAY) {
  1089. //If we're going to drop the HOUR field's value,
  1090. // we want to do this our own way.
  1091. offset = val.get(Calendar.HOUR_OF_DAY);
  1092. if (offset >= 12) {
  1093. offset -= 12;
  1094. }
  1095. roundUp = offset >= 6;
  1096. offsetSet = true;
  1097. }
  1098. break;
  1099. }
  1100. if (!offsetSet) {
  1101. int min = val.getActualMinimum(fields[i][0]);
  1102. int max = val.getActualMaximum(fields[i][0]);
  1103. //Calculate the offset from the minimum allowed value
  1104. offset = val.get(fields[i][0]) - min;
  1105. //Set roundUp if this is more than half way between the minimum and maximum
  1106. roundUp = offset > ((max - min) / 2);
  1107. }
  1108. //We need to remove this field
  1109. if (offset != 0) {
  1110. val.set(fields[i][0], val.get(fields[i][0]) - offset);
  1111. }
  1112. }
  1113. throw new IllegalArgumentException("The field " + field + " is not supported");
  1114.  
  1115. }
  1116.  
  1117. //-----------------------------------------------------------------------
  1118. /**
  1119. * <p>This constructs an <code>Iterator</code> over each day in a date
  1120. * range defined by a focus date and range style.</p>
  1121. *
  1122. * <p>For instance, passing Thursday, July 4, 2002 and a
  1123. * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
  1124. * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
  1125. * 2002, returning a Calendar instance for each intermediate day.</p>
  1126. *
  1127. * <p>This method provides an iterator that returns Calendar objects.
  1128. * The days are progressed using {@link Calendar#add(int, int)}.</p>
  1129. *
  1130. * @param focus the date to work with, not null
  1131. * @param rangeStyle the style constant to use. Must be one of
  1132. * {@link DateUtils#RANGE_MONTH_SUNDAY},
  1133. * {@link DateUtils#RANGE_MONTH_MONDAY},
  1134. * {@link DateUtils#RANGE_WEEK_SUNDAY},
  1135. * {@link DateUtils#RANGE_WEEK_MONDAY},
  1136. * {@link DateUtils#RANGE_WEEK_RELATIVE},
  1137. * {@link DateUtils#RANGE_WEEK_CENTER}
  1138. * @return the date iterator, which always returns Calendar instances
  1139. * @throws IllegalArgumentException if the date is <code>null</code>
  1140. * @throws IllegalArgumentException if the rangeStyle is invalid
  1141. */
  1142. public static Iterator iterator(Date focus, int rangeStyle) {
  1143. if (focus == null) {
  1144. throw new IllegalArgumentException("The date must not be null");
  1145. }
  1146. Calendar gval = Calendar.getInstance();
  1147. gval.setTime(focus);
  1148. return iterator(gval, rangeStyle);
  1149. }
  1150.  
  1151. /**
  1152. * <p>This constructs an <code>Iterator</code> over each day in a date
  1153. * range defined by a focus date and range style.</p>
  1154. *
  1155. * <p>For instance, passing Thursday, July 4, 2002 and a
  1156. * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
  1157. * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
  1158. * 2002, returning a Calendar instance for each intermediate day.</p>
  1159. *
  1160. * <p>This method provides an iterator that returns Calendar objects.
  1161. * The days are progressed using {@link Calendar#add(int, int)}.</p>
  1162. *
  1163. * @param focus the date to work with
  1164. * @param rangeStyle the style constant to use. Must be one of
  1165. * {@link DateUtils#RANGE_MONTH_SUNDAY},
  1166. * {@link DateUtils#RANGE_MONTH_MONDAY},
  1167. * {@link DateUtils#RANGE_WEEK_SUNDAY},
  1168. * {@link DateUtils#RANGE_WEEK_MONDAY},
  1169. * {@link DateUtils#RANGE_WEEK_RELATIVE},
  1170. * {@link DateUtils#RANGE_WEEK_CENTER}
  1171. * @return the date iterator
  1172. * @throws IllegalArgumentException if the date is <code>null</code>
  1173. * @throws IllegalArgumentException if the rangeStyle is invalid
  1174. */
  1175. public static Iterator iterator(Calendar focus, int rangeStyle) {
  1176. if (focus == null) {
  1177. throw new IllegalArgumentException("The date must not be null");
  1178. }
  1179. Calendar start = null;
  1180. Calendar end = null;
  1181. int startCutoff = Calendar.SUNDAY;
  1182. int endCutoff = Calendar.SATURDAY;
  1183. switch (rangeStyle) {
  1184. case RANGE_MONTH_SUNDAY:
  1185. case RANGE_MONTH_MONDAY:
  1186. //Set start to the first of the month
  1187. start = truncate(focus, Calendar.MONTH);
  1188. //Set end to the last of the month
  1189. end = (Calendar) start.clone();
  1190. end.add(Calendar.MONTH, 1);
  1191. end.add(Calendar.DATE, -1);
  1192. //Loop start back to the previous sunday or monday
  1193. if (rangeStyle == RANGE_MONTH_MONDAY) {
  1194. startCutoff = Calendar.MONDAY;
  1195. endCutoff = Calendar.SUNDAY;
  1196. }
  1197. break;
  1198. case RANGE_WEEK_SUNDAY:
  1199. case RANGE_WEEK_MONDAY:
  1200. case RANGE_WEEK_RELATIVE:
  1201. case RANGE_WEEK_CENTER:
  1202. //Set start and end to the current date
  1203. start = truncate(focus, Calendar.DATE);
  1204. end = truncate(focus, Calendar.DATE);
  1205. switch (rangeStyle) {
  1206. case RANGE_WEEK_SUNDAY:
  1207. //already set by default
  1208. break;
  1209. case RANGE_WEEK_MONDAY:
  1210. startCutoff = Calendar.MONDAY;
  1211. endCutoff = Calendar.SUNDAY;
  1212. break;
  1213. case RANGE_WEEK_RELATIVE:
  1214. startCutoff = focus.get(Calendar.DAY_OF_WEEK);
  1215. endCutoff = startCutoff - 1;
  1216. break;
  1217. case RANGE_WEEK_CENTER:
  1218. startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3;
  1219. endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3;
  1220. break;
  1221. }
  1222. break;
  1223. default:
  1224. throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid.");
  1225. }
  1226. if (startCutoff < Calendar.SUNDAY) {
  1227. startCutoff += 7;
  1228. }
  1229. if (startCutoff > Calendar.SATURDAY) {
  1230. startCutoff -= 7;
  1231. }
  1232. if (endCutoff < Calendar.SUNDAY) {
  1233. endCutoff += 7;
  1234. }
  1235. if (endCutoff > Calendar.SATURDAY) {
  1236. endCutoff -= 7;
  1237. }
  1238. while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) {
  1239. start.add(Calendar.DATE, -1);
  1240. }
  1241. while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) {
  1242. end.add(Calendar.DATE, 1);
  1243. }
  1244. return new DateIterator(start, end);
  1245. }
  1246.  
  1247. /**
  1248. * <p>This constructs an <code>Iterator</code> over each day in a date
  1249. * range defined by a focus date and range style.</p>
  1250. *
  1251. * <p>For instance, passing Thursday, July 4, 2002 and a
  1252. * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
  1253. * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
  1254. * 2002, returning a Calendar instance for each intermediate day.</p>
  1255. *
  1256. * @param focus the date to work with, either
  1257. * <code>Date</code> or <code>Calendar</code>
  1258. * @param rangeStyle the style constant to use. Must be one of the range
  1259. * styles listed for the {@link #iterator(Calendar, int)} method.
  1260. * @return the date iterator
  1261. * @throws IllegalArgumentException if the date
  1262. * is <code>null</code>
  1263. * @throws ClassCastException if the object type is
  1264. * not a <code>Date</code> or <code>Calendar</code>
  1265. */
  1266. public static Iterator iterator(Object focus, int rangeStyle) {
  1267. if (focus == null) {
  1268. throw new IllegalArgumentException("The date must not be null");
  1269. }
  1270. if (focus instanceof Date) {
  1271. return iterator((Date) focus, rangeStyle);
  1272. } else if (focus instanceof Calendar) {
  1273. return iterator((Calendar) focus, rangeStyle);
  1274. } else {
  1275. throw new ClassCastException("Could not iterate based on " + focus);
  1276. }
  1277. }
  1278.  
  1279. /**
  1280. * <p>Returns the number of milliseconds within the
  1281. * fragment. All datefields greater than the fragment will be ignored.</p>
  1282. *
  1283. * <p>Asking the milliseconds of any date will only return the number of milliseconds
  1284. * of the current second (resulting in a number between 0 and 999). This
  1285. * method will retrieve the number of milliseconds for any fragment.
  1286. * For example, if you want to calculate the number of milliseconds past today,
  1287. * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
  1288. * be all milliseconds of the past hour(s), minutes(s) and second(s).</p>
  1289. *
  1290. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1291. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1292. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1293. * A fragment less than or equal to a SECOND field will return 0.</p>
  1294. *
  1295. * <p>
  1296. * <ul>
  1297. * <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
  1298. * <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
  1299. * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)</li>
  1300. * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
  1301. * (a millisecond cannot be split in milliseconds)</li>
  1302. * </ul>
  1303. * </p>
  1304. *
  1305. * @param date the date to work with, not null
  1306. * @param fragment the Calendar field part of date to calculate
  1307. * @return number of milliseconds within the fragment of date
  1308. * @throws IllegalArgumentException if the date is <code>null</code> or
  1309. * fragment is not supported
  1310. * @since 2.4
  1311. */
  1312. public static long getFragmentInMilliseconds(Date date, int fragment) {
  1313. return getFragment(date, fragment, Calendar.MILLISECOND);
  1314. }
  1315.  
  1316. /**
  1317. * <p>Returns the number of seconds within the
  1318. * fragment. All datefields greater than the fragment will be ignored.</p>
  1319. *
  1320. * <p>Asking the seconds of any date will only return the number of seconds
  1321. * of the current minute (resulting in a number between 0 and 59). This
  1322. * method will retrieve the number of seconds for any fragment.
  1323. * For example, if you want to calculate the number of seconds past today,
  1324. * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
  1325. * be all seconds of the past hour(s) and minutes(s).</p>
  1326. *
  1327. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1328. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1329. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1330. * A fragment less than or equal to a SECOND field will return 0.</p>
  1331. *
  1332. * <p>
  1333. * <ul>
  1334. * <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
  1335. * (equivalent to deprecated date.getSeconds())</li>
  1336. * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
  1337. * (equivalent to deprecated date.getSeconds())</li>
  1338. * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
  1339. * (7*3600 + 15*60 + 10)</li>
  1340. * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
  1341. * (a millisecond cannot be split in seconds)</li>
  1342. * </ul>
  1343. * </p>
  1344. *
  1345. * @param date the date to work with, not null
  1346. * @param fragment the Calendar field part of date to calculate
  1347. * @return number of seconds within the fragment of date
  1348. * @throws IllegalArgumentException if the date is <code>null</code> or
  1349. * fragment is not supported
  1350. * @since 2.4
  1351. */
  1352. public static long getFragmentInSeconds(Date date, int fragment) {
  1353. return getFragment(date, fragment, Calendar.SECOND);
  1354. }
  1355.  
  1356. /**
  1357. * <p>Returns the number of minutes within the
  1358. * fragment. All datefields greater than the fragment will be ignored.</p>
  1359. *
  1360. * <p>Asking the minutes of any date will only return the number of minutes
  1361. * of the current hour (resulting in a number between 0 and 59). This
  1362. * method will retrieve the number of minutes for any fragment.
  1363. * For example, if you want to calculate the number of minutes past this month,
  1364. * your fragment is Calendar.MONTH. The result will be all minutes of the
  1365. * past day(s) and hour(s).</p>
  1366. *
  1367. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1368. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1369. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1370. * A fragment less than or equal to a MINUTE field will return 0.</p>
  1371. *
  1372. * <p>
  1373. * <ul>
  1374. * <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
  1375. * (equivalent to deprecated date.getMinutes())</li>
  1376. * <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
  1377. * (equivalent to deprecated date.getMinutes())</li>
  1378. * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li>
  1379. * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li>
  1380. * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
  1381. * (a millisecond cannot be split in minutes)</li>
  1382. * </ul>
  1383. * </p>
  1384. *
  1385. * @param date the date to work with, not null
  1386. * @param fragment the Calendar field part of date to calculate
  1387. * @return number of minutes within the fragment of date
  1388. * @throws IllegalArgumentException if the date is <code>null</code> or
  1389. * fragment is not supported
  1390. * @since 2.4
  1391. */
  1392. public static long getFragmentInMinutes(Date date, int fragment) {
  1393. return getFragment(date, fragment, Calendar.MINUTE);
  1394. }
  1395.  
  1396. /**
  1397. * <p>Returns the number of hours within the
  1398. * fragment. All datefields greater than the fragment will be ignored.</p>
  1399. *
  1400. * <p>Asking the hours of any date will only return the number of hours
  1401. * of the current day (resulting in a number between 0 and 23). This
  1402. * method will retrieve the number of hours for any fragment.
  1403. * For example, if you want to calculate the number of hours past this month,
  1404. * your fragment is Calendar.MONTH. The result will be all hours of the
  1405. * past day(s).</p>
  1406. *
  1407. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1408. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1409. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1410. * A fragment less than or equal to a HOUR field will return 0.</p>
  1411. *
  1412. * <p>
  1413. * <ul>
  1414. * <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
  1415. * (equivalent to deprecated date.getHours())</li>
  1416. * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
  1417. * (equivalent to deprecated date.getHours())</li>
  1418. * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
  1419. * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
  1420. * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
  1421. * (a millisecond cannot be split in hours)</li>
  1422. * </ul>
  1423. * </p>
  1424. *
  1425. * @param date the date to work with, not null
  1426. * @param fragment the Calendar field part of date to calculate
  1427. * @return number of hours within the fragment of date
  1428. * @throws IllegalArgumentException if the date is <code>null</code> or
  1429. * fragment is not supported
  1430. * @since 2.4
  1431. */
  1432. public static long getFragmentInHours(Date date, int fragment) {
  1433. return getFragment(date, fragment, Calendar.HOUR_OF_DAY);
  1434. }
  1435.  
  1436. /**
  1437. * <p>Returns the number of days within the
  1438. * fragment. All datefields greater than the fragment will be ignored.</p>
  1439. *
  1440. * <p>Asking the days of any date will only return the number of days
  1441. * of the current month (resulting in a number between 1 and 31). This
  1442. * method will retrieve the number of days for any fragment.
  1443. * For example, if you want to calculate the number of days past this year,
  1444. * your fragment is Calendar.YEAR. The result will be all days of the
  1445. * past month(s).</p>
  1446. *
  1447. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1448. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1449. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1450. * A fragment less than or equal to a DAY field will return 0.</p>
  1451. *
  1452. * <p>
  1453. * <ul>
  1454. * <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
  1455. * (equivalent to deprecated date.getDay())</li>
  1456. * <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
  1457. * (equivalent to deprecated date.getDay())</li>
  1458. * <li>January 28, 2008 with Calendar.YEAR as fragment will return 28</li>
  1459. * <li>February 28, 2008 with Calendar.YEAR as fragment will return 59</li>
  1460. * <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
  1461. * (a millisecond cannot be split in days)</li>
  1462. * </ul>
  1463. * </p>
  1464. *
  1465. * @param date the date to work with, not null
  1466. * @param fragment the Calendar field part of date to calculate
  1467. * @return number of days within the fragment of date
  1468. * @throws IllegalArgumentException if the date is <code>null</code> or
  1469. * fragment is not supported
  1470. * @since 2.4
  1471. */
  1472. public static long getFragmentInDays(Date date, int fragment) {
  1473. return getFragment(date, fragment, Calendar.DAY_OF_YEAR);
  1474. }
  1475.  
  1476. /**
  1477. * <p>Returns the number of milliseconds within the
  1478. * fragment. All datefields greater than the fragment will be ignored.</p>
  1479. *
  1480. * <p>Asking the milliseconds of any date will only return the number of milliseconds
  1481. * of the current second (resulting in a number between 0 and 999). This
  1482. * method will retrieve the number of milliseconds for any fragment.
  1483. * For example, if you want to calculate the number of seconds past today,
  1484. * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
  1485. * be all seconds of the past hour(s), minutes(s) and second(s).</p>
  1486. *
  1487. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1488. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1489. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1490. * A fragment less than or equal to a MILLISECOND field will return 0.</p>
  1491. *
  1492. * <p>
  1493. * <ul>
  1494. * <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  1495. * (equivalent to calendar.get(Calendar.MILLISECOND))</li>
  1496. * <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  1497. * (equivalent to calendar.get(Calendar.MILLISECOND))</li>
  1498. * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538
  1499. * (10*1000 + 538)</li>
  1500. * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
  1501. * (a millisecond cannot be split in milliseconds)</li>
  1502. * </ul>
  1503. * </p>
  1504. *
  1505. * @param calendar the calendar to work with, not null
  1506. * @param fragment the Calendar field part of calendar to calculate
  1507. * @return number of milliseconds within the fragment of date
  1508. * @throws IllegalArgumentException if the date is <code>null</code> or
  1509. * fragment is not supported
  1510. * @since 2.4
  1511. */
  1512. public static long getFragmentInMilliseconds(Calendar calendar, int fragment) {
  1513. return getFragment(calendar, fragment, Calendar.MILLISECOND);
  1514. }
  1515. /**
  1516. * <p>Returns the number of seconds within the
  1517. * fragment. All datefields greater than the fragment will be ignored.</p>
  1518. *
  1519. * <p>Asking the seconds of any date will only return the number of seconds
  1520. * of the current minute (resulting in a number between 0 and 59). This
  1521. * method will retrieve the number of seconds for any fragment.
  1522. * For example, if you want to calculate the number of seconds past today,
  1523. * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
  1524. * be all seconds of the past hour(s) and minutes(s).</p>
  1525. *
  1526. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1527. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1528. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1529. * A fragment less than or equal to a SECOND field will return 0.</p>
  1530. *
  1531. * <p>
  1532. * <ul>
  1533. * <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
  1534. * (equivalent to calendar.get(Calendar.SECOND))</li>
  1535. * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
  1536. * (equivalent to calendar.get(Calendar.SECOND))</li>
  1537. * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
  1538. * (7*3600 + 15*60 + 10)</li>
  1539. * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
  1540. * (a millisecond cannot be split in seconds)</li>
  1541. * </ul>
  1542. * </p>
  1543. *
  1544. * @param calendar the calendar to work with, not null
  1545. * @param fragment the Calendar field part of calendar to calculate
  1546. * @return number of seconds within the fragment of date
  1547. * @throws IllegalArgumentException if the date is <code>null</code> or
  1548. * fragment is not supported
  1549. * @since 2.4
  1550. */
  1551. public static long getFragmentInSeconds(Calendar calendar, int fragment) {
  1552. return getFragment(calendar, fragment, Calendar.SECOND);
  1553. }
  1554.  
  1555. /**
  1556. * <p>Returns the number of minutes within the
  1557. * fragment. All datefields greater than the fragment will be ignored.</p>
  1558. *
  1559. * <p>Asking the minutes of any date will only return the number of minutes
  1560. * of the current hour (resulting in a number between 0 and 59). This
  1561. * method will retrieve the number of minutes for any fragment.
  1562. * For example, if you want to calculate the number of minutes past this month,
  1563. * your fragment is Calendar.MONTH. The result will be all minutes of the
  1564. * past day(s) and hour(s).</p>
  1565. *
  1566. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1567. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1568. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1569. * A fragment less than or equal to a MINUTE field will return 0.</p>
  1570. *
  1571. * <p>
  1572. * <ul>
  1573. * <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
  1574. * (equivalent to calendar.get(Calendar.MINUTES))</li>
  1575. * <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
  1576. * (equivalent to calendar.get(Calendar.MINUTES))</li>
  1577. * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li>
  1578. * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li>
  1579. * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
  1580. * (a millisecond cannot be split in minutes)</li>
  1581. * </ul>
  1582. * </p>
  1583. *
  1584. * @param calendar the calendar to work with, not null
  1585. * @param fragment the Calendar field part of calendar to calculate
  1586. * @return number of minutes within the fragment of date
  1587. * @throws IllegalArgumentException if the date is <code>null</code> or
  1588. * fragment is not supported
  1589. * @since 2.4
  1590. */
  1591. public static long getFragmentInMinutes(Calendar calendar, int fragment) {
  1592. return getFragment(calendar, fragment, Calendar.MINUTE);
  1593. }
  1594.  
  1595. /**
  1596. * <p>Returns the number of hours within the
  1597. * fragment. All datefields greater than the fragment will be ignored.</p>
  1598. *
  1599. * <p>Asking the hours of any date will only return the number of hours
  1600. * of the current day (resulting in a number between 0 and 23). This
  1601. * method will retrieve the number of hours for any fragment.
  1602. * For example, if you want to calculate the number of hours past this month,
  1603. * your fragment is Calendar.MONTH. The result will be all hours of the
  1604. * past day(s).</p>
  1605. *
  1606. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1607. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1608. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1609. * A fragment less than or equal to a HOUR field will return 0.</p>
  1610. *
  1611. * <p>
  1612. * <ul>
  1613. * <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
  1614. * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
  1615. * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
  1616. * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
  1617. * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
  1618. * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
  1619. * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
  1620. * (a millisecond cannot be split in hours)</li>
  1621. * </ul>
  1622. * </p>
  1623. *
  1624. * @param calendar the calendar to work with, not null
  1625. * @param fragment the Calendar field part of calendar to calculate
  1626. * @return number of hours within the fragment of date
  1627. * @throws IllegalArgumentException if the date is <code>null</code> or
  1628. * fragment is not supported
  1629. * @since 2.4
  1630. */
  1631. public static long getFragmentInHours(Calendar calendar, int fragment) {
  1632. return getFragment(calendar, fragment, Calendar.HOUR_OF_DAY);
  1633. }
  1634.  
  1635. /**
  1636. * <p>Returns the number of days within the
  1637. * fragment. All datefields greater than the fragment will be ignored.</p>
  1638. *
  1639. * <p>Asking the days of any date will only return the number of days
  1640. * of the current month (resulting in a number between 1 and 31). This
  1641. * method will retrieve the number of days for any fragment.
  1642. * For example, if you want to calculate the number of days past this year,
  1643. * your fragment is Calendar.YEAR. The result will be all days of the
  1644. * past month(s).</p>
  1645. *
  1646. * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
  1647. * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
  1648. * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
  1649. * A fragment less than or equal to a DAY field will return 0.</p>
  1650. *
  1651. * <p>
  1652. * <ul>
  1653. * <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
  1654. * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
  1655. * <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
  1656. * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
  1657. * <li>January 28, 2008 with Calendar.YEAR as fragment will return 28
  1658. * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
  1659. * <li>February 28, 2008 with Calendar.YEAR as fragment will return 59
  1660. * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
  1661. * <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
  1662. * (a millisecond cannot be split in days)</li>
  1663. * </ul>
  1664. * </p>
  1665. *
  1666. * @param calendar the calendar to work with, not null
  1667. * @param fragment the Calendar field part of calendar to calculate
  1668. * @return number of days within the fragment of date
  1669. * @throws IllegalArgumentException if the date is <code>null</code> or
  1670. * fragment is not supported
  1671. * @since 2.4
  1672. */
  1673. public static long getFragmentInDays(Calendar calendar, int fragment) {
  1674. return getFragment(calendar, fragment, Calendar.DAY_OF_YEAR);
  1675. }
  1676.  
  1677. /**
  1678. * Date-version for fragment-calculation in any unit
  1679. *
  1680. * @param date the date to work with, not null
  1681. * @param fragment the Calendar field part of date to calculate
  1682. * @param unit Calendar field defining the unit
  1683. * @return number of units within the fragment of the date
  1684. * @throws IllegalArgumentException if the date is <code>null</code> or
  1685. * fragment is not supported
  1686. * @since 2.4
  1687. */
  1688. private static long getFragment(Date date, int fragment, int unit) {
  1689. if(date == null) {
  1690. throw new IllegalArgumentException("The date must not be null");
  1691. }
  1692. Calendar calendar = Calendar.getInstance();
  1693. calendar.setTime(date);
  1694. return getFragment(calendar, fragment, unit);
  1695. }
  1696.  
  1697. /**
  1698. * Calendar-version for fragment-calculation in any unit
  1699. *
  1700. * @param calendar the calendar to work with, not null
  1701. * @param fragment the Calendar field part of calendar to calculate
  1702. * @param unit Calendar field defining the unit
  1703. * @return number of units within the fragment of the calendar
  1704. * @throws IllegalArgumentException if the date is <code>null</code> or
  1705. * fragment is not supported
  1706. * @since 2.4
  1707. */
  1708. private static long getFragment(Calendar calendar, int fragment, int unit) {
  1709. if(calendar == null) {
  1710. throw new IllegalArgumentException("The date must not be null");
  1711. }
  1712. long millisPerUnit = getMillisPerUnit(unit);
  1713. long result = 0;
  1714.  
  1715. // Fragments bigger than a day require a breakdown to days
  1716. switch (fragment) {
  1717. case Calendar.YEAR:
  1718. result += (calendar.get(Calendar.DAY_OF_YEAR) * MILLIS_PER_DAY) / millisPerUnit;
  1719. break;
  1720. case Calendar.MONTH:
  1721. result += (calendar.get(Calendar.DAY_OF_MONTH) * MILLIS_PER_DAY) / millisPerUnit;
  1722. break;
  1723. }
  1724.  
  1725. switch (fragment) {
  1726. // Number of days already calculated for these cases
  1727. case Calendar.YEAR:
  1728. case Calendar.MONTH:
  1729.  
  1730. // The rest of the valid cases
  1731. case Calendar.DAY_OF_YEAR:
  1732. case Calendar.DATE:
  1733. result += (calendar.get(Calendar.HOUR_OF_DAY) * MILLIS_PER_HOUR) / millisPerUnit;
  1734. //$FALL-THROUGH$
  1735. case Calendar.HOUR_OF_DAY:
  1736. result += (calendar.get(Calendar.MINUTE) * MILLIS_PER_MINUTE) / millisPerUnit;
  1737. //$FALL-THROUGH$
  1738. case Calendar.MINUTE:
  1739. result += (calendar.get(Calendar.SECOND) * MILLIS_PER_SECOND) / millisPerUnit;
  1740. //$FALL-THROUGH$
  1741. case Calendar.SECOND:
  1742. result += (calendar.get(Calendar.MILLISECOND) * 1) / millisPerUnit;
  1743. break;
  1744. case Calendar.MILLISECOND:
  1745. break;//never useful
  1746. default:
  1747. throw new IllegalArgumentException("The fragment " + fragment + " is not supported");
  1748. }
  1749. return result;
  1750. }
  1751.  
  1752. /**
  1753. * Determines if two calendars are equal up to no more than the specified
  1754. * most significant field.
  1755. *
  1756. * @param cal1 the first calendar, not <code>null</code>
  1757. * @param cal2 the second calendar, not <code>null</code>
  1758. * @param field the field from <code>Calendar</code>
  1759. * @return <code>true</code> if equal; otherwise <code>false</code>
  1760. * @throws IllegalArgumentException if any argument is <code>null</code>
  1761. * @see #truncate(Calendar, int)
  1762. * @see #truncatedEquals(Date, Date, int)
  1763. * @since 2.6
  1764. */
  1765. public static boolean truncatedEquals(Calendar cal1, Calendar cal2, int field) {
  1766. return truncatedCompareTo(cal1, cal2, field) == 0;
  1767. }
  1768.  
  1769. /**
  1770. * Determines if two dates are equal up to no more than the specified
  1771. * most significant field.
  1772. *
  1773. * @param date1 the first date, not <code>null</code>
  1774. * @param date2 the second date, not <code>null</code>
  1775. * @param field the field from <code>Calendar</code>
  1776. * @return <code>true</code> if equal; otherwise <code>false</code>
  1777. * @throws IllegalArgumentException if any argument is <code>null</code>
  1778. * @see #truncate(Date, int)
  1779. * @see #truncatedEquals(Calendar, Calendar, int)
  1780. * @since 2.6
  1781. */
  1782. public static boolean truncatedEquals(Date date1, Date date2, int field) {
  1783. return truncatedCompareTo(date1, date2, field) == 0;
  1784. }
  1785.  
  1786. /**
  1787. * Determines how two calendars compare up to no more than the specified
  1788. * most significant field.
  1789. *
  1790. * @param cal1 the first calendar, not <code>null</code>
  1791. * @param cal2 the second calendar, not <code>null</code>
  1792. * @param field the field from <code>Calendar</code>
  1793. * @return a negative integer, zero, or a positive integer as the first
  1794. * calendar is less than, equal to, or greater than the second.
  1795. * @throws IllegalArgumentException if any argument is <code>null</code>
  1796. * @see #truncate(Calendar, int)
  1797. * @see #truncatedCompareTo(Date, Date, int)
  1798. * @since 2.6
  1799. */
  1800. public static int truncatedCompareTo(Calendar cal1, Calendar cal2, int field) {
  1801. Calendar truncatedCal1 = truncate(cal1, field);
  1802. Calendar truncatedCal2 = truncate(cal2, field);
  1803. return truncatedCal1.getTime().compareTo(truncatedCal2.getTime());
  1804. }
  1805.  
  1806. /**
  1807. * Determines how two dates compare up to no more than the specified
  1808. * most significant field.
  1809. *
  1810. * @param date1 the first date, not <code>null</code>
  1811. * @param date2 the second date, not <code>null</code>
  1812. * @param field the field from <code>Calendar</code>
  1813. * @return a negative integer, zero, or a positive integer as the first
  1814. * date is less than, equal to, or greater than the second.
  1815. * @throws IllegalArgumentException if any argument is <code>null</code>
  1816. * @see #truncate(Calendar, int)
  1817. * @see #truncatedCompareTo(Date, Date, int)
  1818. * @since 2.6
  1819. */
  1820. public static int truncatedCompareTo(Date date1, Date date2, int field) {
  1821. Date truncatedDate1 = truncate(date1, field);
  1822. Date truncatedDate2 = truncate(date2, field);
  1823. return truncatedDate1.compareTo(truncatedDate2);
  1824. }
  1825.  
  1826. /**
  1827. * Returns the number of millis of a datefield, if this is a constant value
  1828. *
  1829. * @param unit A Calendar field which is a valid unit for a fragment
  1830. * @return number of millis
  1831. * @throws IllegalArgumentException if date can't be represented in millisenconds
  1832. * @since 2.4
  1833. */
  1834. private static long getMillisPerUnit(int unit) {
  1835. long result = Long.MAX_VALUE;
  1836. switch (unit) {
  1837. case Calendar.DAY_OF_YEAR:
  1838. case Calendar.DATE:
  1839. result = MILLIS_PER_DAY;
  1840. break;
  1841. case Calendar.HOUR_OF_DAY:
  1842. result = MILLIS_PER_HOUR;
  1843. break;
  1844. case Calendar.MINUTE:
  1845. result = MILLIS_PER_MINUTE;
  1846. break;
  1847. case Calendar.SECOND:
  1848. result = MILLIS_PER_SECOND;
  1849. break;
  1850. case Calendar.MILLISECOND:
  1851. result = 1;
  1852. break;
  1853. default: throw new IllegalArgumentException("The unit " + unit + " cannot be represented is milleseconds");
  1854. }
  1855. return result;
  1856. }
  1857.  
  1858. /**
  1859. * <p>Date iterator.</p>
  1860. */
  1861. static class DateIterator implements Iterator {
  1862. private final Calendar endFinal;
  1863. private final Calendar spot;
  1864.  
  1865. /**
  1866. * Constructs a DateIterator that ranges from one date to another.
  1867. *
  1868. * @param startFinal start date (inclusive)
  1869. * @param endFinal end date (not inclusive)
  1870. */
  1871. DateIterator(Calendar startFinal, Calendar endFinal) {
  1872. super();
  1873. this.endFinal = endFinal;
  1874. spot = startFinal;
  1875. spot.add(Calendar.DATE, -1);
  1876. }
  1877.  
  1878. /**
  1879. * Has the iterator not reached the end date yet?
  1880. *
  1881. * @return <code>true</code> if the iterator has yet to reach the end date
  1882. */
  1883. public boolean hasNext() {
  1884. return spot.before(endFinal);
  1885. }
  1886.  
  1887. /**
  1888. * Return the next calendar in the iteration
  1889. *
  1890. * @return Object calendar for the next date
  1891. */
  1892. public Object next() {
  1893. if (spot.equals(endFinal)) {
  1894. throw new NoSuchElementException();
  1895. }
  1896. spot.add(Calendar.DATE, 1);
  1897. return spot.clone();
  1898. }
  1899.  
  1900. /**
  1901. * Always throws UnsupportedOperationException.
  1902. *
  1903. * @throws UnsupportedOperationException
  1904. * @see java.util.Iterator#remove()
  1905. */
  1906. public void remove() {
  1907. throw new UnsupportedOperationException();
  1908. }
  1909. }
  1910.  
  1911. //-------------------------------------------------------------------------
  1912. // Deprecated int constants
  1913. // TODO: Remove in 3.0
  1914.  
  1915. /**
  1916. * Number of milliseconds in a standard second.
  1917. *
  1918. * @deprecated Use MILLIS_PER_SECOND. This will be removed in Commons Lang 3.0.
  1919. */
  1920. public static final int MILLIS_IN_SECOND = 1000;
  1921. /**
  1922. * Number of milliseconds in a standard minute.
  1923. *
  1924. * @deprecated Use MILLIS_PER_MINUTE. This will be removed in Commons Lang 3.0.
  1925. */
  1926. public static final int MILLIS_IN_MINUTE = 60 * 1000;
  1927. /**
  1928. * Number of milliseconds in a standard hour.
  1929. *
  1930. * @deprecated Use MILLIS_PER_HOUR. This will be removed in Commons Lang 3.0.
  1931. */
  1932. public static final int MILLIS_IN_HOUR = 60 * 60 * 1000;
  1933. /**
  1934. * Number of milliseconds in a standard day.
  1935. *
  1936. * @deprecated Use MILLIS_PER_DAY. This will be removed in Commons Lang 3.0.
  1937. */
  1938. public static final int MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
  1939.  
  1940. }

spring DateUtils的更多相关文章

  1. Spring类型转换 ConversionSerivce Convertor

    以String转Date为例:   定义转换器:    import java.text.ParseException; import java.util.Date; import org.apach ...

  2. 基于spring+quartz的分布式定时任务框架

    问题背景 我公司是一个快速发展的创业公司,目前有200人,主要业务是旅游和酒店相关的,应用迭代更新周期比较快,因此,开发人员花费了更多的时间去更=跟上迭代的步伐,而缺乏了对整个系统的把控 没有集群之前 ...

  3. Maven+Spring+Hibernate+Shiro+Mysql简单的demo框架(一)

    相关的maven的 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="ht ...

  4. Quartz Scheduler(2.2.1) - Integration with Spring

    1. maven 依赖: <properties> <spring.version>3.2.3.RELEASE</spring.version> <quart ...

  5. 使用Spring AOP预处理Controller的参数

    实际编程中,可能会有这样一种情况,前台传过来的参数,我们需要一定的处理才能使用,比如有这样一个Controller @Controller public class MatchOddsControll ...

  6. 基于Spring的异步系统实现方案

    一般的实现方案 发送异步消息所使用的工具类: import java.util.Date; import javax.jms.Destination; import javax.jms.JMSExce ...

  7. Spring Data Jpa+SpringMVC+Jquery.pagination.js实现分页

    本博客介绍基于Spring Data这款orm框架加上Jquery.pagination插件实现的分页功能. 介绍一下Spring Data框架 spring Data : Spring 的一个子项目 ...

  8. 使用Spring Boot搭建应用开发框架(一) —— 基础架构

    Spring的简史 第一阶段:XML配置,在Spring1.x时代,使用Spring开发满眼都是xml配置的Bean,随着项目的扩大,我们需要把xml配置文件分放到不同的配置文件里,那时候需要频繁的在 ...

  9. Spring mvc学习指南

    使用flash attribute(闪存传值) 在配置文件中添加<mvc:annotion-driven/> 在controller方法参数里面添加RedirectAttributes r ...

随机推荐

  1. poll和select

    都允许进程决定是否可以对一个或者多个打开的文件做非阻塞的读取或写入.这些调用也会阻塞进程,直到给定的文件描述符集合中的任何一个可读取或写入.常常用于那些要使用多个输入或输出流而又不会阻塞与其中任意一个 ...

  2. LoadRunner参数化

    在场景中,每一个vuser能够按照取唯一值的策略,是unique one , 出现84800错误有以下2种(自我实验中得出) 1.vuser的个数大于参数给定的个数 2.vuser初始时间不够,在可通 ...

  3. 留言本,keyCode

    <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...

  4. lucene 查询 (转载)

    原网址:http://hi.baidu.com/lszhuhaichao/blog/item/ccffc7cb858f1514bf09e66f.html Lucene3.0之查询处理(1):原理201 ...

  5. dos命令创建安卓签名

    1.dos下进入到jdk安装目录的bin目录, 如:C:\Program Files\Java\jdk1.7.0_79\bin 2.输入命令格式如: keytool -genkey -alias aa ...

  6. 基于VirtualBox 安装和配置Fuel OpenStack(V6.1)

    1.环境准备 准备一台内存较大的主机,12G以上 下载安装VirtualBox及其匹配的扩展包 virtualbox: http://download.virtualbox.org/virtualbo ...

  7. ONES 安装、配置以及初始化配置

    环境依赖 bower composer php 5.5.9+ mysql 5.6.5+ PHP和MySQL版本均为最低要求版本,安装前请先确认. 通过CLI安装 $ git clone http:// ...

  8. jq的遍历节点

    1.child()方法 该方法用于取得匹配元素的子元素集合 2.next() 该方法用于取得匹配元素后面紧邻的同辈元素, 3.prev() 该方法用于取得匹配元素前面紧邻的同辈元素 4.sibling ...

  9. angular 实现modal windows效果(即模态窗口,半透明的遮罩层),以及bootstrap(css,components,js)的初步学习

    废话不说,直接上代码.可直接看效果,对着分析..今天算是bootstrap 入门了,开心.. 突然居然很多事情就是那样,不要太多的畏惧,迈出第一步其实就成功了一半了. <html ng-app= ...

  10. [转]HTTPS连接的前几毫秒发生了什么

    本文由 伯乐在线 - 水果泡腾片 翻译.未经许可,禁止转载!英文出处:JEFF MOSER.欢迎加入翻译小组. 提示:英文原文写于2009年,当时的Firefox和最新版的Firefox,界面也有很大 ...