package jp.agentec.adf.util;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import jp.agentec.abook.abv.bl.common.log.Logger;

/**
 * 日付・時間関連の機能を提供します。
 * @author Taejin Hong
 * @version 1.0.0
 *
 */
public class DateTimeUtil {
	public enum DateUnit {
		Millisecond,
		Second,
		Minute,
		Hour,
		Day,
		Month,
		Year
	}
	
	/**
	 * 現在日時を返却します
	 * @return 現在日時の{@link Timestamp}
	 * @since 1.0.0
	 */
	public static Timestamp getCurrentTimestamp() {
		return new Timestamp(System.currentTimeMillis());
	}
	
	/**
	 * 現在時刻を返却します。
	 * @return 現在日時の{@link Date}
	 * @since 1.0.0
	 */
	public static Date getCurrentDate() {
		return new Date(getCurrentTimestamp().getTime());		
	}
	
	/**
	 * 現在時刻(UTC)を返却します。
	 * @return 現在日時の{@link String}
	 */
	public static Date getCurrentUTCDate() {
		SimpleDateFormat df = new SimpleDateFormat(DateTimeFormat.yyyyMMddHHmmssSSS_TZ);
		df.setTimeZone(TimeZone.getTimeZone("UTC"));
		String gmtTime = df.format(getCurrentDate());
		Date utcDate = null;
		try {
			utcDate = df.parse(gmtTime);
		} catch (ParseException e) {
		}
		return utcDate;
	}
	
	/**
	 * UTC時刻を返却します。
	 * @param date {@link Date}
	 * @return UTC日時{@link String}
	 */
	public static String getUTCDate(Date date) {
		SimpleDateFormat df = new SimpleDateFormat(DateTimeFormat.yyyyMMddHHmmssSSS_TZ);
		df.setTimeZone(TimeZone.getTimeZone("UTC"));
		String gmtTime = df.format(date);
		return gmtTime;
	}
	
	/**
	 * 現在時刻を返却します。
	 * @return 現在日時の{@link java.sql.Date}
	 * @since 1.0.0
	 */
	public static java.sql.Date getCurrentSqlDate() {
		return new java.sql.Date(getCurrentTimestamp().getTime());
	}
	
	/**
	 * 文字列を {@link Timestamp} 型に変換します。変換時例外が発生した場合はnullを返却します。<br>
	 * Example : {@code DateTimeUtil.gettimestamp("2011-12-02 17:08:16", DateTimeFormat.yyyyMMddHHmmss_dash);}
	 * @param time 時間を示す文字列
	 * @param format タイムスタンプのフォーマット（ハイフン又はスラッシュ区切りであること）
	 * @return 変換した {@link Timestamp} のインスタンス。
	 * @since 1.0.0
	 */
	public static Timestamp toTimestamp(String time, String format) {
		Timestamp timestamp = null;
		
		if (!StringUtil.isNullOrWhiteSpace(time) && !StringUtil.isNullOrWhiteSpace(format)) {
			time = time.replaceAll(StringUtil.Slash, StringUtil.Hyphen);
			
			try {
				timestamp = new Timestamp(new SimpleDateFormat(format).parse(time).getTime());
			} catch (Exception e) {}
		}
		
		return timestamp;
	}
	
	/**
	 * 指定した date を指定した format のDateに変換します。
	 * @param date 変換する {@link Date} です。
	 * @param format タイムスタンプのフォーマット
	 * @return　変換したDate
	 */
	public static Date formatDate(java.util.Date date, String format) {
		Date newDate = null;
		if (date != null && !StringUtil.isNullOrWhiteSpace(format)) {
			SimpleDateFormat sdf = new SimpleDateFormat(format);
			String s = sdf.format(date);
			try {
				newDate = sdf.parse(s);
			} catch (ParseException e) {
			}
		}
		return newDate;
	}
	
	/**
	 * 文字列を {@link Date} 型に変換します。変換時例外が発生した場合はnullを返却します。<br>
	 * Example : {@code DateTimeUtil.gettimestamp("2011-12-02 17:08:16", DateTimeFormat.yyyyMMddHHmmss_dash);}
	 * @param time 時間を示す文字列
	 * @param format タイムスタンプのフォーマット（ハイフン又はスラッシュ区切りであること）
	 * @return 変換した{@link Date}のインスタンス。
	 * @since 1.0.0
	 */
	public static Date toDate(String time, String format) {
		Date dt = null;
		
		if (!StringUtil.isNullOrWhiteSpace(time) && !StringUtil.isNullOrWhiteSpace(time)) {
			
			try {
				dt = new Date(new SimpleDateFormat(format).parse(time).getTime());
			} catch (Exception e) {
				Logger.e("DateTimeUtil", "[toDate]:" + time + ", format=" + format, e);
			}
		}
		
		return dt;
	}
	
	/**
	 * 文字列を {@link Date} 型に変換します。変換時例外が発生した場合はnullを返却します。<br>
	 * 
	 * @param time
	 * @param timeZone タイムゾーン(引数のtimeのタイムゾーン）
	 * @param format
	 * @return
	 */
	public static Date toDate(String time, String timeZone, String format) {
		Date dt = null;
		
		if (!StringUtil.isNullOrWhiteSpace(time) && !StringUtil.isNullOrWhiteSpace(time)) {
			time = time.replaceAll(StringUtil.Slash, StringUtil.Hyphen);
			
			try {
				SimpleDateFormat sdf = new SimpleDateFormat(format);
				sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
				dt = new Date(sdf.parse(time).getTime());
			} catch (Exception e) {
				Logger.e(e.toString());
			}
		}
		
		return dt;
	}
	
	/**
	 * 文字列を {@link java.sql.Date} 型に変換します。変換時例外が発生した場合はnullを返却します。<br>
	 * Example : {@code DateTimeUtil.gettimestamp("2011-12-02 17:08:16", DateTimeFormat.yyyyMMddHHmmss_dash);}
	 * @param time 時間を示す文字列
	 * @param format タイムスタンプのフォーマット（ハイフン又はスラッシュ区切りであること）
	 * @return 変換した{@link java.sql.Date}のインスタンス。
	 * @since 1.0.0
	 */
	public static java.sql.Date toSqlDate(String time, String format) {
		java.sql.Date dt = null;
		
		if (!StringUtil.isNullOrWhiteSpace(time) && !StringUtil.isNullOrWhiteSpace(time)) {
			time = time.replaceAll(StringUtil.Slash, StringUtil.Hyphen);
			
			try {
				dt = new java.sql.Date(new SimpleDateFormat(format).parse(time).getTime());
			} catch (Exception e) {}
		}

		return dt;
	}
	
	/**
	 * 指定した date を指定した format の文字列に変換します。
	 * @param date 変換する {@link Date} です。
	 * @param format 変換する文字列のフォーマットです。 {@link DateTimeFormat} に色々なフォーマットを予め定義しています。
	 * @return 変換した文字列を返します。
	 * @since 1.0.0
	 */
	public static String toString(java.util.Date date, String format) {
		String s = null;
		
		if (date != null && !StringUtil.isNullOrWhiteSpace(format)) {
			s = new SimpleDateFormat(format).format(date);
		}
		
		return s;
	}
	
	/**
	 * 指定した date を指定した format の文字列に変換します。
	 * @param date 変換する {@link Date} です。
	 * @param format 変換する文字列のフォーマットです。 {@link DateTimeFormat} に色々なフォーマットを予め定義しています。
	 * @param timeZone 変換結果のタイムゾーンです。
	 * @return 変換した文字列を返します。
	 * @since 1.0.0
	 */
	public static String toStringInTimeZone(java.util.Date date, String format, String timeZone) {
		String s = null;
		
		if (date != null && !StringUtil.isNullOrWhiteSpace(format)) {
			SimpleDateFormat df = new SimpleDateFormat(format);
			df.setTimeZone(TimeZone.getTimeZone(timeZone));
			s = df.format(date);
		}
		
		return s;
	}

    public static String toString_yyyyMMddHHmmss_none(java.util.Date date) {
        return toString(date, DateTimeFormat.yyyyMMddHHmmss_none);
    }

    public static String toString_yyyyMMddHHmmss_slash(java.util.Date date) {
        return toString(date, DateTimeFormat.yyyyMMddHHmmss_slash);
    }

    public static String toStringForCmsGMT(java.util.Date date) {
        return toStringInTimeZone(date, DateTimeFormat.yyyyMMddHHmmss_hyphen, "GMT") + ",GMT";
    }
	
	/**
	 * 指定した date を指定した format の文字列に変換します。
	 * @param date 変換する {@link java.sql.Date} です。
	 * @param format 変換する文字列のフォーマットです。 {@link DateTimeFormat} に色々なフォーマットを予め定義しています。
	 * @return 変換した文字列を返します。
	 * @since 1.0.0
	 */
	public static String toString(java.sql.Date date, String format) {
		String s = null;
		
		if (date != null && !StringUtil.isNullOrWhiteSpace(format)) {
			s = new SimpleDateFormat(format).format(date);
		}
		
		return s;
	}

	/**
	 * 指定した date を指定した format の文字列に変換します。
	 * @param date 変換する {@link java.sql.Date} です。
	 * @param format 変換する文字列のフォーマットです。 {@link DateTimeFormat} に色々なフォーマットを予め定義しています。
	 * @param timeZone 変換結果のタイムゾーンです。
	 * @return 変換した文字列を返します。
	 * @since 1.0.0
	 */
	public static String toStringInTimeZone(java.sql.Date date, String format, String timeZone) {
		String s = null;
		
		if (date != null && !StringUtil.isNullOrWhiteSpace(format)) {
			SimpleDateFormat df = new SimpleDateFormat(format);
			df.setTimeZone(TimeZone.getTimeZone(timeZone));
			s = df.format(date);
		}
		
		return s;
	}
	
	/**
	 * 二つの時間の差を計算します。
	 * @param beforeDate 比較する日付です。
	 * @param afterDate 比較する日付です。
	 * @return 時間の差をミリ秒で返します。
	 * @since 1.0.0
	 */
	public static int TimeLagToInt(Date beforeDate, Date afterDate) {
		long duration;
		long before = 0;
		long after = 0;
		if (beforeDate != null) {	        
	        Calendar beforeCal = Calendar.getInstance();
	        beforeCal.setTime(beforeDate);
	        before = beforeCal.getTimeInMillis();
		}
		if (afterDate != null) {	        
	        Calendar afterCal = Calendar.getInstance();
	        afterCal.setTime(afterDate);
	        after = afterCal.getTimeInMillis();
		}        
        duration=(after - before);
		
		return (int)duration;
	}

	/**
	 * 二つの時間の差を計算します。
	 * @param beforeDate 比較する日付です。
	 * @param afterDate 比較する日付です。
	 * @return 時間の差をミリ秒で返します。
	 * @since 1.0.0
	 */
	public static int SqlTimeLagToInt(java.sql.Date beforeDate, java.sql.Date afterDate) {
		long duration;
		long before = 0;
		long after = 0;
		if (beforeDate != null) {	        
	        Calendar beforeCal = Calendar.getInstance();
	        beforeCal.setTime(beforeDate);
	        before = beforeCal.getTimeInMillis();
		}
		if (afterDate != null) {	        
	        Calendar afterCal = Calendar.getInstance();
	        afterCal.setTime(afterDate);
	        after = afterCal.getTimeInMillis();
		}        
        duration=(after - before);
		
		return (int)duration;
	}
	
	/**
	 * 二つの時間の差を計算します。
	 * @param beforeDate 比較する日付です。
	 * @param afterDate 比較する日付です。
	 * @return 時間の差をミリ秒で返します。
	 * @since 1.0.0
	 */
	public static long TimeLagToLong(Date beforeDate,Date afterDate) {
		long duration;
		long before = 0;
		long after = 0;
		if (beforeDate != null) {	        
	        Calendar beforeCal = Calendar.getInstance();
	        beforeCal.setTime(beforeDate);
	        before = beforeCal.getTimeInMillis();
		}
		if (afterDate != null) {	        
	        Calendar afterCal = Calendar.getInstance();
	        afterCal.setTime(afterDate);
	        after = afterCal.getTimeInMillis();
		}        
        duration=(before - after);
		
		return (int)duration;
	}
	
	/**
	 * 二つの時間の差を計算します。
	 * @param beforeDate 比較する日付です。
	 * @param afterDate 比較する日付です。
	 * @return 時間の差をミリ秒で返します。
	 * @since 1.0.0
	 */
	public static long SqlTimeLagToLong(java.sql.Date beforeDate, java.sql.Date afterDate) {
		long duration;
		long before = 0;
		long after = 0;
		if (beforeDate != null) {	        
	        Calendar beforeCal = Calendar.getInstance();
	        beforeCal.setTime(beforeDate);
	        before = beforeCal.getTimeInMillis();
		}
		if (afterDate != null) {	        
	        Calendar afterCal = Calendar.getInstance();
	        afterCal.setTime(afterDate);
	        after = afterCal.getTimeInMillis();
		}        
        duration=(before - after);
		
		return (int)duration;
	}
	
	/**
	 * {@link java.util.Date} を {@link java.sql.Date} に変換します。
	 * @param dt 変換する {@link java.util.Date} のインスタンスです。
	 * @return 変換した {@link java.sql.Date} のインスタンスを返します。
	 * @since 1.0.0
	 */
	public static java.sql.Date dateToSqlDate(Date dt) {
		if (dt != null) {
			return new java.sql.Date(dt.getTime());
		} else {
			return null;
		}
	}
	
	/**
	 * {@link java.sql.Date} を {@link java.util.Date} に変換します。
	 * @param sqlDt 変換する {@link java.sql.Date} のインスタンスです。
	 * @return 変換した {@link java.util.Date} のインスタンスを返します。
	 * @since 1.0.0
	 */
	public static Date sqlDateToDate(java.sql.Date sqlDt) {
		if (sqlDt != null) {
			return new Date(sqlDt.getTime());
		} else {
			return null;
		}
	}
	
	/**
	 * {@link java.util.Date} を {@link java.sql.Timestamp} に変換します。
	 * @param dt 変換する {@link java.util.Date} のインスタンスです。
	 * @return 変換した {@link java.sql.Timestamp} のインスタンスを返します。
	 * @since 1.0.0
	 */
	public static Timestamp dateToTimestamp(Date dt) {
		if (dt != null) {
			return new Timestamp(dt.getTime());
		} else {
			return null;
		}
	}
	
	/**
	 * {@link java.sql.Date} を {@link java.sql.Timestamp} に変換します。
	 * @param sqlDt 変換する {@link java.sql.Date} のインスタンスです。
	 * @return 変換した {@link java.sql.Timestamp} のインスタンスを返します。
	 * @since 1.0.0
	 */
	public static Timestamp sqlDateToTimestamp(java.sql.Date sqlDt) {
		if (sqlDt != null) {
			return new Timestamp(sqlDt.getTime());
		} else {
			return null;
		}
	}
	
	public static Date add(Date dt, DateUnit unit, int value) {
		if (dt != null && unit != null) {
			Calendar cal = Calendar.getInstance();
			cal.setTime(dt);
			
			int field = -1;
			
			switch (unit) {
			case Millisecond:
				field = Calendar.MILLISECOND;
				break;
			case Second:
				field = Calendar.SECOND;
				break;
			case Minute:
				field = Calendar.MINUTE;
				break;
			case Hour:
				field = Calendar.HOUR;
				break;
			case Day:
				field = Calendar.DATE;
				break;
			case Month:
				field = Calendar.MONTH;
				break;
			case Year:
				field = Calendar.YEAR;
				break;
			}
			
			cal.add(field, value);
			
			return cal.getTime();
		} else {
			return null;
		}
	}
	
	public static java.sql.Date add(java.sql.Date dt, DateUnit unit, int value) {
		if (dt != null && unit != null) {
			Calendar cal = Calendar.getInstance();
			cal.setTime(dt);
			
			int field = -1;
			
			switch (unit) {
			case Millisecond:
				field = Calendar.MILLISECOND;
				break;
			case Second:
				field = Calendar.SECOND;
				break;
			case Minute:
				field = Calendar.MINUTE;
				break;
			case Hour:
				field = Calendar.HOUR;
				break;
			case Day:
				field = Calendar.DATE;
				break;
			case Month:
				field = Calendar.MONTH;
				break;
			case Year:
				field = Calendar.YEAR;
				break;
			}
			
			cal.add(field, value);
			
			return dateToSqlDate(cal.getTime());
		} else {
			return null;
		}
	}
	
	/**
	 * 時間の形式（hh:mm:ss）に変換する
	 * 
	 * @param duration
	 * @return
	 */
	public static String toTimeFormat(int duration) {
		int hours = duration / (1000*60*60);
		int minutes = duration % (1000*60*60) / (1000*60);
		int seconds = (duration % (1000*60*60)) % (1000*60) / 1000;
		return (hours>0? hours+":": "") + minutes + ":" + (seconds<10?"0":"") + seconds;
	}

	/**
	 * 端末のTimeZoneを取得
	 * @return LocalのTimezone
     */
	public static String getLocalTimeZone() {
		Calendar c = Calendar.getInstance();
		TimeZone tz = c.getTimeZone();
		return tz.getID();
	}

	/**
	 * 時間の形式（EEE, dd MMM yyyy HH:mm:ss GMT）に変換する
	 *
	 * @param date
	 * @return
	 */
	public static String lastModifiedConnectFormatGmt(Date date) {
		SimpleDateFormat df = new SimpleDateFormat(DateTimeFormat.ifModifiedSinceType, Locale.US);
		df.setTimeZone(TimeZone.getTimeZone("GMT"));
		return df.format(date)+ " GMT";
	}

    /**
     * 端末の日付が2013年未満か判断
     * @return 端末日付が2013年未満はtrue, 2013年以上はfalseで返します。
     *
     */
	public static boolean isAbnormalYear() {
        int currentYear = Integer.parseInt(toStringInTimeZone(new Date(), DateTimeFormat.yyyy, "UTC"));
        return currentYear < 2013;
    }

    /**
     * Whether date1 is before of date2
     * @param date1
     * @param date2
     * @return
     */
    public static boolean isDateBefore(Date date1, Date date2) {
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime( date1 );
        calendar1.set(Calendar.HOUR_OF_DAY, 0);
        calendar1.set(Calendar.MINUTE, 0);
        calendar1.set(Calendar.SECOND, 0);
        calendar1.set(Calendar.MILLISECOND, 0);

        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime( date2 );
        calendar2.set(Calendar.HOUR_OF_DAY, 0);
        calendar2.set(Calendar.MINUTE, 0);
        calendar2.set(Calendar.SECOND, 0);
        calendar2.set(Calendar.MILLISECOND, 0);

        return calendar1.before(calendar2);
    }

	/**
	 * 一日の差があるかどうか判定
	 * @return
	 */
	public static boolean isDiffOneDay(Date date1, Date date2) {
    	long diffTime = date1.getTime() - date2.getTime();
		return (int) (diffTime / (1000 * 60 * 60 * 24)) > 0;
	}
}
