blob: f53bcca36962c93fc4b8dc412bf1fa781e9dc6c0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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.apache.calcite.util;
import org.apache.calcite.avatica.util.DateTimeUtils;
import com.google.common.base.Preconditions;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
/**
* Time with time-zone literal.
*
* <p>Immutable, internally represented as a string (in ISO format),
* and can support unlimited precision (milliseconds, nanoseconds).
*/
public class TimeWithTimeZoneString implements Comparable<TimeWithTimeZoneString> {
final TimeString localTime;
final TimeZone timeZone;
final String v;
/** Creates a TimeWithTimeZoneString. */
public TimeWithTimeZoneString(TimeString localTime, TimeZone timeZone) {
this.localTime = localTime;
this.timeZone = timeZone;
this.v = localTime.toString() + " " + timeZone.getID();
}
/** Creates a TimeWithTimeZoneString. */
public TimeWithTimeZoneString(String v) {
this.localTime = new TimeString(v.substring(0, 8));
String timeZoneString = v.substring(9);
Preconditions.checkArgument(DateTimeStringUtils.isValidTimeZone(timeZoneString));
this.timeZone = TimeZone.getTimeZone(timeZoneString);
this.v = v;
}
/** Creates a TimeWithTimeZoneString for hour, minute, second and millisecond values
* in the given time-zone. */
public TimeWithTimeZoneString(int h, int m, int s, String timeZone) {
this(DateTimeStringUtils.hms(new StringBuilder(), h, m, s).toString() + " " + timeZone);
}
/** Sets the fraction field of a {@code TimeWithTimeZoneString} to a given number
* of milliseconds. Nukes the value set via {@link #withNanos}.
*
* <p>For example,
* {@code new TimeWithTimeZoneString(1970, 1, 1, 2, 3, 4, "UTC").withMillis(56)}
* yields {@code TIME WITH LOCAL TIME ZONE '1970-01-01 02:03:04.056 UTC'}. */
public TimeWithTimeZoneString withMillis(int millis) {
Preconditions.checkArgument(millis >= 0 && millis < 1000);
return withFraction(DateTimeStringUtils.pad(3, millis));
}
/** Sets the fraction field of a {@code TimeString} to a given number
* of nanoseconds. Nukes the value set via {@link #withMillis(int)}.
*
* <p>For example,
* {@code new TimeWithTimeZoneString(1970, 1, 1, 2, 3, 4, "UTC").withNanos(56789)}
* yields {@code TIME WITH LOCAL TIME ZONE '1970-01-01 02:03:04.000056789 UTC'}. */
public TimeWithTimeZoneString withNanos(int nanos) {
Preconditions.checkArgument(nanos >= 0 && nanos < 1000000000);
return withFraction(DateTimeStringUtils.pad(9, nanos));
}
/** Sets the fraction field of a {@code TimeWithTimeZoneString}.
* The precision is determined by the number of leading zeros.
* Trailing zeros are stripped.
*
* <p>For example,
* {@code new TimeWithTimeZoneString(1970, 1, 1, 2, 3, 4, "UTC").withFraction("00506000")}
* yields {@code TIME WITH LOCAL TIME ZONE '1970-01-01 02:03:04.00506 UTC'}. */
public TimeWithTimeZoneString withFraction(String fraction) {
String v = this.v;
int i = v.indexOf('.');
if (i >= 0) {
v = v.substring(0, i);
} else {
v = v.substring(0, 8);
}
while (fraction.endsWith("0")) {
fraction = fraction.substring(0, fraction.length() - 1);
}
if (fraction.length() > 0) {
v = v + "." + fraction;
}
v = v + this.v.substring(8); // time-zone
return new TimeWithTimeZoneString(v);
}
public TimeWithTimeZoneString withTimeZone(TimeZone timeZone) {
if (this.timeZone.equals(timeZone)) {
return this;
}
String localTimeString = localTime.toString();
String v;
String fraction;
int i = localTimeString.indexOf('.');
if (i >= 0) {
v = localTimeString.substring(0, i);
fraction = localTimeString.substring(i + 1);
} else {
v = localTimeString;
fraction = null;
}
final DateTimeUtils.PrecisionTime pt =
DateTimeUtils.parsePrecisionDateTimeLiteral(v,
new SimpleDateFormat(DateTimeUtils.TIME_FORMAT_STRING, Locale.ROOT),
this.timeZone, -1);
pt.getCalendar().setTimeZone(timeZone);
if (fraction != null) {
return new TimeWithTimeZoneString(
pt.getCalendar().get(Calendar.HOUR_OF_DAY),
pt.getCalendar().get(Calendar.MINUTE),
pt.getCalendar().get(Calendar.SECOND),
timeZone.getID())
.withFraction(fraction);
}
return new TimeWithTimeZoneString(
pt.getCalendar().get(Calendar.HOUR_OF_DAY),
pt.getCalendar().get(Calendar.MINUTE),
pt.getCalendar().get(Calendar.SECOND),
timeZone.getID());
}
@Override public String toString() {
return v;
}
@Override public boolean equals(Object o) {
// The value is in canonical form (no trailing zeros).
return o == this
|| o instanceof TimeWithTimeZoneString
&& ((TimeWithTimeZoneString) o).v.equals(v);
}
@Override public int hashCode() {
return v.hashCode();
}
@Override public int compareTo(TimeWithTimeZoneString o) {
return v.compareTo(o.v);
}
public TimeWithTimeZoneString round(int precision) {
Preconditions.checkArgument(precision >= 0);
return new TimeWithTimeZoneString(
localTime.round(precision), timeZone);
}
public static TimeWithTimeZoneString fromMillisOfDay(int i) {
return new TimeWithTimeZoneString(
DateTimeUtils.unixTimeToString(i) + " " + DateTimeUtils.UTC_ZONE.getID())
.withMillis((int) DateTimeUtils.floorMod(i, 1000));
}
/** Converts this TimeWithTimeZoneString to a string, truncated or padded with
* zeroes to a given precision. */
public String toString(int precision) {
Preconditions.checkArgument(precision >= 0);
return localTime.toString(precision) + " " + timeZone.getID();
}
public TimeString getLocalTimeString() {
return localTime;
}
}