All Downloads are FREE. Search and download functionalities are using the official Maven repository.

microsoft.sql.DateTimeOffset Maven / Gradle / Ivy

Go to download

Microsoft JDBC Driver for SQL Server. The Azure Key Vault feature in Microsoft JDBC Driver for SQL Server depends on Azure SDK for JAVA and Azure Active Directory Library For Java.

There is a newer version: 12.7.0.jre11-preview
Show newest version
/*
 * Microsoft JDBC Driver for SQL Server
 * 
 * Copyright(c) Microsoft Corporation All rights reserved.
 * 
 * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
 */

package microsoft.sql;

import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;

/**
 * A Java class for accessing values of the SQL Server DATETIMEOFFSET data type.
 *
 * The DateTimeOffset class represents a java.sql.Timestamp, including fractional seconds, plus an integer representing the number of minutes offset
 * from GMT.
 */
public final class DateTimeOffset extends Object implements java.io.Serializable, java.lang.Comparable {
    private static final long serialVersionUID = 541973748553014280L;

    private final long utcMillis;
    private final int nanos;
    private final int minutesOffset;

    private static final int NANOS_MIN = 0;
    private static final int NANOS_MAX = 999999999;
    private static final int SS_NANOS_MAX = 9999999;
    private static final int MINUTES_OFFSET_MIN = -14 * 60;
    private static final int MINUTES_OFFSET_MAX = 14 * 60;
    private static final int HUNDRED_NANOS_PER_SECOND = 10000000;

    /**
     * Constructs a DateTimeOffset instance.
     *
     * This method does not check that its arguments represent a timestamp value that falls within the range of values acceptable to SQL Server for
     * the DATETIMEOFFSET data type. That is, it is possible to create a DateTimeOffset instance representing a value outside the range from 1 January
     * 1AD 00:00:00 UTC to 31 December 9999 00:00:00 UTC.
     */
    private DateTimeOffset(java.sql.Timestamp timestamp,
            int minutesOffset) {
        // Combined time zone and DST offset must be between -14:00 and 14:00
        if (minutesOffset < MINUTES_OFFSET_MIN || minutesOffset > MINUTES_OFFSET_MAX)
            throw new IllegalArgumentException();
        this.minutesOffset = minutesOffset;

        // Nanos must be between 0 and 999999999 inclusive
        int timestampNanos = timestamp.getNanos();
        if (timestampNanos < NANOS_MIN || timestampNanos > NANOS_MAX)
            throw new IllegalArgumentException();

        // This class represents values to 100ns precision. If the java.sql.Timestamp argument
        // represents a value that is more precise, then nanos in excess of the 100ns precision
        // allowed by this class are rounded to the nearest multiple of 100ns.
        //
        // Values within 50 nanoseconds of the next second are rounded up to the next second.
        // Note: Values within 50 nanoseconds of the end of time wrap back to the beginning.
        int hundredNanos = (timestampNanos + 50) / 100;
        this.nanos = 100 * (hundredNanos % HUNDRED_NANOS_PER_SECOND);
        this.utcMillis = timestamp.getTime() - timestamp.getNanos() / 1000000 + 1000 * (hundredNanos / HUNDRED_NANOS_PER_SECOND);

        // Postconditions
        assert this.minutesOffset >= MINUTES_OFFSET_MIN && this.minutesOffset <= MINUTES_OFFSET_MAX : "minutesOffset: " + this.minutesOffset;
        assert this.nanos >= NANOS_MIN && this.nanos <= NANOS_MAX : "nanos: " + this.nanos;
        assert 0 == this.nanos % 100 : "nanos: " + this.nanos;
        assert 0 == this.utcMillis % 1000L : "utcMillis: " + this.utcMillis;
    }

    /**
     * Converts a java.sql.Timestamp value with an integer offset to the equivalent DateTimeOffset value
     * 
     * @param timestamp
     *            A java.sql.Timestamp value
     * @param minutesOffset
     *            An integer offset in minutes
     * @return The DateTimeOffset value of the input timestamp and minutesOffset
     */
    public static DateTimeOffset valueOf(java.sql.Timestamp timestamp,
            int minutesOffset) {
        return new DateTimeOffset(timestamp, minutesOffset);
    }

    /**
     * Converts a java.sql.Timestamp value with a Calendar value to the equivalent DateTimeOffset value
     * 
     * @param timestamp
     *            A java.sql.Timestamp value
     * @param calendar
     *            A java.util.Calendar value
     * @return The DateTimeOffset value of the input timestamp and calendar
     */
    public static DateTimeOffset valueOf(java.sql.Timestamp timestamp,
            Calendar calendar) {
        // (Re)Set the calendar's time to the value in the timestamp so that get(ZONE_OFFSET) and get(DST_OFFSET) report
        // the correct values for the time indicated, taking into account DST transition times and any historical changes
        // to the DST transition schedule.
        calendar.setTimeInMillis(timestamp.getTime());

        return new DateTimeOffset(timestamp, (calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET)) / (60 * 1000));
    }

    private String formattedValue = null;

    /**
     * Formats a datetimeoffset as yyyy-mm-dd hh:mm:ss[.fffffffff] [+|-]hh:mm, where yyyy-mm-dd hh:mm:ss[.fffffffff] indicates a timestamp that is
     * offset from UTC by the number of minutes indicated by [+|-]hh:mm.
     *
     * @return a String object in yyyy-mm-dd hh:mm:ss[.fffffffff] [+|-]hh:mm format
     */
    @Override
    public String toString() {
        // Because formatting the value as a string is computationally expensive (involving creation of a Calendar and
        // a TimeZone, String formatters, etc.), cache the formatted value the first time it is needed. This can be done
        // simply with the single-check idiom because the DateTimeOffset class is effectively immutable.
        String result = formattedValue;
        if (null == result) {
            // Format the offset as +hh:mm or -hh:mm. Zero offset is formatted as +00:00.
            String formattedOffset = (minutesOffset < 0) ?

                    String.format(Locale.US, "-%1$02d:%2$02d", -minutesOffset / 60, -minutesOffset % 60) :

                    String.format(Locale.US, "+%1$02d:%2$02d", minutesOffset / 60, minutesOffset % 60);

            // Like java.sql.Date.toString() and java.sql.Timestamp.toString(), DateTimeOffset.toString() produces
            // a value that is not locale-sensitive. The date part of the returned string is a Gregorian date, even
            // if the VM default locale would otherwise indicate that a Buddhist calendar should be used.
            Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT" + formattedOffset), Locale.US);

            // Initialize the calendar with the UTC milliseconds value represented by this DateTimeOffset object
            calendar.setTimeInMillis(utcMillis);

            // Assumption: nanos is in a valid range for printing as a 0-prefixed, 7-digit decimal number
            // The DateTimeOffset constructor ensures that this is the case.
            assert nanos >= NANOS_MIN && nanos <= NANOS_MAX;

            // Format the returned string value from the calendar's component fields and the UTC offset
            formattedValue = result = (0 == nanos) ?

                    String.format(Locale.US, "%1$tF %1$tT %2$s", calendar, formattedOffset) :

                    String.format(Locale.US, "%1$tF %1$tT.%2$s %3$s", calendar,                              // Example (nanos = 123456000):
                            java.math.BigDecimal.valueOf(nanos, 9) // -> 0.123456000
                                    .stripTrailingZeros()              // -> 0.123456
                                    .toPlainString()                   // -> "0.123456"
                                    .substring(2),                     // -> "123456"
                            formattedOffset);
        }

        return result;
    }

    @Override
    public boolean equals(Object o) {
        // Fast check for reference equality
        if (this == o)
            return true;

        // Check other object's type (and implicitly test for null)
        if (!(o instanceof DateTimeOffset))
            return false;

        DateTimeOffset other = (DateTimeOffset) o;
        return utcMillis == other.utcMillis && nanos == other.nanos && minutesOffset == other.minutesOffset;
    }

    @Override
    public int hashCode() {

        // Start by approximately folding the date and time components together.
        // Ignore any sub-second component of the utcMillis, which is always 0.
        // Milliseconds are kept in the nanos field.
        assert 0 == utcMillis % 1000L;
        long seconds = utcMillis / 1000L;

        int result = 571;
        result = 2011 * result + (int) seconds;
        result = 3217 * result + (int) (seconds / 60 * 60 * 24 * 365);

        // Fold in nanoseconds/microseconds/milliseconds
        result = 3919 * result + nanos / 100000;
        result = 4463 * result + nanos / 1000;
        result = 5227 * result + nanos;

        // Fold in the hour and minute portions of the time zone offset
        // Typically the minutes are 0, so the hours have more impact on the hash
        result = 6689 * result + minutesOffset;
        result = 7577 * result + minutesOffset / 60;

        // The low order bits of the result should at this point be very
        // sensitive to differences in any of the DateTimeOffset fields,
        // even for small bucket sizes.
        return result;
    }

    /**
     * Gets this DateTimeOffset object's timestamp value.
     * 

* The returned value represents an instant in time as the number of milliseconds since January 1, 1970, 00:00:00 GMT. * * @return this DateTimeOffset object's timestamp component */ public java.sql.Timestamp getTimestamp() { java.sql.Timestamp timestamp = new java.sql.Timestamp(utcMillis); timestamp.setNanos(nanos); return timestamp; } /** * Gets this DateTimeOffset object's offset value. * * @return this DateTimeOffset object's minutes offset from GMT */ public int getMinutesOffset() { return minutesOffset; } /** * Compares this DateTimeOffset object with another DateTimeOffset object to determine their relative order. *

* The ordering is based on the timestamp component only. The offset component is not compared. Two DateTimeOffset objects are considered * equivalent with respect to ordering as long as they represent the same moment in time, regardless of the location of the event. This is how SQL * Server orders DATETIMEOFFSET values. * * @return a negative integer, zero, or a positive integer as this DateTimeOffset is less than, equal to, or greater than the specified * DateTimeOffset. */ public int compareTo(DateTimeOffset other) { // Note that no explicit check for null==other is necessary. The contract for compareTo() // says that a NullPointerException is to be thrown if null is passed as an argument. // The fact that nanos are non-negative guarantees the subtraction at the end // cannot produce a signed value outside the range representable in an int. assert nanos >= 0; assert other.nanos >= 0; return (utcMillis > other.utcMillis) ? 1 : (utcMillis < other.utcMillis) ? -1 : nanos - other.nanos; } private static class SerializationProxy implements java.io.Serializable { private final long utcMillis; private final int nanos; private final int minutesOffset; SerializationProxy(DateTimeOffset dateTimeOffset) { this.utcMillis = dateTimeOffset.utcMillis; this.nanos = dateTimeOffset.nanos; this.minutesOffset = dateTimeOffset.minutesOffset; } private static final long serialVersionUID = 664661379547314226L; private Object readResolve() { java.sql.Timestamp timestamp = new java.sql.Timestamp(utcMillis); timestamp.setNanos(nanos); return new DateTimeOffset(timestamp, minutesOffset); } } private Object writeReplace() { return new SerializationProxy(this); } private void readObject(java.io.ObjectInputStream stream) throws java.io.InvalidObjectException { // For added security/robustness, the only way to rehydrate a serialized DateTimeOffset // is to use a SerializationProxy. Direct use of readObject() is not supported. throw new java.io.InvalidObjectException(""); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy