DateValue.java

/*
** Module   :DateValue.java
** Abstract :Date value holder.
**
** Copyright (c) 2005-2017, Golden Code Development Corporation.
**
** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
** 001 SIY 20050218 ADD  @20017  Created initial version
** 002 SIY 20050310 CHG  @20356  Organized imports and reordered methods.
**                               Fixed format string.
**                               Fixed month calculation.
**                               Fixed equals();
** 003 SIY 20050323 CHG  @20470  Fixed formatting.
** 004 SIY 20050325 CHG  @20493  Fixed format string.
** 005 SIY 20050328 CHG  @20576  Added "implements Comparable" and cleared 
**                               milliseconds field.
** 006 NVS 20050426 CHG  @20890  Class implements Serializable interface.
** 007 SIY 20050429 CHG  @21009  Fixed formatting.  
** 008 EVL 20160223              Javadoc fixes to make compatible with Oracle Java 8 for
**                               Solaris 10.
** 009 IAS 20160331              Moved non-threadsafe format field to ThreadLocal
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
** 
** Additional terms under GNU Affero GPL version 3 section 7:
** 
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
** 
**   0. Attribution Requirement.
** 
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
** 
**   1. No License To Use Trademarks.
** 
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
** 
**   2. No Misrepresentation of Affiliation.
** 
**     You may not represent yourself as Golden Code Development Corporation or FWD.
** 
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
** 
**   3. No Misrepresentation of Source or Origin.
** 
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/

package com.goldencode.p2j.directory;

import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.io.Serializable;

/**
 * The main purpose of this class is to hold date value for storing/saving it
 * in the Directory. Also it allows to distinguish types during processing
 * (this is not so simple for java.util.Date and derivatives). For the
 * convenience it can be easily converted to/from java.util.Date class.
 * 
 * @author  SIY
 * @version 1.0
 */
public class DateValue
   implements Comparable, Serializable
{
   /**
    * Format used for <code>DateValue</code> &lt;-&gt; <code>String</code>
    * conversion.
    */
   private static final ThreadLocal<SimpleDateFormat> format = 
         new ThreadLocal<SimpleDateFormat>() 
   {
       @Override 
       protected SimpleDateFormat initialValue() 
       {
           return new SimpleDateFormat("yyyy-MM-dd");
       }
   };

   /** Internal instance of the Calendar */
   private Calendar calendar;

   /**
    * Construct an instance for the current date.
    */
   public DateValue()
   {
      calendar = Calendar.getInstance();
      enforceTime(calendar.getTime());
   }

   /**
    * Construct new instance from the instance of the <code>Date</code>.
    * 
    * @param   other
    *          An instance which is used as a source of the information.
    */
   public DateValue(Date other)
   {
      calendar = Calendar.getInstance();
      enforceTime(other);
   }

   /**
    * Construct new instance from the other instance of the
    * <code>DateValue</code>.
    * 
    * @param   other
    *          An instance which is used as a source of the information.
    */
   public DateValue(DateValue other)
   {
      calendar = Calendar.getInstance();
      enforceTime(other.getDate());
   }

   /**
    * Construct an instance for specified date.
    * 
    * @param   year
    *          Year value for the new instance.
    * @param   month
    *          Month value for the new instance.
    * @param   day
    *          Day of the month value for the new instance.
    */
   public DateValue(int year, int month, int day)
   {
      calendar = Calendar.getInstance();
      calendar.set(year, month - 1, day, 0, 0, 0);
      enforceTime(calendar.getTime());
   }

   /**
    * Parse <code>String</code> like YYYYMMDD and create an instance of
    * <code>DateValue</code>.
    * 
    * @param   str
    *          Source string.
    * @return  new <code>DateValue</code> instance or <code>null</code> if
    *          source string does not contain properly formatted date.
    */
   public static DateValue valueOf(String str)
   {
      try
      {
         return new DateValue(format.get().parse(str, new ParsePosition(0)));
      }
      catch (Exception e)
      {
         return null;
      }
   }

   /**
    * Compares this instance with instance of <code>Date</code> for
    * ordering.
    * 
    * @param   other
    *          The instance of <code>Date</code> to be compared.
    * @return  <code>0</code> if dates are equal; a value less than
    *          <code>0</code> if this instance date is before date of the
    *          passed parameter; a value greater than <code>0</code> if this
    *          instance date is after date of the parameter.
    */
   public int compareTo(Date other)
   {
      return calendar.getTime().compareTo(other);
   }

   /**
    * Compares two instances for ordering.
    * 
    * @param   other
    *          The instance to be compared.
    * @return  <code>0</code> if instances are equal; a value less than
    *          <code>0</code> if this instance date is before date of the
    *          instance passed as a parameter; a value greater than
    *          <code>0</code> if this instance date is after date of the
    *          instance passed as a parameter.
    */
   public int compareTo(DateValue other)
   {
      return calendar.getTime().compareTo(other.getDate());
   }

   /**
    * Compare this instance with other object.
    * 
    * @param   other
    *          The instance of <code>Date</code> to be compared.
    * @return  <code>0</code> if dates are equal; a value less than
    *          <code>0</code> if this instance date is before date of the
    *          passed parameter; a value greater than <code>0</code> if this
    *          instance date is after date of the parameter.
    * @throws  ClassCastException
    *          if argument is not instance of <code>Date</code> or
    *          <code>DateValue</code>.
    */
   public int compareTo(Object other)
   {
      if (other instanceof Date)
         return calendar.getTime().compareTo((Date)other);

      return compareTo((DateValue)other);
   }

   /**
    * Compares two objects for equality. Calls <code>Calendar.equals()</code>
    * or <code>Date.equals()</code> if possible. Otherwise return
    * <code>false</code>.
    * 
    * @param   o
    *          Object to compare with.
    * @return  <code>true</code> if instances are equal and
    *          <code>false</code> otherwise.
    */
   public boolean equals(Object o)
   {
      if (o instanceof DateValue || o instanceof Calendar)
      {
         Calendar other;

         if (o instanceof DateValue)
            other = ((DateValue)o).calendar;
         else
            other = (Calendar)o;

         if (calendar.get(Calendar.YEAR) == other.get(Calendar.YEAR)
            && calendar.get(Calendar.MONTH) == other.get(Calendar.MONTH)
            && calendar.get(Calendar.DATE) == other.get(Calendar.DATE))
         {
            return true;
         }
         return false;
      }

      if (o instanceof Date)
         return getDate().equals(o);

      return false;
   }

   /**
    * Return current value represented as an instance of <code>Date</code>.
    * 
    * @return  an instance of <code>java.util.Date</code>.
    */
   public Date getDate()
   {
      return calendar.getTime();
   }

   /**
    * Return a hash code for the instance. Use <code>Date.hashCode()</code>
    * for real computation.
    * 
    * @return  hash code for the instance.
    */
   public int hashCode()
   {
      return calendar.getTime().hashCode();
   }

   /**
    * Set current value using instance of <code>Date</code> as a source.
    * 
    * @param   date
    *          Source instance of <code>Date</code> to get values from.
    */
   public void setDate(Date date)
   {
      enforceTime(date);
   }

   /**
    * Convert <code>DateValue</code> instance into <code>String</code>.
    * 
    * @return  string in format YYYYMMMDD.
    */
   public String toString()
   {
      StringBuffer buf = new StringBuffer();

      format.get().format(getDate(), buf, new FieldPosition(DateFormat.YEAR_FIELD));

      return buf.toString();
   }

   /**
    * This method performs all necessary steps to enforce DateValue instance
    * to contain only date, without time part.
    * 
    * @param   input
    *          <code>Date</code> instance to set information from.
    */
   private void enforceTime(Date input)
   {
      //Make sure that time part is set to 0
      calendar.setTime(input);
      calendar.set(Calendar.HOUR_OF_DAY, 0);
      calendar.set(Calendar.MINUTE, 0);
      calendar.set(Calendar.SECOND, 0);
      calendar.set(Calendar.MILLISECOND, 0);
   }
}