8133079: java.time LocalDate and LocalTime ofInstant() factory methods

Reviewed-by: rriggs, scolebourne
This commit is contained in:
Roger Riggs 2015-11-16 15:28:55 -05:00
parent 3c5dd5581c
commit 9322c398b5
4 changed files with 127 additions and 15 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -220,12 +220,8 @@ public final class LocalDate
*/ */
public static LocalDate now(Clock clock) { public static LocalDate now(Clock clock) {
Objects.requireNonNull(clock, "clock"); Objects.requireNonNull(clock, "clock");
// inline to avoid creating object and Instant checks
final Instant now = clock.instant(); // called once final Instant now = clock.instant(); // called once
ZoneOffset offset = clock.getZone().getRules().getOffset(now); return ofInstant(now, clock.getZone());
long epochSec = now.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later
long epochDay = Math.floorDiv(epochSec, SECONDS_PER_DAY);
return LocalDate.ofEpochDay(epochDay);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -298,6 +294,30 @@ public final class LocalDate
return new LocalDate(year, moy.getValue(), dom); return new LocalDate(year, moy.getValue(), dom);
} }
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code LocalDate} from an {@code Instant} and zone ID.
* <p>
* This creates a local date based on the specified instant.
* First, the offset from UTC/Greenwich is obtained using the zone ID and instant,
* which is simple as there is only one valid offset for each instant.
* Then, the instant and offset are used to calculate the local date.
*
* @param instant the instant to create the date from, not null
* @param zone the time-zone, which may be an offset, not null
* @return the local date, not null
* @throws DateTimeException if the result exceeds the supported range
*/
public static LocalDate ofInstant(Instant instant, ZoneId zone) {
Objects.requireNonNull(instant, "instant");
Objects.requireNonNull(zone, "zone");
ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant);
long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY);
return ofEpochDay(localEpochDay);
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Obtains an instance of {@code LocalDate} from the epoch day count. * Obtains an instance of {@code LocalDate} from the epoch day count.

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -272,12 +272,8 @@ public final class LocalTime
*/ */
public static LocalTime now(Clock clock) { public static LocalTime now(Clock clock) {
Objects.requireNonNull(clock, "clock"); Objects.requireNonNull(clock, "clock");
// inline OffsetTime factory to avoid creating object and InstantProvider checks
final Instant now = clock.instant(); // called once final Instant now = clock.instant(); // called once
ZoneOffset offset = clock.getZone().getRules().getOffset(now); return ofInstant(now, clock.getZone());
long localSecond = now.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later
int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + now.getNano());
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -343,6 +339,27 @@ public final class LocalTime
return create(hour, minute, second, nanoOfSecond); return create(hour, minute, second, nanoOfSecond);
} }
/**
* Obtains an instance of {@code LocalTime} from an {@code Instant} and zone ID.
* <p>
* This creates a local time based on the specified instant.
* First, the offset from UTC/Greenwich is obtained using the zone ID and instant,
* which is simple as there is only one valid offset for each instant.
* Then, the instant and offset are used to calculate the local time.
*
* @param instant the instant to create the time from, not null
* @param zone the time-zone, which may be an offset, not null
* @return the local time, not null
*/
public static LocalTime ofInstant(Instant instant, ZoneId zone) {
Objects.requireNonNull(instant, "instant");
Objects.requireNonNull(zone, "zone");
ZoneOffset offset = zone.getRules().getOffset(instant);
long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + instant.getNano());
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Obtains an instance of {@code LocalTime} from a second-of-day value. * Obtains an instance of {@code LocalTime} from a second-of-day value.

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -86,8 +86,6 @@ import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertSame; import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.time.Clock; import java.time.Clock;
import java.time.DateTimeException; import java.time.DateTimeException;
import java.time.DayOfWeek; import java.time.DayOfWeek;
@ -135,6 +133,7 @@ public class TCKLocalDate extends AbstractDateTimeTest {
private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1);
private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2);
private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2);
private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris");
private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza"); private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza");
@ -475,6 +474,48 @@ public class TCKLocalDate extends AbstractDateTimeTest {
return date.withDayOfMonth(date.getMonth().length(isIsoLeap(date.getYear()))); return date.withDayOfMonth(date.getMonth().length(isIsoLeap(date.getYear())));
} }
//-----------------------------------------------------------------------
// ofInstant()
//-----------------------------------------------------------------------
@DataProvider(name="instantFactory")
Object[][] data_instantFactory() {
return new Object[][] {
{Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), ZONE_PARIS, LocalDate.of(1970, 1, 2)},
{Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), OFFSET_MTWO, LocalDate.of(1970, 1, 1)},
{Instant.ofEpochSecond(-86400 + 4, 500), OFFSET_PTWO, LocalDate.of(1969, 12, 31)},
{OffsetDateTime.of(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0), ZoneOffset.UTC).toInstant(),
ZoneOffset.UTC, LocalDate.MIN},
{OffsetDateTime.of(LocalDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 999_999_999), ZoneOffset.UTC).toInstant(),
ZoneOffset.UTC, LocalDate.MAX},
};
}
@Test(dataProvider="instantFactory")
public void factory_ofInstant(Instant instant, ZoneId zone, LocalDate expected) {
LocalDate test = LocalDate.ofInstant(instant, zone);
assertEquals(test, expected);
}
@Test(expectedExceptions=DateTimeException.class)
public void factory_ofInstant_instantTooBig() {
LocalDate.ofInstant(Instant.MAX, OFFSET_PONE);
}
@Test(expectedExceptions=DateTimeException.class)
public void factory_ofInstant_instantTooSmall() {
LocalDate.ofInstant(Instant.MIN, OFFSET_PONE);
}
@Test(expectedExceptions=NullPointerException.class)
public void factory_ofInstant_nullInstant() {
LocalDate.ofInstant((Instant) null, ZONE_GAZA);
}
@Test(expectedExceptions=NullPointerException.class)
public void factory_ofInstant_nullZone() {
LocalDate.ofInstant(Instant.EPOCH, (ZoneId) null);
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ofEpochDay() // ofEpochDay()
//----------------------------------------------------------------------- //-----------------------------------------------------------------------

View file

@ -102,6 +102,7 @@ import java.time.LocalTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.OffsetTime; import java.time.OffsetTime;
import java.time.Period; import java.time.Period;
import java.time.Year;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
@ -137,6 +138,7 @@ import org.testng.annotations.Test;
public class TCKLocalTime extends AbstractDateTimeTest { public class TCKLocalTime extends AbstractDateTimeTest {
private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2);
private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2);
private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris");
private LocalTime TEST_12_30_40_987654321; private LocalTime TEST_12_30_40_987654321;
@ -420,6 +422,38 @@ public class TCKLocalTime extends AbstractDateTimeTest {
LocalTime.of(0, 0, 0, 1000000000); LocalTime.of(0, 0, 0, 1000000000);
} }
//-----------------------------------------------------------------------
// ofInstant()
//-----------------------------------------------------------------------
@DataProvider(name="instantFactory")
Object[][] data_instantFactory() {
return new Object[][] {
{Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), ZONE_PARIS, LocalTime.of(2, 2, 4, 500)},
{Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), OFFSET_MTWO, LocalTime.of(23, 2, 4, 500)},
{Instant.ofEpochSecond(-86400 + 4, 500), OFFSET_PTWO, LocalTime.of(2, 0, 4, 500)},
{OffsetDateTime.of(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0), ZoneOffset.UTC).toInstant(),
ZoneOffset.UTC, LocalTime.MIN},
{OffsetDateTime.of(LocalDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 999_999_999), ZoneOffset.UTC).toInstant(),
ZoneOffset.UTC, LocalTime.MAX},
};
}
@Test(dataProvider="instantFactory")
public void factory_ofInstant(Instant instant, ZoneId zone, LocalTime expected) {
LocalTime test = LocalTime.ofInstant(instant, zone);
assertEquals(test, expected);
}
@Test(expectedExceptions=NullPointerException.class)
public void factory_ofInstant_nullInstant() {
LocalTime.ofInstant((Instant) null, ZONE_PARIS);
}
@Test(expectedExceptions=NullPointerException.class)
public void factory_ofInstant_nullZone() {
LocalTime.ofInstant(Instant.EPOCH, (ZoneId) null);
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ofSecondOfDay(long) // ofSecondOfDay(long)
//----------------------------------------------------------------------- //-----------------------------------------------------------------------