1 /* 2 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * * Neither the name of JSR-310 nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.threeten.bp.temporal; 33 34 import static org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH; 35 import static org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK; 36 import static org.threeten.bp.temporal.ChronoField.DAY_OF_YEAR; 37 import static org.threeten.bp.temporal.ChronoUnit.DAYS; 38 import static org.threeten.bp.temporal.ChronoUnit.MONTHS; 39 import static org.threeten.bp.temporal.ChronoUnit.YEARS; 40 41 import org.threeten.bp.DayOfWeek; 42 import org.threeten.bp.jdk8.Jdk8Methods; 43 44 /** 45 * Common implementations of {@code TemporalAdjuster}. 46 * <p> 47 * This class provides common implementations of {@link TemporalAdjuster}. 48 * They are especially useful to document the intent of business logic and 49 * often link well to requirements. 50 * For example, these two pieces of code do the same thing, but the second 51 * one is clearer (assuming that there is a static import of this class): 52 * <pre> 53 * // direct manipulation 54 * date.withDayOfMonth(1).plusMonths(1).minusDays(1); 55 * // use of an adjuster from this class 56 * date.with(lastDayOfMonth()); 57 * </pre> 58 * There are two equivalent ways of using a {@code TemporalAdjuster}. 59 * The first is to invoke the method on the interface directly. 60 * The second is to use {@link Temporal#with(TemporalAdjuster)}: 61 * <pre> 62 * // these two lines are equivalent, but the second approach is recommended 63 * dateTime = adjuster.adjustInto(dateTime); 64 * dateTime = dateTime.with(adjuster); 65 * </pre> 66 * It is recommended to use the second approach, {@code with(TemporalAdjuster)}, 67 * as it is a lot clearer to read in code. 68 * 69 * <h3>Specification for implementors</h3> 70 * This is a thread-safe utility class. 71 * All returned adjusters are immutable and thread-safe. 72 * <p> 73 * The JDK 8 ofDateAdjuster(UnaryOperator) method is not backported. 74 */ 75 public final class TemporalAdjusters { 76 77 /** 78 * Private constructor since this is a utility class. 79 */ TemporalAdjusters()80 private TemporalAdjusters() { 81 } 82 83 //----------------------------------------------------------------------- 84 /** 85 * Returns the "first day of month" adjuster, which returns a new date set to 86 * the first day of the current month. 87 * <p> 88 * The ISO calendar system behaves as follows:<br> 89 * The input 2011-01-15 will return 2011-01-01.<br> 90 * The input 2011-02-15 will return 2011-02-01. 91 * <p> 92 * The behavior is suitable for use with most calendar systems. 93 * It is equivalent to: 94 * <pre> 95 * temporal.with(DAY_OF_MONTH, 1); 96 * </pre> 97 * 98 * @return the first day-of-month adjuster, not null 99 */ firstDayOfMonth()100 public static TemporalAdjuster firstDayOfMonth() { 101 return Impl.FIRST_DAY_OF_MONTH; 102 } 103 104 /** 105 * Returns the "last day of month" adjuster, which returns a new date set to 106 * the last day of the current month. 107 * <p> 108 * The ISO calendar system behaves as follows:<br> 109 * The input 2011-01-15 will return 2011-01-31.<br> 110 * The input 2011-02-15 will return 2011-02-28.<br> 111 * The input 2012-02-15 will return 2012-02-29 (leap year).<br> 112 * The input 2011-04-15 will return 2011-04-30. 113 * <p> 114 * The behavior is suitable for use with most calendar systems. 115 * It is equivalent to: 116 * <pre> 117 * long lastDay = temporal.range(DAY_OF_MONTH).getMaximum(); 118 * temporal.with(DAY_OF_MONTH, lastDay); 119 * </pre> 120 * 121 * @return the last day-of-month adjuster, not null 122 */ lastDayOfMonth()123 public static TemporalAdjuster lastDayOfMonth() { 124 return Impl.LAST_DAY_OF_MONTH; 125 } 126 127 /** 128 * Returns the "first day of next month" adjuster, which returns a new date set to 129 * the first day of the next month. 130 * <p> 131 * The ISO calendar system behaves as follows:<br> 132 * The input 2011-01-15 will return 2011-02-01.<br> 133 * The input 2011-02-15 will return 2011-03-01. 134 * <p> 135 * The behavior is suitable for use with most calendar systems. 136 * It is equivalent to: 137 * <pre> 138 * temporal.with(DAY_OF_MONTH, 1).plus(1, MONTHS); 139 * </pre> 140 * 141 * @return the first day of next month adjuster, not null 142 */ firstDayOfNextMonth()143 public static TemporalAdjuster firstDayOfNextMonth() { 144 return Impl.FIRST_DAY_OF_NEXT_MONTH; 145 } 146 147 //----------------------------------------------------------------------- 148 /** 149 * Returns the "first day of year" adjuster, which returns a new date set to 150 * the first day of the current year. 151 * <p> 152 * The ISO calendar system behaves as follows:<br> 153 * The input 2011-01-15 will return 2011-01-01.<br> 154 * The input 2011-02-15 will return 2011-01-01.<br> 155 * <p> 156 * The behavior is suitable for use with most calendar systems. 157 * It is equivalent to: 158 * <pre> 159 * temporal.with(DAY_OF_YEAR, 1); 160 * </pre> 161 * 162 * @return the first day-of-year adjuster, not null 163 */ firstDayOfYear()164 public static TemporalAdjuster firstDayOfYear() { 165 return Impl.FIRST_DAY_OF_YEAR; 166 } 167 168 /** 169 * Returns the "last day of year" adjuster, which returns a new date set to 170 * the last day of the current year. 171 * <p> 172 * The ISO calendar system behaves as follows:<br> 173 * The input 2011-01-15 will return 2011-12-31.<br> 174 * The input 2011-02-15 will return 2011-12-31.<br> 175 * <p> 176 * The behavior is suitable for use with most calendar systems. 177 * It is equivalent to: 178 * <pre> 179 * long lastDay = temporal.range(DAY_OF_YEAR).getMaximum(); 180 * temporal.with(DAY_OF_YEAR, lastDay); 181 * </pre> 182 * 183 * @return the last day-of-year adjuster, not null 184 */ lastDayOfYear()185 public static TemporalAdjuster lastDayOfYear() { 186 return Impl.LAST_DAY_OF_YEAR; 187 } 188 189 /** 190 * Returns the "first day of next year" adjuster, which returns a new date set to 191 * the first day of the next year. 192 * <p> 193 * The ISO calendar system behaves as follows:<br> 194 * The input 2011-01-15 will return 2012-01-01. 195 * <p> 196 * The behavior is suitable for use with most calendar systems. 197 * It is equivalent to: 198 * <pre> 199 * temporal.with(DAY_OF_YEAR, 1).plus(1, YEARS); 200 * </pre> 201 * 202 * @return the first day of next month adjuster, not null 203 */ firstDayOfNextYear()204 public static TemporalAdjuster firstDayOfNextYear() { 205 return Impl.FIRST_DAY_OF_NEXT_YEAR; 206 } 207 208 //----------------------------------------------------------------------- 209 /** 210 * Enum implementing the adjusters. 211 */ 212 private static class Impl implements TemporalAdjuster { 213 /** First day of month adjuster. */ 214 private static final Impl FIRST_DAY_OF_MONTH = new Impl(0); 215 /** Last day of month adjuster. */ 216 private static final Impl LAST_DAY_OF_MONTH = new Impl(1); 217 /** First day of next month adjuster. */ 218 private static final Impl FIRST_DAY_OF_NEXT_MONTH = new Impl(2); 219 /** First day of year adjuster. */ 220 private static final Impl FIRST_DAY_OF_YEAR = new Impl(3); 221 /** Last day of year adjuster. */ 222 private static final Impl LAST_DAY_OF_YEAR = new Impl(4); 223 /** First day of next month adjuster. */ 224 private static final Impl FIRST_DAY_OF_NEXT_YEAR = new Impl(5); 225 /** The ordinal. */ 226 private final int ordinal; Impl(int ordinal)227 private Impl(int ordinal) { 228 this.ordinal = ordinal; 229 } 230 @Override adjustInto(Temporal temporal)231 public Temporal adjustInto(Temporal temporal) { 232 switch (ordinal) { 233 case 0: return temporal.with(DAY_OF_MONTH, 1); 234 case 1: return temporal.with(DAY_OF_MONTH, temporal.range(DAY_OF_MONTH).getMaximum()); 235 case 2: return temporal.with(DAY_OF_MONTH, 1).plus(1, MONTHS); 236 case 3: return temporal.with(DAY_OF_YEAR, 1); 237 case 4: return temporal.with(DAY_OF_YEAR, temporal.range(DAY_OF_YEAR).getMaximum()); 238 case 5: return temporal.with(DAY_OF_YEAR, 1).plus(1, YEARS); 239 } 240 throw new IllegalStateException("Unreachable"); 241 } 242 } 243 244 //----------------------------------------------------------------------- 245 /** 246 * Returns the first in month adjuster, which returns a new date 247 * in the same month with the first matching day-of-week. 248 * This is used for expressions like 'first Tuesday in March'. 249 * <p> 250 * The ISO calendar system behaves as follows:<br> 251 * The input 2011-12-15 for (MONDAY) will return 2011-12-05.<br> 252 * The input 2011-12-15 for (FRIDAY) will return 2011-12-02.<br> 253 * <p> 254 * The behavior is suitable for use with most calendar systems. 255 * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields 256 * and the {@code DAYS} unit, and assumes a seven day week. 257 * 258 * @param dayOfWeek the day-of-week, not null 259 * @return the first in month adjuster, not null 260 */ firstInMonth(DayOfWeek dayOfWeek)261 public static TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek) { 262 Jdk8Methods.requireNonNull(dayOfWeek, "dayOfWeek"); 263 return new DayOfWeekInMonth(1, dayOfWeek); 264 } 265 266 /** 267 * Returns the last in month adjuster, which returns a new date 268 * in the same month with the last matching day-of-week. 269 * This is used for expressions like 'last Tuesday in March'. 270 * <p> 271 * The ISO calendar system behaves as follows:<br> 272 * The input 2011-12-15 for (MONDAY) will return 2011-12-26.<br> 273 * The input 2011-12-15 for (FRIDAY) will return 2011-12-30.<br> 274 * <p> 275 * The behavior is suitable for use with most calendar systems. 276 * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields 277 * and the {@code DAYS} unit, and assumes a seven day week. 278 * 279 * @param dayOfWeek the day-of-week, not null 280 * @return the first in month adjuster, not null 281 */ lastInMonth(DayOfWeek dayOfWeek)282 public static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) { 283 Jdk8Methods.requireNonNull(dayOfWeek, "dayOfWeek"); 284 return new DayOfWeekInMonth(-1, dayOfWeek); 285 } 286 287 /** 288 * Returns the day-of-week in month adjuster, which returns a new date 289 * in the same month with the ordinal day-of-week. 290 * This is used for expressions like the 'second Tuesday in March'. 291 * <p> 292 * The ISO calendar system behaves as follows:<br> 293 * The input 2011-12-15 for (1,TUESDAY) will return 2011-12-06.<br> 294 * The input 2011-12-15 for (2,TUESDAY) will return 2011-12-13.<br> 295 * The input 2011-12-15 for (3,TUESDAY) will return 2011-12-20.<br> 296 * The input 2011-12-15 for (4,TUESDAY) will return 2011-12-27.<br> 297 * The input 2011-12-15 for (5,TUESDAY) will return 2012-01-03.<br> 298 * The input 2011-12-15 for (-1,TUESDAY) will return 2011-12-27 (last in month).<br> 299 * The input 2011-12-15 for (-4,TUESDAY) will return 2011-12-06 (3 weeks before last in month).<br> 300 * The input 2011-12-15 for (-5,TUESDAY) will return 2011-11-29 (4 weeks before last in month).<br> 301 * The input 2011-12-15 for (0,TUESDAY) will return 2011-11-29 (last in previous month).<br> 302 * <p> 303 * For a positive or zero ordinal, the algorithm is equivalent to finding the first 304 * day-of-week that matches within the month and then adding a number of weeks to it. 305 * For a negative ordinal, the algorithm is equivalent to finding the last 306 * day-of-week that matches within the month and then subtracting a number of weeks to it. 307 * The ordinal number of weeks is not validated and is interpreted leniently 308 * according to this algorithm. This definition means that an ordinal of zero finds 309 * the last matching day-of-week in the previous month. 310 * <p> 311 * The behavior is suitable for use with most calendar systems. 312 * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields 313 * and the {@code DAYS} unit, and assumes a seven day week. 314 * 315 * @param ordinal the week within the month, unbounded but typically from -5 to 5 316 * @param dayOfWeek the day-of-week, not null 317 * @return the day-of-week in month adjuster, not null 318 */ dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek)319 public static TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) { 320 Jdk8Methods.requireNonNull(dayOfWeek, "dayOfWeek"); 321 return new DayOfWeekInMonth(ordinal, dayOfWeek); 322 } 323 324 /** 325 * Class implementing day-of-week in month adjuster. 326 */ 327 private static final class DayOfWeekInMonth implements TemporalAdjuster { 328 /** The ordinal. */ 329 private final int ordinal; 330 /** The day-of-week value, from 1 to 7. */ 331 private final int dowValue; 332 DayOfWeekInMonth(int ordinal, DayOfWeek dow)333 private DayOfWeekInMonth(int ordinal, DayOfWeek dow) { 334 super(); 335 this.ordinal = ordinal; 336 this.dowValue = dow.getValue(); 337 } 338 @Override adjustInto(Temporal temporal)339 public Temporal adjustInto(Temporal temporal) { 340 if (ordinal >= 0) { 341 Temporal temp = temporal.with(DAY_OF_MONTH, 1); 342 int curDow = temp.get(DAY_OF_WEEK); 343 long daysDiff = (dowValue - curDow + 7) % 7; 344 daysDiff += (ordinal - 1L) * 7L; // safe from overflow 345 return temp.plus(daysDiff, DAYS); 346 } else { 347 Temporal temp = temporal.with(DAY_OF_MONTH, temporal.range(DAY_OF_MONTH).getMaximum()); 348 int curDow = temp.get(DAY_OF_WEEK); 349 long daysDiff = dowValue - curDow; 350 daysDiff = (daysDiff == 0 ? 0 : (daysDiff > 0 ? daysDiff - 7 : daysDiff)); 351 daysDiff -= (-ordinal - 1L) * 7L; // safe from overflow 352 return temp.plus(daysDiff, DAYS); 353 } 354 } 355 } 356 357 //----------------------------------------------------------------------- 358 /** 359 * Returns the next day-of-week adjuster, which adjusts the date to the 360 * first occurrence of the specified day-of-week after the date being adjusted. 361 * <p> 362 * The ISO calendar system behaves as follows:<br> 363 * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-17 (two days later).<br> 364 * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-19 (four days later).<br> 365 * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-22 (seven days later). 366 * <p> 367 * The behavior is suitable for use with most calendar systems. 368 * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit, 369 * and assumes a seven day week. 370 * 371 * @param dayOfWeek the day-of-week to move the date to, not null 372 * @return the next day-of-week adjuster, not null 373 */ next(DayOfWeek dayOfWeek)374 public static TemporalAdjuster next(DayOfWeek dayOfWeek) { 375 return new RelativeDayOfWeek(2, dayOfWeek); 376 } 377 378 /** 379 * Returns the next-or-same day-of-week adjuster, which adjusts the date to the 380 * first occurrence of the specified day-of-week after the date being adjusted 381 * unless it is already on that day in which case the same object is returned. 382 * <p> 383 * The ISO calendar system behaves as follows:<br> 384 * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-17 (two days later).<br> 385 * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-19 (four days later).<br> 386 * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-15 (same as input). 387 * <p> 388 * The behavior is suitable for use with most calendar systems. 389 * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit, 390 * and assumes a seven day week. 391 * 392 * @param dayOfWeek the day-of-week to check for or move the date to, not null 393 * @return the next-or-same day-of-week adjuster, not null 394 */ nextOrSame(DayOfWeek dayOfWeek)395 public static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek) { 396 return new RelativeDayOfWeek(0, dayOfWeek); 397 } 398 399 /** 400 * Returns the previous day-of-week adjuster, which adjusts the date to the 401 * first occurrence of the specified day-of-week before the date being adjusted. 402 * <p> 403 * The ISO calendar system behaves as follows:<br> 404 * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-10 (five days earlier).<br> 405 * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-12 (three days earlier).<br> 406 * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-08 (seven days earlier). 407 * <p> 408 * The behavior is suitable for use with most calendar systems. 409 * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit, 410 * and assumes a seven day week. 411 * 412 * @param dayOfWeek the day-of-week to move the date to, not null 413 * @return the previous day-of-week adjuster, not null 414 */ previous(DayOfWeek dayOfWeek)415 public static TemporalAdjuster previous(DayOfWeek dayOfWeek) { 416 return new RelativeDayOfWeek(3, dayOfWeek); 417 } 418 419 /** 420 * Returns the previous-or-same day-of-week adjuster, which adjusts the date to the 421 * first occurrence of the specified day-of-week before the date being adjusted 422 * unless it is already on that day in which case the same object is returned. 423 * <p> 424 * The ISO calendar system behaves as follows:<br> 425 * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-10 (five days earlier).<br> 426 * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-12 (three days earlier).<br> 427 * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-15 (same as input). 428 * <p> 429 * The behavior is suitable for use with most calendar systems. 430 * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit, 431 * and assumes a seven day week. 432 * 433 * @param dayOfWeek the day-of-week to check for or move the date to, not null 434 * @return the previous-or-same day-of-week adjuster, not null 435 */ previousOrSame(DayOfWeek dayOfWeek)436 public static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek) { 437 return new RelativeDayOfWeek(1, dayOfWeek); 438 } 439 440 /** 441 * Implementation of next, previous or current day-of-week. 442 */ 443 private static final class RelativeDayOfWeek implements TemporalAdjuster { 444 /** Whether the current date is a valid answer. */ 445 private final int relative; 446 /** The day-of-week value, from 1 to 7. */ 447 private final int dowValue; 448 RelativeDayOfWeek(int relative, DayOfWeek dayOfWeek)449 private RelativeDayOfWeek(int relative, DayOfWeek dayOfWeek) { 450 Jdk8Methods.requireNonNull(dayOfWeek, "dayOfWeek"); 451 this.relative = relative; 452 this.dowValue = dayOfWeek.getValue(); 453 } 454 455 @Override adjustInto(Temporal temporal)456 public Temporal adjustInto(Temporal temporal) { 457 int calDow = temporal.get(DAY_OF_WEEK); 458 if (relative < 2 && calDow == dowValue) { 459 return temporal; 460 } 461 if ((relative & 1) == 0) { 462 int daysDiff = calDow - dowValue; 463 return temporal.plus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS); 464 } else { 465 int daysDiff = dowValue - calDow; 466 return temporal.minus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS); 467 } 468 } 469 } 470 471 } 472