/*********************************************************************
*	All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*	システム名		：eo顧客基幹システム
*	モジュール名	：JPCDateUtil
*	ソースファイル名：JPCDateUtil.java
*	作成者			：富士通
*	日付			：2011年02月17日
*＜機能概要＞
*	日付に関する共通処理を提供する。
*＜修正履歴＞
*	バージョン	修正日		修正者		修正内容
*	v1.00.00	2011/02/17	富士通		新規作成
*
**********************************************************************/
package eo.common.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

import com.fujitsu.futurity.model.base.jcc.util.JCCDateUtil;

/**
 * 日付に関する共通処理を行うクラスです。<p>
 * <br>
 * @author 富士通
 */
public final class JPCDateUtil extends JPCCommonUtil 
{
	/** 
	 * 年月日書式(yyyyMMdd)の桁数 
	 */
	private static final int YMD = 8;

	/** 
	 * 日時書式(yyyyMMddHHmmss)の桁数 
	 */
	private static final int YMDHMS = 14;

	/**
	 * <dd>コンストラクタ名：JPCDateUtil
	 * <dd>コンストラクタ説明：日付共通処理部品クラスを生成する。<br>
	 * <dd>備考：なし。
	 */
	private JPCDateUtil(){
	}

	/**
	 * 日付の加減算を行います。
	 * <br>
	 * @param arg0 加算対象の日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @param arg1 加算する日数
	 * @return String 加減算した日付
	 */
	public static String addDay(String arg0, int arg1) 
	{
		if (!isValidParameter(arg0, YMD) && !isValidParameter(arg0, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		return JCCDateUtil.addDay(arg0, arg1);
	}

	/**
	 * 日差の算出を行います。
	 * <br>
	 * @param arg0 基準となる日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @param arg1 差分対象の日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @return int （基準となる日付−差分対象の日付）の日数
	 * @see eo.common.JCCDateUtil#subtractDay(java.lang.String, java.lang.String)
	 */
	public static int subtractDay(String arg0, String arg1) 
	{
		if (!isValidParameter(arg0, YMD) && !isValidParameter(arg0, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		if (!isValidParameter(arg1, YMD) && !isValidParameter(arg1, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg1 + "]" + "は不正です。");
		}

		if (!isValidParameter(arg0, arg1.length()) ) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "," + arg1 + "]" + "の組み合わせは不正です。");
		}

		return JCCDateUtil.subtractDay(arg0, arg1);
	}

	/**
	 * 月数を加減算します。
	 * <br>
	 * @param arg0 対象日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @param arg1 加減算値
	 * @return String 加減算された日付
	 * @throws ParseException 月の加減算処理にて、例外が発生した場合
	 */
	public static String addMonth(String arg0, int arg1) throws ParseException
	{
		if (!isValidParameter(arg0, YMD) && !isValidParameter(arg0, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		return addSubtractMonth(arg0, arg1);
	}

	/**
	 * 月差を算出します。
	 * <br>
	 * @param arg0 対象日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @param arg1 比較日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @return int 月差
	 */
	public static int subtractMonth(String arg0, String arg1) 
	{
		if (!isValidParameter(arg0, YMD) && !isValidParameter(arg0, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		if (!isValidParameter(arg1, YMD) && !isValidParameter(arg1, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg1 + "]" + "は不正です。");
		}

		return calSubtractMonth(arg0, arg1);
	}

	/**
	 * 指定された年月の月末日を返却します。
	 * <br>
     * @param arg0 年月
     * @return String 末日
     */
	public static String getEndOfMonth(String arg0) 
	{
		// 日付妥当性チェック
		if (!checkDate(arg0, 6)) 
		{
			return "";
		}

		// 「次月の０日」を指定することによって、該当月の月末日を取得する。
		Calendar calendar = 
			new GregorianCalendar(Integer.parseInt(arg0.substring(0, 4)), 
					Integer.parseInt(arg0.substring(4, 6)), 0);

		// 変数lastに末日が格納される
		int last = calendar.get(Calendar.DATE);

		return Integer.toString(last);
	}

	/**
	 * 対象日付の曜日を取得します。
	 * <br>
     * @param arg0 対象日付
     * @return String 曜日（1:日曜日〜7:土曜日）
     */
	public static String getDayOfWeek(String arg0) 
	{
		if (!isValidParameter(arg0, YMD) && !isValidParameter(arg0, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		// 曜日（返却値）
		String dayOfWeek = "";

		// 日付妥当性チェック
		if (!checkDate(arg0, 8)) 
		{
			return dayOfWeek;
		}

		// カレンダーオブジェクトを生成する
		int year = Integer.parseInt(arg0.substring(0, 4)); 	// 年

		int month = Integer.parseInt(arg0.substring(4, 6)); 	// 月
		
		int day = Integer.parseInt(arg0.substring(6, 8)); 	// 日

		GregorianCalendar cal = new GregorianCalendar(year, month - 1, day);

		// 曜日を取得する
		dayOfWeek = String.valueOf(cal.get(GregorianCalendar.DAY_OF_WEEK));

		return dayOfWeek;
	}
	
	/**
	 * 年数を加減算します。
	 * <br>
	 * @param arg0 対象日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @param arg1 加減算値
	 * @return String 算出された年
	 * @throws ParseException 年の加減算処理にて、例外が発生した場合
	 */
	public static String addYear(String arg0, int arg1) throws ParseException
	{
		if (!isValidParameter(arg0, YMD) && !isValidParameter(arg0, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		return addSubtractYear(arg0, arg1);
	}

	/**
	 * 年差を算出します。
	 * <br>
	 * @param arg0 対象日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @param arg1 比較日付(yyyyMMddまたはyyyyMMddHHmmss)
	 * @return int 年差
	 */
	public static int subtractYear(String arg0, String arg1) 
	{
		// パラメータチェックを行う。
		if(!isValidParameter(arg0, YMD) && !isValidParameter(arg0, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		if(!isValidParameter(arg1, YMD) && !isValidParameter(arg1, YMDHMS)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		// 年差を算出する
		return calSubtractYear(arg0, arg1);
	}

	/**
	 * 日付に対し、月の加減算を行い、結果の日付を返します。
	 * <br>
	 * @param arg0 加減算する日付 （８桁、１４桁、１７桁）
	 * @param arg1  加算する月数 （負値は減算）
	 * @return String 加減算された日付
	 * @throws ParseException 日付書式の変換処理にて、例外が発生した場合
	 */
	private static String addSubtractMonth(String arg0, int arg1) throws ParseException
	{
		// 日付チェックを行う
		if(!dateCheck(arg0)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		int arg0Length = arg0.length();

		Calendar calendar = transCalendar(arg0, arg0Length);

		SimpleDateFormat form = transFormat(arg0Length);

		calendar.add(Calendar.MONTH, arg1);

		return form.format(calendar.getTime());
	}

	/**
	 * 月を比較し、差分の月数を返します。
	 * <br>
	 * @param arg0　対象とする日付 （８桁、１４桁、１７桁）
	 * @param arg1  比較する日付 （８桁、１４桁、１７桁）
	 * @return int arg0 - arg1 の結果月数
	 */
	private static int calSubtractMonth(String arg0,String arg1) 
	{
		// 日付チェックを行う
		if(!dateCheck(arg0)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		// 日付チェックを行う
		if(!dateCheck(arg1)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg1 + "]" + "は不正です。");
		}

		int yyyy1 = Integer.parseInt(arg0.substring(0, 4));

		int mm1 = Integer.parseInt(arg0.substring(4, 6));

		int yyyy2 = Integer.parseInt(arg1.substring(0, 4));

		int mm2 = Integer.parseInt(arg1.substring(4, 6));

		int year = yyyy1 - yyyy2  - 1;

		int month = mm1 + 12 - mm2;

		int mothSa = year * 12 + month;

		return mothSa;
	}

	/**
	 * 日付に対し、年の加減算を行い、結果の日付を返します。
	 * <br>
	 * @param arg0 加減算する日付 （８桁、１４桁、１７桁）
	 * @param arg1  加算する年数 （負値は減算）
	 * @return String 加減算された日付
	 * @throws ParseException 日付書式の変換処理にて、例外が発生した場合
	 */
	private static String addSubtractYear(String arg0, int arg1) throws ParseException
	{
		// 日付チェックを行う
		if(!dateCheck(arg0)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		int arg0_length = arg0.length();

		Calendar calendar = transCalendar(arg0, arg0_length);

		SimpleDateFormat form = transFormat(arg0_length);

		calendar.add(Calendar.YEAR, arg1);

		return form.format(calendar.getTime());
	}

	/**
	 * 年を比較し、差分の年数を返します。
	 * <br>
	 * @param arg0　対象とする日付 （８桁、１４桁、１７桁）
	 * @param arg1  比較する日付 （８桁、１４桁、１７桁）
	 * @return int arg0 - arg1 の結果日数
	 */
	private static int calSubtractYear(String arg0,String arg1) 
	{
		// 日付チェックを行う
		if(!dateCheck(arg0)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg0 + "]" + "は不正です。");
		}

		/* ++++++++++ v1.01.00 追加開始 ++++++++++ */
		// 日付チェックを行う
		if(!dateCheck(arg1)) 
		{
			throw new IllegalArgumentException("引数の文字列[" + arg1 + "]" + "は不正です。");
		}
		/* ++++++++++ v1.01.00 追加終了 ++++++++++ */

		int year1 = Integer.parseInt(arg0.substring(0,4));

		int year2 = Integer.parseInt(arg1.substring(0,4));

		year1 = year1 - year2;

		return year1;
	}

	/**
	 * String型で渡された日付をCalendar型に変換します。
	 * <br>
	 * @param date 変換する日付
	 * @param length 変換する日付書式の桁数
	 * @return Calendar 日付書式に変換された文字列
	 * @exception ParseException 日付書式の変換処理にて、例外が発生した場合
	 * 	*/
	private static Calendar transCalendar(String date,int length) throws ParseException
	{
		SimpleDateFormat form=transFormat(length);

		Calendar carender = Calendar.getInstance();

		carender.setTime(form.parse(date));

		return carender;
	}

	/**
	 * int型で渡された桁数からSimpleDateFormat型を返します。
	 * <br>
	 * @param length 変換する日付書式の桁数
	 * @return SimpleDateFormat 日付書式に変換された文字列 
	 */
	private static SimpleDateFormat transFormat(int length) 
	{
		SimpleDateFormat form;

		if(length==8) 
		{
			form = new SimpleDateFormat("yyyyMMdd");
		}
		else if(length==14) 
		{
			form = new SimpleDateFormat("yyyyMMddHHmmss");
		}
		else 
		{
			form = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		}

		return form;
	}

	/**
	 * 日付のチェックを行います。
	 * <br>
	 * @param arg0 チェックを行う文字列
	 * @return boolean チェック結果
	 */
	private static boolean dateCheck(String arg0) 
	{
		if(Integer.parseInt(arg0.substring(0,8)) == 0) 
		{
			return false;
		}

		if(arg0.indexOf('-') != -1 || arg0.indexOf('.') != -1) 
		{
			return false;
		}

		int arg0Length = arg0.length();

		if(arg0Length != 8 && arg0Length != 14 && arg0Length != 17 ) 
		{
			return false;
		}

		return true;
	}

	/**
     * 日付の妥当性チェックを行います。
     * <br>
     * @param date yyyyMMddもしくはyyyyMM形式の日付
     * @param length チェック対象の日付の桁数
     * @return boolean
     */
	private static boolean checkDate(String date, int length) 
	{
		// 引数チェック
		if(isNull(date)) 
		{
			return false;
		}

		try 
		{
			int yyyy = Integer.parseInt(date.substring(0, 4));

			int mm = Integer.parseInt(date.substring(4, 6));

			int dd = 1;

			if (length == 8) 
			{
				dd = Integer.parseInt(date.substring(6, 8));
			}

			// 月は 0 から始まる。（0:１月 となる。）
			Calendar cl = new GregorianCalendar(yyyy, mm - 1, dd);

			// カレンダークラスにより、日付が調整されてしまっている場合は、エラーである。
			if ((cl.get(Calendar.YEAR) != yyyy) 
					|| ((cl.get(Calendar.MONTH) + 1) != mm) 
					|| (cl.get(Calendar.DATE) != dd)) 
			{
				return false;
			}
			else 
			{
				return true;
			}
		}
		catch (NumberFormatException e) 
		{
			return false;
		}
	}
}

