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

net.snowflake.common.core.SFTime Maven / Gradle / Ivy

There is a newer version: 5.1.4
Show newest version
package net.snowflake.common.core;

import java.lang.Math;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;

/**
 * Represents times without date.
 *
 * Stores fractional seconds since midnight, with nanosecond precision.
 *
 * Instances of this class are immutable.
 *
 * @author mkember
 */
public class SFTime extends SFInstant
{
  // The java.sql.Time class is too cumbersome to use (it only supports
  // millisecond precision), so we store nanoseconds since midnight directly.
  private final long nanos;

  // Used for unit conversions.
  private final static long NS_IN_SECOND = 1000000000;
  private final static long NS_IN_MILLIS = 1000000;
  private final static long NS_IN_DAY = 86400 * NS_IN_SECOND;

  /**
   * Constructs an SFTime given nanoseconds since midnight.
   * @param nanos nanoseconds
   * @return SFTime instance
   */
  public static SFTime fromNanoseconds(long nanos)
  {
    return new SFTime(nanos);
  }

  /**
   * Constructs an SFTime from fractional seconds at a particular scale.
   *
   * @param value
   *   Fractional seconds since midnight.
   * @param scale
   *   The scale for interpreting {@code value}. A scale of 0 means seconds, and
   *   a scale of 9 means nanoseconds.
   * @return A new SFTime object.
   */
  public static SFTime fromFractionalSeconds(long value, int scale)
  {
    assert scale >= 0 && scale <= 9;
    int powerOfTen = SFInstant.POWERS_OF_TEN[9 - scale];
    return new SFTime(value * powerOfTen);
  }

  /**
   * Constructs an SFTime from an SFTimestamp.
   *
   * The resulting SFTime contains the time of day
   * @param ts SFTimestamp
   * @return SFTime
   */
  public static SFTime fromTimestamp(SFTimestamp ts)
  {
    long secondsSinceMidnight = ts.extract(Calendar.HOUR_OF_DAY) * 3600
      + ts.extract(Calendar.MINUTE) * 60
      + ts.extract(Calendar.SECOND);
    long nsSinceMidnight = secondsSinceMidnight * NS_IN_SECOND;
    long additionalNs = ts.getNanos();
    return new SFTime(nsSinceMidnight + additionalNs);
  }

  private SFTime(long nanos)
  {
    assert nanos >= 0 && nanos < 86400 * NS_IN_SECOND;
    this.nanos = nanos;
  }

  /**
   * Returns nanoseconds since midnight.
   * @return nanoseconds
   */
  public long getNanoseconds()
  {
    return nanos;
  }

  /**
   * Returns nanoseconds since the last whole-number second.
   * @return nanoseconds
   */
  public int getNanosecondsWithinSecond()
  {
    return (int)(nanos % SFInstant.POWERS_OF_TEN[9]);
  }

  /**
   * Returns fractional seconds since midnight at the given scale, truncated
   * from the internal nanosecond representation.
   *
   * @param scale scale
   * @return fractional seconds
   * @see #fromFractionalSeconds(long, int)
   */
  public long getFractionalSeconds(int scale)
  {
    assert scale >= 0 && scale <= 9;
    return nanos / SFInstant.POWERS_OF_TEN[9 - scale];
  }

  /**
   * Create an SFTime by adding hours, minutes, seconds, or milliseconds.
   *
   * Performs the addition modulo 24 hours so that the result stays in range.
   *
   * @param component
   *   The time component from the Calendar class.
   * @param increment
   *   Number of hours/minutes/etc. to add.
   * @return
   *   A new SFTime with the result of the addition.
   */
  public SFTime addComponent(int component, long increment)
  {
    // Reduce increment mod 24h and convert to nanoseconds.
    switch (component)
    {
      case Calendar.HOUR_OF_DAY:
        increment = 3600 * NS_IN_SECOND * (increment % 24);
        break;
      case Calendar.MINUTE:
        increment = 60 * NS_IN_SECOND * (increment % (24 * 60));
        break;
      case Calendar.SECOND:
        increment = NS_IN_SECOND * (increment % (24 * 60 * 60));
        break;
      case Calendar.MILLISECOND:
        increment = NS_IN_MILLIS * (increment % (24 * 60 * 60 * 1000));
        break;
      default:
        throw new IllegalArgumentException("invalid component " + component);
    }

    // Get the positive remainder.
    // TODO(mkember): Use Math.floorMod when GSCommon is on Java 1.8.
    long newNanos = ((nanos + increment) % NS_IN_DAY + NS_IN_DAY) % NS_IN_DAY;
    return new SFTime(newNanos);
  }

 /**
  * Create an SFTime with an adjusted scale.
  *
  * @param scale
  *   The desired scale.
  * @return
  *   Returns a new SFTime if changing the scale would result in a different
  *   (less precise) time. Returns this object otherwise.
  */
  public SFTime adjustScale(int scale)
  {
    long powerOfTen = SFInstant.POWERS_OF_TEN[9 - scale];
    long extraDigits = nanos % powerOfTen;
    return extraDigits == 0 ? this : new SFTime(nanos - extraDigits);
  }

  /**
   * Constructs a string that can be safely passed to XP.
   * @return a UTC string
   */
  public String toUTCString()
  {
    long nanoPart = nanos % NS_IN_SECOND;
    String nanoStr = String.format(".%1$09d", nanoPart);
    DateFormat df = new SimpleDateFormat("HH:mm:ss" + nanoStr);
    df.setCalendar(CalendarCache.get(SFInstant.GMT));
    return df.format(nanos / NS_IN_MILLIS);
  }

  /**
   * Constructs a string containing the number of seconds since midnight with
   * the given number of decimal places.
   * @param decimalPlaces decimal places
   * @return seconds in a string
   */
  public String toSecondsString(int decimalPlaces)
  {
    StringBuilder result = new StringBuilder();
    result.append(nanos / NS_IN_SECOND);
    if (decimalPlaces > 0)
    {
      long nanoPart = nanos % NS_IN_SECOND;
      long powerOfTen = SFInstant.POWERS_OF_TEN[9 - decimalPlaces];
      long afterDecimal = nanoPart / powerOfTen;
      result.append('.');
      String fmt = "%0" + decimalPlaces + "d";
      result.append(String.format(fmt, afterDecimal));
    }
    return result.toString();
  }

  /**
   * Compares with other SFTime
   * @param other target SFTime
   * @return 1 if larger, 0 if equal otherwise -1
   */
  public int compareTo(SFTime other)
  {
    return Long.compare(nanos, other.nanos);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int extract(int field, Integer optWeekStart, Integer optWoyPolicy)
  {
    return extract(field, SFInstant.GMT, nanos / NS_IN_MILLIS,
        optWeekStart, optWoyPolicy);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int hashCode()
  {
    return (int)(nanos % Integer.MAX_VALUE);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean equals(Object other)
  {
    return other instanceof SFTime && nanos == ((SFTime)other).nanos;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String toString()
  {
    return "SFTime(nanos=" + nanos + ")";
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy