001/*
002 * #%L
003 * Netarchivesuite - common
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023package dk.netarkivet.common.utils;
024
025import java.util.Calendar;
026
027import dk.netarkivet.common.exceptions.ArgumentNotValid;
028
029/**
030 * Various utilities for waiting some time.
031 */
032public class TimeUtils {
033
034    /** Constant for the number of milliseconds per second: 1000. */
035    public static final long SECOND_IN_MILLIS = 1000;
036    /** Constant for the number of seconds per minute: 60. */
037    public static final long MINUTE_IN_SECONDS = 60;
038    /** Constant for the number of minutes per hour: 60. */
039    public static final long HOUR_IN_MINUTES = 60;
040    /** Constant for the number of hours per day: 24. */
041    public static final long DAY_IN_HOURS = 24;
042    /** Constant for the number of days per week: 7. */
043    public static final long WEEK_IN_DAYS = 7;
044
045    /**
046     * Sleep for an exponentially backing off amount of time, in milliseconds. Thus the first attempt will sleep for 1
047     * ms, the second for 2, the third for 4, etc.
048     *
049     * @param attempt The attempt number, which is the log2 of the number of milliseconds spent asleep.
050     */
051    public static void exponentialBackoffSleep(int attempt) {
052        exponentialBackoffSleep(attempt, Calendar.MILLISECOND);
053    }
054
055    /**
056     * Sleep for an exponentially backing off amount of time. The mode describes the unit of time as defined by @see
057     * java.util.Calendar
058     *
059     * @param attempt The attempt number, which is the log2 of the number of timeunits spent asleep.
060     * @param timeunit the specified timeunit in miliseconds
061     * @throws ArgumentNotValid if timeunit is unsupported.
062     */
063    public static void exponentialBackoffSleep(int attempt, int timeunit) {
064        ArgumentNotValid.checkNotNegative(attempt, "int attempt");
065        ArgumentNotValid.checkTrue(timeunit >= 0 && timeunit < Calendar.FIELD_COUNT,
066                "Time unit must be one of the fields defined" + " by Calendar, not " + timeunit);
067
068        Calendar now = Calendar.getInstance();
069        long startTime = now.getTimeInMillis();
070        now.add(timeunit, 1);
071        long endTime = now.getTimeInMillis();
072        long multiplyBy = endTime - startTime;
073
074        try {
075            Thread.sleep((long) (Math.pow(2, attempt)) * multiplyBy);
076        } catch (InterruptedException e) {
077            // Early wake-up is not a problem
078        }
079    }
080
081    /**
082     * Method for translating a time in milliseconds to a human readable String. E.g. the argument "604800000" should
083     * result in "7 days".
084     *
085     * @param millis The amount of milliseconds.
086     * @return The human readable string.
087     */
088    public static String readableTimeInterval(long millis) {
089        // check whether it is in seconds (if not return in milliseconds).
090        if ((millis % SECOND_IN_MILLIS) != 0) {
091            if (millis == 1) {
092                return millis + " millisecond";
093            }
094            return millis + " milliseconds";
095        }
096        long seconds = millis / SECOND_IN_MILLIS;
097
098        // check whether it is in minutes (if not return in seconds).
099        if ((seconds % MINUTE_IN_SECONDS) != 0) {
100            if (seconds == 1) {
101                return seconds + " second";
102            }
103            return seconds + " seconds";
104        }
105        long minutes = seconds / MINUTE_IN_SECONDS;
106
107        // check whether it is in hours (if not return in minutes).
108        if ((minutes % HOUR_IN_MINUTES) != 0) {
109            if (minutes == 1) {
110                return minutes + " minute";
111            }
112            return minutes + " minutes";
113        }
114        long hours = minutes / HOUR_IN_MINUTES;
115
116        // check whether it is in days (if not return in hours).
117        if ((hours % DAY_IN_HOURS) != 0) {
118            if (hours == 1) {
119                return hours + " hour";
120            }
121            return hours + " hours";
122        }
123        long days = hours / DAY_IN_HOURS;
124
125        if ((days % WEEK_IN_DAYS) != 0) {
126            if (days == 1) {
127                return days + " day";
128            }
129            return days + " days";
130        }
131        long weeks = days / WEEK_IN_DAYS;
132
133        if (weeks == 1) {
134            return weeks + " week";
135        }
136        return weeks + " weeks";
137    }
138
139}