This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/struts.git
The following commit(s) were added to refs/heads/master by this push: new aacac5b WW-5016 Support java8 date/time in date tag new c58e492 Merge pull request #330 from aleksandr-m/feature/java8_date aacac5b is described below commit aacac5b9fc0cbf4c191fcb2e8be232e3fa736dd0 Author: Aleksandr Mashchenko <amashche...@apache.org> AuthorDate: Wed Feb 6 23:06:09 2019 +0200 WW-5016 Support java8 date/time in date tag --- .../java/org/apache/struts2/components/Date.java | 108 ++++++++++----------- .../apache/struts2/views/jsp/ui/DateTagTest.java | 61 ++++++++++-- 2 files changed, 107 insertions(+), 62 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/components/Date.java b/core/src/main/java/org/apache/struts2/components/Date.java index ba2c82d..a68e339 100644 --- a/core/src/main/java/org/apache/struts2/components/Date.java +++ b/core/src/main/java/org/apache/struts2/components/Date.java @@ -28,12 +28,15 @@ import org.apache.struts2.views.annotations.StrutsTagAttribute; import java.io.IOException; import java.io.Writer; -import java.text.DateFormat; -import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import java.util.TimeZone; /** * <!-- START SNIPPET: javadoc --> @@ -220,11 +223,11 @@ public class Date extends ContextBean { * @param date the date * @return the date nicely */ - public String formatTime(TextProvider tp, java.util.Date date) { - java.util.Date now = new java.util.Date(); + public String formatTime(TextProvider tp, ZonedDateTime date) { + ZonedDateTime now = ZonedDateTime.now(); StringBuilder sb = new StringBuilder(); - List args = new ArrayList(); - long secs = Math.abs((now.getTime() - date.getTime()) / 1000); + List<Object> args = new ArrayList<>(); + long secs = Math.abs(now.toEpochSecond() - date.toEpochSecond()); long mins = secs / 60; long sec = secs % 60; int min = (int) mins % 60; @@ -267,7 +270,7 @@ public class Date extends ContextBean { args.clear(); args.add(sb.toString()); - if (date.before(now)) { + if (date.isBefore(now)) { // looks like this date is passed return tp.getText(DATETAG_PROPERTY_PAST, DATETAG_DEFAULT_PAST, args); } else { @@ -275,54 +278,55 @@ public class Date extends ContextBean { } } + @Override public boolean end(Writer writer, String body) { - String msg; - java.util.Date date = null; + ZonedDateTime date = null; + final ZoneId tz = getTimeZone(); // find the name on the valueStack - try { - //support Calendar also - Object dateObject = findValue(name); - if (dateObject instanceof java.util.Date) { - date = (java.util.Date) dateObject; - } else if (dateObject instanceof Calendar) { - date = ((Calendar) dateObject).getTime(); - } else if (dateObject instanceof Long) { - date = new java.util.Date((long) dateObject); - } else { - if (devMode) { - TextProvider tp = findProviderInStack(); - String developerNotification = ""; - if (tp != null) { - developerNotification = findProviderInStack().getText( - "devmode.notification", - "Developer Notification:\n{0}", - new String[]{ - "Expression [" + name + "] passed to <s:date/> tag which was evaluated to [" + dateObject + "](" - + (dateObject != null ? dateObject.getClass() : "null") + ") isn't instance of java.util.Date nor java.util.Calendar nor long!" - } - ); - } - LOG.warn(developerNotification); - } else { - LOG.debug("Expression [{}] passed to <s:date/> tag which was evaluated to [{}]({}) isn't instance of java.util.Date nor java.util.Calendar nor long!", - name, dateObject, (dateObject != null ? dateObject.getClass() : "null")); + Object dateObject = findValue(name); + if (dateObject instanceof java.util.Date) { + date = ((java.util.Date) dateObject).toInstant().atZone(tz); + } else if (dateObject instanceof Calendar) { + date = ((Calendar) dateObject).toInstant().atZone(tz); + } else if (dateObject instanceof Long) { + date = Instant.ofEpochMilli((long) dateObject).atZone(tz); + } else if (dateObject instanceof LocalDateTime) { + date = ((LocalDateTime) dateObject).atZone(tz); + } else if (dateObject instanceof Instant) { + date = ((Instant) dateObject).atZone(tz); + } else { + if (devMode) { + TextProvider tp = findProviderInStack(); + String developerNotification = ""; + if (tp != null) { + developerNotification = findProviderInStack().getText( + "devmode.notification", + "Developer Notification:\n{0}", + new String[]{ + "Expression [" + name + "] passed to <s:date/> tag which was evaluated to [" + dateObject + "](" + + (dateObject != null ? dateObject.getClass() : "null") + ") isn't supported!" + } + ); } + LOG.warn(developerNotification); + } else { + LOG.debug("Expression [{}] passed to <s:date/> tag which was evaluated to [{}]({}) isn't supported!", + name, dateObject, (dateObject != null ? dateObject.getClass() : "null")); } - } catch (Exception e) { - LOG.error("Could not convert object with key '{}' to a java.util.Date instance", name); } //try to find the format on the stack if (format != null) { format = findString(format); } + String msg; if (date != null) { TextProvider tp = findProviderInStack(); if (tp != null) { if (nice) { msg = formatTime(tp, date); } else { - TimeZone tz = getTimeZone(); + DateTimeFormatter dtf; if (format == null) { String globalFormat = null; @@ -335,23 +339,15 @@ public class Date extends ContextBean { // DATETAG_PROPERTY if (globalFormat != null && !DATETAG_PROPERTY.equals(globalFormat)) { - SimpleDateFormat sdf = new SimpleDateFormat(globalFormat, - ActionContext.getContext().getLocale()); - sdf.setTimeZone(tz); - msg = sdf.format(date); + dtf = DateTimeFormatter.ofPattern(globalFormat, ActionContext.getContext().getLocale()); } else { - DateFormat df = DateFormat.getDateTimeInstance( - DateFormat.MEDIUM, DateFormat.MEDIUM, - ActionContext.getContext().getLocale()); - df.setTimeZone(tz); - msg = df.format(date); + dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) + .withLocale(ActionContext.getContext().getLocale()); } } else { - SimpleDateFormat sdf = new SimpleDateFormat(format, ActionContext - .getContext().getLocale()); - sdf.setTimeZone(tz); - msg = sdf.format(date); + dtf = DateTimeFormatter.ofPattern(format, ActionContext.getContext().getLocale()); } + msg = dtf.format(date); } if (msg != null) { try { @@ -369,15 +365,15 @@ public class Date extends ContextBean { return super.end(writer, ""); } - private TimeZone getTimeZone() { - TimeZone tz = TimeZone.getDefault(); + private ZoneId getTimeZone() { + ZoneId tz = ZoneId.systemDefault(); if (timezone != null) { timezone = stripExpressionIfAltSyntax(timezone); String actualTimezone = (String) getStack().findValue(timezone, String.class); if (actualTimezone != null) { timezone = actualTimezone; } - tz = TimeZone.getTimeZone(timezone); + tz = ZoneId.of(timezone); } return tz; } diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/DateTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/DateTagTest.java index 185ee10..ccf5063 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/DateTagTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/DateTagTest.java @@ -19,11 +19,17 @@ package org.apache.struts2.views.jsp.ui; import com.opensymphony.xwork2.ActionContext; + +import org.apache.struts2.TestAction; import org.apache.struts2.views.jsp.AbstractTagTest; import org.apache.struts2.views.jsp.DateTag; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; @@ -50,18 +56,33 @@ public class DateTagTest extends AbstractTagTest { assertEquals(formatted, writer.toString()); } + public void testCustomGlobalFormatFormat() throws Exception { + String format = "yyyy/MM/dd hh:mm:ss"; + Date now = new Date(); + String formatted = new SimpleDateFormat(format).format(now); + context.put("myDate", now); + + ((TestAction) action).setText(org.apache.struts2.components.Date.DATETAG_PROPERTY, format); + + tag.setName("myDate"); + tag.setNice(false); + tag.doStartTag(); + tag.doEndTag(); + assertEquals(formatted, writer.toString()); + } + public void testCustomFormatWithTimezone() throws Exception { String format = "yyyy/MM/dd hh:mm:ss"; - Date now = Calendar.getInstance(TimeZone.getTimeZone("UTC+1")).getTime(); + Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+1")).getTime(); SimpleDateFormat sdf = new SimpleDateFormat(format); - sdf.setTimeZone(TimeZone.getTimeZone("UTC+1")); + sdf.setTimeZone(TimeZone.getTimeZone("GMT+1")); String formatted = sdf.format(now); context.put("myDate", now); tag.setName("myDate"); tag.setNice(false); tag.setFormat(format); - tag.setTimezone("UTC+1"); + tag.setTimezone("GMT+1"); tag.doStartTag(); tag.doEndTag(); assertEquals(formatted, writer.toString()); @@ -69,12 +90,12 @@ public class DateTagTest extends AbstractTagTest { public void testCustomFormatWithTimezoneAsExpression() throws Exception { String format = "yyyy/MM/dd hh:mm:ss"; - Date now = Calendar.getInstance(TimeZone.getTimeZone("UTC+2")).getTime(); + Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+2")).getTime(); SimpleDateFormat sdf = new SimpleDateFormat(format); - sdf.setTimeZone(TimeZone.getTimeZone("UTC+2")); + sdf.setTimeZone(TimeZone.getTimeZone("GMT+2")); String formatted = sdf.format(now); context.put("myDate", now); - context.put("myTimezone", "UTC+2"); + context.put("myTimezone", "GMT+2"); tag.setName("myDate"); tag.setNice(false); @@ -114,6 +135,34 @@ public class DateTagTest extends AbstractTagTest { assertEquals(formatted, writer.toString()); } + public void testCustomFormatLocalDateTime() throws Exception { + String format = "yyyy/MM/dd hh:mm:ss"; + LocalDateTime date = LocalDateTime.now(); + String formatted = date.format(DateTimeFormatter.ofPattern(format)); + context.put("myDate", date); + + tag.setName("myDate"); + tag.setNice(false); + tag.setFormat(format); + tag.doStartTag(); + tag.doEndTag(); + assertEquals(formatted, writer.toString()); + } + + public void testCustomFormatInstant() throws Exception { + String format = "yyyy/MM/dd hh:mm:ss"; + Instant date = Instant.now(); + String formatted = DateTimeFormatter.ofPattern(format).format(date.atZone(ZoneId.systemDefault())); + context.put("myDate", date); + + tag.setName("myDate"); + tag.setNice(false); + tag.setFormat(format); + tag.doStartTag(); + tag.doEndTag(); + assertEquals(formatted, writer.toString()); + } + public void testDefaultFormat() throws Exception { Date now = new Date(); String formatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,