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