/*
 * Copyright (c) 2009 OrangeSignal.com All rights reserved.
 * 
 * これは Apache ライセンス Version 2.0 (以下、このライセンスと記述) に
 * 従っています。このライセンスに準拠する場合以外、このファイルを使用
 * してはなりません。このライセンスのコピーは以下から入手できます。
 * 
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 * 
 * 適用可能な法律がある、あるいは文書によって明記されている場合を除き、
 * このライセンスの下で配布されているソフトウェアは、明示的であるか暗黙の
 * うちであるかを問わず、「保証やあらゆる種類の条件を含んでおらず」、
 * 「あるがまま」の状態で提供されるものとします。
 * このライセンスが適用される特定の許諾と制限については、このライセンス
 * を参照してください。
 */

package jp.sf.orangesignal.ta.util;

import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * <p>日付/時刻のフォーマットと解析をロケールを考慮して行うユーティリティを提供します。</p>
 * <p>このクラスは、{@link SimpleDateFormat} の使用を簡素化する為のユーティリティ機能を提供します。</p>
 * 
 * @author 杉澤 浩二
 * @since 2.1
 */
public abstract class DateFormatUtils {

	/**
	 * <p>指定された複数の日付/時刻書式文字列を順番に使用して、指定された日付/時刻文字列を解析します。</p>
	 * 
	 * @param source 解析する日付/時刻文字列
	 * @param patterns 日付/時刻書式文字列群
	 * @return 解析された日付/時刻
	 * @throws IllegalArgumentException  日付/時刻文字列または日付/時刻書式文字列群に不正な値が指定された場合
	 * @throws ParseException 指定された日付/時刻文字列が解析できない場合
	 * @see #parse(String, String[], Locale, TimeZone)
	 */
	public static Date parse(final String source, final String[] patterns) throws ParseException {
		return parse(source, patterns, null, null);
	}

	/**
	 * <p>指定された複数の日付/時刻書式文字列を順番に使用して、指定された日付/時刻文字列を解析します。</p>
	 * 
	 * @param source 解析する日付/時刻文字列
	 * @param patterns 日付/時刻書式文字列群
	 * @param locale 解析で使用する地域情報
	 * @return 解析された日付/時刻
	 * @throws IllegalArgumentException  日付/時刻文字列または日付/時刻書式文字列群に不正な値が指定された場合
	 * @throws ParseException 指定された日付/時刻文字列が解析できない場合
	 * @see #parse(String, String[], Locale, TimeZone)
	 */
	public static Date parse(final String source, final String[] patterns, final Locale locale) throws ParseException {
		return parse(source, patterns, locale, null);
	}

	/**
	 * <p>指定された複数の日付/時刻書式文字列を順番に使用して、指定された日付/時刻文字列を解析します。</p>
	 * <p>このメソッドでは Java 仮想マシンの言語や地域などのホスト環境に依存せずに、日付/時刻の解析を行うことが可能です。</p>
	 * 
	 * <pre>
	 * 以下は Java 仮想マシンの言語や地域などのホスト環境に依存せずに、日本の日付/時刻形式で表現されているロシアのモスクワ時間を解析する場合の例です。<br />
	 * Date date = DateFormatUtils.parse("2009/07/27 23:38:02.123", new String[]{ "yyyy/MM/dd HH:mm:ss.SSS" }, Locale.JAPAN, TimeZone.getTimeZone("Russia/Moscow"));
	 * </pre>
	 * 
	 * @param source 解析する日付/時刻文字列
	 * @param patterns 日付/時刻書式文字列群
	 * @param locale 解析で使用する地域情報
	 * @param zone 解析で使用するタイムゾーン
	 * @return 解析された日付/時刻
	 * @throws IllegalArgumentException  日付/時刻文字列または日付/時刻書式文字列群に不正な値が指定された場合
	 * @throws ParseException 指定された日付/時刻文字列が解析できない場合
	 */
	public static Date parse(final String source, final String[] patterns, final Locale locale, final TimeZone zone) throws ParseException {
		Assert.notNull(source, "Source must not be null");
		Assert.notNull(patterns, "Patterns must not be null");
		Assert.noNullElements(patterns, "Patterns must not contain null elements");

		SimpleDateFormat parser = null;
		final ParsePosition pos = new ParsePosition(0);
		for (final String pattern : patterns) {
			if (parser == null) {
				parser = new SimpleDateFormat(pattern, locale != null ? locale : Locale.getDefault());
				if (zone != null)
					parser.setTimeZone(zone);
			} else {
				parser.applyPattern(pattern);
			}
			pos.setIndex(0);
			final Date date = parser.parse(source, pos);
			if (date != null /* && pos.getIndex() == source.length() */)
				return date;
		}
		throw new ParseException("Unable to parse the date: " + source, -1);
	}

	/**
	 * <p>指定された日時をフォーマットして文字列を作成します。</p>
	 * 
	 * @param date フォーマットする日時
	 * @param pattern 日付/時刻書式文字列
	 * @return フォーマットされた日時文字列
	 * @throws IllegalArgumentException  日時または日付/時刻書式文字列に <code>null</code> が指定された場合。
	 * または日付/時刻書式文字列に指定されたパターンが正しくない場合。
	 * または指定された日時をフォーマットできない場合。
	 * @see #format(Date, String, Locale, TimeZone)
	 */
	public static String format(final Date date, final String pattern) {
		return format(date, pattern, null, null);
	}

	/**
	 * <p>指定された日時をフォーマットして文字列を作成します。</p>
	 * 
	 * @param date フォーマットする日時
	 * @param pattern 日付/時刻書式文字列
	 * @param locale フォーマットで使用する地域情報
	 * @return フォーマットされた日時文字列
	 * @throws IllegalArgumentException  日時または日付/時刻書式文字列に <code>null</code> が指定された場合。
	 * または日付/時刻書式文字列に指定されたパターンが正しくない場合。
	 * または指定された日時をフォーマットできない場合。
	 * @see #format(Date, String, Locale, TimeZone)
	 */
	public static String format(final Date date, final String pattern, final Locale locale) {
		return format(date, pattern, locale, null);
	}

	/**
	 * <p>指定された日時をフォーマットして文字列を作成します。</p>
	 * <p>このメソッドでは Java 仮想マシンの言語や地域などのホスト環境に依存せずに、日付/時刻のフォーマットを行うことが可能です。</p>
	 * 
	 * <pre>
	 * 以下は Java 仮想マシンの言語や地域などのホスト環境に依存せずに、日本の数値形式でアメリカのニューヨーク日時文字列へフォーマットする場合の例です。<br />
	 * String str = DateFormatUtils.format(new Date(), "yyyy/MM/dd HH:mm:ss.SSS", Locale.JAPAN, TimeZone.getTimeZone("America/New_York"));
	 * </pre>
	 * 
	 * @param date フォーマットする日時
	 * @param pattern 日付/時刻書式文字列
	 * @param locale フォーマットで使用する地域情報
	 * @param zone タイムゾーン
	 * @return フォーマットされた日時文字列
	 * @throws IllegalArgumentException  日時または日付/時刻書式文字列に <code>null</code> が指定された場合。
	 * または日付/時刻書式文字列に指定されたパターンが正しくない場合。
	 * または指定された日時をフォーマットできない場合。
	 */
	public static String format(final Date date, final String pattern, final Locale locale, final TimeZone zone) {
		Assert.notNull(date, "Date must not be null");
		Assert.notNull(pattern, "Pattern must not be null");

		final SimpleDateFormat formatter;
		if (locale != null)
			formatter = new SimpleDateFormat(pattern, locale);
		else
			formatter = new SimpleDateFormat(pattern);

		if (zone != null)
			formatter.setTimeZone(zone);

		return formatter.format(date);
	}

}
