Human readable duration format in Java

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 yearsdayshoursminutes 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)
Code language: Java (java)

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 seconds1 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"); } }
Code language: Java (java)

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; } }
Code language: Java (java)

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; } }
Code language: Java (java)

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)); } }
Code language: Java (java)
Tags:
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments