diff --git a/pgpainless-core/src/main/kotlin/openpgp/DateExtensions.kt b/pgpainless-core/src/main/kotlin/openpgp/DateExtensions.kt index 2763cb55..0dc6de40 100644 --- a/pgpainless-core/src/main/kotlin/openpgp/DateExtensions.kt +++ b/pgpainless-core/src/main/kotlin/openpgp/DateExtensions.kt @@ -14,7 +14,6 @@ import java.util.* * Since '0' is a special date value in the OpenPGP specification (e.g. '0' means no expiration for * expiration dates), this method will return 'null' if seconds is 0. * - * @param date date * @param seconds number of seconds to be added * @return date plus seconds or null if seconds is '0' */ @@ -25,9 +24,19 @@ fun Date.plusSeconds(seconds: Long): Date? { return if (seconds == 0L) null else Date(this.time + 1000 * seconds) } +/** This date in seconds since epoch. */ val Date.asSeconds: Long get() = time / 1000 +/** + * Return the number of seconds that would need to be added to this date in order to reach the later + * date. Note: This method requires the [later] date to be indeed greater or equal to this date, + * otherwise an [IllegalArgumentException] is thrown. + * + * @param later later date + * @return difference between this and [later] in seconds + * @throws IllegalArgumentException if later is not greater or equal to this date + */ fun Date.secondsTill(later: Date): Long { require(this <= later) { "Timestamp MUST be before the later timestamp." } return later.asSeconds - this.asSeconds diff --git a/pgpainless-core/src/test/java/openpgp/DateExtensionTest.java b/pgpainless-core/src/test/java/openpgp/DateExtensionTest.java new file mode 100644 index 00000000..c36315bb --- /dev/null +++ b/pgpainless-core/src/test/java/openpgp/DateExtensionTest.java @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package openpgp; + +import org.junit.jupiter.api.Test; + +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DateExtensionTest { + + @Test + public void testDatePlusSecondsBaseCase() { + Date t0 = DateExtensionsKt.parseUTC("2025-05-12 10:36:53 UTC"); + Date t1 = DateExtensionsKt.plusSeconds(t0, 7); + assertEquals("2025-05-12 10:37:00 UTC", DateExtensionsKt.formatUTC(t1)); + } + + @Test + public void testDatePlusZeroReturnsNull() { + Date t0 = DateExtensionsKt.parseUTC("2025-05-12 10:36:53 UTC"); + Date t1 = DateExtensionsKt.plusSeconds(t0, 0); + assertNull(t1); + } + + @Test + public void testDatePlusSecondsOverflowing() { + Date now = new Date(); + // expect IAE because of time field overflowing + assertThrows(IllegalArgumentException.class, () -> + DateExtensionsKt.plusSeconds(now, Long.MAX_VALUE - 10000)); + } + + @Test + public void testParsingMalformedUTCTimestampThrows() { + assertThrows(IllegalArgumentException.class, () -> + DateExtensionsKt.parseUTC("2025-05-12 10:36:XX UTC")); + } +}