package org.itscool.commons.util;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日付コンポーネント用ユーティリティクラスです
 */
public class DateFormatUtil {
    private static final int[] DAY_OF_KIND = { 4, 6, 9, 11 };
    
    public static Date createDate(int year, int month, int day){
        Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month-1);
        cal.set(Calendar.DAY_OF_MONTH, day);
        Date date = cal.getTime();
        return date;
    }
    
    public static Date createMonth(int year, int month){
        Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month-1);
        Date date = cal.getTime();
        return date;
    }
    
    /**
     * 当月の日数を取得します
     * @param cal Calendarインスタンス
     * @return 当月の日数
     */
    public static int getMaxDay(Calendar cal){
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH) + 1;
        
        if(month==2){
            if( (year%4==0 && year%100!=0) || (year%400==0) ){
                return 29;
            }else{
                return 28;
            }
        }
        
        for(int i=0; i<DAY_OF_KIND.length; i++){
            if( DAY_OF_KIND[i] == month){
                return 30;
            }
        }
        return 31;
    }
    
    /**
     * 先月の最終週の日曜日をCalendarインスタンスで取得
     * @param year 当年
     * @param month 当月
     * @return Calendarインスタンスを返します
     */
    public static Calendar getLastMonthWeek(int year, int month ){
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month-1);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        int firstYobi = cal.get(Calendar.DAY_OF_WEEK);
        cal.add(Calendar.DAY_OF_MONTH, -( firstYobi-1 ));
        return cal;
    }
    
    /**
     * 現在日をyy/mm/dd形式で取得します
     *
     * @return 現在日をyy/mm/ddで返します
     */
    public static String encodeYYMMDDString(){
        Calendar cal = Calendar.getInstance();
        return encodeYYMMDDString(cal);
    }
    
    
    /**
     * 指定日をyy/mm/dd形式で取得します
     * @param cal Calendarインスタンス
     * @return 指定日をyy/mm/ddで返します
     */
    public static String encodeYYMMDDString(Calendar cal){
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH)+1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        
        String yearStr = String.valueOf(year).substring(2, 4);
        String monthStr = NumberFormatUtil.convZeroPadding(month, 2);
        String dayStr = NumberFormatUtil.convZeroPadding(day, 2);
        
        String ret = yearStr + "/" + monthStr + "/" + dayStr;
        
        return ret;
    }
    
    /**
     * 指定日をyy/mm/dd形式で取得します
     * @param date Dateインスタンス
     * @return 指定日をyy/mm/ddで返します
     */
    public static String encodeYYMMDDString(Date date){
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH)+1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        
        String yearStr = String.valueOf(year).substring(2, 4);
        String monthStr = NumberFormatUtil.convZeroPadding(month, 2);
        String dayStr = NumberFormatUtil.convZeroPadding(day, 2);
        
        String ret = yearStr + "/" + monthStr + "/" + dayStr;
        
        return ret;
    }
    
    /**
     * 現在日をyyyy/mm/dd形式で取得します
     *
     * @return 現在日をyyyy/mm/ddで返します
     */
    public static String encodeYYYYMMDDString(){
        Calendar cal = Calendar.getInstance();
        return encodeYYYYMMDDString(cal);
    }
    
    /**
     * 指定日をyyyy/mm/dd形式で取得します
     * @param cal Calendarインスタンス
     * @return 指定日をyyyy/mm/ddで返します
     */
    public static String encodeYYYYMMDDString(Calendar cal){
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH)+1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        
        String yearStr = String.valueOf(year);
        String monthStr = NumberFormatUtil.convZeroPadding(month, 2);
        String dayStr = NumberFormatUtil.convZeroPadding(day, 2);
        
        String ret = yearStr + "/" + monthStr + "/" + dayStr;
        
        return ret;
    }
    
    /**
     * yyyy/MM/dd形式の文字列をCalendarインスタンスに変換します
     * @param yyyymmdd yyyy/MM/dd形式の日付
     * @return Calendarインスタンス
     */
    public static Calendar decodeYYYYMMDDString(String yyyymmdd){
        GregorianCalendar cal = new GregorianCalendar();
        cal.clear();
        try{
            StringTokenizer dataSt = new StringTokenizer(yyyymmdd, "/");
            dataSt.hasMoreTokens();
            int year = Integer.parseInt(dataSt.nextToken());
            dataSt.hasMoreTokens();
            int month = Integer.parseInt(dataSt.nextToken());
            dataSt.hasMoreTokens();
            int day = Integer.parseInt(dataSt.nextToken());
            cal.set(Calendar.YEAR, year);
            cal.set(Calendar.MONTH, month - 1 );
            cal.set(Calendar.DAY_OF_MONTH, day);
            return cal;
        }catch(Exception e){
            return null;
        }
    }
    
    /**
     * 指定された文字列がCalendarインスタンスに変換可能か判定する
     * @param str 判定文字列（yyyy/mm/dd形式）
     * @return 判定文字列が数値変換可能な場合はtrueを返します
     */
    public static boolean isCalendar(String str){
        Calendar cal = decodeYYYYMMDDString(str);
        if( cal != null ){
            return true;
        }else{
            return false;
        }
    }
    
    /**
     * さまざまな日付型文字列からDateインスタンスに変換するメソッド
     * @param str 日付け文字列
     * @return Dateインスタンスを返します
     */
    public static Date parse(String str){
        if( str == null || str.length() == 0 ){
            return null;
        }
        str = str.trim();
        String format = searchDateFormat(str);
        if( format == null || str == null || str.length() == 0 ){
            return null;
        }
        Date date = new Date();
        SimpleDateFormat sdf1 = new SimpleDateFormat(format);
        try{
            date = sdf1.parse(str);
            
        }catch(Exception e){
            return null;
        }
        return date;
    }
    
    /**
     * 引数に指定されたdata文字列が日付の各書式にマッチするか
     * チェックします。
     * 日付けの書式にマッチする場合はその書式を返します。
     * マッチしない場合（日付として妥当で無い場合)はnullを返します
     * @param data 日付を表す文字列
     * @return この日付を表す書式
     */
    public static String searchDateFormat(String data){
        String koubun;
        Pattern pattern;
        Matcher matcher;
        
        if( data == null ){
            return null;
        }
        
        //日付 yyyy/mm/dd形式の検査
        koubun= "\\d{4,4}/\\d{1,2}/\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yyyy/MM/dd";
        }
        
        koubun = "\\d{2,2}/\\d{1,2}/\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yy/MM/dd";
        }
        
        //日付 yyyy-mm-dd形式の検査
        koubun = "\\d{4,4}-\\d{1,2}-\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yyyy-MM-dd";
        }
        
        koubun = "\\d{2,2}-\\d{1,2}-\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yy-MM-dd";
        }
        
        //日付 yyyy/mm/dd hh:mm:ss形式の検査
        koubun= "\\d{4,4}/\\d{1,2}/\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yyyy/MM/dd HH:mm:ss";
        }
        
        koubun = "\\d{2,2}/\\d{1,2}/\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yy/MM/dd HH:mm:ss";
        }
        
        //日付 yyyy-mm-dd hh:mm:ss形式の検査
        koubun = "\\d{4,4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yyyy-MM-dd HH:mm:ss";
        }
        
        koubun = "\\d{2,2}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yy-MM-dd HH:mm:ss";
        }
        
        //hh:mm:ssの検査
        koubun= "\\d{1,2}:\\d{1,2}:\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "HH:mm:ss";
        }
        
        //hh:mmの検査
        koubun= "\\d{1,2}:\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "HH:mm";
        }
        
        //日付 mm/dd/yyyy形式の検査(SQL形式)
        koubun= "\\d{1,2}/\\d{1,2}/\\d{4,4}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "MM/dd/yyyy";
        }
        
        koubun= "\\d{1,2}/\\d{1,2}/\\d{4,4} \\d{1,2}:\\d{1,2}:\\d{1,2}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "MM/dd/yyyy HH:mm:ss";
        }
        
        //YYYYMMDD形式
        koubun= "\\d{8,8}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yyyyMMdd";
        }
        
        //YYMMDD形式
        koubun= "\\d{6,6}";
        pattern = Pattern.compile(koubun);
        matcher = pattern.matcher(data);
        if(matcher.matches()){
            return "yyMMdd";
        }
        
        return null;
    }
    
    public static void main(String[] argv){
//        System.out.println(searchDateFormat("2004/05/01"));
//        System.out.println(searchDateFormat("04/05/01"));
//        System.out.println(searchDateFormat("04/5/1"));
        
        System.out.println(parse("2004/05/01"));
        System.out.println(parse("04/05/01"));
        System.out.println(parse("04/5/1"));
        System.out.println(parse("2004-05-01"));
        System.out.println(parse("04-05-01"));
        System.out.println(parse("04-5-1"));
        
        System.out.println(parse("2004/05/01 23:59:30"));
        System.out.println(parse("04/05/01 23:59:30"));
        System.out.println(parse("04/5/1 23:59:30"));
        
        System.out.println(parse("2004-05-01 23:59:30"));
        System.out.println(parse("04-05-01 23:59:30"));
        System.out.println(parse("04-5-1 23:59:30"));
        
        System.out.println(parse("01:01:01"));
        System.out.println(parse("1:1:1"));
        
        System.out.println(parse("05/01/2004"));
        System.out.println(parse("05/01/2004 23:59:30"));
        System.out.println(parse("20040501"));
        System.out.println(parse("040501"));
        
        System.out.println("0501-->"+parse("0501"));
        System.out.println("05/01-->"+parse("05/01"));
        System.out.println("05-01-->"+parse("05-01"));
        
    }
}
