org.neo4j.values.storable.LocalDateTimeValue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-values Show documentation
Show all versions of neo4j-values Show documentation
Neo4j property value system.
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.values.storable;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
import java.time.temporal.TemporalUnit;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.neo4j.exceptions.InvalidArgumentException;
import org.neo4j.exceptions.UnsupportedTemporalUnitException;
import org.neo4j.internal.helpers.collection.Pair;
import org.neo4j.values.AnyValue;
import org.neo4j.values.StructureBuilder;
import org.neo4j.values.ValueMapper;
import org.neo4j.values.virtual.MapValue;
import static java.time.Instant.ofEpochSecond;
import static java.time.LocalDateTime.ofInstant;
import static java.time.ZoneOffset.UTC;
import static java.util.Objects.requireNonNull;
import static org.neo4j.memory.HeapEstimator.LOCAL_DATE_TIME_SIZE;
import static org.neo4j.memory.HeapEstimator.shallowSizeOfInstance;
import static org.neo4j.values.storable.DateTimeValue.parseZoneName;
import static org.neo4j.values.storable.DateValue.DATE_PATTERN;
import static org.neo4j.values.storable.DateValue.parseDate;
import static org.neo4j.values.storable.IntegralValue.safeCastIntegral;
import static org.neo4j.values.storable.LocalTimeValue.TIME_PATTERN;
import static org.neo4j.values.storable.LocalTimeValue.parseTime;
public final class LocalDateTimeValue extends TemporalValue
{
private static final long INSTANCE_SIZE = shallowSizeOfInstance( LocalDateTimeValue.class ) + LOCAL_DATE_TIME_SIZE;
public static final LocalDateTimeValue MIN_VALUE = new LocalDateTimeValue( LocalDateTime.MIN );
public static final LocalDateTimeValue MAX_VALUE = new LocalDateTimeValue( LocalDateTime.MAX );
private final LocalDateTime value;
private final long epochSecondsInUTC;
private LocalDateTimeValue( LocalDateTime value )
{
this.value = value;
this.epochSecondsInUTC = this.value.toEpochSecond(UTC);
}
public static LocalDateTimeValue localDateTime( DateValue date, LocalTimeValue time )
{
return new LocalDateTimeValue( LocalDateTime.of( date.temporal(), time.temporal() ) );
}
public static LocalDateTimeValue localDateTime(
int year, int month, int day, int hour, int minute, int second, int nanoOfSecond )
{
return new LocalDateTimeValue( assertValidArgument( () -> LocalDateTime.of( year, month, day, hour, minute, second, nanoOfSecond ) ) );
}
public static LocalDateTimeValue localDateTime( LocalDateTime value )
{
return new LocalDateTimeValue( requireNonNull( value, "LocalDateTime" ) );
}
public static LocalDateTimeValue localDateTime( long epochSecond, long nano )
{
return new LocalDateTimeValue( localDateTimeRaw( epochSecond, nano ) );
}
public static LocalDateTime localDateTimeRaw( long epochSecond, long nano )
{
return assertValidArgument( () -> ofInstant( ofEpochSecond( epochSecond, nano ), UTC ) );
}
public static LocalDateTimeValue parse( CharSequence text )
{
return parse( LocalDateTimeValue.class, PATTERN, LocalDateTimeValue::parse, text );
}
public static LocalDateTimeValue parse( TextValue text )
{
return parse( LocalDateTimeValue.class, PATTERN, LocalDateTimeValue::parse, text );
}
public static LocalDateTimeValue now( Clock clock )
{
return new LocalDateTimeValue( LocalDateTime.now( clock ) );
}
public static LocalDateTimeValue now( Clock clock, String timezone )
{
return now( clock.withZone( parseZoneName( timezone ) ) );
}
public static LocalDateTimeValue now( Clock clock, Supplier defaultZone )
{
return now( clock.withZone( defaultZone.get() ) );
}
public static LocalDateTimeValue build( MapValue map, Supplier defaultZone )
{
return StructureBuilder.build( builder( defaultZone ), map );
}
public static LocalDateTimeValue select( AnyValue from, Supplier defaultZone )
{
return builder( defaultZone ).selectDateTime( from );
}
public static LocalDateTimeValue truncate(
TemporalUnit unit,
TemporalValue input,
MapValue fields,
Supplier defaultZone )
{
Pair pair = getTruncatedDateAndTime( unit, input, "local date time" );
LocalDate truncatedDate = pair.first();
LocalTime truncatedTime = pair.other();
LocalDateTime truncatedLDT = LocalDateTime.of( truncatedDate, truncatedTime );
if ( fields.size() == 0 )
{
return localDateTime( truncatedLDT );
}
else
{
return updateFieldMapWithConflictingSubseconds( fields, unit, truncatedLDT,
( mapValue, localDateTime ) -> {
if ( mapValue.size() == 0 )
{
return localDateTime( localDateTime );
}
else
{
return build( mapValue.updatedWith( "datetime", localDateTime( localDateTime ) ),
defaultZone );
}
} );
}
}
private static final LocalDateTime DEFAULT_LOCAL_DATE_TIME =
LocalDateTime.of( TemporalFields.year.defaultValue, TemporalFields.month.defaultValue,
TemporalFields.day.defaultValue, TemporalFields.hour.defaultValue,
TemporalFields.minute.defaultValue );
private static DateTimeValue.DateTimeBuilder builder( Supplier defaultZone )
{
return new DateTimeValue.DateTimeBuilder<>( defaultZone )
{
@Override
protected boolean supportsTimeZone()
{
return false;
}
@Override
protected boolean supportsEpoch()
{
return false;
}
@Override
public LocalDateTimeValue buildInternal()
{
boolean selectingDate = fields.containsKey( TemporalFields.date );
boolean selectingTime = fields.containsKey( TemporalFields.time );
boolean selectingDateTime = fields.containsKey( TemporalFields.datetime );
LocalDateTime result;
if ( selectingDateTime )
{
AnyValue dtField = fields.get( TemporalFields.datetime );
if ( !(dtField instanceof TemporalValue) )
{
throw new InvalidArgumentException( String.format( "Cannot construct local date time from: %s", dtField ) );
}
TemporalValue dt = (TemporalValue) dtField;
result = LocalDateTime.of( dt.getDatePart(), dt.getLocalTimePart() );
}
else if ( selectingTime || selectingDate )
{
LocalTime time;
if ( selectingTime )
{
AnyValue timeField = fields.get( TemporalFields.time );
if ( !(timeField instanceof TemporalValue) )
{
throw new InvalidArgumentException( String.format( "Cannot construct local time from: %s", timeField ) );
}
TemporalValue t = (TemporalValue) timeField;
time = t.getLocalTimePart();
}
else
{
time = LocalTimeValue.DEFAULT_LOCAL_TIME;
}
LocalDate date;
if ( selectingDate )
{
AnyValue dateField = fields.get( TemporalFields.date );
if ( !(dateField instanceof TemporalValue) )
{
throw new InvalidArgumentException( String.format( "Cannot construct date from: %s", dateField ) );
}
TemporalValue t = (TemporalValue) dateField;
date = t.getDatePart();
}
else
{
date = DateValue.DEFAULT_CALENDER_DATE;
}
result = LocalDateTime.of( date, time );
}
else
{
result = DEFAULT_LOCAL_DATE_TIME;
}
if ( fields.containsKey( TemporalFields.week ) && !selectingDate && !selectingDateTime )
{
// Be sure to be in the start of the week based year (which can be later than 1st Jan)
result = result
.with( IsoFields.WEEK_BASED_YEAR, safeCastIntegral( TemporalFields.year.name(), fields.get( TemporalFields.year ),
TemporalFields.year.defaultValue ) )
.with( IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1 )
.with( ChronoField.DAY_OF_WEEK, 1 );
}
result = assignAllFields( result );
return localDateTime( result );
}
private LocalDateTime getLocalDateTimeOf( AnyValue temporal )
{
if ( temporal instanceof TemporalValue )
{
TemporalValue v = (TemporalValue) temporal;
LocalDate datePart = v.getDatePart();
LocalTime timePart = v.getLocalTimePart();
return LocalDateTime.of( datePart, timePart );
}
throw new InvalidArgumentException( String.format( "Cannot construct date from: %s", temporal ) );
}
@Override
protected LocalDateTimeValue selectDateTime( AnyValue datetime )
{
if ( datetime instanceof LocalDateTimeValue )
{
return (LocalDateTimeValue) datetime;
}
return localDateTime( getLocalDateTimeOf( datetime ) );
}
};
}
@Override
protected int unsafeCompareTo( Value other )
{
LocalDateTimeValue that = (LocalDateTimeValue) other;
int cmp = Long.compare( epochSecondsInUTC, that.epochSecondsInUTC );
if ( cmp == 0 )
{
cmp = value.getNano() - that.value.getNano();
}
return cmp;
}
@Override
public String getTypeName()
{
return "LocalDateTime";
}
@Override
LocalDateTime temporal()
{
return value;
}
@Override
LocalDate getDatePart()
{
return value.toLocalDate();
}
@Override
LocalTime getLocalTimePart()
{
return value.toLocalTime();
}
@Override
OffsetTime getTimePart( Supplier defaultZone )
{
ZoneOffset currentOffset = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.now(), defaultZone.get() ) ).getOffset();
return OffsetTime.of(value.toLocalTime(), currentOffset);
}
@Override
ZoneId getZoneId( Supplier defaultZone )
{
return defaultZone.get();
}
@Override
ZoneOffset getZoneOffset()
{
throw new UnsupportedTemporalUnitException( String.format( "Cannot get the offset of: %s", this ) );
}
@Override
public boolean supportsTimeZone()
{
return false;
}
@Override
boolean hasTime()
{
return true;
}
@Override
public boolean equals( Value other )
{
return other instanceof LocalDateTimeValue && value.equals( ((LocalDateTimeValue) other).value );
}
@Override
public void writeTo( ValueWriter writer ) throws E
{
writer.writeLocalDateTime( value );
}
@Override
public String prettyPrint()
{
return assertPrintable( () -> value.format( DateTimeFormatter.ISO_LOCAL_DATE_TIME ) );
}
@Override
public ValueRepresentation valueRepresentation()
{
return ValueRepresentation.LOCAL_DATE_TIME;
}
@Override
protected int computeHashToMemoize()
{
return value.toInstant( UTC ).hashCode();
}
@Override
public T map( ValueMapper mapper )
{
return mapper.mapLocalDateTime( this );
}
@Override
public LocalDateTimeValue add( DurationValue duration )
{
return replacement( assertValidArithmetic( () -> value.plus( duration ) ) );
}
@Override
public LocalDateTimeValue sub( DurationValue duration )
{
return replacement( assertValidArithmetic( () -> value.minus( duration ) ) );
}
@Override
LocalDateTimeValue replacement( LocalDateTime dateTime )
{
return dateTime == value ? this : new LocalDateTimeValue( dateTime );
}
@Override
public long estimatedHeapUsage()
{
return INSTANCE_SIZE;
}
private static final Pattern PATTERN = Pattern.compile(
DATE_PATTERN + "(?