package ca.uhn.fhir.narrative.template.filters; import java.text.SimpleDateFormat; import java.util.HashSet; import java.util.Locale; import java.util.Set; class Date extends Filter { private static Locale locale = Locale.ENGLISH; private static Set<String> datePatterns = new HashSet<String>(); private final static java.util.Map<Character, SimpleDateFormat> LIQUID_TO_JAVA_FORMAT = new java.util.HashMap<Character, SimpleDateFormat>(); static { addDatePattern("yyyy-MM-dd HH:mm:ss"); addDatePattern("EEE MMM ddhh:mm:ss yyyy"); init(); } /* * (Object) date(input, format) * * Reformat a date * * %a - The abbreviated weekday name (``Sun'') * %A - The full weekday name (``Sunday'') * %b - The abbreviated month name (``Jan'') * %B - The full month name (``January'') * %c - The preferred local date and time representation * %d - Day of the month (01..31) * %H - Hour of the day, 24-hour clock (00..23) * %I - Hour of the day, 12-hour clock (01..12) * %j - Day of the year (001..366) * %m - Month of the year (01..12) * %M - Minute of the hour (00..59) * %p - Meridian indicator (``AM'' or ``PM'') * %S - Second of the minute (00..60) * %U - Week number of the current year, * starting with the first Sunday as the first * day of the first week (00..53) * %W - Week number of the current year, * starting with the first Monday as the first * day of the first week (00..53) * %w - Day of the week (Sunday is 0, 0..6) * %x - Preferred representation for the date alone, no time * %X - Preferred representation for the time alone, no date * %y - Year without a century (00..99) * %Y - Year with century * %Z - Time zone name * %% - Literal ``%'' character */ @Override public Object apply(Object value, Object... params) { try { final Long seconds; if(super.asString(value).equals("now")) { seconds = System.currentTimeMillis() / 1000L; } else if(super.isNumber(value)) { // No need to divide this by 1000, the param is expected to be in seconds already! seconds = super.asNumber(value).longValue(); } else { seconds = trySeconds(super.asString(value)); // formatter.parse(super.asString(value)).getTime() / 1000L; if(seconds == null) { return value; } } final java.util.Date date = new java.util.Date(seconds * 1000L); final String format = super.asString(super.get(0, params)); if(format == null || format.trim().isEmpty()) { return value; } final java.util.Calendar calendar = java.util.Calendar.getInstance(); calendar.setTime(date); StringBuilder builder = new StringBuilder(); for (int i = 0; i < format.length(); i++) { char ch = format.charAt(i); if (ch == '%') { i++; if (i == format.length()) { // a trailing (single) '%' sign: just append it builder.append("%"); break; } char next = format.charAt(i); SimpleDateFormat javaFormat = LIQUID_TO_JAVA_FORMAT.get(next); if (javaFormat == null) { // no valid date-format: append the '%' and the 'next'-char builder.append("%").append(next); } else { builder.append(javaFormat.format(date)); } } else { builder.append(ch); } } return builder.toString(); } catch (Exception e) { return value; } } private static void init() { // %% - Literal ``%'' character LIQUID_TO_JAVA_FORMAT.put('%', new SimpleDateFormat("%", locale)); // %a - The abbreviated weekday name (``Sun'') LIQUID_TO_JAVA_FORMAT.put('a', new SimpleDateFormat("EEE", locale)); // %A - The full weekday name (``Sunday'') LIQUID_TO_JAVA_FORMAT.put('A', new SimpleDateFormat("EEEE", locale)); // %b - The abbreviated month name (``Jan'') LIQUID_TO_JAVA_FORMAT.put('b', new SimpleDateFormat("MMM", locale)); LIQUID_TO_JAVA_FORMAT.put('h', new SimpleDateFormat("MMM", locale)); // %B - The full month name (``January'') LIQUID_TO_JAVA_FORMAT.put('B', new SimpleDateFormat("MMMM", locale)); // %c - The preferred local date and time representation LIQUID_TO_JAVA_FORMAT.put('c', new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", locale)); // %d - Day of the month (01..31) LIQUID_TO_JAVA_FORMAT.put('d', new SimpleDateFormat("dd", locale)); // %H - Hour of the day, 24-hour clock (00..23) LIQUID_TO_JAVA_FORMAT.put('H', new SimpleDateFormat("HH", locale)); // %I - Hour of the day, 12-hour clock (01..12) LIQUID_TO_JAVA_FORMAT.put('I', new SimpleDateFormat("hh", locale)); // %j - Day of the year (001..366) LIQUID_TO_JAVA_FORMAT.put('j', new SimpleDateFormat("DDD", locale)); // %m - Month of the year (01..12) LIQUID_TO_JAVA_FORMAT.put('m', new SimpleDateFormat("MM", locale)); // %M - Minute of the hour (00..59) LIQUID_TO_JAVA_FORMAT.put('M', new SimpleDateFormat("mm", locale)); // %p - Meridian indicator (``AM'' or ``PM'') LIQUID_TO_JAVA_FORMAT.put('p', new SimpleDateFormat("a", locale)); // %S - Second of the minute (00..60) LIQUID_TO_JAVA_FORMAT.put('S', new SimpleDateFormat("ss", locale)); // %U - Week number of the current year, // starting with the first Sunday as the first // day of the first week (00..53) LIQUID_TO_JAVA_FORMAT.put('U', new SimpleDateFormat("ww", locale)); // %W - Week number of the current year, // starting with the first Monday as the first // day of the first week (00..53) LIQUID_TO_JAVA_FORMAT.put('W', new SimpleDateFormat("ww", locale)); // %w - Day of the week (Sunday is 0, 0..6) LIQUID_TO_JAVA_FORMAT.put('w', new SimpleDateFormat("F", locale)); // %x - Preferred representation for the date alone, no time LIQUID_TO_JAVA_FORMAT.put('x', new SimpleDateFormat("MM/dd/yy", locale)); // %X - Preferred representation for the time alone, no date LIQUID_TO_JAVA_FORMAT.put('X', new SimpleDateFormat("HH:mm:ss", locale)); // %y - Year without a century (00..99) LIQUID_TO_JAVA_FORMAT.put('y', new SimpleDateFormat("yy", locale)); // %Y - Year with century LIQUID_TO_JAVA_FORMAT.put('Y', new SimpleDateFormat("yyyy", locale)); // %Z - Time zone name LIQUID_TO_JAVA_FORMAT.put('Z', new SimpleDateFormat("z", locale)); } /** * Changes the locale. * * @param locale the new locale. */ public static void setLocale(Locale locale) { Date.locale = locale; init(); } /** * Adds a new Date-pattern to be used when parsing a string to a Date. * * @param pattern the pattern. */ public static void addDatePattern(String pattern) { if(pattern == null) { throw new NullPointerException("date-pattern cannot be null"); } datePatterns.add(pattern); } /** * Removed a Date-pattern to be used when parsing a string to a Date. * * @param pattern the pattern. */ public static void removeDatePattern(String pattern) { datePatterns.remove(pattern); } /* * Try to parse `str` into a Date and return this Date as seconds * since EPOCH, or null if it could not be parsed. */ private Long trySeconds(String str) { for(String pattern : datePatterns) { SimpleDateFormat parser = new SimpleDateFormat(pattern, locale); try { long milliseconds = parser.parse(str).getTime(); return milliseconds / 1000L; } catch(Exception e) { // Just ignore and try the next pattern in `datePatterns`. } } // Could not parse the string into a meaningful date, return null. return null; } }