/**
* 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.
*
* Copyright 2012-2016 the original author or authors.
*/
package org.assertj.db.type;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.Calendar;
/**
* This class represents a date/time value in the database.
*
* @author RĂ©gis Pouiller
*
*/
public class DateTimeValue implements Comparable<DateTimeValue>, DateValueContainer {
/**
* The date part.
*/
private final DateValue date;
/**
* The time part.
*/
private final TimeValue time;
/**
* Indicates where there are the digits in the {@code String} for {@link DateValue#DateValue(String)}.
*/
private static final String DATE_FORMAT = "\\d\\d\\d\\d-\\d\\d-\\d\\d";
/**
* Indicates where there are the digits for {@code String} for {@link TimeValue#TimeValue(String)}.
*/
private static final String TIME_FORMAT = "\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d";
/**
* Indicates where there are the digits in style with seconds for {@code String} for
* {@link TimeValue#TimeValue(String)}.
*/
private static final String TIME_FORMAT_WITH_SECONDS = "\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d";
/**
* Indicates where there are the digits in style with nanoseconds for {@code String} for
* {@link TimeValue#TimeValue(String)}.
*/
private static final String TIME_FORMAT_WITH_NANO = "\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d\\d\\d\\d\\d\\d\\d";
/**
* Makes an instance of date/time value from a date with time at 00:00AM.
*
* @param date The date.
* @return An instance of date/time.
*/
public static DateTimeValue of(DateValue date) {
return new DateTimeValue(date, TimeValue.of(0, 0));
}
/**
* Makes an instance of date/time value from a date and a time.
*
* @param date The date.
* @param time The time.
* @return An instance of date/time.
*/
public static DateTimeValue of(DateValue date, TimeValue time) {
return new DateTimeValue(date, time);
}
/**
* Makes an instance of date/time value from a {@code String} in {@code yyyy-mm-dd}, {@code yyyy-mm-ddThh:mm},
* {@code yyyy-mm-ddThh:mm:ss} or {@code yyyy-mm-ddThh:mm:ss.nnnnnnnnn} format.
*
* @param dateTime Date/time in {@code String} format ({@code yyyy-mm-dd}).
* @throws NullPointerException If {@code dateTime} is {@code null}.
* @throws ParseException If {@code date} don't respect the {@code yyyy-mm-dd}, {@code yyyy-mm-ddThh:mm},
* {@code yyyy-mm-ddThh:mm:ss} or {@code yyyy-mm-ddThh:mm:ss.nnnnnnnnn} format.
* @return An instance of date/time value.
*/
public static DateTimeValue parse(String dateTime) throws ParseException {
return new DateTimeValue(dateTime);
}
/**
* Makes an instance of date/time value from a {@link Timestamp}.
*
* @param timestamp Timestamp.
* @throws NullPointerException If {@code timestamp} is {@code null}.
* @return An instance of date/time value.
*/
public static DateTimeValue from(Timestamp timestamp) {
return new DateTimeValue(timestamp);
}
/**
* Makes an instance of date/time value from a {@link Calendar}.
*
* @param calendar Calendar.
* @throws NullPointerException If {@code calendar} is {@code null}.
* @return An instance of date/time value.
* @since 1.1.0
*/
public static DateTimeValue from(Calendar calendar) {
return new DateTimeValue(calendar);
}
/**
* Makes an instance of the date/time value corresponding to now.
*
* @return An instance of date/time value.
* @since 1.1.0
*/
public static DateTimeValue now() {
return from(Calendar.getInstance());
}
/**
* Constructor.
*
* @param date The date.
* @param time The time.
* @throws NullPointerException If {@code date} or {@code time} is {@code null}.
*/
public DateTimeValue(DateValue date, TimeValue time) {
if (date == null) {
throw new NullPointerException("date should be not null");
}
if (time == null) {
throw new NullPointerException("time should be not null");
}
this.date = date;
this.time = time;
}
/**
* Constructor.
*
* @param dateTime Time in {@code String} format ({@code yyyy-mm-dd}, {@code yyyy-mm-ddThh:mm},
* {@code yyyy-mm-ddThh:mm:ss} or {@code yyyy-mm-ddThh:mm:ss.nnnnnnnnn}).
* @throws NullPointerException If {@code dateTime} is {@code null}.
* @throws ParseException If {@code date} don't respect the {@code yyyy-mm-dd}, {@code yyyy-mm-ddThh:mm},
* {@code yyyy-mm-ddThh:mm:ss} or {@code yyyy-mm-ddThh:mm:ss.nnnnnnnnn} format.
*/
public DateTimeValue(String dateTime) throws ParseException {
if (dateTime == null) {
throw new NullPointerException("date/time should be not null");
}
if (dateTime.matches(DATE_FORMAT)) {
date = DateValue.parse(dateTime);
time = new TimeValue(0, 0);
} else if (dateTime.matches(TIME_FORMAT) || dateTime.matches(TIME_FORMAT_WITH_SECONDS)
|| dateTime.matches(TIME_FORMAT_WITH_NANO)) {
date = DateValue.parse(dateTime.substring(0, 10));
time = TimeValue.parse(dateTime.substring(11));
} else {
throw new ParseException("date/time must respect yyyy-mm-dd, yyyy-mm-ddThh:mm, "
+ "yyyy-mm-ddThh:mm:ss or yyyy-mm-ddThh:mm:ss.nnnnnnnnn format", dateTime.length());
}
}
/**
* Constructor.
*
* @param timestamp Timestamp.
* @throws NullPointerException If {@code dateTime} is {@code null}.
*/
public DateTimeValue(Timestamp timestamp) {
if (timestamp == null) {
throw new NullPointerException("date/time should be not null");
}
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestamp.getTime());
date = DateValue.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1,
calendar.get(Calendar.DAY_OF_MONTH));
time = TimeValue.of(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE),
calendar.get(Calendar.SECOND), timestamp.getNanos());
}
/**
* Constructor.
*
* @param calendar Calendar.
* @throws NullPointerException If {@code calendar} is {@code null}.
*/
public DateTimeValue(Calendar calendar) {
if (calendar == null) {
throw new NullPointerException("date/time should be not null");
}
date = DateValue.from(calendar);
time = TimeValue.from(calendar);
}
/** {@inheritDoc} */
@Override
public DateValue getDate() {
return date;
}
public boolean isMidnight() {
return time.getHours() == 0 && time.getMinutes() == 0 && time.getSeconds() == 0 && time.getNanoSeconds() == 0;
}
/**
* Returns the time.
*
* @return The time.
*/
public TimeValue getTime() {
return time;
}
@Override
public String toString() {
return String.format("%4d-%02d-%02dT%02d:%02d:%02d.%09d", date.getYear(), date.getMonth(), date.getDayOfTheMonth(),
time.getHours(), time.getMinutes(), time.getSeconds(), time.getNanoSeconds());
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DateTimeValue) {
DateTimeValue dateTimeValue = (DateTimeValue) obj;
return date.equals(dateTimeValue.date) && time.equals(dateTimeValue.time);
} else if (obj instanceof DateValueContainer) {
DateValueContainer value = (DateValueContainer) obj;
return date.equals(value.getDate()) && isMidnight();
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + date.hashCode();
result = prime * result + time.hashCode();
return result;
}
@Override
public int compareTo(DateTimeValue other) {
int compareDate = date.compareTo(other.date);
if (compareDate != 0) {
return compareDate;
}
return time.compareTo(other.time);
}
/**
* Returns if this date/time value is before the date/time value in parameter.
*
* @param dateTime The date/time value to compare to.
* @return If this date/time value is before the date/time value in parameter.
*/
public boolean isBefore(DateTimeValue dateTime) {
return compareTo(dateTime) == -1;
}
/**
* Returns if this date/time value is after the date/time value in parameter.
*
* @param dateTime The date/time value to compare to.
* @return If this date/time value is after the date/time value in parameter.
*/
public boolean isAfter(DateTimeValue dateTime) {
return compareTo(dateTime) == 1;
}
/**
* Moves the date/time with the value in parameter.
* @param date Value to move the date.
* @return The date/time moved.
*/
public DateTimeValue move(DateValue date) {
TimeValue timeValue = getTime();
DateValue dateValue = getDate();
DateValue movedDateValue = dateValue.move(date);
return of(movedDateValue, timeValue);
}
/**
* Moves the date/time with the value in parameter.
* @param time Value to move the date.
* @return The date/time moved.
*/
public DateTimeValue move(TimeValue time) {
TimeValue timeValue = getTime();
TimeValue movedTimeValue = timeValue.move(time);
int hours = movedTimeValue.getHours();
int days = hours / 24;
if (hours > 0) {
hours -= days * 24;
}
else {
hours += days * 24;
}
if (hours < 0) {
days--;
hours += 24;
}
DateValue dateValue = getDate();
DateValue movedDateValue = dateValue.move(DateValue.of(0, 0, days));
return of(movedDateValue, TimeValue.of(hours, movedTimeValue.getMinutes(),
movedTimeValue.getSeconds(), movedTimeValue.getNanoSeconds()));
}
/**
* Moves the date/time with the value in parameter.
* @param dateTime Value to move the date.
* @return The date/time moved.
*/
public DateTimeValue move(DateTimeValue dateTime) {
DateTimeValue aDateTime = move(dateTime.getDate());
return aDateTime.move(dateTime.getTime());
}
/**
* Returns the reverse of the date/time.
* @return The reverse.
*/
public DateTimeValue reverse() {
return of(date.reverse(), time.reverse());
}
}