From 9665b4d907f34479de218ee0e34e73328fcc7c86 Mon Sep 17 00:00:00 2001 From: BanulaKumarage Date: Wed, 1 Oct 2025 00:16:38 +0530 Subject: [PATCH 1/5] Added-time-converter-algorithm --- .../conversions/TimeConverter.java | 103 ++++++++++++++++++ .../conversions/TimeConverterTest.java | 81 ++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 src/main/java/com/thealgorithms/conversions/TimeConverter.java create mode 100644 src/test/java/com/thealgorithms/conversions/TimeConverterTest.java diff --git a/src/main/java/com/thealgorithms/conversions/TimeConverter.java b/src/main/java/com/thealgorithms/conversions/TimeConverter.java new file mode 100644 index 000000000000..c303c90e530a --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/TimeConverter.java @@ -0,0 +1,103 @@ +package com.thealgorithms.conversions; + +import java.util.Locale; +import java.util.Map; + +/** + * A utility class to convert between different units of time. + * + *

This class supports conversions between the following units: + *

+ * + *

The conversion is based on predefined constants in seconds. + * Results are rounded to three decimal places for consistency. + * + *

This class is final and cannot be instantiated. + */ +public final class TimeConverter { + + private TimeConverter() { + // Prevent instantiation + } + + /** + * Supported time units with their equivalent in seconds. + */ + private enum TimeUnit { + SECONDS(1.0), + MINUTES(60.0), + HOURS(3600.0), + DAYS(86400.0), + WEEKS(604800.0), + MONTHS(2629800.0), // 30.44 days + YEARS(31557600.0); // 365.25 days + + private final double seconds; + + TimeUnit(double seconds) { + this.seconds = seconds; + } + + public double toSeconds(double value) { + return value * seconds; + } + + public double fromSeconds(double secondsValue) { + return secondsValue / seconds; + } + } + + private static final Map UNIT_LOOKUP = + Map.ofEntries( + Map.entry("seconds", TimeUnit.SECONDS), + Map.entry("minutes", TimeUnit.MINUTES), + Map.entry("hours", TimeUnit.HOURS), + Map.entry("days", TimeUnit.DAYS), + Map.entry("weeks", TimeUnit.WEEKS), + Map.entry("months", TimeUnit.MONTHS), + Map.entry("years", TimeUnit.YEARS)); + + /** + * Converts a time value from one unit to another. + * + * @param timeValue the numeric value of time to convert; must be non-negative + * @param unitFrom the unit of the input value (e.g., "minutes", "hours") + * @param unitTo the unit to convert into (e.g., "seconds", "days") + * @return the converted value in the target unit, rounded to three decimals + * @throws IllegalArgumentException if {@code timeValue} is negative + * @throws IllegalArgumentException if either {@code unitFrom} or {@code unitTo} is not supported + */ + public static double convertTime(double timeValue, String unitFrom, String unitTo) { + if (timeValue < 0) { + throw new IllegalArgumentException("timeValue must be a non-negative number."); + } + + TimeUnit from = resolveUnit(unitFrom); + TimeUnit to = resolveUnit(unitTo); + + double secondsValue = from.toSeconds(timeValue); + double converted = to.fromSeconds(secondsValue); + + return Math.round(converted * 1000.0) / 1000.0; + } + + private static TimeUnit resolveUnit(String unit) { + if (unit == null) { + throw new IllegalArgumentException("Unit cannot be null."); + } + TimeUnit resolved = UNIT_LOOKUP.get(unit.toLowerCase(Locale.ROOT)); + if (resolved == null) { + throw new IllegalArgumentException( + "Invalid unit '" + unit + "'. Supported units are: " + UNIT_LOOKUP.keySet()); + } + return resolved; + } +} diff --git a/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java b/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java new file mode 100644 index 000000000000..24f71d71ccc6 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java @@ -0,0 +1,81 @@ +package com.thealgorithms.conversions; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class TimeConverterTest { + + @ParameterizedTest(name = "{0} {1} -> {2} {3}") + @CsvSource({ + "60, seconds, minutes, 1", + "120, seconds, minutes, 2", + "2, minutes, seconds, 120", + "2, hours, minutes, 120", + "1, days, hours, 24", + "1, weeks, days, 7", + "1, months, days, 30.438", + "1, years, days, 365.25", + "3600, seconds, hours, 1", + "86400, seconds, days, 1", + "604800, seconds, weeks, 1", + "31557600, seconds, years, 1" + }) + void testValidConversions(double value, String from, String to, double expected) { + assertEquals(expected, TimeConverter.convertTime(value, from, to)); + } + + @Test + @DisplayName("Zero conversion returns zero") + void testZeroValue() { + assertEquals(0.0, TimeConverter.convertTime(0, "seconds", "hours")); + } + + @Test + @DisplayName("Same-unit conversion returns original value") + void testSameUnitConversion() { + assertEquals(123.456, TimeConverter.convertTime(123.456, "minutes", "minutes")); + } + + @Test + @DisplayName("Negative value throws exception") + void testNegativeValue() { + assertThrows(IllegalArgumentException.class, () -> + TimeConverter.convertTime(-5, "seconds", "minutes")); + } + + @ParameterizedTest + @CsvSource({ + "lightyears, seconds", + "minutes, centuries" + }) + void testInvalidUnits(String from, String to) { + assertThrows(IllegalArgumentException.class, () -> + TimeConverter.convertTime(10, from, to)); + } + + static Stream roundTripCases() { + return Stream.of( + org.junit.jupiter.params.provider.Arguments.of(1.0, "hours", "minutes"), + org.junit.jupiter.params.provider.Arguments.of(2.5, "days", "hours"), + org.junit.jupiter.params.provider.Arguments.of(1000, "seconds", "minutes")); + } + + @ParameterizedTest + @MethodSource("roundTripCases") + @DisplayName("Round-trip conversion returns original value") + void testRoundTripConversion(double value, String from, String to) { + double converted = TimeConverter.convertTime(value, from, to); + double roundTrip = TimeConverter.convertTime(converted, to, from); + assertEquals( + Math.round(value * 1000.0) / 1000.0, + Math.round(roundTrip * 1000.0) / 1000.0, + 0.05); + } +} From c5c0d2316df7382b7e1ba3e9ae3a723608c176e4 Mon Sep 17 00:00:00 2001 From: BanulaKumarage Date: Wed, 1 Oct 2025 00:22:11 +0530 Subject: [PATCH 2/5] Updated-java-docs --- src/main/java/com/thealgorithms/conversions/TimeConverter.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/thealgorithms/conversions/TimeConverter.java b/src/main/java/com/thealgorithms/conversions/TimeConverter.java index c303c90e530a..2a269cd7c740 100644 --- a/src/main/java/com/thealgorithms/conversions/TimeConverter.java +++ b/src/main/java/com/thealgorithms/conversions/TimeConverter.java @@ -21,6 +21,8 @@ * Results are rounded to three decimal places for consistency. * *

This class is final and cannot be instantiated. + * + * @see Wikipedia: Unit of time */ public final class TimeConverter { From 59a2d0f28d25819502a603dedfeb99b44d5ad2ad Mon Sep 17 00:00:00 2001 From: BanulaKumarage Date: Wed, 1 Oct 2025 00:36:01 +0530 Subject: [PATCH 3/5] Fixed-formatting-issues --- .../conversions/TimeConverter.java | 18 ++----- .../conversions/TimeConverterTest.java | 47 +++++-------------- 2 files changed, 17 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/thealgorithms/conversions/TimeConverter.java b/src/main/java/com/thealgorithms/conversions/TimeConverter.java index 2a269cd7c740..41cae37d7ad1 100644 --- a/src/main/java/com/thealgorithms/conversions/TimeConverter.java +++ b/src/main/java/com/thealgorithms/conversions/TimeConverter.java @@ -39,8 +39,8 @@ private enum TimeUnit { HOURS(3600.0), DAYS(86400.0), WEEKS(604800.0), - MONTHS(2629800.0), // 30.44 days - YEARS(31557600.0); // 365.25 days + MONTHS(2629800.0), // 30.44 days + YEARS(31557600.0); // 365.25 days private final double seconds; @@ -57,15 +57,8 @@ public double fromSeconds(double secondsValue) { } } - private static final Map UNIT_LOOKUP = - Map.ofEntries( - Map.entry("seconds", TimeUnit.SECONDS), - Map.entry("minutes", TimeUnit.MINUTES), - Map.entry("hours", TimeUnit.HOURS), - Map.entry("days", TimeUnit.DAYS), - Map.entry("weeks", TimeUnit.WEEKS), - Map.entry("months", TimeUnit.MONTHS), - Map.entry("years", TimeUnit.YEARS)); + private static final Map UNIT_LOOKUP + = Map.ofEntries(Map.entry("seconds", TimeUnit.SECONDS), Map.entry("minutes", TimeUnit.MINUTES), Map.entry("hours", TimeUnit.HOURS), Map.entry("days", TimeUnit.DAYS), Map.entry("weeks", TimeUnit.WEEKS), Map.entry("months", TimeUnit.MONTHS), Map.entry("years", TimeUnit.YEARS)); /** * Converts a time value from one unit to another. @@ -97,8 +90,7 @@ private static TimeUnit resolveUnit(String unit) { } TimeUnit resolved = UNIT_LOOKUP.get(unit.toLowerCase(Locale.ROOT)); if (resolved == null) { - throw new IllegalArgumentException( - "Invalid unit '" + unit + "'. Supported units are: " + UNIT_LOOKUP.keySet()); + throw new IllegalArgumentException("Invalid unit '" + unit + "'. Supported units are: " + UNIT_LOOKUP.keySet()); } return resolved; } diff --git a/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java b/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java index 24f71d71ccc6..c23762a56347 100644 --- a/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java +++ b/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java @@ -1,33 +1,21 @@ package com.thealgorithms.conversions; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.*; - class TimeConverterTest { @ParameterizedTest(name = "{0} {1} -> {2} {3}") - @CsvSource({ - "60, seconds, minutes, 1", - "120, seconds, minutes, 2", - "2, minutes, seconds, 120", - "2, hours, minutes, 120", - "1, days, hours, 24", - "1, weeks, days, 7", - "1, months, days, 30.438", - "1, years, days, 365.25", - "3600, seconds, hours, 1", - "86400, seconds, days, 1", - "604800, seconds, weeks, 1", - "31557600, seconds, years, 1" - }) - void testValidConversions(double value, String from, String to, double expected) { + @CsvSource({"60, seconds, minutes, 1", "120, seconds, minutes, 2", "2, minutes, seconds, 120", "2, hours, minutes, 120", "1, days, hours, 24", "1, weeks, days, 7", "1, months, days, 30.438", "1, years, days, 365.25", "3600, seconds, hours, 1", "86400, seconds, days, 1", + "604800, seconds, weeks, 1", "31557600, seconds, years, 1"}) + void + testValidConversions(double value, String from, String to, double expected) { assertEquals(expected, TimeConverter.convertTime(value, from, to)); } @@ -46,25 +34,17 @@ void testSameUnitConversion() { @Test @DisplayName("Negative value throws exception") void testNegativeValue() { - assertThrows(IllegalArgumentException.class, () -> - TimeConverter.convertTime(-5, "seconds", "minutes")); + assertThrows(IllegalArgumentException.class, () -> TimeConverter.convertTime(-5, "seconds", "minutes")); } @ParameterizedTest - @CsvSource({ - "lightyears, seconds", - "minutes, centuries" - }) + @CsvSource({"lightyears, seconds", "minutes, centuries"}) void testInvalidUnits(String from, String to) { - assertThrows(IllegalArgumentException.class, () -> - TimeConverter.convertTime(10, from, to)); + assertThrows(IllegalArgumentException.class, () -> TimeConverter.convertTime(10, from, to)); } static Stream roundTripCases() { - return Stream.of( - org.junit.jupiter.params.provider.Arguments.of(1.0, "hours", "minutes"), - org.junit.jupiter.params.provider.Arguments.of(2.5, "days", "hours"), - org.junit.jupiter.params.provider.Arguments.of(1000, "seconds", "minutes")); + return Stream.of(org.junit.jupiter.params.provider.Arguments.of(1.0, "hours", "minutes"), org.junit.jupiter.params.provider.Arguments.of(2.5, "days", "hours"), org.junit.jupiter.params.provider.Arguments.of(1000, "seconds", "minutes")); } @ParameterizedTest @@ -73,9 +53,6 @@ static Stream roundTripCases() { void testRoundTripConversion(double value, String from, String to) { double converted = TimeConverter.convertTime(value, from, to); double roundTrip = TimeConverter.convertTime(converted, to, from); - assertEquals( - Math.round(value * 1000.0) / 1000.0, - Math.round(roundTrip * 1000.0) / 1000.0, - 0.05); + assertEquals(Math.round(value * 1000.0) / 1000.0, Math.round(roundTrip * 1000.0) / 1000.0, 0.05); } } From a9bb0b9fd4300dc1b5a2f42e085b0baf87190ced Mon Sep 17 00:00:00 2001 From: BanulaKumarage Date: Wed, 1 Oct 2025 00:43:50 +0530 Subject: [PATCH 4/5] Enhanced-unit-tests --- .../thealgorithms/conversions/TimeConverterTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java b/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java index c23762a56347..5d81067e8abc 100644 --- a/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java +++ b/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java @@ -43,6 +43,16 @@ void testInvalidUnits(String from, String to) { assertThrows(IllegalArgumentException.class, () -> TimeConverter.convertTime(10, from, to)); } + @Test + @DisplayName("Null unit throws exception") + void testNullUnit() { + assertThrows(IllegalArgumentException.class, () -> TimeConverter.convertTime(10, null, "seconds")); + + assertThrows(IllegalArgumentException.class, () -> TimeConverter.convertTime(10, "minutes", null)); + + assertThrows(IllegalArgumentException.class, () -> TimeConverter.convertTime(10, null, null)); + } + static Stream roundTripCases() { return Stream.of(org.junit.jupiter.params.provider.Arguments.of(1.0, "hours", "minutes"), org.junit.jupiter.params.provider.Arguments.of(2.5, "days", "hours"), org.junit.jupiter.params.provider.Arguments.of(1000, "seconds", "minutes")); } From 65813c60ff84a58e5c5ef2c6492f57f7fb0afe2e Mon Sep 17 00:00:00 2001 From: BanulaKumarage Date: Wed, 1 Oct 2025 00:49:09 +0530 Subject: [PATCH 5/5] Fixed-failing-check-style --- .../java/com/thealgorithms/conversions/TimeConverterTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java b/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java index 5d81067e8abc..4e10318d4563 100644 --- a/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java +++ b/src/test/java/com/thealgorithms/conversions/TimeConverterTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.conversions; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName;