/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.jcr.value.basic;
import static org.modeshape.common.util.DateTimeUtil.UTC;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.common.util.CheckArg;
import org.modeshape.common.util.DateTimeUtil;
import org.modeshape.jcr.api.value.DateTime;
/**
* Implementation of DateTime based on JDK 8 functionality.
*/
@Immutable
public class ModeShapeDateTime implements DateTime {
private static final long serialVersionUID = -730188225988292422L;
private static final int SECONDS_IN_HOUR = 60 * 60;
private final ZonedDateTime instance;
private final long millisInUtc;
public ModeShapeDateTime() {
this.instance = ZonedDateTime.now();
this.millisInUtc = utcMillis(instance);
}
public ModeShapeDateTime( String iso8601 ) {
this.instance = DateTimeUtil.jodaParse(iso8601);
this.millisInUtc = utcMillis(instance);
}
public ModeShapeDateTime( String iso8601,
String timeZoneId ) {
this.instance = DateTimeUtil.jodaParse(iso8601).withZoneSameInstant(ZoneId.of(timeZoneId));
this.millisInUtc = utcMillis(instance);
}
public ModeShapeDateTime( long milliseconds ) {
this.instance = ZonedDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), UTC);
this.millisInUtc = utcMillis(instance);
}
public ModeShapeDateTime( long milliseconds,
String timeZoneId ) {
this.instance = ZonedDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.of(timeZoneId));
this.millisInUtc = utcMillis(instance);
}
public ModeShapeDateTime( int year,
int monthOfYear,
int dayOfMonth,
int hourOfDay,
int minuteOfHour,
int secondOfMinute,
int millisecondsOfSecond ) {
int nano = (int) TimeUnit.NANOSECONDS.convert(millisecondsOfSecond, TimeUnit.MILLISECONDS);
this.instance = ZonedDateTime.of(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, nano, UTC);
this.millisInUtc = instance.toInstant().toEpochMilli();
}
public ModeShapeDateTime( java.util.Date jdkDate ) {
this.instance = ZonedDateTime.ofInstant(jdkDate.toInstant(), TimeZone.getDefault().toZoneId());
this.millisInUtc = utcMillis(this.instance);
}
public ModeShapeDateTime( java.util.Calendar jdkCalendar ) {
this.instance = ZonedDateTime.ofInstant(jdkCalendar.toInstant(), jdkCalendar.getTimeZone().toZoneId());
this.millisInUtc = utcMillis(instance);
}
protected ModeShapeDateTime( ZonedDateTime zonedDateTime ) {
this.instance = zonedDateTime;
this.millisInUtc = utcMillis(instance);
}
@Override
public int getMillisOfSecond() {
return this.instance.get(ChronoField.MILLI_OF_SECOND);
}
@Override
public long getMilliseconds() {
return this.instance.toInstant().toEpochMilli();
}
@Override
public long getMillisecondsInUtc() {
return millisInUtc;
}
@Override
public String getString() {
return DateTimeUtil.jodaFormat(instance);
}
@Override
public int getTimeZoneOffsetHours() {
return this.instance.getOffset().getTotalSeconds()/ SECONDS_IN_HOUR;
}
@Override
public String getTimeZoneId() {
return this.instance.getZone().getId();
}
@Override
public Calendar toCalendar() {
return toCalendar(null);
}
@Override
public Calendar toCalendar( Locale locale ) {
Calendar calendar = locale != null ? Calendar.getInstance(locale) : Calendar.getInstance();
calendar.setTimeInMillis(getMilliseconds());
return calendar;
}
@Override
public Date toDate() {
return new Date(getMilliseconds());
}
@Override
public ZonedDateTime toZonedDateTime() {
return instance;
}
@Override
public LocalDateTime toLocalDateTime() {
return instance.toLocalDateTime();
}
@Override
public int compareTo( org.modeshape.jcr.api.value.DateTime that ) {
long diff = this.getMillisecondsInUtc() - that.getMillisecondsInUtc();
return diff == 0 ? 0 : diff > 0 ? 1 : -1;
}
@Override
public int hashCode() {
return (int)this.millisInUtc;
}
@Override
public boolean equals( Object obj ) {
if (obj == this) return true;
if (obj instanceof org.modeshape.jcr.api.value.DateTime) {
org.modeshape.jcr.api.value.DateTime that = (org.modeshape.jcr.api.value.DateTime)obj;
return this.getMillisecondsInUtc() == that.getMillisecondsInUtc();
}
if (obj instanceof ZonedDateTime) {
ZonedDateTime that = (ZonedDateTime)obj;
return this.getMillisecondsInUtc() == that.withZoneSameInstant(UTC).toInstant().toEpochMilli();
}
return false;
}
@Override
public String toString() {
return getString();
}
@Override
public ModeShapeDateTime toUtcTimeZone() {
if (this.instance.getZone().equals(UTC)) {
return this;
}
return new ModeShapeDateTime(this.instance.withZoneSameInstant(UTC));
}
@Override
public ModeShapeDateTime toTimeZone( String timeZoneId ) {
CheckArg.isNotNull(timeZoneId, "time zone identifier");
ZoneId zoneId = ZoneId.of(timeZoneId);
if (this.instance.getZone().equals(zoneId)) {
return this;
}
return new ModeShapeDateTime(this.instance.withZoneSameInstant(zoneId));
}
@Override
public boolean isBefore( DateTime other ) {
return this.compareTo(other) < 0;
}
@Override
public boolean isSameAs( DateTime other ) {
return other == this || instance.isEqual(other.toZonedDateTime());
}
@Override
public boolean isAfter( DateTime other ) {
return this.compareTo(other) > 0;
}
@Override
public DateTime minus( Duration duration ) {
CheckArg.isNotNull(duration, "unit");
return new ModeShapeDateTime(this.instance.minus(duration));
}
@Override
public DateTime plus( Duration duration ) {
CheckArg.isNotNull(duration, "unit");
return new ModeShapeDateTime(this.instance.plus(duration));
}
private long utcMillis(ZonedDateTime zonedDateTime) {
return zonedDateTime.withZoneSameInstant(UTC).toInstant().toEpochMilli();
}
}