com.cinchapi.concourse.Timestamp.java Source code

Java tutorial

Introduction

Here is the source code for com.cinchapi.concourse.Timestamp.java

Source

/*
 * Copyright (c) 2013-2016 Cinchapi Inc.
 * 
 * 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 com.cinchapi.concourse;

import java.util.concurrent.TimeUnit;

import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;

import org.joda.time.Chronology;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import com.cinchapi.concourse.annotate.PackagePrivate;
import com.cinchapi.concourse.time.Time;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Longs;

/**
 * A wrapper class for a Unix timestamp with microsecond precision. A
 * {@link Timestamp} is required for historical operations in {@link Concourse}.
 * This class provides interoperability with Joda {@link DateTime} objects with
 * the {@link #fromJoda(DateTime)} and {@link #getJoda()} methods.
 * 
 * @author Jeff Nelson
 */
@Immutable
@ThreadSafe
public final class Timestamp {

    // Joda DateTime uses millisecond instead of microsecond precision, so this
    // class is a wrapper that will handle microseconds so we don't ever lose
    // data that happens within the same millisecond.

    /**
     * Return a {@code Timestamp} that corresponds to the system
     * epoch timestamp with microsecond precision.
     * 
     * @return the timestamp for system epoch
     */
    public static Timestamp epoch() {
        return Timestamp.fromMicros(-1);
    }

    /**
     * Return the {@code Timestamp} that corresponds to the provided joda
     * DateTime object.
     * 
     * @param joda a {@link DateTime} object
     * @return the timestamp for {@code joda}
     */
    public static Timestamp fromJoda(DateTime joda) {
        return new Timestamp(joda);
    }

    /**
     * Return a {@code Timestamp} that corresponds to the provided Unix
     * timestamp with microsecond precision.
     * 
     * @param microseconds the number of microseconds since the Unix epoch
     * @return the timestamp for {@code microseconds}
     */
    public static Timestamp fromMicros(long microseconds) {
        return new Timestamp(microseconds);
    }

    /**
     * Take the {@code description} and return a {@link Timestamp} that can be
     * passed to {@link Concourse driver} API methods.
     * <p>
     * Timestamp description are parsed by Concourse Server, so this method only
     * returns a wrapper that is meant to be passed over the wire. Timestamps
     * returned from this method are <em>non-operable</em> and will throw
     * exceptions if you call methods that would return a precise instant (i.e.
     * {@link #getJoda()} or {@link #getMicros()}).
     * </p>
     * 
     * @param description a relative or absolute natural language description of
     *            an instant.
     * @return a hollow {@link Timestamp} that wraps the description
     */
    public static Timestamp fromString(String description) {
        return new Timestamp(description);
    }

    /**
     * Return a {@code Timestamp} set the current system microsecond time using
     * <code>ISOChronology</code> in the default time zone.
     * 
     * @return the current timestamp, not null
     */
    public static Timestamp now() {
        return new Timestamp(Time.now());
    }

    /**
     * Return a {@code Timestamp} set to the current system microsecond time
     * using the specified chronology.
     * 
     * @param chronology the chronology, not null
     * @return the current timestamp, not null
     */
    public static Timestamp now(Chronology chronology) {
        long microseconds = Time.now();
        return new Timestamp(microseconds,
                new DateTime(TimeUnit.MILLISECONDS.convert(microseconds, TimeUnit.MICROSECONDS), chronology));
    }

    /**
     * Return a {@code Timestamp} set to the current system microsecond time
     * using <code>ISOChronology</code> in the specified time zone.
     * 
     * @param zone the time zone, not null
     * @return the current timestamp, not null
     */
    public static Timestamp now(DateTimeZone zone) {
        long microseconds = Time.now();
        return new Timestamp(microseconds,
                new DateTime(TimeUnit.MILLISECONDS.convert(microseconds, TimeUnit.MICROSECONDS), zone));
    }

    /**
     * Parses a {@code Timestamp} from the specified string using a formatter.
     * 
     * @param str the string to parse, not null
     * @param formatter the formatter to use, not null
     * @return the parsed timestamp
     */
    public static Timestamp parse(String str, DateTimeFormatter formatter) {
        return new Timestamp(DateTime.parse(str, formatter));
    }

    /**
     * The default formatter that is used to display objects of this class.
     */
    public static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormat
            .forPattern("E MMM dd, yyyy @ h:mm:ss:SS a z");

    /**
     * A relative or absolute description of an instant that is translated to an
     * actual microsecond timestamp in Concourse Server. By convention, a
     * {@link Timestamp} object is considered to be {@link #isString() hollow}
     * if and only if the value of this variable is non-null.
     */
    private final String description;

    /**
     * A {@link DateTime} object that corresponds to this {@link Timestamp}. We
     * use this for the {@link #toString() string} representation and for any
     * other comparative operations that need to take things like timezones into
     * account.
     */
    private final DateTime joda;

    /**
     * The number of microseconds since the Unix epoch that identify the instant
     * represented by this {@link Timestamp}.
     */
    private final long microseconds;

    /**
     * Construct a new instance.
     * 
     * @param joda
     */
    private Timestamp(DateTime joda) {
        this.joda = joda;
        this.microseconds = TimeUnit.MICROSECONDS.convert(joda.getMillis(), TimeUnit.MILLISECONDS);
        this.description = null;
    }

    /**
     * Construct a new instance.
     * 
     * @param microseconds
     */
    private Timestamp(long microseconds) {
        this.microseconds = microseconds;
        this.joda = new DateTime(TimeUnit.MILLISECONDS.convert(microseconds, TimeUnit.MICROSECONDS));
        this.description = null;
    }

    /**
     * Construct a new instance.
     * 
     * @param microseconds
     * @param joda
     */
    private Timestamp(long microseconds, DateTime joda) {
        this.microseconds = microseconds;
        this.joda = joda;
        this.description = null;
    }

    /**
     * Construct a {@link #isString()} instance.
     * 
     * @param description the description to be resolved into an instant by
     *            Concourse Server
     */
    private Timestamp(String description) {
        this.microseconds = 0;
        this.joda = null;
        this.description = description;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Timestamp && !isString()) {
            return Longs.compare(microseconds, ((Timestamp) obj).microseconds) == 0;
        }
        // NOTE: By convention, two hollow timestamps are NEVER equal
        return false;
    }

    /**
     * Return the Joda {@link DateTime} object that corresponds to this
     * Timestamp.
     * 
     * @return the corresponding joda DateTime
     */
    public DateTime getJoda() {
        Preconditions.checkState(!isString(),
                "Only Concourse Server can parse a DateTime " + "from a Timestamp created from a string.");
        return joda;
    }

    /**
     * Return the number of microseconds since the Unix epoch that is
     * represented by this Timestamp.
     * 
     * @return the microseconds
     */
    public long getMicros() {
        Preconditions.checkState(!isString(),
                "Only Concourse Server can parse microseconds " + "from a Timestamp created from a string.");
        return microseconds;
    }

    @Override
    public int hashCode() {
        return isString() ? description.hashCode() : Longs.hashCode(microseconds);
    }

    @Override
    public String toString() {
        return isString() ? description : joda.toString(DEFAULT_FORMATTER);
    }

    /**
     * The {@link com.cinchapi.concourse.thrift.ConcourseService thrift} API
     * allows specifying timestamps using either microseconds from the unix
     * epoch ( {@link Long long}) or a natural language description of an
     * absolute or relative instant ({@link String}). But we can't define
     * overloaded methods in the {@link Concourse driver} API that take a long
     * or String for the timestamp parameter because that signatures would be
     * ambiguous (i.e. does the method {@link Concourse#select(String, String)}
     * mean {@code select(key, ccl)} or {@code select(ccl, timestring)}?).
     * <p>
     * Therefore, we allow a {@link Timestamp} to be created
     * {@link #fromString(String) from a string description}, which will be
     * translated and resolved by Concourse Server. But these objects are
     * considered to be hollow because the driver and client code is unable to
     * use the objects in any way.
     * </p>
     * <p>
     * For a hollow Timestamp, use the {@link #toString()} method to get the
     * description.
     * </p>
     * 
     * @return {@code true} if the timestamp was created
     *         {@link #fromString(String) using a description}
     */
    @PackagePrivate
    boolean isString() {
        return description != null;
    }

}