package cn.hmsoft.helper;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.util.StringUtils;

import cn.hmsoft.exception.AppException;

/***************************
 * 日期辅助类,解决SimpleDateFormat线程不安全问题,使用ThreadLocal保证线程安全
 * @author zxq
 * @create 2019-05-06 15:55:32
 * @version 2.0.0
 * @email: revisit@126.com
 * @Company: www.hmsoft.cn
 */
public class DateHelper {
	/** 存放不同的日期模板格式的sdf的Map */
	private static Map<String, ThreadLocal<SimpleDateFormat>> SimpleDateForamtThreadLocalMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
	public final static String ShortDateString = "yyyy-MM-dd";
	public final static String ShortDateStringWithoutSplit = "yyyyMMdd";

	public final static String ShortTimeString = "HH:mm:ss";
	public final static String ShortTimeStringWithoutSplit = "HHmmss";

	public final static String LongDateString = "yyyy-MM-dd HH:mm:ss";
	public final static String LongDateStringWithoutSplit = "yyyyMMddHHmmss";
	public final static String MillisecondDateString = "yyyyMMddHHmmssSSS";
	public final static String HourMinuteString = "HH:mm";

	public final static int HourZoneOffset = Calendar.getInstance().get(
			java.util.Calendar.ZONE_OFFSET) / 3600000;
	// 星期名字
	public final static String[] WeekNames = new String[] { "星期日", "星期一",
			"星期二", "星期三", "星期四", "星期五", "星期六" };
	// 月份名字
	public final static String[] MonthNames = new String[] { "一月", "二月", "三月",
			"四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" };
	/***************************
	 * 一天的秒数
	 */
	public final static int DaySeconds = 86400;
	/*************************
	 * 一个小时的秒数
	 */
	public final static int HourSeconds = 3600;

	static {
		// 初始化常见的日期类型
		synchronized (DateHelper.class) {
			SimpleDateForamtThreadLocalMap.put(ShortDateString,
					InitThreadLocal(ShortDateString));
			SimpleDateForamtThreadLocalMap.put(ShortDateStringWithoutSplit,
					InitThreadLocal(ShortDateStringWithoutSplit));
			SimpleDateForamtThreadLocalMap.put(LongDateString,
					InitThreadLocal(LongDateString));
			SimpleDateForamtThreadLocalMap.put(LongDateStringWithoutSplit,
					InitThreadLocal(LongDateStringWithoutSplit));
			SimpleDateForamtThreadLocalMap.put(MillisecondDateString,
					InitThreadLocal(MillisecondDateString));
			SimpleDateForamtThreadLocalMap.put(ShortTimeString,
					InitThreadLocal(ShortTimeString));
			SimpleDateForamtThreadLocalMap.put(ShortTimeStringWithoutSplit,
					InitThreadLocal(ShortTimeStringWithoutSplit));
		}
	}

	/*****************************
	 * 初始化SimpleDateForamt的ThreadLoacl
	 */
	public static ThreadLocal<SimpleDateFormat> InitThreadLocal(
			final String pattern) {
		ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>() {
			@Override
			protected SimpleDateFormat initialValue() {
				return new SimpleDateFormat(pattern);
			}
		};
		return sdf;
	}

	/**
	 * 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
	 */
	public static SimpleDateFormat getFormatter(final String pattern) {
		ThreadLocal<SimpleDateFormat> tl = SimpleDateForamtThreadLocalMap
				.get(pattern);
		// 此处的双重判断和同步是为了防止SimpleDateForamtThreadLocalMap这个单例被多次put重复的sdf
		if (tl == null) {
			synchronized (DateHelper.class) {
				if (tl == null) {
					tl = SimpleDateForamtThreadLocalMap.get(pattern);
					if (tl == null)
						SimpleDateForamtThreadLocalMap.put(pattern,
								InitThreadLocal(pattern));
				}
			}
			tl = SimpleDateForamtThreadLocalMap.get(pattern);
		}
		return tl.get();
	}

	/***************
	 * 格式化短时间格式
	 */
	public static String formatTimeString() {
		return formatTimeString(new Date());
	}

	/***************
	 * 格式化短时间格式
	 */
	public static String formatTimeString(Date date) {
		return formatShortString(date, ShortTimeString);
	}

	/***************
	 * 格式化短日期格式
	 */
	public static String formatShortString() {
		return formatShortString(new Date());
	}

	/***************
	 * 格式化短日期格式
	 */
	public static String formatShortString(Date date) {
		return formatShortString(date, ShortDateString);
	}

	/***************
	 * 格式化短日期格式
	 */
	public static String formatShortString(Date date, String pattern) {
		try {
			return getFormatter(pattern).format(date);
		} catch (Exception e) {
			return null;
		}
	}

	/***************
	 * 格式化长日期格式
	 */
	public static String formatLongString() {
		return formatLongString(new Date());
	}

	/***************
	 * 格式化长日期格式
	 */
	public static String formatLongString(Date date) {
		return format(date, LongDateString);
	}

	/***************
	 * 格式化日期到指定的格式
	 */
	public static String format(Date date, String pattern) {
		SimpleDateFormat sdf = getFormatter(pattern);
		try {
			return sdf.format(date);
		} catch (Exception e) {

		}
		return null;
	}

	/***************
	 * 格式化日期到指定的格式
	 */
	public static String format(String pattern) {
		return format(new Date(), pattern);
	}

	/************************
	 * 将source转换到短日期格式
	 */
	public static Date parseShortString(String source) {
		return parse(source, ShortDateString);
	}

	/***********************
	 * 只保留日期部分
	 */
	public static Date zeroDate(String source) {
		return parse(source, ShortDateString);
	}

	/***********************
	 * 只保留日期部分
	 */
	public static Date zeroDate(Date source) {
		return zeroDate(format(source, ShortDateString));
	}

	public static Date zeroDate(Date source, String pattern) {
		return zeroDate(format(source, pattern));
	}

	/***********************
	 * 1970-1-1，只保留小时分钟秒
	 */
	public static Date initTime(String source) {
		return initTime(parse(source, LongDateString));
	}

	/***********************
	 * 1970-1-1，只保留小时分钟秒
	 */
	public static Date initTime(Date source) {
		return new Date(source.getTime() - zeroDate(source).getTime());
	}

	/******************
	 * 获取当前时间的秒数
	 */
	public static int getDateSeconds(Date source) {
		return (int) ((source.getTime() - zeroDate(source).getTime()) / 1000);
	}

	/******************
	 * 获取当前时间的秒数
	 */
	public static int getDateSeconds(String source) {
		return getDateSeconds(parse(source, LongDateString));
	}

	/************************
	 * 将source转换到短日期格式
	 */
	public static Date parseLongString(String source) {
		return parse(source, LongDateString);
	}

	/******************
	 * 将日期字符串转换到指定格式
	 */
	public static Date parse(String source, String pattern) {
		if (StringUtils.isEmpty(source))
			return null;
		SimpleDateFormat sdf = getFormatter(pattern);
		try {
			return sdf.parse(source);
		} catch (Exception e) {

		}
		return null;
	}

	/*************************
	 * 根据年、月、日设置日期，并返回设置好的日期
	 */
	public static Date setDate(int year, int month, int date) {
		Calendar cal = Calendar.getInstance();
		cal.clear();
		cal.set(year, month - 1, date);
		return cal.getTime();
	}

	/*************************
	 * 根据年、月、日、时、分、秒设置日期，并返回设置好的日期
	 */
	public static Date setDate(int year, int month, int date, int hour,
			int minute, int second) {
		Calendar cal = Calendar.getInstance();
		cal.clear();
		cal.set(year, month - 1, date, hour, minute, second);
		return cal.getTime();
	}

	/************************
	 * 为当前日期设置增加或减少时间
	 */
	public static Date addValues(int filed, int value) {
		return addValues(new Date(), filed, value);
	}

	/************************
	 * 为date设置增加或减少时间
	 */
	public static Date addValues(Date date, int filed, int value) {
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		c.add(filed, value);
		return c.getTime();
	}

	/***********
	 * 验证自定义时间表达式是否正确
	 */
	public static boolean checkSecondByValue(String source) {
		return checkSecondByValue(source, 0);
	}

	/***********
	 * 验证自定义时间表达式是否正确,并判断是否小于最小值
	 */
	public static boolean checkSecondByValue(String source, int minSecond) {
		Integer value = null;
		try {
			value = getSecondByValue(source);
		} catch (Exception e) {
			return false;
		}
		if (value < minSecond)
			return false;
		return true;
	}

	/****
	 * 对字符串进行时间转换，转换成秒 Source 格式 1d2h3m4s
	 */
	public static Integer getSecondByValue(String source) {
		if (StringUtils.isEmpty(source))
			return null;
		source = source.trim();
		// String value = "时间格式错误：请按照格式 1d2h3m4s(d:天,h小时,m分钟,s秒)";
		String number = "";
		// 倍数
		int seconds = 0;
		for (int i = 0; i < source.length(); i++) {
			char c = source.charAt(i);
			int tmp = (int) c;
			if (tmp == 100) {
				try {
					seconds = seconds + Integer.parseInt(number) * 24 * 60 * 60;
				} catch (Exception e) {
				}
				number = "";
			} else if (tmp == 104) {
				// h
				try {
					seconds = seconds + Integer.parseInt(number) * 60 * 60;
				} catch (Exception e) {
				}
				number = "";
			} else if (tmp == 109) {
				try {
					seconds = seconds + Integer.parseInt(number) * 60;
				} catch (Exception e) {
				}
				number = "";
			} else if (tmp == 115) {
				try {
					seconds = seconds + Integer.parseInt(number);
				} catch (Exception e) {
				}
				number = "";
				// s
			} else if (tmp >= 48 && tmp <= 57) {
				number = number + c;
			} else {
			}
		}
		return seconds;
	}

	/******
	 * 返回中文日期 2010年11月11日 星期二
	 */
	public static String toChineseString() {
		return toChineseString(new Date());
	}

	/******
	 * 返回中文日期 2010年11月11日 星期二
	 */
	public static String toChineseString(Date date) {
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
		return sdf.format(date) + " "
				+ WeekNames[c.get(Calendar.DAY_OF_WEEK) - 1];
	}

	public static String getTicketTimes(Date maxTime, Date minTime) {
		if (maxTime == null || minTime == null)
			return "";
		long times = maxTime.getTime() - minTime.getTime();
		if (times < 0)
			times = 0 - times;
		int value = Integer.parseInt(times + "");

		int day = value / (DaySeconds * 1000);
		value = value - day * DaySeconds * 1000;

		int hour = value / (HourSeconds * 1000);
		value = value - hour * HourSeconds * 1000;

		int minute = value / 6000;

		value = value - minute * 6000;

		int second = value / 1000;

		String returnValue = "";
		if (day > 0)
			returnValue = day + "d";
		if (hour > 0)
			returnValue = returnValue + hour + "h";
		if (minute > 0)
			returnValue = returnValue + minute + "m";
		if (second > 0)
			returnValue = returnValue + second + "s";
		return returnValue;

	}

	/********************
	 * 将当前时间转换为UTC时间
	 */
	public static Date transToUTC(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.HOUR, 0 - HourZoneOffset);
		return cal.getTime();
	}

	/***********************
	 * 计算时间差
	 * 
	 * @param end 大的时间
	 * @param start 小的时间
	 * @param compare_type 时间类型，请传递Calendar的常量
	 * @return
	 */
	public static int calTimeDiff(Date end, Date start, int compare_type) {
		Calendar endCal = Calendar.getInstance();
		Calendar startCal = Calendar.getInstance();
		endCal.setTime(end);
		startCal.setTime(start);

		// 秒数
		int value = (int) ((endCal.getTimeInMillis() - startCal
				.getTimeInMillis()) / 1000);
		if (compare_type == Calendar.SECOND)
			return value;
		if (compare_type == Calendar.MINUTE)
			return value / 60;
		if (compare_type == Calendar.HOUR)
			return value / (60 * 60);
		if (compare_type == Calendar.DATE)
			return value / (60 * 60 * 24);
		throw new AppException("不支持此模式的时间比较");
	}

	/***********************
	 * 计算时间差,永远返回正值
	 * 
	 * @param end 大的时间
	 * @param start 小的时间
	 * @param compare_type 时间类型，请传递Calendar的常量
	 * @return
	 */
	public static int calTimeDiffWithoutNegative(Date end, Date start,
			int compare_type) {
		int value = calTimeDiff(end, start, compare_type);
		if (value < 0)
			return 0 - value;
		return value;
	}

	/***************************
	 * 根据日期和时间合成争取得时间
	 * 
	 * @param date 日期
	 * @param time 时间
	 * @return
	 */
	public static Date calCurrentTime(Date date, Date time) {
		return parseLongString(formatShortString(date) + " "
				+ format(time, HourMinuteString) + ":00");
	}

	/************************
	 * 判断当前时间是否再一个时间段短内
	 * 
	 * @param start 开始时间
	 * @param end 结束时间
	 * @return
	 */
	public static boolean isBetwwen(Date start, Date end) {
		return isBetwwen(new Date(), start, end);
	}

	/************************
	 * 判断一个时间是否再一个时间段短内
	 * 
	 * @param now 判断得时间
	 * @param start 开始时间
	 * @param end 结束时间
	 * @return
	 */
	public static boolean isBetwwen(Date now, Date start, Date end) {
		if (now.after(start) && now.before(end))
			return true;
		else
			return false;
	}

}
