/*
 * $Id: GeoDate.java,v 1.17 2003/08/22 23:02:40 dwd Exp $
 *
 * This software is provided by NOAA for full, free and open release.  It is
 * understood by the recipient/user that NOAA assumes no liability for any
 * errors contained in the code.  Although this software is released without
 * conditions or restrictions in its use, it is expected that appropriate
 * credit be given to its author and to the National Oceanic and Atmospheric
 * Administration should the software be included by the recipient as an
 * element in other product development.
 */

package sgtplot.util;

import  java.util.Date;
import  java.util.Calendar;
import  java.util.GregorianCalendar;
import  java.util.TimeZone;

import  java.text.DateFormat;
import  java.text.SimpleDateFormat;
import  java.text.ParsePosition;

/**
 * GeoDate extends the capabilities of Date.
 * Additional features of GeoDate include methods for
 * incrementing and decrementing, adding and substracting
 * GeoDate objects. All GeoDate objects
 * share the same GregorianCalendar set to a "GMT" time
 * zone. Thus, all GeoDate times are in "GMT". This
 * simplifies the conversion to and from String
 * representations of time.
 *
 * @see GregorianCalendar
 *
 * @author Donald Denbo
 * @version $Revision: 1.17 $, $Date: 2003/08/22 23:02:40 $
 * @since sgtplot 1.0
 */
public class GeoDate extends java.util.Date implements java.io.Serializable {
  private int max_day_[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  private boolean splitDone_;
  private int yearday_;
  private int dayofweek_;
  private int year_;
  private int month_;
  private int day_;
  private int hour_;
  private int minute_;
  private int second_;
  private int msec_;
  private int MSec_;
  private int JDay_;
  private boolean EPICTimeDone_;
  private boolean relativeTime_ = false;
  private static Calendar cal_ = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
  /** Increment or decrement in days. */
  public static final int DAYS = 1;
  /** Increment or decrement in months.  */
  public static final int MONTHS = 2;
  /** Increment or decrement in years.  */
  public static final int YEARS = 4;
  /** Increment or decrement in hours.  */
  public static final int HOURS = 5;
  /** Increment or decrement in minutes  */
  public static final int MINUTES = 6;
  /** Increment or decrement in seonds  */
  public static final int SECONDS = 7;
  /** Increment or decrement in milliseconds */
  public static final int MSEC = 8;
  /** Number of milliseconds in a day. */
  public static final long MSECS_IN_DAY = 86400000;
  /** Construct a new GeoDate. */
  public GeoDate() {
    super();
  }
  /**
   * Construct a new GeoDate from a
   * String. Formatting is done using
   * SimpleDateFormat. The specified time
   * is taken to be "GMT".
   *
   * @param time character representation of time
   * @param format codes used to read time
   */
  public GeoDate(String time,String format) throws IllegalTimeValue {
    super();
    ParsePosition pos = new ParsePosition(0);
    DateFormat df = new SimpleDateFormat(format);
    df.setCalendar(cal_);

    Date dte = df.parse(time, pos);
    if(dte == null) {
      throw new IllegalTimeValue("Parse error: " + time + ", " + format);
    }
    setTime(dte.getTime());
  }
  /**
   * Constructs a new GeoDate from an
   * existing GeoDate.
   *
   * @param t GeoDate
   */
  public GeoDate(GeoDate t) {
    super(t.getTime());
  }
  /**
   * Construct a new GeoDate from values. The
   * specified time is taken to be "GMT".
   *
   * @param mon month
   * @param day day of the month
   * @param year year (no offset!)
   * @param hour hour
   * @param min minutes
   * @param sec seconds
   * @param msec milliseconds
   * @exception IllegalTimeValue The constructor was called with a set
   * of parameters that does not constitute a legitimate time value.
   */
  public GeoDate(int mon,int day,int year,int hour,int min,int sec,int msec) throws IllegalTimeValue  {
    this.set(mon, day, year, hour, min, sec, msec);
  }
  /**
   * Construct a new GeoDate from a
   * Date object. No time zone conversion
   * is done.
   *
   * @param date Date object
   */
  public GeoDate(Date date) {
    setTime(date.getTime());
  }
  /**
   * Construct a new GeoDate from EPIC double integers.
   * Time zone for conversion is "GMT".
   *
   * @param jday julian day
   * @param msec milliseconds since midnight
   */
  public GeoDate(int jday, int msec) {
    set(jday, msec);
  }

  /**
   * Allocates a GeoDate object and initializes it to represent
   * the specified number of milliseconds since the standard
   * base time know as "the epoch", namely January 1, 1970, 00:00:00
   * GMT.
   */
  public GeoDate(long date) {
    setTime(date);
  }
  /**
   * Set the relativeTime flag.  The relativeTime flag indicates that
   * the GeoDate object does not represent an actual
   * absolute time, but a temporal duration.
   */
  public void setRelativeTime(boolean relative) {
    relativeTime_ = relative;
  }
  /**
   * Tests the relativeTime flag.
   *
   * @return if true, time is a duration
   */
  public boolean isRelativeTime() {
    return relativeTime_;
  }
  /**
   * Change value of GeoDate from EPIC double
   * integers.
   * Time zone for conversion is "GMT".
   *
   * @param jday julian day
   * @param msec milliseconds since midnight
   */
  public void set(int jday, int msec) {
    int ja, jb, jc, jd, je;
    double jalpha, second;
    int day, month, year, hour, minute, sec;

    if(jday > 2299161) {
      jalpha = ((double)(jday - 1867216)-0.25)/36524.25;
      ja = jday + 1 + (int)jalpha - (int)(0.25*jalpha);
    } else {
      ja = jday;
    }
    jb = ja + 1524;
    jc = (int)(6680.0 + ((double)(jb - 2439870) - 122.1)/365.25);
    jd = (int)(365*jc + (0.25*jc));
    je = (int)((jb - jd)/30.6001);

    day = (int)(jb - jd) - (int)(30.6001*je);
    month = (int)(je - 1);
    if(month > 12) month -= 12;
    year = (int)(jc - 4715);
    if(month > 2) --year;
    if(year <= 0) --year;
    ja = msec/1000;
    hour = (int)(ja/3600);
    minute = (int)((ja - hour*3600)/60);
    second = (double)(msec - (hour*3600 + minute*60)*1000)/1000.0;
    sec = (int)second;
    msec = ((int)(second*1000.0))%1000;
    try{
      set(month, day, year, hour, minute, sec, msec);
    } catch (IllegalTimeValue e) {}
  }
  /**
   * Change value of GeoDate from values.
   * Time zone for conversion is "GMT".
   *
   * @param mon month (1=January, 12=December)
   * @param day day of the month
   * @param year year (no offset!)
   * @param hour hour
   * @param min minutes
   * @param sec seconds
   * @param msec milliseconds
   * @exception IllegalTimeValue The parameters passed to this method represent a time
   * value that is invalid
   *
   */
  public void set(int mon,int day,int year,int hour,int min,int sec,int msec) throws IllegalTimeValue  {
    int jy, jm, ja, jul;
    int leap = (year%4 != 0? 0: (year%400 == 0? 1: (year%100==0? 0: 1)));
    max_day_[1] = 28 + leap;

    if(mon > 12 || mon < 1) {
      this.setTime(0);
      throw new IllegalTimeValue("value of month out of range");
    }
    if(day > max_day_[mon-1] || day < 1) {
      this.setTime(0);
      throw new IllegalTimeValue("value of day out of range");
    }
    if(hour >= 24 || hour < 0) {
      this.setTime(0);
      throw new IllegalTimeValue("value of hour out of range");
    }
    if(min >= 60 || min < 0) {
      this.setTime(0);
      throw new IllegalTimeValue("value of minute out of range");
    }
    if(sec >= 60 || sec < 0) {
      this.setTime(0);
      throw new IllegalTimeValue("value of second out of range");
    }
    if(msec >= 1000 || msec < 0) {
      this.setTime(0);
      throw new IllegalTimeValue("value of msec out of range");
    }

    cal_.clear();
    cal_.set(year, mon - 1, day, hour, min, sec);

    //    this.setTime(cal.getTimeInMillis() + msec);
    this.setTime((cal_.getTime()).getTime() + msec);
    splitDone_ = false;
    EPICTimeDone_ = false;
  }
  /**
   * Get the number of days in the current month.
   *
   * @return number of days in current month
   **/
  public int getDaysInMonth() {
    int leap;
    int year = cal_.get(Calendar.YEAR);
    leap = (year%4 != 0? 0: (year%400 == 0? 1: (year%100==0? 0: 1)));
    max_day_[1] = 28 + leap;
    return max_day_[month_-1];
  }
  /**
   * Set to current time.
   */
  public void now() {
    Date nw = new Date();
    this.setTime(nw.getTime());
  }
  /**
   * Add time to current GeoDate. This operation only
   * makes since if time is a relative time value,
   * i.e. the result of a
   * GeoDate subrtraction.
   *
   * @param time GeoDate
   * @return new GeoDate
   */
  public GeoDate add(GeoDate time) {
    GeoDate time2 = new GeoDate();
    long MSec = getTime() + time.getTime();

    time2.splitDone_ = false;
    time2.EPICTimeDone_ = false;
    time2.setTime(MSec);
    return time2;
  }
  /**
   * Subtract time2 from current GeoDate.
   *
   * @param time2 subtracthend
   * @return new GeoDate
   */
  public GeoDate subtract(GeoDate time2) {
    GeoDate delta = new GeoDate();
    long MSec = getTime() - time2.getTime();

    delta.splitDone_ = false;
    delta.EPICTimeDone_ = false;
    delta.setTime(MSec);
    delta.setRelativeTime(true);
    return delta;
  }
  /**
   * Divide by value. Current time should the result of adding two times or
   * subtracting two times to be a meaningful calculation.
   *
   * @param val divisor
   * @return new GeoDate
   */
  public GeoDate divide(double val) {
    GeoDate result = new GeoDate();

    if(val == 0.0) return null;

    result.setTime((long)(((double)getTime())/val));

    result.splitDone_ = false;
    result.EPICTimeDone_ = false;
    result.setRelativeTime(true);
    return result;
  }
  void splitTimeFormat() {
    int ja, jb, jc, jd, je;
    double jalpha;
    GeoDate gt;

    if(splitDone_) return;

    cal_.setTime(this);
    day_ = cal_.get(Calendar.DAY_OF_MONTH);
    month_ = cal_.get(Calendar.MONTH) + 1;
    year_ = cal_.get(Calendar.YEAR);
    hour_ = cal_.get(Calendar.HOUR_OF_DAY);
    minute_ = cal_.get(Calendar.MINUTE);
    second_ = cal_.get(Calendar.SECOND);
    msec_ = cal_.get(Calendar.MILLISECOND);
    dayofweek_ = cal_.get(Calendar.DAY_OF_WEEK) - 1;
    yearday_ = cal_.get(Calendar.DAY_OF_YEAR);

    splitDone_ = true;
  }
  /**
   * Increment current GeoDate
   * by SECONDS, MINUTES, HOURS,
   * DAYS, MONTHS, or YEARS
   *
   * @param val amount to increment
   * @param tu time units (SECONDS, MINUTES, HOURS, DAYS, MONTHS, or YEARS)
   * operation.
   */
  public GeoDate increment(float val,int tu) {
    return increment((double)val, tu);
  }
  /**
   * Increment current GeoDate
   * by SECONDS, MINUTES, HOURS,
   * DAYS, MONTHS, or YEARS
   *
   * @param val amount to increment
   * @param tu time units (SECONDS, MINUTES, HOURS, DAYS, MONTHS, or YEARS)
   * operation.
   */
  public GeoDate increment(double val,int tu) {
    int leap;
    int ival = (int)val;
    double fract = (val - ival);
    long MSec = getTime();
    switch(tu) {
    case MSEC:
      MSec += (long)val;
      setTime(MSec);
      break;
    case SECONDS:
      MSec += (long)(val*1000);
      setTime(MSec);
      break;
    case MINUTES:
      MSec += (long)(val*60000);
      setTime(MSec);
      break;
    case HOURS:
      MSec += (long)(val*3600000);
      setTime(MSec);
      break;
    case DAYS:                          // days and fraction days
      MSec += (long)(val*MSECS_IN_DAY);
      setTime(MSec);
      break;
    case MONTHS:                        // nearest day
      splitTimeFormat();
      month_ += ival;
      year_ += ((month_-1)/12);
      month_ -= ((month_-1)/12)*12;
      if(month_ == 0) month_ = 1;
      leap = (year_%4 != 0? 0: (year_%400 == 0? 1: (year_%100==0? 0: 1)));
      max_day_[1] = 28 + leap;
      day_ += (int)(fract*max_day_[month_-1]);
      if(day_ > max_day_[month_-1]) {
        day_ -= max_day_[month_-1];
        month_++;
        year_ += ((month_-1)/12);
        month_ -= ((month_-1)/12)*12;
        if(month_ == 0) month_ =1;
      }
      try {
        this.set(month_, day_, year_, hour_, minute_, second_, msec_);
      } catch (IllegalTimeValue e) {
        System.err.println(e);
      }
      break;
    case YEARS:                         // nearest day
      splitTimeFormat();
      year_ += ival;
      leap = (year_%4 != 0? 0: (year_%400 == 0? 1: (year_%100==0? 0: 1)));
      max_day_[1] = 28 + leap;
      if(day_ > max_day_[month_-1]) day_ = max_day_[month_-1];
      try {
        this.set(month_, day_, year_, hour_, minute_, second_, msec_);
      } catch (IllegalTimeValue e) {
        System.err.println(e);
      }
      setTime(getTime() + (long)(fract*365.25)*MSECS_IN_DAY);
      break;
    }
    splitDone_ = false;
    EPICTimeDone_ = false;
    return this;
  }
  /**
   * Decrement current GeoDate
   * by SECONDS, MINUTES, HOURS,
   * DAYS, MONTHS, or YEARS
   *
   * @param val amount to decrement
   * @param tu time units (SECONDS, MINUTES, HOURS, DAYS, MONTHS, or YEARS)
   * operation.
   */
  public GeoDate decrement(float val,int tu) {
    return decrement((double)val, tu);
  }
  /**
   * Decrement current GeoDate
   * by SECONDS, MINUTES, HOURS,
   * DAYS, MONTHS, or YEARS
   *
   * @param val amount to decrement
   * @param tu time units (SECONDS, MINUTES, HOURS, DAYS, MONTHS, or YEARS)
   * operation.
   */
  public GeoDate decrement(double val,int tu) {
    int leap;
    int ival = (int)val;
    double fract = (val - ival);
    long MSec = getTime();

    switch(tu) {
    case MSEC:
      MSec -= (long)val;
      setTime(MSec);
      break;
    case SECONDS:
      MSec -= (long)(val*1000);
      setTime(MSec);
      break;
    case MINUTES:
      MSec -= (long)(val*60000);
      setTime(MSec);
      break;
    case HOURS:
      MSec -= (long)(val*3600000);
      setTime(MSec);
      break;
    case DAYS:
      MSec -= (long)(val*MSECS_IN_DAY);
      setTime(MSec);
      break;
    case MONTHS:
      splitTimeFormat();
      month_ -= ival;
      if(month_ <= 0) {
	year_ -= (month_-1)/12 + 1;
	month_ += ((month_-1)/12)*12 + 12;
      }
      if(month_ == 0) month_ = 12;
      leap = (year_%4 != 0? 0: (year_%400 == 0? 1: (year_%100==0? 0: 1)));
      max_day_[1] = 28 + leap;
      day_ -= (int)(fract*max_day_[month_-1]);
      if(day_ > max_day_[month_-1]) {
	day_ -= max_day_[month_-1];
	month_--;
	if(month_ <= 0) {
	  year_ -= (month_-1)/12 + 1;
	  month_ += ((month_-1)/12)*12;
	}
	if(month_ == 0) month_ = 12;
      }
      try {
	this.set(month_, day_, year_, hour_, minute_, second_, msec_);
      } catch (IllegalTimeValue e) {
	System.err.println(e);
      }
      break;
    case YEARS:
      splitTimeFormat();
      year_ -= ival;
      leap = (year_%4 != 0? 0: (year_%400 == 0? 1: (year_%100==0? 0: 1)));
      max_day_[1] = 28 + leap;
      if(day_ > max_day_[month_-1]) day_ = max_day_[month_-1];
      try {
	this.set(month_, day_, year_, hour_, minute_, second_, msec_);
      } catch (IllegalTimeValue e) {
	System.err.println(e);
      }
      setTime(getTime() - (long)(fract*365.25)*MSECS_IN_DAY);
      break;
    }
    splitDone_ = false;
    EPICTimeDone_ = false;
    return this;
  }
  /**
   * Time offset from reference GeoDate
   *
   * @param ref reference GeoDate
   * @return offset in days
   */
  public double offset(GeoDate ref) {
    double val;
    val = ((double)(this.getTime() - ref.getTime()))/86400000.0;
    return val;
  }
  /**
   * Set a GeoDate from year and year-day.
   *
   * @param year year
   * @param yearday year-day number (Jan 1 = 1)
   * parameters that does not constitute a legitimate time value.
   */
  public void setYearYearDay(int year,int yearday) {
    try {
      this.set(1, 1, year, 0, 0, 0, 0);
    } catch (IllegalTimeValue e) {
      System.err.println(e);
    }
    setTime(getTime() + (yearday - 1)*MSECS_IN_DAY);
  }
  /**
   * Get a reference to the GregorianCalendar set
   * to the current GeoDate time.
   *
   * @return Calendar
   */
  public Calendar getCalendar() {
    cal_.setTime(this);
    return cal_;
  }
  /**
   * Get year-day number (Jan 1 = 1)
   */
  public int getYearday() {
    splitTimeFormat();
    return yearday_;
  }
  /**
   * Get year
   *
   * @deprecated Overrides a deprecated method, replaced by {@link #getGMTYear}.
   */
  public int getYear() {
    splitTimeFormat();
    return year_;
  }
  /**
   * Get year.
   */
  public int getGMTYear() {
    splitTimeFormat();
    return year_;
  }
  /**
   * Get month
   *
   * @deprecated Overrides a deprecated method, replaced by {@link #getGMTMonth}.
   */
  public int getMonth() {
    splitTimeFormat();
    return month_;
  }
  /**
   * Get month
   */
  public int getGMTMonth() {
    splitTimeFormat();
    return month_;
  }
  /**
   * Get day
   *
   * @deprecated Overrides a deprecated method, replaced by {@link #getGMTDay}.
   */
  public int getDay() {
    splitTimeFormat();
    return day_;
  }
  /**
   * Get day
   */
  public int getGMTDay() {
    splitTimeFormat();
    return day_;
  }
  /**
   * Get hours
   *
   * @deprecated Overrides a deprecated method, replaced by {@link #getGMTHours}.
   */
  public int getHours() {
    splitTimeFormat();
    return hour_;
  }
  /**
   * Get hours
   */
  public int getGMTHours() {
    splitTimeFormat();
    return hour_;
  }
  /**
   * Get minutes
   *
   * @deprecated Overrides a deprecated method, replaced by {@link #getGMTMinutes}.
   */
  public int getMinutes() {
    splitTimeFormat();
    return minute_;
  }
  /**
   * Get minutes
   */
  public int getGMTMinutes() {
    splitTimeFormat();
    return minute_;
  }
  /**
   * Get secondss
   * @deprecated replaced by {@link #getGMTSeconds}.
   */
  public double getSecondss() {
    splitTimeFormat();
    return second_;
  }
  /**
   * Get seconds and fraction of seconds.
   */
  public double getGMTSeconds() {
    splitTimeFormat();
    return second_;
  }
  /**
   * get EPIC Julian Day
   */
  public int getJDay() {
    splitTimeFormat();
    computeEPICTime();
    return JDay_;
  }
  /**
   * get milliseconds since midnight
   */
  public int getMSec() {
    splitTimeFormat();
    computeEPICTime();
    return MSec_;
  }

  private void computeEPICTime() {
    if(EPICTimeDone_) return;
    int GREGORIAN__ = (15+31*(10+12*1582));
    int jy, jm, ja, jul;
    int year = year_;
    int mon = month_;
    int day = day_;
    int hour = hour_;
    int min = minute_;
    int sec = second_;
    int msec = msec_;

    int leap = (year%4 != 0? 0: (year%400 == 0? 1: (year%100==0? 0: 1)));

    if(year < 0) ++year;
    if(mon > 2) {
      jy = year;
      jm = mon + 1;
    } else {
      jy = year - 1;
      jm = mon + 13;
    }
    jul = (int)(Math.floor(365.25*jy)+Math.floor(30.6001*jm)+day+1720995);
    if(day + 31L*(mon+12L*year) >= GREGORIAN__) {
      ja = (int)(0.01*jy);
      jul += 2 - ja + (int)(0.25*ja);
    }

    JDay_ = jul;
    MSec_ = (int)((hour*3600L+min*60L)*1000L+sec*1000L+msec);

    EPICTimeDone_ = true;
  }
  /**
   * Convert GeoDate to String
   * using standard format "yyyy-MM-dd HH:mm:ss z"
   * and "GMT" time zone.
   *
   * @return date
   */
  public String toString() {
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
    df.setCalendar(cal_);
    return df.format(this);
  }
  /**
   * Convert GeoDate to String
   * using provided format. The SimpleDateFormat
   * and the GregorianCalendar is used to format
   * the GeoDate. A format of "decade" will create
   * a string of the form 1990 or 1980.
   *
   * @see SimpleDateFormat
   *
   * @param format String containing codes used to write time.
   */
  public String toString(String format) {
    if(format.equals("decade")) {
      splitTimeFormat();
      return Integer.toString((year_/10)*10);
    } else {
      DateFormat df = new SimpleDateFormat(format);
      df.setCalendar(cal_);
      return df.format(this);
    }
  }
  /**
   * Tests to see if value represents missing data. A value from the
   * getTime() method of Long.MIN_VALUE represents a
   * missing observation.
   *
   * @see sgtplot.dm.SGTData#getXRange()
   */
  public boolean isMissing() {
    return getTime() == Long.MIN_VALUE;
  }
  /**
   * Tests to see if the current GeoDate is less than, equal, or greater than.
   * another GeoDate
   */
  public int compareTo(GeoDate anotherDate) {
  	if (this.getTime() < anotherDate.getTime())
  		return -1;
  	else if (this.getTime() == anotherDate.getTime())
  		return 0;
  	else
  		return 1;
  }
}

 

Ads help maintain this website.