Skip to content

Commit

Permalink
Interval conversion from/to Duration
Browse files Browse the repository at this point in the history
The conversion algorithm assumes a year last 12 months and a month lasts 30 days, as Postgres does and ISO 8601 suggests.

See https://github.com/postgres/postgres/blob/5bbdfa8a18dc56d3e64aa723a68e02e897cb5ec3/src/include/datatype/timestamp.h#L116

Signed-off-by: Thomas Segismont <tsegismont@gmail.com>
  • Loading branch information
tsegismont committed Sep 9, 2024
1 parent f763432 commit 009b8bf
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 4 deletions.
54 changes: 50 additions & 4 deletions vertx-pg-client/src/main/java/io/vertx/pgclient/data/Interval.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.vertx.pgclient.data;

import io.vertx.codegen.annotations.DataObject;
import io.vertx.core.json.JsonObject;
import java.time.Duration;

/**
* Postgres Interval is date and time based
Expand Down Expand Up @@ -84,6 +83,35 @@ public static Interval of(int years) {
return new Interval(years);
}

/**
* Creates an instance from the given {@link Duration}.
* <p>
* The conversion algorithm assumes a year last 12 months and a month lasts 30 days, as <a href="https://github.com/postgres/postgres/blob/5bbdfa8a18dc56d3e64aa723a68e02e897cb5ec3/src/include/datatype/timestamp.h#L116">Postgres does</a> and ISO 8601 suggests.
*
* @param duration the value to convert
* @return a new instance of {@link Interval}
*/
public static Interval of(Duration duration) {
long totalSeconds = duration.getSeconds();

int years = (int) (totalSeconds / 31104000);
long remainder = totalSeconds % 31104000;

int months = (int) (remainder / 2592000);
remainder = totalSeconds % 2592000;

int days = (int) (remainder / 86400);
remainder = remainder % 86400;

int hours = (int) (remainder / 3600);
remainder = remainder % 3600;

int minutes = (int) (remainder / 60);
remainder = remainder % 60;

return new Interval(years, months, days, hours, minutes, (int) remainder, duration.getNano() / 1000);
}

public Interval years(int years) {
this.years = years;
return this;
Expand Down Expand Up @@ -203,7 +231,25 @@ public int hashCode() {

@Override
public String toString() {
return "Interval( " + years + " years " + months + " months " + days + " days " + hours + " hours " +
minutes + " minutes " + seconds + (microseconds == 0 ? "" : "." + Math.abs(microseconds)) + " seconds )";
return "Interval( "
+ years + " years "
+ months + " months "
+ days + " days "
+ hours + " hours "
+ minutes + " minutes "
+ seconds + " seconds "
+ microseconds + " microseconds )";
}

/**
* Convert this interval to an instance of {@link Duration}.
* <p>
* The conversion algorithm assumes a year last 12 months and a month lasts 30 days, as <a href="https://github.com/postgres/postgres/blob/5bbdfa8a18dc56d3e64aa723a68e02e897cb5ec3/src/include/datatype/timestamp.h#L116">Postgres does</a> and ISO 8601 suggests.
*
* @return an instance of {@link Duration} representing the same amount of time as this interval
*/
public Duration toDuration() {
return Duration.ofSeconds(((((years * 12L + months) * 30L + days) * 24L + hours) * 60 + minutes) * 60 + seconds)
.plusNanos(microseconds * 1000L);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2011-2024 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/

package io.vertx.pgclient.data;

import org.junit.Test;

import java.time.Duration;
import java.time.LocalDateTime;

import static java.time.temporal.ChronoUnit.MICROS;
import static org.junit.Assert.assertEquals;

public class IntervalTest {

@Test
public void testFromDuration() {
assertEquals(Interval.of(), Interval.of(Duration.ZERO));

assertEquals(
Interval.of(0, 0, 0, 0, 0, -1, 999999),
Interval.of(Duration.ZERO.minus(1, MICROS)));

assertEquals(
Interval.of(0, 0, -15, 0, -12, -3),
Interval.of(Duration.ofDays(-15)
.minusMinutes(12)
.minusSeconds(3)));

assertEquals(
Interval.of(1, 3, 14, 3, 55, 5, 463123),
Interval.of(Duration.ofDays(12 * 30 + 3 * 30 + 14)
.plusHours(3)
.plusMinutes(55)
.plusSeconds(5)
.plusNanos(463123 * 1000)));
}

@Test
public void testToDuration() {
assertEquals(Duration.ZERO, Interval.of().toDuration());

assertEquals(
Duration.ZERO.minus(1, MICROS),
Interval.of(0, 0, 0, 0, 0, 0, -1).toDuration());

assertEquals(
Duration.ofDays(-15)
.minusMinutes(12)
.minusSeconds(3),
Interval.of(0, 0, -15, 0, -12, -3).toDuration());

assertEquals(
Duration.between(
LocalDateTime.of(2024, 2, 1, 13, 30),
LocalDateTime.of(2024, 3, 1, 12, 0)),
Interval.of(0, 0, 29, -1, -30, 0, 0).toDuration());
}
}

0 comments on commit 009b8bf

Please sign in to comment.