src/net/fortuna/ical4j/model/Dur.java

Tue, 10 Feb 2015 18:12:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 10 Feb 2015 18:12:00 +0100
changeset 0
fb9019fb1bf7
child 4
45d57ecba757
permissions
-rw-r--r--

Import initial revisions of existing project AndroidCaldavSyncAdapater,
forked from upstream repository at 27e8a0f8495c92e0780d450bdf0c7cec77a03a55.

michael@0 1 /**
michael@0 2 * Copyright (c) 2012, Ben Fortuna
michael@0 3 * All rights reserved.
michael@0 4 *
michael@0 5 * Redistribution and use in source and binary forms, with or without
michael@0 6 * modification, are permitted provided that the following conditions
michael@0 7 * are met:
michael@0 8 *
michael@0 9 * o Redistributions of source code must retain the above copyright
michael@0 10 * notice, this list of conditions and the following disclaimer.
michael@0 11 *
michael@0 12 * o Redistributions in binary form must reproduce the above copyright
michael@0 13 * notice, this list of conditions and the following disclaimer in the
michael@0 14 * documentation and/or other materials provided with the distribution.
michael@0 15 *
michael@0 16 * o Neither the name of Ben Fortuna nor the names of any other contributors
michael@0 17 * may be used to endorse or promote products derived from this software
michael@0 18 * without specific prior written permission.
michael@0 19 *
michael@0 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
michael@0 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
michael@0 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
michael@0 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
michael@0 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
michael@0 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
michael@0 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
michael@0 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 31 */
michael@0 32 package net.fortuna.ical4j.model;
michael@0 33
michael@0 34 import java.io.IOException;
michael@0 35 import java.io.Serializable;
michael@0 36 import java.util.Calendar;
michael@0 37 import java.util.Date;
michael@0 38 import java.util.StringTokenizer;
michael@0 39 import net.fortuna.ical4j.util.Dates;
michael@0 40
michael@0 41 import org.apache.commons.lang.builder.HashCodeBuilder;
michael@0 42
michael@0 43 /**
michael@0 44 * $Id$
michael@0 45 *
michael@0 46 * Created on 20/06/2005
michael@0 47 *
michael@0 48 * Represents a duration of time in iCalendar. Note that according to RFC2445 durations represented in weeks are
michael@0 49 * mutually exclusive of other duration fields.
michael@0 50 *
michael@0 51 * <pre>
michael@0 52 * 4.3.6 Duration
michael@0 53 *
michael@0 54 * Value Name: DURATION
michael@0 55 *
michael@0 56 * Purpose: This value type is used to identify properties that contain
michael@0 57 * a duration of time.
michael@0 58 *
michael@0 59 * Formal Definition: The value type is defined by the following
michael@0 60 * notation:
michael@0 61 *
michael@0 62 * dur-value = ([&quot;+&quot;] / &quot;-&quot;) &quot;P&quot; (dur-date / dur-time / dur-week)
michael@0 63 *
michael@0 64 * dur-date = dur-day [dur-time]
michael@0 65 * dur-time = &quot;T&quot; (dur-hour / dur-minute / dur-second)
michael@0 66 * dur-week = 1*DIGIT &quot;W&quot;
michael@0 67 * dur-hour = 1*DIGIT &quot;H&quot; [dur-minute]
michael@0 68 * dur-minute = 1*DIGIT &quot;M&quot; [dur-second]
michael@0 69 * dur-second = 1*DIGIT &quot;S&quot;
michael@0 70 * dur-day = 1*DIGIT &quot;D&quot;
michael@0 71 * </pre>
michael@0 72 *
michael@0 73 * @author Ben Fortuna
michael@0 74 */
michael@0 75 public class Dur implements Comparable, Serializable {
michael@0 76
michael@0 77 private static final long serialVersionUID = 5013232281547134583L;
michael@0 78
michael@0 79 private static final int DAYS_PER_WEEK = 7;
michael@0 80
michael@0 81 private static final int SECONDS_PER_MINUTE = 60;
michael@0 82
michael@0 83 private static final int MINUTES_PER_HOUR = 60;
michael@0 84
michael@0 85 private static final int HOURS_PER_DAY = 24;
michael@0 86
michael@0 87 private static final int DAYS_PER_YEAR = 365;
michael@0 88
michael@0 89 private boolean negative;
michael@0 90
michael@0 91 private int weeks;
michael@0 92
michael@0 93 private int days;
michael@0 94
michael@0 95 private int hours;
michael@0 96
michael@0 97 private int minutes;
michael@0 98
michael@0 99 private int seconds;
michael@0 100
michael@0 101 /**
michael@0 102 * Constructs a new duration instance from a string representation.
michael@0 103 * @param value a string representation of a duration
michael@0 104 */
michael@0 105 public Dur(final String value) {
michael@0 106 negative = false;
michael@0 107 weeks = 0;
michael@0 108 days = 0;
michael@0 109 hours = 0;
michael@0 110 minutes = 0;
michael@0 111 seconds = 0;
michael@0 112
michael@0 113 String token = null;
michael@0 114 String prevToken = null;
michael@0 115
michael@0 116 final StringTokenizer t = new StringTokenizer(value, "+-PWDTHMS", true);
michael@0 117 while (t.hasMoreTokens()) {
michael@0 118 prevToken = token;
michael@0 119 token = t.nextToken();
michael@0 120
michael@0 121 if ("+".equals(token)) {
michael@0 122 negative = false;
michael@0 123 }
michael@0 124 else if ("-".equals(token)) {
michael@0 125 negative = true;
michael@0 126 }
michael@0 127 else if ("P".equals(token)) {
michael@0 128 // does nothing..
michael@0 129 }
michael@0 130 else if ("W".equals(token)) {
michael@0 131 weeks = Integer.parseInt(prevToken);
michael@0 132 }
michael@0 133 else if ("D".equals(token)) {
michael@0 134 days = Integer.parseInt(prevToken);
michael@0 135 }
michael@0 136 else if ("T".equals(token)) {
michael@0 137 // does nothing..
michael@0 138 }
michael@0 139 else if ("H".equals(token)) {
michael@0 140 hours = Integer.parseInt(prevToken);
michael@0 141 }
michael@0 142 else if ("M".equals(token)) {
michael@0 143 minutes = Integer.parseInt(prevToken);
michael@0 144 }
michael@0 145 else if ("S".equals(token)) {
michael@0 146 seconds = Integer.parseInt(prevToken);
michael@0 147 }
michael@0 148 }
michael@0 149 }
michael@0 150
michael@0 151 /**
michael@0 152 * Constructs a new duration from the specified weeks.
michael@0 153 * @param weeks a duration in weeks.
michael@0 154 */
michael@0 155 public Dur(final int weeks) {
michael@0 156 this.weeks = Math.abs(weeks);
michael@0 157 this.days = 0;
michael@0 158 this.hours = 0;
michael@0 159 this.minutes = 0;
michael@0 160 this.seconds = 0;
michael@0 161 this.negative = weeks < 0;
michael@0 162 }
michael@0 163
michael@0 164 /**
michael@0 165 * Constructs a new duration from the specified arguments.
michael@0 166 * @param days duration in days
michael@0 167 * @param hours duration in hours
michael@0 168 * @param minutes duration in minutes
michael@0 169 * @param seconds duration in seconds
michael@0 170 */
michael@0 171 public Dur(final int days, final int hours, final int minutes,
michael@0 172 final int seconds) {
michael@0 173
michael@0 174 if (!(days >= 0 && hours >= 0 && minutes >= 0 && seconds >= 0)
michael@0 175 && !(days <= 0 && hours <= 0 && minutes <= 0 && seconds <= 0)) {
michael@0 176
michael@0 177 throw new IllegalArgumentException("Invalid duration representation");
michael@0 178 }
michael@0 179
michael@0 180 this.weeks = 0;
michael@0 181 this.days = Math.abs(days);
michael@0 182 this.hours = Math.abs(hours);
michael@0 183 this.minutes = Math.abs(minutes);
michael@0 184 this.seconds = Math.abs(seconds);
michael@0 185
michael@0 186 this.negative = days < 0 || hours < 0 || minutes < 0 || seconds < 0;
michael@0 187 }
michael@0 188
michael@0 189 /**
michael@0 190 * Constructs a new duration representing the time between the two specified dates. The end date may precede the
michael@0 191 * start date in order to represent a negative duration.
michael@0 192 * @param date1 the first date of the duration
michael@0 193 * @param date2 the second date of the duration
michael@0 194 */
michael@0 195 public Dur(final Date date1, final Date date2) {
michael@0 196
michael@0 197 Date start = null;
michael@0 198 Date end = null;
michael@0 199
michael@0 200 // Negative range? (start occurs after end)
michael@0 201 negative = date1.compareTo(date2) > 0;
michael@0 202 if (negative) {
michael@0 203 // Swap the dates (which eliminates the need to bother with
michael@0 204 // negative after this!)
michael@0 205 start = date2;
michael@0 206 end = date1;
michael@0 207 }
michael@0 208 else {
michael@0 209 start = date1;
michael@0 210 end = date2;
michael@0 211 }
michael@0 212
michael@0 213 final Calendar startCal;
michael@0 214 if (start instanceof net.fortuna.ical4j.model.Date) {
michael@0 215 startCal = Dates.getCalendarInstance((net.fortuna.ical4j.model.Date)start);
michael@0 216 } else {
michael@0 217 startCal = Calendar.getInstance();
michael@0 218 }
michael@0 219 startCal.setTime(start);
michael@0 220 final Calendar endCal = Calendar.getInstance(startCal.getTimeZone());
michael@0 221 endCal.setTime(end);
michael@0 222
michael@0 223 // Init our duration interval (which is in units that evolve as we
michael@0 224 // compute, below)
michael@0 225 int dur = 0;
michael@0 226
michael@0 227 // Count days to get to the right year (loop in the very rare chance
michael@0 228 // that a leap year causes us to come up short)
michael@0 229 int nYears = endCal.get(Calendar.YEAR) - startCal.get(Calendar.YEAR);
michael@0 230 while (nYears > 0) {
michael@0 231 startCal.add(Calendar.DATE, DAYS_PER_YEAR * nYears);
michael@0 232 dur += DAYS_PER_YEAR * nYears;
michael@0 233 nYears = endCal.get(Calendar.YEAR) - startCal.get(Calendar.YEAR);
michael@0 234 }
michael@0 235
michael@0 236 // Count days to get to the right day
michael@0 237 dur += endCal.get(Calendar.DAY_OF_YEAR)
michael@0 238 - startCal.get(Calendar.DAY_OF_YEAR);
michael@0 239
michael@0 240 // Count hours to get to right hour
michael@0 241 dur *= HOURS_PER_DAY; // days -> hours
michael@0 242 dur += endCal.get(Calendar.HOUR_OF_DAY)
michael@0 243 - startCal.get(Calendar.HOUR_OF_DAY);
michael@0 244
michael@0 245 // ... to the right minute
michael@0 246 dur *= MINUTES_PER_HOUR; // hours -> minutes
michael@0 247 dur += endCal.get(Calendar.MINUTE) - startCal.get(Calendar.MINUTE);
michael@0 248
michael@0 249 // ... and second
michael@0 250 dur *= SECONDS_PER_MINUTE; // minutes -> seconds
michael@0 251 dur += endCal.get(Calendar.SECOND) - startCal.get(Calendar.SECOND);
michael@0 252
michael@0 253 // Now unwind our units
michael@0 254 seconds = dur % SECONDS_PER_MINUTE;
michael@0 255 dur = dur / SECONDS_PER_MINUTE; // seconds -> minutes (drop remainder seconds)
michael@0 256 minutes = dur % MINUTES_PER_HOUR;
michael@0 257 dur /= MINUTES_PER_HOUR; // minutes -> hours (drop remainder minutes)
michael@0 258 hours = dur % HOURS_PER_DAY;
michael@0 259 dur /= HOURS_PER_DAY; // hours -> days (drop remainder hours)
michael@0 260 days = dur;
michael@0 261 weeks = 0;
michael@0 262
michael@0 263 // Special case for week-only representation
michael@0 264 if (seconds == 0 && minutes == 0 && hours == 0
michael@0 265 && (days % DAYS_PER_WEEK) == 0) {
michael@0 266 weeks = days / DAYS_PER_WEEK;
michael@0 267 days = 0;
michael@0 268 }
michael@0 269 }
michael@0 270
michael@0 271 /**
michael@0 272 * Returns a date representing the end of this duration from the specified start date.
michael@0 273 * @param start the date to start the duration
michael@0 274 * @return the end of the duration as a date
michael@0 275 */
michael@0 276 public final Date getTime(final Date start) {
michael@0 277 final Calendar cal;
michael@0 278 if (start instanceof net.fortuna.ical4j.model.Date) {
michael@0 279 cal = Dates.getCalendarInstance((net.fortuna.ical4j.model.Date)start);
michael@0 280 } else {
michael@0 281 cal = Calendar.getInstance();
michael@0 282 }
michael@0 283
michael@0 284 cal.setTime(start);
michael@0 285 if (isNegative()) {
michael@0 286 cal.add(Calendar.WEEK_OF_YEAR, -weeks);
michael@0 287 cal.add(Calendar.DAY_OF_WEEK, -days);
michael@0 288 cal.add(Calendar.HOUR_OF_DAY, -hours);
michael@0 289 cal.add(Calendar.MINUTE, -minutes);
michael@0 290 cal.add(Calendar.SECOND, -seconds);
michael@0 291 }
michael@0 292 else {
michael@0 293 cal.add(Calendar.WEEK_OF_YEAR, weeks);
michael@0 294 cal.add(Calendar.DAY_OF_WEEK, days);
michael@0 295 cal.add(Calendar.HOUR_OF_DAY, hours);
michael@0 296 cal.add(Calendar.MINUTE, minutes);
michael@0 297 cal.add(Calendar.SECOND, seconds);
michael@0 298 }
michael@0 299 return cal.getTime();
michael@0 300 }
michael@0 301
michael@0 302 /**
michael@0 303 * Provides a negation of this instance.
michael@0 304 * @return a Dur instance that represents a negation of this instance
michael@0 305 */
michael@0 306 public final Dur negate() {
michael@0 307 final Dur negated = new Dur(days, hours, minutes, seconds);
michael@0 308 negated.weeks = weeks;
michael@0 309 negated.negative = !negative;
michael@0 310 return negated;
michael@0 311 }
michael@0 312
michael@0 313 /**
michael@0 314 * Add two durations. Durations may only be added if they are both positive
michael@0 315 * or both negative durations.
michael@0 316 * @param duration the duration to add to this duration
michael@0 317 * @return a new instance representing the sum of the two durations.
michael@0 318 */
michael@0 319 public final Dur add(final Dur duration) {
michael@0 320 if ((!isNegative() && duration.isNegative())
michael@0 321 || (isNegative() && !duration.isNegative())) {
michael@0 322
michael@0 323 throw new IllegalArgumentException(
michael@0 324 "Cannot add a negative and a positive duration");
michael@0 325 }
michael@0 326
michael@0 327 Dur sum = null;
michael@0 328 if (weeks > 0 && duration.weeks > 0) {
michael@0 329 sum = new Dur(weeks + duration.weeks);
michael@0 330 }
michael@0 331 else {
michael@0 332 int daySum = (weeks > 0) ? weeks * DAYS_PER_WEEK + days : days;
michael@0 333 int hourSum = hours;
michael@0 334 int minuteSum = minutes;
michael@0 335 int secondSum = seconds;
michael@0 336
michael@0 337 if ((secondSum + duration.seconds) / SECONDS_PER_MINUTE > 0) {
michael@0 338 minuteSum += (secondSum + duration.seconds) / SECONDS_PER_MINUTE;
michael@0 339 secondSum = (secondSum + duration.seconds) % SECONDS_PER_MINUTE;
michael@0 340 }
michael@0 341 else {
michael@0 342 secondSum += duration.seconds;
michael@0 343 }
michael@0 344
michael@0 345 if ((minuteSum + duration.minutes) / MINUTES_PER_HOUR > 0) {
michael@0 346 hourSum += (minuteSum + duration.minutes) / MINUTES_PER_HOUR;
michael@0 347 minuteSum = (minuteSum + duration.minutes) % MINUTES_PER_HOUR;
michael@0 348 }
michael@0 349 else {
michael@0 350 minuteSum += duration.minutes;
michael@0 351 }
michael@0 352
michael@0 353 if ((hourSum + duration.hours) / HOURS_PER_DAY > 0) {
michael@0 354 daySum += (hourSum + duration.hours) / HOURS_PER_DAY;
michael@0 355 hourSum = (hourSum + duration.hours) % HOURS_PER_DAY;
michael@0 356 }
michael@0 357 else {
michael@0 358 hourSum += duration.hours;
michael@0 359 }
michael@0 360
michael@0 361 daySum += (duration.weeks > 0) ? duration.weeks * DAYS_PER_WEEK
michael@0 362 + duration.days : duration.days;
michael@0 363
michael@0 364 sum = new Dur(daySum, hourSum, minuteSum, secondSum);
michael@0 365 }
michael@0 366 sum.negative = negative;
michael@0 367 return sum;
michael@0 368 }
michael@0 369
michael@0 370 /**
michael@0 371 * {@inheritDoc}
michael@0 372 */
michael@0 373 public final String toString() {
michael@0 374 final StringBuffer b = new StringBuffer();
michael@0 375 if (negative) {
michael@0 376 b.append('-');
michael@0 377 }
michael@0 378 b.append('P');
michael@0 379 if (weeks > 0) {
michael@0 380 b.append(weeks);
michael@0 381 b.append('W');
michael@0 382 }
michael@0 383 else {
michael@0 384 if (days > 0) {
michael@0 385 b.append(days);
michael@0 386 b.append('D');
michael@0 387 }
michael@0 388 if (hours > 0 || minutes > 0 || seconds > 0) {
michael@0 389 b.append('T');
michael@0 390 if (hours > 0) {
michael@0 391 b.append(hours);
michael@0 392 b.append('H');
michael@0 393 }
michael@0 394 if (minutes > 0) {
michael@0 395 b.append(minutes);
michael@0 396 b.append('M');
michael@0 397 }
michael@0 398 if (seconds > 0) {
michael@0 399 b.append(seconds);
michael@0 400 b.append('S');
michael@0 401 }
michael@0 402 }
michael@0 403 // handle case of zero length duration
michael@0 404 if ((hours + minutes + seconds + days + weeks) == 0) {
michael@0 405 b.append("T0S");
michael@0 406 }
michael@0 407 }
michael@0 408 return b.toString();
michael@0 409 }
michael@0 410
michael@0 411 /**
michael@0 412 * {@inheritDoc}
michael@0 413 */
michael@0 414 public final int compareTo(final Object arg0) {
michael@0 415 return compareTo((Dur) arg0);
michael@0 416 }
michael@0 417
michael@0 418 /**
michael@0 419 * Compares this duration with another, acording to their length.
michael@0 420 * @param arg0 another duration instance
michael@0 421 * @return a postive value if this duration is longer, zero if the duration
michael@0 422 * lengths are equal, otherwise a negative value
michael@0 423 */
michael@0 424 public final int compareTo(final Dur arg0) {
michael@0 425 int result;
michael@0 426 if (isNegative() != arg0.isNegative()) {
michael@0 427 // return Boolean.valueOf(isNegative()).compareTo(Boolean.valueOf(arg0.isNegative()));
michael@0 428 // for pre-java 1.5 compatibility..
michael@0 429 if (isNegative()) {
michael@0 430 return Integer.MIN_VALUE;
michael@0 431 }
michael@0 432 else {
michael@0 433 return Integer.MAX_VALUE;
michael@0 434 }
michael@0 435 }
michael@0 436 else if (getWeeks() != arg0.getWeeks()) {
michael@0 437 result = getWeeks() - arg0.getWeeks();
michael@0 438 }
michael@0 439 else if (getDays() != arg0.getDays()) {
michael@0 440 result = getDays() - arg0.getDays();
michael@0 441 }
michael@0 442 else if (getHours() != arg0.getHours()) {
michael@0 443 result = getHours() - arg0.getHours();
michael@0 444 }
michael@0 445 else if (getMinutes() != arg0.getMinutes()) {
michael@0 446 result = getMinutes() - arg0.getMinutes();
michael@0 447 }
michael@0 448 else {
michael@0 449 result = getSeconds() - arg0.getSeconds();
michael@0 450 }
michael@0 451 // invert sense of all tests if both durations are negative
michael@0 452 if (isNegative()) {
michael@0 453 return -result;
michael@0 454 }
michael@0 455 else {
michael@0 456 return result;
michael@0 457 }
michael@0 458 }
michael@0 459
michael@0 460 /**
michael@0 461 * {@inheritDoc}
michael@0 462 */
michael@0 463 public boolean equals(final Object obj) {
michael@0 464 if (obj instanceof Dur) {
michael@0 465 return ((Dur) obj).compareTo(this) == 0;
michael@0 466 }
michael@0 467 return super.equals(obj);
michael@0 468 }
michael@0 469
michael@0 470 /**
michael@0 471 * {@inheritDoc}
michael@0 472 */
michael@0 473 public int hashCode() {
michael@0 474 return new HashCodeBuilder().append(weeks).append(days).append(
michael@0 475 hours).append(minutes).append(seconds).append(negative).toHashCode();
michael@0 476 }
michael@0 477
michael@0 478 /**
michael@0 479 * @return Returns the days.
michael@0 480 */
michael@0 481 public final int getDays() {
michael@0 482 return days;
michael@0 483 }
michael@0 484
michael@0 485 /**
michael@0 486 * @return Returns the hours.
michael@0 487 */
michael@0 488 public final int getHours() {
michael@0 489 return hours;
michael@0 490 }
michael@0 491
michael@0 492 /**
michael@0 493 * @return Returns the minutes.
michael@0 494 */
michael@0 495 public final int getMinutes() {
michael@0 496 return minutes;
michael@0 497 }
michael@0 498
michael@0 499 /**
michael@0 500 * @return Returns the negative.
michael@0 501 */
michael@0 502 public final boolean isNegative() {
michael@0 503 return negative;
michael@0 504 }
michael@0 505
michael@0 506 /**
michael@0 507 * @return Returns the seconds.
michael@0 508 */
michael@0 509 public final int getSeconds() {
michael@0 510 return seconds;
michael@0 511 }
michael@0 512
michael@0 513 /**
michael@0 514 * @return Returns the weeks.
michael@0 515 */
michael@0 516 public final int getWeeks() {
michael@0 517 return weeks;
michael@0 518 }
michael@0 519
michael@0 520 /**
michael@0 521 * @param stream
michael@0 522 * @throws IOException
michael@0 523 * @throws ClassNotFoundException
michael@0 524 */
michael@0 525 private void readObject(final java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
michael@0 526 stream.defaultReadObject();
michael@0 527 }
michael@0 528 }

mercurial