package com.hubspot.jinjava.objects.date;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
import org.apache.commons.lang3.StringUtils;
/**
* Datetime format string formatter, supporting both python and java compatible format strings by converting any percent-tokens from python into their java equivalents.
*
* @author jstehler
*/
public class StrftimeFormatter {
public static final String DEFAULT_DATE_FORMAT = "%H:%M / %d-%m-%Y";
/*
* Mapped from http://strftime.org/, http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
*/
private static final String[] CONVERSIONS = new String[255];
static {
CONVERSIONS['a'] = "EEE";
CONVERSIONS['A'] = "EEEE";
CONVERSIONS['b'] = "MMM";
CONVERSIONS['B'] = "MMMM";
CONVERSIONS['c'] = "EEE MMM dd HH:mm:ss yyyy";
CONVERSIONS['d'] = "dd";
CONVERSIONS['e'] = "d"; // The day of the month like with %d, but padded with blank (range 1 through 31).
CONVERSIONS['f'] = "SSSS";
CONVERSIONS['H'] = "HH";
CONVERSIONS['h'] = "hh";
CONVERSIONS['I'] = "hh";
CONVERSIONS['j'] = "DDD";
CONVERSIONS['k'] = "H"; // The hour as a decimal number, using a 24-hour clock like %H, but padded with blank (range 0 through 23).
CONVERSIONS['l'] = "h"; // The hour as a decimal number, using a 12-hour clock like %I, but padded with blank (range 1 through 12).
CONVERSIONS['m'] = "MM";
CONVERSIONS['M'] = "mm";
CONVERSIONS['p'] = "a";
CONVERSIONS['S'] = "ss";
CONVERSIONS['U'] = "ww";
CONVERSIONS['w'] = "e";
CONVERSIONS['W'] = "ww";
CONVERSIONS['x'] = "MM/dd/yy";
CONVERSIONS['X'] = "HH:mm:ss";
CONVERSIONS['y'] = "yy";
CONVERSIONS['Y'] = "yyyy";
CONVERSIONS['z'] = "Z";
CONVERSIONS['Z'] = "ZZZ";
CONVERSIONS['%'] = "%";
}
/**
* Parses a string in python strftime format, returning the equivalent string in java date time format.
*
* @param strftime
* @return date formatted as string
*/
private static String toJavaDateTimeFormat(String strftime) {
if (!StringUtils.contains(strftime, '%')) {
return replaceL(strftime);
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < strftime.length(); i++) {
char c = strftime.charAt(i);
if (c == '%') {
c = strftime.charAt(++i);
boolean stripLeadingZero = false;
if (c == '-') {
stripLeadingZero = true;
c = strftime.charAt(++i);
}
if (stripLeadingZero) {
result.append(CONVERSIONS[c].substring(1));
} else {
result.append(CONVERSIONS[c]);
}
} else if (Character.isLetter(c)) {
result.append("'");
while (Character.isLetter(c)) {
result.append(c);
if (++i < strftime.length()) {
c = strftime.charAt(i);
} else {
c = 0;
}
}
result.append("'");
--i; // re-consume last char
} else {
result.append(c);
}
}
return replaceL(result.toString());
}
private static String replaceL(String s) {
return StringUtils.replaceChars(s, 'L', 'M');
}
public static DateTimeFormatter formatter(String strftime) {
return formatter(strftime, Locale.ENGLISH);
}
public static DateTimeFormatter formatter(String strftime, Locale locale) {
DateTimeFormatter fmt;
switch (strftime.toLowerCase()) {
case "short":
fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
break;
case "medium":
fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
break;
case "long":
fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
break;
case "full":
fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL);
break;
default:
try {
fmt = DateTimeFormatter.ofPattern(toJavaDateTimeFormat(strftime));
break;
} catch (IllegalArgumentException e) {
throw new InvalidDateFormatException(strftime, e);
}
}
return fmt.withLocale(locale);
}
public static String format(ZonedDateTime d) {
return format(d, DEFAULT_DATE_FORMAT);
}
public static String format(ZonedDateTime d, Locale locale) {
return format(d, DEFAULT_DATE_FORMAT, locale);
}
public static String format(ZonedDateTime d, String strftime) {
return format(d, strftime, Locale.ENGLISH);
}
public static String format(ZonedDateTime d, String strftime, Locale locale) {
return formatter(strftime, locale).format(d);
}
}