The challenge
Your task in order to complete this challenge is to write a function which formats a duration, given as a number of seconds, in a human-friendly way.
The function must accept a non-negative integer. If it is zero, it just returns "now"
. Otherwise, the duration is expressed as a combination of years
, days
, hours
, minutes
and seconds
.
It is much easier to understand with an example:
//returns "1 minute and 2 seconds"
TimeFormatter.formatDuration(62)
//returns "1 hour, 1 minute and 2 seconds"
TimeFormatter.formatDuration(3662)
For the purpose of this challenge, a year is 365 days and a day is 24 hours.
Note that spaces are important.
Detailed rules
The resulting expression is made of components like 4 seconds
, 1 year
, etc. In general, a positive integer and one of the valid units of time, separated by a space. The unit of time is used in plural if the integer is greater than 1.
The components are separated by a comma and a space (", "
). Except the last component, which is separated by " and "
, just like it would be written in English.
A more significant units of time will occur before than a least significant one. Therefore, 1 second and 1 year
is not correct, but 1 year and 1 second
is.
Different components have different unit of times. So there is not repeated units like in 5 seconds and 1 second
.
A component will not appear at all if its value happens to be zero. Hence, 1 minute and 0 seconds
is not valid, but it should be just 1 minute
.
A unit of time must be used “as much as possible”. It means that the function should not return 61 seconds
, but 1 minute and 1 second
instead. Formally, the duration specified by of a component must not be greater than any valid more significant unit of time.
The solution in Java code
Option 1:
import java.util.Arrays;
import java.util.stream.*;
public class TimeFormatter {
public static String formatDuration(int seconds) {
return seconds == 0 ? "now" :
Arrays.stream(
new String[]{
formatTime("year", (seconds / 31536000)),
formatTime("day", (seconds / 86400)%365),
formatTime("hour", (seconds / 3600)%24),
formatTime("minute",(seconds / 60)%60),
formatTime("second",(seconds%3600)%60)})
.filter(e->e!="")
.collect(Collectors.joining(", "))
.replaceAll(", (?!.+,)", " and ");
}
public static String formatTime(String s, int time){
return time==0 ? "" : Integer.toString(time)+ " " + s + (time==1?"" : "s");
}
}
Option 2:
public class TimeFormatter {
public static String formatDuration(int seconds) {
String res = "";
int[] units = new int[] {31536000, 86400, 3600, 60, 1};
String[] labels = new String[] {"year", "day", "hour", "minute", "second"};
if (seconds == 0) return "now";
for (int i = 0; i < 5; i++) {
if (seconds >= units[i]) {
int q = seconds / units[i];
seconds = seconds % units[i];
res += (res.equals("") ? "" : (seconds == 0 ? " and " : ", "))
+ q + " " + labels[i] + (q > 1 ? "s" : "");
}
}
return res;
}
}
Option 3:
import java.util.*;
public class TimeFormatter {
private static final String[] TIME_WORDS =
{
" years",
" days",
" hours",
" minutes",
" seconds"
};
public static String formatDuration(int seconds) {
if(seconds == 0)
return "now";
int[] timeUnits =
{
seconds/(60*60*24*365),
(seconds/(60*60*24))%365,
(seconds/(60*60))%24,
(seconds/60)%60,
seconds%60
};
List<String> timeElements = new ArrayList<>();
for(int i=0; i<timeUnits.length; i++) {
if(timeUnits[i] != 0)
timeElements.add(timeUnits[i] + getTimeWord(timeUnits[i], TIME_WORDS[i]));
}
StringBuilder result = new StringBuilder();
for(int i=0; i<timeElements.size(); i++) {
result.append(timeElements.get(i) + getSeparator(i, timeElements.size()));
}
return result.toString();
}
private static String getTimeWord(int timeUnit, String timeWord) {
if(timeUnit == 1)
return timeWord.substring(0, timeWord.length()-1);
return timeWord;
}
private static String getSeparator(int idx, int sz) {
String separator = "";
if(idx < sz-2)
separator = ", ";
else if(idx == sz-2)
separator = " and ";
return separator;
}
}
Test cases to validate our solution
import org.junit.Test;
import static org.junit.Assert.*;
public class TimeFormatterTest {
@Test
public void exampleTests() {
assertEquals("1 second", TimeFormatter.formatDuration(1));
assertEquals("1 minute and 2 seconds", TimeFormatter.formatDuration(62));
assertEquals("2 minutes", TimeFormatter.formatDuration(120));
assertEquals("1 hour", TimeFormatter.formatDuration(3600));
assertEquals("1 hour, 1 minute and 2 seconds", TimeFormatter.formatDuration(3662));
}
}