import java.util.Calendar;
import java.util.Date;

/**
 * DateUtil's class is to give user the ability to manipulate date.
 * Example of Usage:
 * DateUtil du = new DateUtil(); // initiate DateUtil into the present time
 * and date.
 * DateUtil du = new DateUtil(12-1-2004); // initiate DateUtil into
 * 12 January 2004. It can takes SQL's date format of yyyy-mm-dd.
 * 
 * For more details of initiating DateUtil, look at the constructor below.
 * 
 * System.out.println("today is "+du.getFormNDate());
 * System.out.println("today is "+du.getWOM()+" week of the month");
 * System.out.println("today is "+du.getDOY()+" day of the year");
 *
 * @author Edison Chindrawaly
 * @version 1.1 [6/2/2004]
 * 
 */

public class DateUtil implements DateUtility
{
 protected int date = 0; // date
 protected int month= 0; // month
 protected int year = 0; // year
 protected int day  = 0; // monday - sunday
 protected int wom  = 0; // week of month
 protected int woy  = 0; // week of year
 protected int doy  = 0; // day of year

 protected int hour = 0;
 protected int minute = 0;
 protected int second = 0;

 private Calendar cal = null;

/**
 * default constructor. It sets Calendar to the current time/date
 */
 public DateUtil()
 { 
  initialize(); 
  setAll(true,0);
 }

/**
 * constructor that takes single String of date. Date in SQL format
 * [YYYY-MM-DD] or in International format [DD-MM-YYYY]. It takes
 * '-','/', or '\' as the delimiter for each parameter. If it fails
 * to phrase the date, it will automatically obtain the current time/date.
 */
 public DateUtil(String uDate)
 { 
  processPhrase(uDate);
 }

/**
 * constructor that takes long time in millisecond.
 */
 public DateUtil(long timeInMill)
 {
  if(timeInMill < 0)
   initialize();
  else
  {
   initialize();
   cal.setTimeInMillis(timeInMill);
  }
  setAll(true,0);
 }

/**
 * initialize's method is to initialize the calendar so it can be
 * used by all method in the class. It sets lenient to be true
 * so we can use set,roll, and add method by negative number.
 * @param none
 * @return none
 */
 private void initialize()
 {
  cal = Calendar.getInstance();
  cal.setLenient(true);
 }

/**
 * setCalendar's method is to set calendar using the string parameter
 * that contains SQL-Date format or Normal-Date format. It takes 3 type
 * of parameters '-','/', or '\'. If it the phrasing of the string fails,
 * it will automatically obtain the current time/date.
 * @param String uDate 
 * @return none
 */
 public void setCalendar(String uDate)
 {
  processPhrase(uDate);
 }

/**
 * setCalendar's method is set Calendar using three integer parameter.
 * It calls Calendar.set(int,int,int) to complete the task. It compensates
 * for the month JAN=0 and it sets flag = false for setAll's method.
 * @param int gYear, int gMonth, int gDate
 * @return none
 */
 public void setCalendar(int gYear,int gMonth,int gDate)
 {
  if(gYear < 1 || gMonth < 1 || gDate < 1)
  {
   initialize();
   setAll(true,0);
  }
  else
  {
   cal.set(gYear,gMonth-1,gDate); // to compensate for month JAN=0
   setAll(false,gMonth);
  }
 }

/**
 * setAll's method is to set all class variables in the DateUtil's class.
 * It has a special boolean flag to differenciate the month's field.
 * If the calling method is Calendar.set's method, it sets the flag to false.
 * Otherwise it sets the flag to true and add (1) to compensate for the month
 * which starts from 0 = January.
 * @param boolean addMonth, int rMonth
 * @return none
 */
 public void setAll(boolean addMonth,int rMonth)
 {
  date = cal.get(Calendar.DATE);
  day  = cal.get(Calendar.DAY_OF_WEEK);
  if(addMonth)
   month = cal.get(Calendar.MONTH)+1; // TO COMPENSATE JAN = 0
  else
   month = rMonth;
  year = cal.get(Calendar.YEAR);
  doy  = cal.get(Calendar.DAY_OF_YEAR);
  wom  = cal.get(Calendar.WEEK_OF_MONTH);
  woy  = cal.get(Calendar.WEEK_OF_YEAR);
  hour = cal.get(Calendar.HOUR_OF_DAY); // USE 24-HOUR 
  minute = cal.get(Calendar.MINUTE);
  second = cal.get(Calendar.SECOND);
 }

/**
 * addCalendar's method is too add number of days (negative/positive)
 * and it can add back to previous year or to the next year. It affects all
 * fields of java.util.Calendar. It is the preferred way of doing the
 * date calculation. The preferred option of date calculation is using
 * the option 1 : Calendar.DAY_OF_MONTH
 * @param int option, int number
 * @return none
 */
 public void addCalendar(int option,int number)
 {
  switch(option)
  {
   case 1 : cal.add(Calendar.DAY_OF_MONTH, number); break;
   case 2 : cal.add(Calendar.MONTH, number); break;
   case 3 : cal.add(Calendar.YEAR, number); break;
   case 4 : cal.add(Calendar.DAY_OF_YEAR, number);
   case 5 : cal.add(Calendar.WEEK_OF_MONTH, number); break;
   case 6 : cal.add(Calendar.WEEK_OF_YEAR, number); break;
   case 7 : cal.add(Calendar.HOUR, number); break; 
   case 8 : cal.add(Calendar.MINUTE, number); break;
   case 9 : cal.add(Calendar.SECOND, number); break; 
   default: initialize(); break;
  }
  setAll(true,0);
 }

/**
 * setCalendar's method is unreliable so far. Avoid using it.
 * @param int option, int number
 * @return none
 */
 public void setCalendar(int option,int number)
 {
  switch(option)
  {
   case 1 : cal.set(Calendar.DAY_OF_MONTH, number); break;
   case 2 : cal.set(Calendar.MONTH, number); break;
   case 3 : cal.set(Calendar.YEAR, number); break;
   case 4 : cal.set(Calendar.DAY_OF_YEAR, number);
   case 5 : cal.set(Calendar.WEEK_OF_MONTH, number); break;
   case 6 : cal.set(Calendar.WEEK_OF_YEAR, number); break;
   case 7 : cal.set(Calendar.HOUR, number); break; 
   case 8 : cal.set(Calendar.MINUTE, number); break;
   case 9 : cal.set(Calendar.SECOND, number); break; 
   default: initialize(); break;
  }
  setAll(true,0);
 }

/**
 * rollCalendar's method is strict to the option. If we use to roll backward
 * to the previous month, it will roll back within the same month. It won't
 * go to the previous year either. However It is as accurate as the add's 
 * method as long as we use option 1: Calendar.DAY_OF_MONTH
 * @param int option, int number
 * @return none
 */
 public void rollCalendar(int option,int number)
 {
  switch(option)
  {
   case 1 : cal.roll(Calendar.DAY_OF_MONTH, number); break;
   case 2 : cal.roll(Calendar.MONTH, number); break;
   case 3 : cal.roll(Calendar.YEAR, number); break;
   case 4 : cal.roll(Calendar.DAY_OF_YEAR, number);
   case 5 : cal.roll(Calendar.WEEK_OF_MONTH, number); break;
   case 6 : cal.roll(Calendar.WEEK_OF_YEAR, number); break;
   case 7 : cal.roll(Calendar.HOUR, number); break; 
   case 8 : cal.roll(Calendar.MINUTE, number); break;
   case 9 : cal.roll(Calendar.SECOND, number); break; 
   default: initialize(); break;
  }
  setAll(true,0);
 }

/**
 * processPhrase's method is to parse the date-month-year out of the 
 * given date and enter them as the parameter to set the calendar.
 * @param String uDate - valid date of any SQL or Normal date format
 * @return none
 */
 private void processPhrase(String uDate)
 {
  int gDate, gMonth, gYear;
  initialize();
  if(uDate!=null && verifyDateFormat(uDate))
  {
   gDate = extractDate(uDate);
   gMonth= extractMonth(uDate);
   gYear = extractYear(uDate);
   cal.set(gYear,gMonth-1,gDate); // reduce 1 to compensate the month
   setAll(false,gMonth);
  }
  else
  {
   setAll(true,0);
  }
 }

/**
 * compareDate's method is to compare date against the input date.
 * if equal then return 0. if date is smaller than input date, return -1.
 * Else return 1. if error, return -2.
 * @param String inDate - input date
 * @return int 0 if equal, -1 if smaller, 1 if bigger else -2 error
 */
 public int compareDate(String inDate)
 {
  if(inDate == null) return -2;
  if(!verifyDateFormat(inDate)) return -2; 
	int gDate = extractDate(inDate);
	int gMonth= extractMonth(inDate);
	int gYear = extractYear(inDate);
	if(year < gYear) return -1;
	else if(year > gYear) return 1;
	if(month < gMonth) return -1;
	else if(month > gMonth) return 1;
	if(date < gDate) return -1;
	else if(date > gDate) return 1;
	return 0;
 }

/**
 * compareDate's method is to compare date1 against date2. if date1 ==
 * date2 then return 0. if date1 < date2 return -1. else return 1.
 * if date1 and/or date2 is not valid, return -2
 * @param String date1, String date2
 * @return int 0 if equal, -1 if smaller else 1 and -2 if error
 */
 public static int compareDate(String date1,String date2)
 {
  if(date1 == null || date2 == null) return -2;
  if(!verifyDateFormat(date1) || !verifyDateFormat(date2)) return -2;
	int y1 = extractYear(date1);
	int y2 = extractYear(date1);
	if(y1 < y2) return -1;
	else if(y1 > y2) return 1;
	int m1 = extractMonth(date1);
	int m2 = extractMonth(date2);
	if(m1 < m2) return -1;
	else if(m1 > m2) return 1;
	int d1 = extractDate(date1);
	int d2 = extractDate(date2);
	if(d1 < d2) return -1;
	else if(d1 > d2) return 1;
	return 0;
 }

/**
 * extractDate's method is to extract the date out of the uDate. 
 * uDate's date format can take SQL-Date format or Normal Date format with
 * three types of delimiters ['/','-','\']
 * @param String uDate - valid date format [dd-mm-yyyy] or [yyyy-mm-dd]
 * @return int date if success else return -1 as error
 */
 public static int extractDate(String uDate)
 {
  if(uDate == null) return -1;

  int pos = getFirstDelimiter(uDate); 
  if(pos == 2 || pos == 1)
   return Integer.parseInt(uDate.substring(0,pos));
  int pos2 = getSecondDelimiter(uDate,pos+1);
  if(pos2 < 0) return -1;
  if(uDate.length() - (pos2+1) > 2) 
  {
   return -1; 
  }
  return Integer.parseInt(uDate.substring(pos2+1,uDate.length()));
 }

/**
 * extractMonth's method is to extract the month out of the uDate. 
 * uDate's date format can take SQL-Date format or Normal Date format with
 * three types of delimiters ['/','-','\']
 * @param String uDate - valid date format [dd-mm-yyyy] or [yyyy-mm-dd]
 * @return int month if success else return -1 as error
 */
 public static int extractMonth(String uDate)
 {
  if(uDate == null) return -1;
  int pos1 = getFirstDelimiter(uDate);
  if(pos1 < 0) return -1;
  int pos2 = getSecondDelimiter(uDate,pos1+1);
  if(pos2 < 0) return -1;
  if((pos2 - (pos1+1)) > 2) return -1;
  return Integer.parseInt(uDate.substring(pos1+1,pos2));
 }

/**
 * extractYear's method is to extract the year out of the uDate. 
 * uDate's date format can take SQL-Date format or Normal Date format with
 * three types of delimiters ['/','-','\']
 * @param String uDate - valid date format [dd-mm-yyyy] or [yyyy-mm-dd]
 * @return int year if success else return -1 as error 
 */
 public static int extractYear(String uDate)
 {
  if(uDate == null) return -1;

  int pos1 = getFirstDelimiter(uDate);
  if(pos1 < 0) return -1;
  int pos2 = getSecondDelimiter(uDate,pos1+1);
  if(pos2 < 0) return -1;
  if(uDate.length() - (pos2+1) != 4) // year could be at pos1 
  {
   if(pos1 != 4)
   {
    return -1; // year not in the pos1
   }
   return Integer.parseInt(uDate.substring(0,pos1));
  }
  return Integer.parseInt(uDate.substring(pos2+1,uDate.length()));	
 }

/**
 * getFirstDelimiter's method is to get the first delimiter of the date
 * in any form of delimiter ['\','/','-'] and return the position of the
 * delimiter if successfully found.
 * @param String uDate - date format
 * @return int position of the delimiter if found else return -1
 */
 public static int getFirstDelimiter(String uDate)
 {
  if(uDate == null) return -1;

  int pos = uDate.indexOf("-");
  if(pos < 0) pos = uDate.indexOf("/");
  else if(pos < 0) pos = uDate.indexOf("\\");
  return pos;
 }

/**
 * getSecondDelimiter's method is to get the second delimiter of the date
 * in any form of delimiter ['/','\','-'] and return the position of the
 * delimiter if successfully found. it has to give the last position of 
 * the found delimiter so it can continue to search for the second
 * @param String uDate - date format.
 *        int pos2 - the first delimiter's pos
 * @return int position if found else return -1
 */
 public static int getSecondDelimiter(String uDate,int pos2)
 {
  if(uDate == null) return -1;

  int pos = uDate.indexOf("-",pos2);
  if(pos < 0) pos = uDate.indexOf("/");
  else if(pos < 0) pos = uDate.indexOf("\\");
  return pos;
 }

/**
 * verifyDateFormat's method is to verify the date format in any form
 * of SQL-Date [yyyy-mm-dd] or Normal Date [dd-mm-yyyy]. It also checks
 * for the bound of date,month,year input.
 * @param String uDate - date format
 * @return boolean true if it is in valid date format. else return false
 */
 public static boolean verifyDateFormat(String uDate)
 {
  if(uDate == null) return false;

  int pos1 = getFirstDelimiter(uDate); 
  if(pos1 < 0) return false;

  int pos2 = getSecondDelimiter(uDate,pos1+1);
  if(pos2 < 0) return false;
  
  if(!checkDigit(uDate)) return false;
  int gDate = extractDate(uDate);
  int gMonth= extractMonth(uDate);
  int gYear = extractYear(uDate);
  if(gDate < 1 || gMonth < 1 || gYear <1)
  {
   return false;
  }
  if(gDate > 31 || gMonth > 12)
  {
   return false;
  }
  return true;
 }

/**
 * verifySQLDate's method is to verify SQL Date format [yyyy-mm-dd]
 * @param String uDate
 * @return boolean true if uDate is SQL Date format else return false
 */
 public static boolean verifySQLDate(String uDate)
 {
  if(uDate == null) return false;
  if(uDate.length() < 8 || uDate.length() > 10) return false;
  if(!checkDigit(uDate)) return false;

  int pos1 = getFirstDelimiter(uDate);
  if(pos1 < 0 || pos1 < 4) return false;
  int pos2 = getSecondDelimiter(uDate,pos1+1);
  if(pos2 < 0) return false;
  int diff = (pos2-1) - (pos1+1); 
  if(diff != 1 && diff != 2) return false;
  return true;
 }

/**
 * verifyNormalDate's method is to verify the normal date format [dd-mm-yyyy]
 * @param String uDate - date
 * @return boolean true if it is normal date format else false
 */
 public static boolean verifyNormalDate(String uDate)
 {
  if(uDate == null) return false;
  if(uDate.length() < 8 || uDate.length() > 10) return false;
  if(!checkDigit(uDate)) return false;

  int pos1 = getFirstDelimiter(uDate);
  if(pos1 < 0 || pos1 > 2)
   return false;
  int pos2 = getSecondDelimiter(uDate,pos1+1);
  if(pos2 < 0)
   return false;
  int diff = (pos2-1) - (pos1+1); 
  if(diff != 0 && diff != 1) return false;
  return true;
 }

/**
 * formSQLDate's method is to form SQL Date format [yyyy-mm-dd] given the date
 * @param String normalDate - contains date of any format
 * @return String SQL date's format.
 */
 public static String formSQLDate(String normalDate)
 {
  if(!verifyDateFormat(normalDate)) return null;
  int pdate = extractDate(normalDate);
  int pmonth= extractMonth(normalDate);
  int pyear = extractYear(normalDate);
  return new String(pyear+"-"+pmonth+"-"+pdate);
 }

/**
 * formSQLDate's method is to form SQL Date format from the calendar.
 * @param none
 * @return String SQL date's format
 */
 public String formSQLDate()
 {
  return new String(year+"-"+month+"-"+date);
 }

/**
 * formNDate's method is to form normal date format [dd-mm-yyyy] given the date
 * @param String sqlDate - or any form of date [sql date or normal date]
 * @return String normal date [dd-mm-yyyy]
 */
 public static String formNDate(String sqlDate)
 {
  if(!verifySQLDate(sqlDate)) return null;
  int pdate = extractDate(sqlDate);
  int pmonth= extractMonth(sqlDate);
  int pyear = extractYear(sqlDate);
  return new String(pdate+"-"+pmonth+"-"+pyear);
 }

/**
 * getFormDay's method is to form the day. It converts the day 
 * in number into name. 
 * @param none
 * @return String name of day
 */
 public String getFormDay()
 {
  switch(day)
  {
   case 1 : return "Sunday";
   case 2 : return "Monday";
   case 3 : return "Tuesday";
   case 4 : return "Wednesday";
   case 5 : return "Thursday";
   case 6 : return "Friday";
   case 7 : return "Saturday";
   default: break;
  }
  return null;
 }

/**
 * getFormMonth's method is to form month. It converts the month in form
 * of number into name.
 * @param none
 * @return String month in name
 */
 public String getFormMonth()
 {
  switch(month)
  {
   case  1: return "January"; 
   case  2: return "Febuary"; 
   case  3: return "March"; 
   case  4: return "April"; 
   case  5: return "May"; 
   case  6: return "June"; 
   case  7: return "July"; 
   case  8: return "August"; 
   case  9: return "September"; 
   case 10: return "October"; 
   case 11: return "November"; 
   case 12: return "December"; 
   default: break;
  }
  return null;
 }

/**
 * checkDigit's method is to check the string input whether it is
 * all in digit form or not. It will count '/','-' and '\' as
 * the valid input considering that the method is checking for date
 * related numbering.
 * @param String input 
 * @return true if all digit else false
 */
 public static boolean checkDigit(String input)
 {
  char ch;
  int countDelimiter = 0;
  int length = input.length();
  for(int i=0;i<length;i++)
  {
   ch = input.charAt(i);
   if(!Character.isDigit(ch))
   {
    if(countDelimiter == 2) return false;
    if((ch != '/') && (ch != '-') && (ch != '\\')) 
     return false;
    countDelimiter+=1;
   }
  }
  return true;
 }

/**
 * formNDate's method is to form normal date [dd-mm-yyyy]
 * @param none
 * @return String normal date [dd-mm-yyyy]
 */
 public String formNDate()
 {
  return new String(getDate()+"-"+getMonth()+"-"+getYear());
 }

/**
 * formNUSDate's method is to form normal US date [mm-dd-yyyy]
 * @param none
 * @return String normal US date [mm-dd-yyyy]
 */
 public String formNUSDate()
 {
  return new String(getMonth()+"-"+getDate()+"-"+getYear());
 }

/**
 * getFormIntlDate's method is extactly like getFormNDate with
 * the only different of the month being stated as in name.
 * @param none
 * @return dd-mm-yyyy [mm is stated in name]
 */
 public String getFormIntlDate()
 {
  return new String(getDate()+" "+getFormMonth()+" "+getYear());
 }

/**
 * getFormUSDate's method is to get US-date format with the only
 * different of the month being stated as in name.
 * @param none
 * @return mm-dd-yyyy [mm is stated in name]
 */
 public String getFormUSDate()
 {
  return new String(getFormMonth()+" "+getDate()+", "+getYear());
 }

/**
 * getFormTime's method is to get the time [hh:mm:ss]
 * @param none
 * @return String time [hh:mm:ss]
 */
 public String getFormTime()
 {
  return new String(getHour()+":"+getMinute()+":"+getSecond());
 }

/**
 * getHour's method is to get the hour of the time
 * @param none
 * @return int hour
 */
 public int getHour()
 {
  return hour;
 }

/**
 * getMinute's method is to get the minute of the time
 * @param none
 * @return int minute
 */
 public int getMinute()
 {
  return minute;
 }

/**
 * getSecond's method is to get second of the time
 * @param none
 * @return int second
 */
 public int getSecond()
 {
  return second;
 }

/**
 * getTimeInMill's method is to get time in milli-second 10^-6
 * @param none
 * @return long time [in milli-second]
 */
 public long getTimeInMill()
 {
  return cal.getTimeInMillis();
 }

/**
 * getMonth is to get the month. Java's month is 0-based; thus, DateUtil
 * automatically adds 1 to confirm with the usual month of 1-based 
 * (meaning January is 1 and December is 12 instead of January is 0)
 * @param none
 * @return int month
 */
 public int getMonth()
 {
  return month;
 }

/**
 * getYear's method is the year. 
 * @param none
 * @return int year
 */
 public int getYear()
 {
  return year;
 }

/**
 * getDate's method is to get date
 * @param none
 * @return int date
 */
 public int getDate()
 {
  return date;
 }

/**
 * getWeekOfMonth's method is to get the week of the month. For example:
 * Feb 1 2004 is consider as 1st week of the month.
 * @param none
 * @return week of the month
 */
 public int getWeekOfMonth()
 {
  return wom;
 }

/**
 * getWeekOfYear's method is to get the week of the year. For example:
 * Feb 1 2004 is the 5th week of the year.
 * @param none
 * @return int week of year
 */
 public int getWeekOfYear()
 {
  return woy;
 }

/**
 * getDayOfYear's method is to get day of the year. For example:
 * Feb 1 2004 is the 32nd day of the year.
 * @param none
 * @return int day of the year
 */
 public int getDayOfYear()
 {
  return doy;
 }

/**
 * getDay's method is to get day [in form of number]. In Java's standard,
 * the week begins with Sunday; thus, day begins with Sunday being 1 and
 * Saturday being 7. Instead of Sunday being 7.
 * @param none
 * @return int day
 */
 public int getDay()
 {
  return day;
 }

/**
 * clearCalendar's method is clear the parameters in Calendar's class that
 * is set when the DateUtil is being initiated
 * @param none
 * @return none
 */
 public void clearCalendar()
 {
  cal.clear();
 }

/**
 * getMaxDate's method is to get the maximum date in the month. It takes
 * the max date directly from Calendar's class [which is set in the beginning]
 * @param none
 * @return int max date in the given month from the Calendar's class
 */
 public int getMaxDate()
 {
  return cal.getActualMaximum(cal.DAY_OF_MONTH);
 }

/**
 * getTime's method is to get time directly from Calendar's class
 * @param none
 * @return String time
 */
 public String getTime()
 {
  return cal.getTime().toString();
 }

}
