/* DateFormats.java


	Purpose: 
	Description: 
	History:
	91/01/17 15:22:22, Create, Tom M. Yeh.

Copyright (C) 2001 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.text;

import java.util.Locale;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.ParseException;

import org.zkoss.util.Locales;

/**
 * DateFormat relevant utilities.
 *
 * @author tomyeh
 */
public class DateFormats {
	private final static
		InheritableThreadLocal<DateFormatInfo> _thdLocale = new InheritableThreadLocal<DateFormatInfo>();

	/** Sets the info of date/time format for the current thread.
	 * It does not have effect on other threads.
	 * <p>When Invoking this method under a thread,
	 * remember to clean up the setting upon completing each request.
	 *
	 * <pre><code>DateFormatInfo old = DateFormats.setLocalFormatFormatInfo(info);
	 *try { 
	 *  ...
	 *} finally {
	 *  DateFormats.setLocalFormatInfo(old);
	 *}</code></pre>
	 * @param info the format info. It could be null (and the JVM's default
	 * is used).
	 * @return the previous format info being set, or null if not set.
	 * @since 5.0.7
	 */
	public static DateFormatInfo setLocalFormatInfo(DateFormatInfo info) {
		DateFormatInfo old = _thdLocale.get();
		_thdLocale.set(info);
		return old;
	}
	/** Returns the info of date/time format  for the current thread,
	 * or null if not set.
	 * @since 5.0.7
	 */
	public static DateFormatInfo getLocalFormatInfo() {
		return _thdLocale.get();
	}
	/** Returns the current date format; never null.
	 * <p>If a format info is set by {@link #setLocalFormatInfo},
	 * {@link DateFormatInfo#getDateFormat} of the info will be called first.
	 * If no info is set or the info returns null,
	 * <code>java.text.DateFormat.getDateInstance(style, locale)</code>
	 * will be called instead.
	 * If no format is found and the value of <code>defaultFormat</code> is not null,
	 * <code>defaultFormat</code> will be returned.
	 * Otherwise, "M/d/yy" is returned.
	 *
	 * @param style the giving formatting style. For example,
	 * {@link DateFormat#SHORT} for "M/d/yy" in the US locale.
	 * @param locale the locale. If null, {@link Locales#getCurrent}
	 * is assumed.
	 * @param defaultFormat the default format. It is used if no default
	 * format. If null, "M/d/yy" is assumed.
	 * @since 5.0.7
	 */
	public static
	String getDateFormat(int style, Locale locale, String defaultFormat) {
		if (locale == null)
			locale = Locales.getCurrent();

		final DateFormatInfo info = getLocalFormatInfo();
		if (info != null) {
			final String fmt = info.getDateFormat(style, locale);
			if (fmt != null)
				return fmt;
		}

		final DateFormat df = DateFormat.getDateInstance(style, locale);
		if (df instanceof SimpleDateFormat) {
			final String fmt = ((SimpleDateFormat)df).toPattern();
			if (fmt != null && !"M/d/yy h:mm a".equals(fmt))
				return fmt; // note: JVM use "M/d/yy h:mm a" if not found!
		}
		return defaultFormat != null ? defaultFormat: "M/d/yy";
	}

	/** Returns the current time format; never null.
	 * <p>If a format info is set by {@link #setLocalFormatInfo},
	 * {@link DateFormatInfo#getTimeFormat} of the info will be called first.
	 * If no info is set or the info returns null,
	 * <code>java.text.DateFormat.getTimeInstance(style, locale)</code>
	 * will be called instead.
	 * If no format is found and the value of <code>defaultFormat</code> is not null,
	 * <code>defaultFormat</code> will be returned.
	 * Otherwise, "h:mm a" is returned.
	 *
	 * @param style the giving formatting style. For example,
	 * {@link DateFormat#SHORT} for "h:mm a" in the US locale.
	 * @param locale the locale. If null, {@link Locales#getCurrent}
	 * is assumed.
	 * @param defaultFormat the default format. It is used if no default
	 * format. If null, "h:mm a" is assumed.
	 * @since 5.0.7
	 */
	public static final
	String getTimeFormat(int style, Locale locale, String defaultFormat) {
		if (locale == null)
			locale = Locales.getCurrent();

		final DateFormatInfo info = getLocalFormatInfo();
		if (info != null) {
			final String fmt = info.getTimeFormat(style, locale);
			if (fmt != null)
				return fmt;
		}

		final DateFormat df = DateFormat.getTimeInstance(style, locale);
		if (df instanceof SimpleDateFormat) {
			final String fmt = ((SimpleDateFormat)df).toPattern();
			if (fmt != null && !"M/d/yy h:mm a".equals(fmt))
				return fmt; // note: JVM use "M/d/yy h:mm a" if not found!
		}
		return "h:mm a";
	}

	/** Returns the current date/time format; never null.
	 * <p>If a format info is set by {@link #setLocalFormatInfo},
	 * {@link DateFormatInfo#getDateTimeFormat} of the info will be called first.
	 * If no info is set or the info returns null,
	 * <code>java.text.DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale)</code>
	 * will be called instead.
	 * If no format is found and the value of <code>defaultFormat</code> is not null,
	 * <code>defaultFormat</code> will be returned.
	 * Otherwise, "M/d/yy h:mm a" is returned.
	 *
	 * @param dateStyle the giving formatting style. For example,
	 * {@link DateFormat#SHORT} for "M/d/yy" in the US locale.
	 * @param timeStyle the giving formatting style. For example,
	 * {@link DateFormat#SHORT} for "h:mm a" in the US locale.
	 * @param locale the locale. If null, {@link Locales#getCurrent}
	 * is assumed.
	 * @since 5.0.7
	 */
	public static final
	String getDateTimeFormat(int dateStyle, int timeStyle, Locale locale,
	String defaultFormat) {
		if (locale == null)
			locale = Locales.getCurrent();

		final DateFormatInfo info = getLocalFormatInfo();
		if (info != null) {
			final String fmt = info.getDateTimeFormat(dateStyle, timeStyle, locale);
			if (fmt != null)
				return fmt;
		}

		final DateFormat df =
			DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
		if (df instanceof SimpleDateFormat) {
			final String fmt = ((SimpleDateFormat)df).toPattern();
			if (fmt != null)
				return fmt;
		}
		return defaultFormat != null ? defaultFormat: "M/d/yy h:mm a";
	}

	/** Formats a Date object based on the current Locale
	 * and the default date format ({@link DateFormat#DEFAULT}.
	 *
	 * @param dateOnly indicates whether to show only date; false to show
	 * both date and time
	 */
	public static final String format(Date d, boolean dateOnly) {
		return getDateFormat(dateOnly).format(d);
	}
	/**
	 * Parses a string, that is formatted by {@link #format}, to a date.
	 * It assumes the current locale is {@link Locales#getCurrent}.
	 *
	 * @param dateOnly indicates whether to show only date; false to show
	 * both date and time
	 * @since 5.0.5
	 */
	public static final Date parse(String s, boolean dateOnly)
	throws ParseException {
		return getDateFormat(dateOnly).parse(s);
	}
	private static final DateFormat getDateFormat(boolean dateOnly) {
		final Locale locale = Locales.getCurrent();
		return dateOnly ?
			DateFormat.getDateInstance(DateFormat.DEFAULT, locale):
			DateFormat.getDateTimeInstance(
					DateFormat.DEFAULT, DateFormat.DEFAULT, locale);
	}
}
