001/*
002 * SonarQube
003 * Copyright (C) 2009-2017 SonarSource SA
004 * mailto:info AT sonarsource DOT com
005 *
006 * This program is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.utils.internal;
021
022import com.google.common.annotations.VisibleForTesting;
023import java.io.Serializable;
024import javax.annotation.Nullable;
025import org.apache.commons.lang.builder.ToStringBuilder;
026import org.apache.commons.lang.builder.ToStringStyle;
027
028/**
029 * @since 4.2
030 */
031public class WorkDuration implements Serializable {
032
033  static final int DAY_POSITION_IN_LONG = 10_000;
034  static final int HOUR_POSITION_IN_LONG = 100;
035  static final int MINUTE_POSITION_IN_LONG = 1;
036
037  public enum UNIT {
038    DAYS, HOURS, MINUTES
039  }
040
041  private int hoursInDay;
042
043  private long durationInMinutes;
044  private int days;
045  private int hours;
046  private int minutes;
047
048  private WorkDuration(long durationInMinutes, int days, int hours, int minutes, int hoursInDay) {
049    this.durationInMinutes = durationInMinutes;
050    this.days = days;
051    this.hours = hours;
052    this.minutes = minutes;
053    this.hoursInDay = hoursInDay;
054  }
055
056  public static WorkDuration create(int days, int hours, int minutes, int hoursInDay) {
057    long durationInSeconds = 60L * days * hoursInDay;
058    durationInSeconds += 60L * hours;
059    durationInSeconds += minutes;
060    return new WorkDuration(durationInSeconds, days, hours, minutes, hoursInDay);
061  }
062
063  public static WorkDuration createFromValueAndUnit(int value, UNIT unit, int hoursInDay) {
064    switch (unit) {
065      case DAYS:
066        return create(value, 0, 0, hoursInDay);
067      case HOURS:
068        return create(0, value, 0, hoursInDay);
069      case MINUTES:
070        return create(0, 0, value, hoursInDay);
071      default:
072        throw new IllegalStateException("Cannot create work duration");
073    }
074  }
075
076  static WorkDuration createFromLong(long duration, int hoursInDay) {
077    int days = 0;
078    int hours = 0;
079    int minutes = 0;
080
081    long time = duration;
082    Long currentTime = time / WorkDuration.DAY_POSITION_IN_LONG;
083    if (currentTime > 0) {
084      days = currentTime.intValue();
085      time = time - (currentTime * WorkDuration.DAY_POSITION_IN_LONG);
086    }
087
088    currentTime = time / WorkDuration.HOUR_POSITION_IN_LONG;
089    if (currentTime > 0) {
090      hours = currentTime.intValue();
091      time = time - (currentTime * WorkDuration.HOUR_POSITION_IN_LONG);
092    }
093
094    currentTime = time / WorkDuration.MINUTE_POSITION_IN_LONG;
095    if (currentTime > 0) {
096      minutes = currentTime.intValue();
097    }
098    return WorkDuration.create(days, hours, minutes, hoursInDay);
099  }
100
101  static WorkDuration createFromMinutes(long duration, int hoursInDay) {
102    int days = (int)(duration / (double)hoursInDay / 60.0);
103    Long currentDurationInMinutes = duration - (60L * days * hoursInDay);
104    int hours = (int)(currentDurationInMinutes / 60.0);
105    currentDurationInMinutes = currentDurationInMinutes - (60L * hours);
106    return new WorkDuration(duration, days, hours, currentDurationInMinutes.intValue(), hoursInDay);
107  }
108
109  /**
110   * Return the duration in number of working days.
111   * For instance, 3 days and 4 hours will return 3.5 days (if hoursIndDay is 8).
112   */
113  public double toWorkingDays() {
114    return durationInMinutes / 60d / hoursInDay;
115  }
116
117  /**
118   * Return the duration using the following format DDHHMM, where DD is the number of days, HH is the number of months, and MM the number of minutes.
119   * For instance, 3 days and 4 hours will return 030400 (if hoursIndDay is 8).
120   */
121  public long toLong() {
122    int workingDays = days;
123    int workingHours = hours;
124    if (hours >= hoursInDay) {
125      int nbAdditionalDays = hours / hoursInDay;
126      workingDays += nbAdditionalDays;
127      workingHours = hours - (nbAdditionalDays * hoursInDay);
128    }
129    return 1L * workingDays * DAY_POSITION_IN_LONG + workingHours * HOUR_POSITION_IN_LONG + minutes * MINUTE_POSITION_IN_LONG;
130  }
131
132  public long toMinutes() {
133    return durationInMinutes;
134  }
135
136  public WorkDuration add(@Nullable WorkDuration with) {
137    if (with != null) {
138      return WorkDuration.createFromMinutes(this.toMinutes() + with.toMinutes(), this.hoursInDay);
139    } else {
140      return this;
141    }
142  }
143
144  public WorkDuration subtract(@Nullable WorkDuration with) {
145    if (with != null) {
146      return WorkDuration.createFromMinutes(this.toMinutes() - with.toMinutes(), this.hoursInDay);
147    } else {
148      return this;
149    }
150  }
151
152  public WorkDuration multiply(int factor) {
153    return WorkDuration.createFromMinutes(this.toMinutes() * factor, this.hoursInDay);
154  }
155
156  public int days() {
157    return days;
158  }
159
160  public int hours() {
161    return hours;
162  }
163
164  public int minutes() {
165    return minutes;
166  }
167
168  @VisibleForTesting
169  int hoursInDay() {
170    return hoursInDay;
171  }
172
173  @Override
174  public boolean equals(Object o) {
175    if (this == o) {
176      return true;
177    }
178    if (o == null || getClass() != o.getClass()) {
179      return false;
180    }
181
182    WorkDuration that = (WorkDuration) o;
183    return durationInMinutes == that.durationInMinutes;
184
185  }
186
187  @Override
188  public int hashCode() {
189    return (int) (durationInMinutes ^ (durationInMinutes >>> 32));
190  }
191
192  @Override
193  public String toString() {
194    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
195  }
196}