package net.bootsfaces.utils;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* This class converts a date format string from the momement.js format to the Java SimpleDateFormater
* format string, and vice versa. It has been inspired by
* https://github.com/MadMG/moment-jdateformatparser/blob/master/moment-jdateformatparser.js.
* @author chongma
*
*/
public class LocaleUtils {
static long time = 0l;
@SuppressWarnings("serial")
private static final HashMap<String, String> momentToJava = new HashMap<String, String>() {
{
put("d", ""); // (day in week - 0=Sunday, 6=Saturday)");
put("D", "d"); // (day in month - one or two digits)");
put("DD", "dd"); // (day in month - two digits)");
put("Do", null); // (day of month with ordinal)");
put("Y", null); // (year - any number of digits and sign");
put("YY", "yy"); // (year - two digits)");
put("YYYY", "yyyy"); // (year - all digits)");
put("a", null); // (am or pm)");
put("A", "a"); // (AM or PM)");
put("M", "M"); // (month in year - two digits, 01..12)");
put("MM", "MM"); // (month in year - two digits, 01..12)");
put("MMM", "MMM"); // (month in year - short text)");
put("MMMM", "MMMM"); // (month in year - full text)");
put("h", "h"); // (hour - one or two digits, 12 hours, 1..12)");
put("hh", "hh"); // (hour - two or two digits, 12 hours, 01..12)");
put("H", "H"); // (hour - one or two digits, 24 hours, 0..23)");
put("HH", "HH"); // (hour - two digits, 24 hours, 00..23)");
put("k", "k"); // (hour - one or two digits, 24 hours, 1..24)");
put("kk", "kk"); // (hour - two digits, 24 hours, 01..24)");
put("m", "m"); // (minutes - one or two digits)");
put("mm", "mm"); // (minutes - two digits)");
put("s", "s"); // (seconds - one or two digits)");
put("ss", "ss"); // (second - two digits)");
put("S", "S"); // (fractional seconds - 0...999");
put("SS", "SS"); // (fractional seconds - 00...999");
put("SSS", "SSS"); // (fractional seconds - 000...999");
put("ddd", "E"); // (day name - short)");
put("dddd", "EEEE"); // (day name - full)");
put("DDD", "D"); // (day in year)");
put("DDDD", "DDD"); // (day in year)");
put("W", "w"); // (week in year - one or two digits, 1..53)");
put("WW", "ww"); // (week in year - two digits, 01..53)");
put("w", null); // (locale week in year - one or two digits, 1..53)");
put("ww", null); // (locale week in year - two digits, 01..53)");
put("ZZ", "ZZ"); // (offset from UTC)");
put("Z", "XXX"); // (offset from UTC)");
put("E", "u"); // (ISO day of week - 1..7)");
put("e", null); // (Locale day of week - 0..6)");
put("Q", null); // (quarter in year - 1..4");
put("X", null); // (UNIX timestamp)");
put("x", null); // (UNIX timestamp - milliseconds)");
put("[", "'"); // (escape character)");
put("]", "'"); // (escape character)");
}
};
@SuppressWarnings("serial")
private static final HashMap<String, String> javaToMoment = new HashMap<String, String>() {
{
put("d", "D"); // (day in month - one or two digits)");
put("dd", "DD"); // (day in month - two digits)");
put("D", "DDD"); // (day in year - one to three digits)");
put("DD", null); // (day in year- two or three digits)");
put("DDD", "DDDD"); // (day in year- three digits)");
put("F", null); // (day of week in month)");
put("y", "YYYY"); // (year - every digit)");
put("yy", "YY"); // (year - two digits)");
put("yyy", "YYYY"); // (year - three digits)");
put("yyyy", "YYYY"); // (year - every digit)");
put("Y", null); // (week year - two digits)");
put("YY", "gg"); // (week year - two digits)");
put("YYY", null); // (week year - three digits)");
put("YYYY", "gggg"); // (week year - all digits)");
put("a", "A"); // (AM or PM)");
put("G", null); // (era - AD or BC)");
put("M", "M"); // (month in year - two digits, 1..12)");
put("MM", "MM"); // (month in year - two digits, 01..12)");
put("MMM", "MMM"); // (month in year - short text)");
put("MMMM", "MMMM "); // (month in year - full text)");
put("h", "h"); // (hour - one or two digits, 12 hours, 1..12)");
put("hh", "hh"); // (hour - two digits, 12 hours, 01..12)");
put("H", "H"); // (hour - one or two digits, 24 hours, 0..23)");
put("HH", "HH"); // (hour - two digits, 24 hours, 00..23)");
put("k", "k"); // (hour - one or two digits, 12 hours, 1..24)");
put("kk", "kk"); // (hour - two digits, 24 hours, 01..24)");
put("K", null); // (hour - one or two digits, 12 hours, 0..11)");
put("KK", null); // (hour - two digits, 12 hours, 00..11)");
put("m", "m"); // (minutes - one or two digits)");
put("mm", "mm"); // (minutes - two digits)");
put("s", "s"); // (seconds- one or two digits)");
put("ss", "ss"); // (seconds - two digits)");
put("S", "S"); // (millisecond)");
put("SS", "SS "); // (millisecond)");
put("SSS", "SSS "); // (millisecond)");
put("E", "ddd"); // (day name in week - short)");
put("EE", "ddd "); // (day name in week - short)");
put("EEE", "ddd "); // (day name in week - short)");
put("EEEE", "dddd "); // (day name in week - full)");
put("w", "W"); // (week in year - one or two digits)");
put("ww", "WW"); // (week in year - two digits, zero-padded)");
put("W", null); // (week in month - one or two digits)");
put("WW", null); // (week in month - two digits, zero-padded)");
put("z", null); // (General time zone)");
put("zz", null); // (General time zone)");
put("zzz", null); // (General time zone)");
put("zzzz", null); // (General time zone)");
put("Z", "ZZ"); // (RFC 822 time zone)");
put("X", null); // (ISO 8601 time zone - - hours only)");
put("XX", "ZZ"); // (ISO 8601 time zone - short)");
put("XXX", "Z"); // (ISO 8601 time zone - long)");
put("u", "E"); // (day number of week - 1=Monday, 7=Sunday)");
put("'", "[]"); // (escape character);
}
};
private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {
private static final long serialVersionUID = 1L;
{
put("^\\d{8}$", "yyyyMMdd");
put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
put("^\\d{12}$", "yyyyMMddHHmm");
put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
put("^\\d{14}$", "yyyyMMddHHmmss");
put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
}
};
/**
* Determine SimpleDateFormat pattern matching with the given date string.
* Returns null if format is unknown. You can simply extend DateUtil with
* more formats if needed.
*
* @param dateString
* The date string to determine the SimpleDateFormat pattern for.
* @return The matching SimpleDateFormat pattern, or null if format is
* unknown.
* @see SimpleDateFormat
*/
public static String determineDateFormat(String dateString) {
for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
if (dateString.toLowerCase().matches(regexp)) {
return DATE_FORMAT_REGEXPS.get(regexp);
}
}
return null; // Unknown format.
}
/**
* Try to auto-parse the date format
*
* @param dateString
* @return
*/
public static Date autoParseDateFormat(String dateString) {
// STEP 1: try to detect standard locale based java date format
for (Locale locale : DateFormat.getAvailableLocales()) {
for (int style = DateFormat.FULL; style <= DateFormat.SHORT; style++) {
DateFormat df = DateFormat.getDateInstance(style, locale);
try {
return df.parse(dateString);
// either return "true", or return the Date obtained Date
// object
} catch (ParseException ex) {
continue; // unparsable, try the next one
}
}
}
// STEP 2: if the date format was not found, check with predefined dates
String format = determineDateFormat(dateString);
if (format != null) {
DateFormat df = new SimpleDateFormat(format);
try {
return df.parse(dateString);
} catch (ParseException e) {
return null;
}
}
return null;
}
/**
* Translate java format to moment.js format
*
* @param formatString
* @return
*/
public static String javaToMomentFormat(String formatString) {
return translateFormat(formatString, javaToMoment, "'", "'", "[", "]");
}
/**
* Translate moment.js format to java format
*
* @param formatString
* @return
*/
public static String momentToJavaFormat(String formatString) {
return translateFormat(formatString, momentToJava, "[", "]", "'", "'");
}
/**
* Internal method to do translations
*
* @param formatString
* @param mapping
* @return
*/
private static String translateFormat(String formatString, Map<String, String> mapping, String escapeStart, String escapeEnd, String targetEscapeStart, String targetEscapeEnd) {
int beginIndex = 0;
int i = 0;
char lastChar = 0;
char currentChar = 0;
String resultString = "";
char esc1 = escapeStart.charAt(0);
char esc2 = escapeEnd.charAt(0);
for (; i < formatString.length(); i++) {
currentChar = formatString.charAt(i);
if (i > 0 && lastChar != currentChar) {
resultString += mapSubformat(formatString, mapping, beginIndex, i, escapeStart, escapeEnd, targetEscapeStart, targetEscapeEnd);
beginIndex = i;
}
lastChar = currentChar;
if (currentChar == esc1) {
i++;
while (i< formatString.length() && formatString.charAt(i) != esc2) {
i++;
}
resultString += targetEscapeStart;
resultString += formatString.substring(beginIndex+1, i);
resultString += targetEscapeEnd;
i++;
if (i < formatString.length()) {
lastChar = formatString.charAt(i);
}
beginIndex = i;
}
}
if (beginIndex < formatString.length() && i <= formatString.length()) {
return resultString + mapSubformat(formatString, mapping, beginIndex, i, escapeStart, escapeEnd, targetEscapeStart, targetEscapeEnd);
} else {
return resultString;
}
}
/**
* Append the new mapping
*
* @param formatString
* @param mapping
* @param beginIndex
* @param currentIndex
* @param resultString
* @return
*/
private static String mapSubformat(String formatString, Map<String, String> mapping, int beginIndex,
int currentIndex, String escapeStart, String escapeEnd, String targetEscapeStart, String targetEscapeEnd) {
String subformat = formatString.substring(beginIndex, currentIndex);
if (subformat.equals(escapeStart) || subformat.equals(escapeEnd)) {
return targetEscapeStart + "(error: cannot ecape escape characters)" + targetEscapeEnd;
}
if (mapping.containsKey(subformat)) {
String result = mapping.get(subformat);
if (result==null || result.length()==0) {
return targetEscapeStart + "(error: " + subformat + " cannot be converted)" + targetEscapeEnd;
}
return result;
}
return subformat;
}
// TEST METHOD
public static void main(String[] args) {
isCorrect(momentToJavaFormat("DD/MM/YYYY"), "dd/MM/yyyy");
isCorrect(momentToJavaFormat("DD[/]MM[/]YYYY"), "dd'/'MM'/'yyyy");
isCorrect(javaToMomentFormat("DD/MM/YYYY"), "[(error: DD can't be converted)]/MM/gggg");
isCorrect(javaToMomentFormat("DD[/]MM"), "[(error: DD cannot be converted)][(error: cannot ecape escape characters)]/[(error: cannot ecape escape characters)]MM");
isCorrect(javaToMomentFormat("dd/MM/yyyy"), "DD/MM/YYYY");
}
// TEST METHOD
private static void isCorrect(String one, String two) {
if (one != null && one.equals(two)) {
System.out.println(one + " correct");
} else {
System.out.println(one + " wrong - should be " + two);
}
}
}