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.

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

mercurial