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 java.io.Serializable;
023import javax.annotation.Nullable;
024import org.apache.commons.lang.builder.ToStringBuilder;
025import org.apache.commons.lang.builder.ToStringStyle;
026
027/**
028 * @since 4.2
029 */
030public class WorkDuration implements Serializable {
031
032  static final int DAY_POSITION_IN_LONG = 10_000;
033  static final int HOUR_POSITION_IN_LONG = 100;
034  static final int MINUTE_POSITION_IN_LONG = 1;
035
036  public enum UNIT {
037    DAYS, HOURS, MINUTES
038  }
039
040  private int hoursInDay;
041
042  private long durationInMinutes;
043  private int days;
044  private int hours;
045  private int minutes;
046
047  private WorkDuration(long durationInMinutes, int days, int hours, int minutes, int hoursInDay) {
048    this.durationInMinutes = durationInMinutes;
049    this.days = days;
050    this.hours = hours;
051    this.minutes = minutes;
052    this.hoursInDay = hoursInDay;
053  }
054
055  public static WorkDuration create(int days, int hours, int minutes, int hoursInDay) {
056    long durationInSeconds = 60L * days * hoursInDay;
057    durationInSeconds += 60L * hours;
058    durationInSeconds += minutes;
059    return new WorkDuration(durationInSeconds, days, hours, minutes, hoursInDay);
060  }
061
062  public static WorkDuration createFromValueAndUnit(int value, UNIT unit, int hoursInDay) {
063    switch (unit) {
064      case DAYS:
065        return create(value, 0, 0, hoursInDay);
066      case HOURS:
067        return create(0, value, 0, hoursInDay);
068      case MINUTES:
069        return create(0, 0, value, hoursInDay);
070      default:
071        throw new IllegalStateException("Cannot create work duration");
072    }
073  }
074
075  static WorkDuration createFromLong(long duration, int hoursInDay) {
076    int days = 0;
077    int hours = 0;
078    int minutes = 0;
079
080    long time = duration;
081    Long currentTime = time / WorkDuration.DAY_POSITION_IN_LONG;
082    if (currentTime > 0) {
083      days = currentTime.intValue();
084      time = time - (currentTime * WorkDuration.DAY_POSITION_IN_LONG);
085    }
086
087    currentTime = time / WorkDuration.HOUR_POSITION_IN_LONG;
088    if (currentTime > 0) {
089      hours = currentTime.intValue();
090      time = time - (currentTime * WorkDuration.HOUR_POSITION_IN_LONG);
091    }
092
093    currentTime = time / WorkDuration.MINUTE_POSITION_IN_LONG;
094    if (currentTime > 0) {
095      minutes = currentTime.intValue();
096    }
097    return WorkDuration.create(days, hours, minutes, hoursInDay);
098  }
099
100  static WorkDuration createFromMinutes(long duration, int hoursInDay) {
101    int days = (int)(duration / (double)hoursInDay / 60.0);
102    Long currentDurationInMinutes = duration - (60L * days * hoursInDay);
103    int hours = (int)(currentDurationInMinutes / 60.0);
104    currentDurationInMinutes = currentDurationInMinutes - (60L * hours);
105    return new WorkDuration(duration, days, hours, currentDurationInMinutes.intValue(), hoursInDay);
106  }
107
108  /**
109   * Return the duration in number of working days.
110   * For instance, 3 days and 4 hours will return 3.5 days (if hoursIndDay is 8).
111   */
112  public double toWorkingDays() {
113    return durationInMinutes / 60d / hoursInDay;
114  }
115
116  /**
117   * 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.
118   * For instance, 3 days and 4 hours will return 030400 (if hoursIndDay is 8).
119   */
120  public long toLong() {
121    int workingDays = days;
122    int workingHours = hours;
123    if (hours >= hoursInDay) {
124      int nbAdditionalDays = hours / hoursInDay;
125      workingDays += nbAdditionalDays;
126      workingHours = hours - (nbAdditionalDays * hoursInDay);
127    }
128    return 1L * workingDays * DAY_POSITION_IN_LONG + workingHours * HOUR_POSITION_IN_LONG + minutes * MINUTE_POSITION_IN_LONG;
129  }
130
131  public long toMinutes() {
132    return durationInMinutes;
133  }
134
135  public WorkDuration add(@Nullable WorkDuration with) {
136    if (with != null) {
137      return WorkDuration.createFromMinutes(this.toMinutes() + with.toMinutes(), this.hoursInDay);
138    } else {
139      return this;
140    }
141  }
142
143  public WorkDuration subtract(@Nullable WorkDuration with) {
144    if (with != null) {
145      return WorkDuration.createFromMinutes(this.toMinutes() - with.toMinutes(), this.hoursInDay);
146    } else {
147      return this;
148    }
149  }
150
151  public WorkDuration multiply(int factor) {
152    return WorkDuration.createFromMinutes(this.toMinutes() * factor, this.hoursInDay);
153  }
154
155  public int days() {
156    return days;
157  }
158
159  public int hours() {
160    return hours;
161  }
162
163  public int minutes() {
164    return minutes;
165  }
166
167  int hoursInDay() {
168    return hoursInDay;
169  }
170
171  @Override
172  public boolean equals(Object o) {
173    if (this == o) {
174      return true;
175    }
176    if (o == null || getClass() != o.getClass()) {
177      return false;
178    }
179
180    WorkDuration that = (WorkDuration) o;
181    return durationInMinutes == that.durationInMinutes;
182
183  }
184
185  @Override
186  public int hashCode() {
187    return (int) (durationInMinutes ^ (durationInMinutes >>> 32));
188  }
189
190  @Override
191  public String toString() {
192    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
193  }
194}