001 /* 002 * Copyright 2001-2006 Stephen Colebourne 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.joda.time; 017 018 import org.joda.time.base.BaseSingleFieldPeriod; 019 import org.joda.time.field.FieldUtils; 020 import org.joda.time.format.ISOPeriodFormat; 021 import org.joda.time.format.PeriodFormatter; 022 023 /** 024 * An immutable time period representing a number of hours. 025 * <p> 026 * <code>Hours</code> is an immutable period that can only store hours. 027 * It does not store years, months or minutes for example. As such it is a 028 * type-safe way of representing a number of hours in an application. 029 * <p> 030 * The number of hours is set in the constructor, and may be queried using 031 * <code>getHours()</code>. Basic mathematical operations are provided - 032 * <code>plus()</code>, <code>minus()</code>, <code>multipliedBy()</code> and 033 * <code>dividedBy()</code>. 034 * <p> 035 * <code>Hours</code> is thread-safe and immutable. 036 * 037 * @author Stephen Colebourne 038 * @since 1.4 039 */ 040 public final class Hours extends BaseSingleFieldPeriod { 041 042 /** Constant representing zero hours. */ 043 public static final Hours ZERO = new Hours(0); 044 /** Constant representing one hour. */ 045 public static final Hours ONE = new Hours(1); 046 /** Constant representing two hours. */ 047 public static final Hours TWO = new Hours(2); 048 /** Constant representing three hours. */ 049 public static final Hours THREE = new Hours(3); 050 /** Constant representing four hours. */ 051 public static final Hours FOUR = new Hours(4); 052 /** Constant representing five hours. */ 053 public static final Hours FIVE = new Hours(5); 054 /** Constant representing six hours. */ 055 public static final Hours SIX = new Hours(6); 056 /** Constant representing seven hours. */ 057 public static final Hours SEVEN = new Hours(7); 058 /** Constant representing eight hours. */ 059 public static final Hours EIGHT = new Hours(8); 060 /** Constant representing the maximum number of hours that can be stored in this object. */ 061 public static final Hours MAX_VALUE = new Hours(Integer.MAX_VALUE); 062 /** Constant representing the minimum number of hours that can be stored in this object. */ 063 public static final Hours MIN_VALUE = new Hours(Integer.MIN_VALUE); 064 065 /** The paser to use for this class. */ 066 private static final PeriodFormatter PARSER = ISOPeriodFormat.standard().withParseType(PeriodType.hours()); 067 /** Serialization version. */ 068 private static final long serialVersionUID = 87525275727380864L; 069 070 //----------------------------------------------------------------------- 071 /** 072 * Obtains an instance of <code>Hours</code> that may be cached. 073 * <code>Hours</code> is immutable, so instances can be cached and shared. 074 * This factory method provides access to shared instances. 075 * 076 * @param hours the number of hours to obtain an instance for 077 * @return the instance of Hours 078 */ 079 public static Hours hours(int hours) { 080 switch (hours) { 081 case 0: 082 return ZERO; 083 case 1: 084 return ONE; 085 case 2: 086 return TWO; 087 case 3: 088 return THREE; 089 case 4: 090 return FOUR; 091 case 5: 092 return FIVE; 093 case 6: 094 return SIX; 095 case 7: 096 return SEVEN; 097 case 8: 098 return EIGHT; 099 case Integer.MAX_VALUE: 100 return MAX_VALUE; 101 case Integer.MIN_VALUE: 102 return MIN_VALUE; 103 default: 104 return new Hours(hours); 105 } 106 } 107 108 //----------------------------------------------------------------------- 109 /** 110 * Creates a <code>Hours</code> representing the number of whole hours 111 * between the two specified datetimes. 112 * 113 * @param start the start instant, must not be null 114 * @param end the end instant, must not be null 115 * @return the period in hours 116 * @throws IllegalArgumentException if the instants are null or invalid 117 */ 118 public static Hours hoursBetween(ReadableInstant start, ReadableInstant end) { 119 int amount = BaseSingleFieldPeriod.between(start, end, DurationFieldType.hours()); 120 return Hours.hours(amount); 121 } 122 123 /** 124 * Creates a <code>Hours</code> representing the number of whole hours 125 * between the two specified partial datetimes. 126 * <p> 127 * The two partials must contain the same fields, for example you can specify 128 * two <code>LocalTime</code> objects. 129 * 130 * @param start the start partial date, must not be null 131 * @param end the end partial date, must not be null 132 * @return the period in hours 133 * @throws IllegalArgumentException if the partials are null or invalid 134 */ 135 public static Hours hoursBetween(ReadablePartial start, ReadablePartial end) { 136 if (start instanceof LocalTime && end instanceof LocalTime) { 137 Chronology chrono = DateTimeUtils.getChronology(start.getChronology()); 138 int hours = chrono.hours().getDifference( 139 ((LocalTime) end).getLocalMillis(), ((LocalTime) start).getLocalMillis()); 140 return Hours.hours(hours); 141 } 142 int amount = BaseSingleFieldPeriod.between(start, end, ZERO); 143 return Hours.hours(amount); 144 } 145 146 /** 147 * Creates a <code>Hours</code> representing the number of whole hours 148 * in the specified interval. 149 * 150 * @param interval the interval to extract hours from, null returns zero 151 * @return the period in hours 152 * @throws IllegalArgumentException if the partials are null or invalid 153 */ 154 public static Hours hoursIn(ReadableInterval interval) { 155 if (interval == null) { 156 return Hours.ZERO; 157 } 158 int amount = BaseSingleFieldPeriod.between(interval.getStart(), interval.getEnd(), DurationFieldType.hours()); 159 return Hours.hours(amount); 160 } 161 162 /** 163 * Creates a new <code>Hours</code> representing the number of complete 164 * standard length hours in the specified period. 165 * <p> 166 * This factory method converts all fields from the period to hours using standardised 167 * durations for each field. Only those fields which have a precise duration in 168 * the ISO UTC chronology can be converted. 169 * <ul> 170 * <li>One week consists of 7 days. 171 * <li>One day consists of 24 hours. 172 * <li>One hour consists of 60 minutes. 173 * <li>One minute consists of 60 seconds. 174 * <li>One second consists of 1000 milliseconds. 175 * </ul> 176 * Months and Years are imprecise and periods containing these values cannot be converted. 177 * 178 * @param period the period to get the number of hours from, null returns zero 179 * @return the period in hours 180 * @throws IllegalArgumentException if the period contains imprecise duration values 181 */ 182 public static Hours standardHoursIn(ReadablePeriod period) { 183 int amount = BaseSingleFieldPeriod.standardPeriodIn(period, DateTimeConstants.MILLIS_PER_HOUR); 184 return Hours.hours(amount); 185 } 186 187 /** 188 * Creates a new <code>Hours</code> by parsing a string in the ISO8601 format 'PTnH'. 189 * <p> 190 * The parse will accept the full ISO syntax of PnYnMnWnDTnHnMnS however only the 191 * hours component may be non-zero. If any other component is non-zero, an exception 192 * will be thrown. 193 * 194 * @param periodStr the period string, null returns zero 195 * @return the period in hours 196 * @throws IllegalArgumentException if the string format is invalid 197 */ 198 public static Hours parseHours(String periodStr) { 199 if (periodStr == null) { 200 return Hours.ZERO; 201 } 202 Period p = PARSER.parsePeriod(periodStr); 203 return Hours.hours(p.getHours()); 204 } 205 206 //----------------------------------------------------------------------- 207 /** 208 * Creates a new instance representing a number of hours. 209 * You should consider using the factory method {@link #hours(int)} 210 * instead of the constructor. 211 * 212 * @param hours the number of hours to represent 213 */ 214 private Hours(int hours) { 215 super(hours); 216 } 217 218 /** 219 * Resolves singletons. 220 * 221 * @return the singleton instance 222 */ 223 private Object readResolve() { 224 return Hours.hours(getValue()); 225 } 226 227 //----------------------------------------------------------------------- 228 /** 229 * Gets the duration field type, which is <code>hours</code>. 230 * 231 * @return the period type 232 */ 233 public DurationFieldType getFieldType() { 234 return DurationFieldType.hours(); 235 } 236 237 /** 238 * Gets the period type, which is <code>hours</code>. 239 * 240 * @return the period type 241 */ 242 public PeriodType getPeriodType() { 243 return PeriodType.hours(); 244 } 245 246 //----------------------------------------------------------------------- 247 /** 248 * Converts this period in hours to a period in weeks assuming a 249 * 7 day week and 24 hour day. 250 * <p> 251 * This method allows you to convert between different types of period. 252 * However to achieve this it makes the assumption that all weeks are 7 days 253 * long and all days are 24 hours long. 254 * This is not true when daylight savings time is considered, and may also 255 * not be true for some unusual chronologies. However, it is included as it 256 * is a useful operation for many applications and business rules. 257 * 258 * @return a period representing the number of whole weeks for this number of hours 259 */ 260 public Weeks toStandardWeeks() { 261 return Weeks.weeks(getValue() / DateTimeConstants.HOURS_PER_WEEK); 262 } 263 264 /** 265 * Converts this period in hours to a period in days assuming a 266 * 24 hour day. 267 * <p> 268 * This method allows you to convert between different types of period. 269 * However to achieve this it makes the assumption that all days are 24 hours long. 270 * This is not true when daylight savings time is considered, and may also 271 * not be true for some unusual chronologies. However, it is included as it 272 * is a useful operation for many applications and business rules. 273 * 274 * @return a period representing the number of whole days for this number of hours 275 */ 276 public Days toStandardDays() { 277 return Days.days(getValue() / DateTimeConstants.HOURS_PER_DAY); 278 } 279 280 /** 281 * Converts this period in hours to a period in minutes assuming a 282 * 60 minute hour. 283 * <p> 284 * This method allows you to convert between different types of period. 285 * However to achieve this it makes the assumption that all hours are 60 minutes long. 286 * This may not be true for some unusual chronologies. However, it is included 287 * as it is a useful operation for many applications and business rules. 288 * 289 * @return a period representing the number of minutes for this number of hours 290 * @throws ArithmeticException if the number of minutes is too large to be represented 291 */ 292 public Minutes toStandardMinutes() { 293 return Minutes.minutes(FieldUtils.safeMultiply(getValue(), DateTimeConstants.MINUTES_PER_HOUR)); 294 } 295 296 /** 297 * Converts this period in hours to a period in seconds assuming a 298 * 60 minute hour and 60 second minute. 299 * <p> 300 * This method allows you to convert between different types of period. 301 * However to achieve this it makes the assumption that all hours are 302 * 60 minutes long and all minutes are 60 seconds long. 303 * This may not be true for some unusual chronologies. However, it is included 304 * as it is a useful operation for many applications and business rules. 305 * 306 * @return a period representing the number of seconds for this number of hours 307 * @throws ArithmeticException if the number of seconds is too large to be represented 308 */ 309 public Seconds toStandardSeconds() { 310 return Seconds.seconds(FieldUtils.safeMultiply(getValue(), DateTimeConstants.SECONDS_PER_HOUR)); 311 } 312 313 //----------------------------------------------------------------------- 314 /** 315 * Converts this period in hours to a duration in milliseconds assuming a 316 * 60 minute hour and 60 second minute. 317 * <p> 318 * This method allows you to convert from a period to a duration. 319 * However to achieve this it makes the assumption that all hours are 320 * 60 minutes and all minutes are 60 seconds. This might not be true for an 321 * unusual chronology, for example one that takes leap seconds into account. 322 * However, the method is included as it is a useful operation for many 323 * applications and business rules. 324 * 325 * @return a duration equivalent to this number of hours 326 */ 327 public Duration toStandardDuration() { 328 long hours = getValue(); // assign to a long 329 return new Duration(hours * DateTimeConstants.MILLIS_PER_HOUR); 330 } 331 332 //----------------------------------------------------------------------- 333 /** 334 * Gets the number of hours that this period represents. 335 * 336 * @return the number of hours in the period 337 */ 338 public int getHours() { 339 return getValue(); 340 } 341 342 //----------------------------------------------------------------------- 343 /** 344 * Returns a new instance with the specified number of hours added. 345 * <p> 346 * This instance is immutable and unaffected by this method call. 347 * 348 * @param hours the amount of hours to add, may be negative 349 * @return the new period plus the specified number of hours 350 * @throws ArithmeticException if the result overflows an int 351 */ 352 public Hours plus(int hours) { 353 if (hours == 0) { 354 return this; 355 } 356 return Hours.hours(FieldUtils.safeAdd(getValue(), hours)); 357 } 358 359 /** 360 * Returns a new instance with the specified number of hours added. 361 * <p> 362 * This instance is immutable and unaffected by this method call. 363 * 364 * @param hours the amount of hours to add, may be negative, null means zero 365 * @return the new period plus the specified number of hours 366 * @throws ArithmeticException if the result overflows an int 367 */ 368 public Hours plus(Hours hours) { 369 if (hours == null) { 370 return this; 371 } 372 return plus(hours.getValue()); 373 } 374 375 //----------------------------------------------------------------------- 376 /** 377 * Returns a new instance with the specified number of hours taken away. 378 * <p> 379 * This instance is immutable and unaffected by this method call. 380 * 381 * @param hours the amount of hours to take away, may be negative 382 * @return the new period minus the specified number of hours 383 * @throws ArithmeticException if the result overflows an int 384 */ 385 public Hours minus(int hours) { 386 return plus(FieldUtils.safeNegate(hours)); 387 } 388 389 /** 390 * Returns a new instance with the specified number of hours taken away. 391 * <p> 392 * This instance is immutable and unaffected by this method call. 393 * 394 * @param hours the amount of hours to take away, may be negative, null means zero 395 * @return the new period minus the specified number of hours 396 * @throws ArithmeticException if the result overflows an int 397 */ 398 public Hours minus(Hours hours) { 399 if (hours == null) { 400 return this; 401 } 402 return minus(hours.getValue()); 403 } 404 405 //----------------------------------------------------------------------- 406 /** 407 * Returns a new instance with the hours multiplied by the specified scalar. 408 * <p> 409 * This instance is immutable and unaffected by this method call. 410 * 411 * @param scalar the amount to multiply by, may be negative 412 * @return the new period multiplied by the specified scalar 413 * @throws ArithmeticException if the result overflows an int 414 */ 415 public Hours multipliedBy(int scalar) { 416 return Hours.hours(FieldUtils.safeMultiply(getValue(), scalar)); 417 } 418 419 /** 420 * Returns a new instance with the hours divided by the specified divisor. 421 * The calculation uses integer division, thus 3 divided by 2 is 1. 422 * <p> 423 * This instance is immutable and unaffected by this method call. 424 * 425 * @param divisor the amount to divide by, may be negative 426 * @return the new period divided by the specified divisor 427 * @throws ArithmeticException if the divisor is zero 428 */ 429 public Hours dividedBy(int divisor) { 430 if (divisor == 1) { 431 return this; 432 } 433 return Hours.hours(getValue() / divisor); 434 } 435 436 //----------------------------------------------------------------------- 437 /** 438 * Returns a new instance with the hours value negated. 439 * 440 * @return the new period with a negated value 441 * @throws ArithmeticException if the result overflows an int 442 */ 443 public Hours negated() { 444 return Hours.hours(FieldUtils.safeNegate(getValue())); 445 } 446 447 //----------------------------------------------------------------------- 448 /** 449 * Is this hours instance greater than the specified number of hours. 450 * 451 * @param other the other period, null means zero 452 * @return true if this hours instance is greater than the specified one 453 */ 454 public boolean isGreaterThan(Hours other) { 455 if (other == null) { 456 return getValue() > 0; 457 } 458 return getValue() > other.getValue(); 459 } 460 461 /** 462 * Is this hours instance less than the specified number of hours. 463 * 464 * @param other the other period, null means zero 465 * @return true if this hours instance is less than the specified one 466 */ 467 public boolean isLessThan(Hours other) { 468 if (other == null) { 469 return getValue() < 0; 470 } 471 return getValue() < other.getValue(); 472 } 473 474 //----------------------------------------------------------------------- 475 /** 476 * Gets this instance as a String in the ISO8601 duration format. 477 * <p> 478 * For example, "PT4H" represents 4 hours. 479 * 480 * @return the value as an ISO8601 string 481 */ 482 public String toString() { 483 return "PT" + String.valueOf(getValue()) + "H"; 484 } 485 486 }