Skip to content

Commit

Permalink
Update the tag date helper to be compatible with java 8 date types
Browse files Browse the repository at this point in the history
  • Loading branch information
jameskleeh committed Dec 11, 2018
1 parent 5c33cda commit 20e6ad4
Show file tree
Hide file tree
Showing 2 changed files with 295 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.grails.plugins.web

import groovy.transform.CompileStatic
import org.apache.commons.lang.time.FastDateFormat

import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.OffsetDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.format.FormatStyle
import java.time.temporal.TemporalAccessor

/**
* The default implementation of {@link GrailsTagDateHelper}
Expand All @@ -31,59 +43,106 @@ class DefaultGrailsTagDateHelper implements GrailsTagDateHelper {
@Override
Object getTimeZone(Object timeZone) {
if (timeZone != null) {
if (!(timeZone instanceof TimeZone)) {
TimeZone.getTimeZone(timeZone as String)
} else {
if (timeZone instanceof ZoneId) {
timeZone
} else if (timeZone instanceof TimeZone) {
timeZone.toZoneId()
} else {
ZoneId.of(timeZone.toString())
}
} else {
TimeZone.getDefault()
ZoneId.systemDefault()
}
}

@Override
Object getFormatFromPattern(String format, Object timeZone, Locale locale) {
FastDateFormat.getInstance(format, (TimeZone)timeZone, locale)
DateTimeFormatter.ofPattern(format, locale).withZone((ZoneId)timeZone)

}

@Override
Object getDateFormat(String style, Object timeZone, Locale locale) {
FastDateFormat.getDateInstance(parseStyle(style), (TimeZone)timeZone, locale)
Object getDateFormat(String dateStyle, Object timeZone, Locale locale) {
new DateTimeFormatterBuilder()
.appendLocalized(parseStyle(dateStyle), null)
.toFormatter(locale)
.withZone((ZoneId)timeZone)
}

@Override
Object getTimeFormat(String style, Object timeZone, Locale locale) {
FastDateFormat.getTimeInstance(parseStyle(style), (TimeZone)timeZone, locale)
Object getTimeFormat(String timeStyle, Object timeZone, Locale locale) {
new DateTimeFormatterBuilder()
.appendLocalized(null, parseStyle(timeStyle))
.toFormatter(locale)
.withZone((ZoneId)timeZone)
}

@Override
Object getDateTimeFormat(String dateStyle, String timeStyle, Object timeZone, Locale locale) {
FastDateFormat.getDateTimeInstance(parseStyle(dateStyle), parseStyle(timeStyle), (TimeZone)timeZone, locale)
new DateTimeFormatterBuilder()
.appendLocalized(parseStyle(dateStyle), parseStyle(timeStyle))
.toFormatter(locale)
.withZone((ZoneId)timeZone)
}

@Override
String format(Object formatter, Object date) {
((FastDateFormat)formatter).format(date)
TemporalAccessor instant
if (date instanceof Date) {
instant = date.toInstant()
} else if (date instanceof Calendar) {
instant = date.toInstant()
} else if (date instanceof Long) {
instant = Instant.ofEpochMilli(date)
} else if (date instanceof TemporalAccessor) {
instant = date
} else {
throw new IllegalArgumentException("Cannot format class as date: " +
(date == null ? "<null>" : date.getClass().getName()));
}
((DateTimeFormatter)formatter).format(instant)
}

private static int parseStyle(String styleStr) {
private static FormatStyle parseStyle(String styleStr) {
switch (styleStr) {
case 'FULL': return FastDateFormat.FULL
case 'LONG': return FastDateFormat.LONG
case 'MEDIUM': return FastDateFormat.MEDIUM
default: return FastDateFormat.SHORT
case 'FULL': return FormatStyle.FULL
case 'LONG': return FormatStyle.LONG
case 'MEDIUM': return FormatStyle.MEDIUM
default: return FormatStyle.SHORT
}
}

@Override
Boolean supportsDatePicker(Class clazz) {
clazz == Date
clazz == Date || TemporalAccessor.isAssignableFrom(clazz)
}


@Override
GregorianCalendar buildCalendar(Object date) {
GregorianCalendar c = new GregorianCalendar()
c.setTime((Date)date)
c
if (date instanceof Date) {
GregorianCalendar c = new GregorianCalendar()
c.setTime((Date)date)
c
} else {
ZonedDateTime zonedDateTime
if (date instanceof LocalDateTime) {
zonedDateTime = ZonedDateTime.of(date, ZoneId.systemDefault())
} else if (date instanceof LocalDate) {
zonedDateTime = ZonedDateTime.of(date, LocalTime.MIN, ZoneId.systemDefault())
} else if (date instanceof OffsetDateTime) {
zonedDateTime = ((OffsetDateTime) date).toZonedDateTime()

} else if (date instanceof ZonedDateTime) {
zonedDateTime = (ZonedDateTime) date

} else if (date instanceof TemporalAccessor) {
zonedDateTime = ZonedDateTime.from(date)
}
if (zonedDateTime == null) {
return null
}
GregorianCalendar.from(zonedDateTime)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package org.grails.plugins.web

import spock.lang.IgnoreIf
import spock.lang.Requires
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll

import java.time.*
import java.time.format.DateTimeFormatter

class DefaultDateHelperSpec extends Specification {

@Shared
LocalDate localDate = LocalDate.of(1941, 1, 5)

@Shared
LocalTime localTime = LocalTime.of(8,0,0)

@Shared
DefaultGrailsTagDateHelper helper = new DefaultGrailsTagDateHelper()

void "test getTimeZone"() {
expect:
helper.getTimeZone(TimeZone.getTimeZone('UTC')) == ZoneId.of("UTC")
helper.getTimeZone(ZoneId.of("UTC")) == ZoneId.of("UTC")
helper.getTimeZone("UTC") == ZoneId.of("UTC")
helper.getTimeZone(null) == ZoneId.systemDefault()
}

void "test getFormatFromPattern"() {
given:
DateTimeFormatter format = helper.getFormatFromPattern("yyyy-MM-dd", ZoneId.of('UTC'), Locale.ENGLISH)

expect:
format.zone == ZoneId.of('UTC')
format.format(localDate) == "1941-01-05"
}

void "test getDateFormat"() {
given:
DateTimeFormatter format

when:
format = helper.getDateFormat(style, ZoneId.of('UTC'), Locale.ENGLISH)

then:
format.zone == ZoneId.of('UTC')
format.format(localDate) == expected

where:
style | expected
'FULL' | 'Sunday, January 5, 1941'
'LONG' | 'January 5, 1941'
'MEDIUM' | 'Jan 5, 1941'
null | '1/5/41'
}

@Unroll
void "getTimeFormat for style #style returns #expected"(String style, String expected) {
given:
DateTimeFormatter format

when:
format = helper.getTimeFormat(style, ZoneId.of('UTC'), Locale.ENGLISH)

then:
format.zone == ZoneId.of('UTC')
format.format(localTime) == expected

where:
style | expected
'LONG' | '8:00:00 AM UTC'
'MEDIUM' | '8:00:00 AM'
null | '8:00 AM'
}

@Requires({ jvm.isJava8() })
@Unroll
void "Java 8 - Full getTimeFormat for style #style returns #expected"(String style, String expected) {
given:
DateTimeFormatter format

when:
format = helper.getTimeFormat(style, ZoneId.of('UTC'), Locale.ENGLISH)

then:
format.zone == ZoneId.of('UTC')
format.format(localTime) == expected

where:
style | expected
'FULL' | '8:00:00 AM UTC'
}

@IgnoreIf({ jvm.isJava8() })
@Unroll
void "Full getTimeFormat for style #style returns #expected"(String style, String expected) {
given:
DateTimeFormatter format

when:
format = helper.getTimeFormat(style, ZoneId.of('UTC'), Locale.ENGLISH)

then:
format.zone == ZoneId.of('UTC')
format.format(localTime) == expected

where:
style | expected
'FULL' | '8:00:00 AM Coordinated Universal Time'
}

@Requires({ jvm.isJava8() })
@Unroll("for getDateTimeFormat(#dateStyle, #timeStyle) => #expected")
void "Java 8 - test getDateTimeFormat"(String dateStyle, String timeStyle, String expected) {
given:
DateTimeFormatter format

when:
format = helper.getDateTimeFormat(dateStyle, timeStyle, ZoneId.of('UTC'), Locale.ENGLISH)

then:
format.zone == ZoneId.of('UTC')
format.format(LocalDateTime.of(localDate, localTime)) == expected

where:
dateStyle | timeStyle | expected
'FULL' | 'FULL' | 'Sunday, January 5, 1941 8:00:00 AM UTC'
'LONG' | 'LONG' | 'January 5, 1941 8:00:00 AM UTC'
'MEDIUM' | 'MEDIUM' | 'Jan 5, 1941 8:00:00 AM'
null | null | '1/5/41 8:00 AM'
}

@IgnoreIf({ jvm.isJava8() })
@Unroll("for getDateTimeFormat(#dateStyle, #timeStyle) => #expected")
void "test getDateTimeFormat"(String dateStyle, String timeStyle, String expected) {
given:
DateTimeFormatter format

when:
format = helper.getDateTimeFormat(dateStyle, timeStyle, ZoneId.of('UTC'), Locale.ENGLISH)

then:
format.zone == ZoneId.of('UTC')
format.format(LocalDateTime.of(localDate, localTime)) == expected

where:
dateStyle | timeStyle | expected
'FULL' | 'FULL' | 'Sunday, January 5, 1941 at 8:00:00 AM Coordinated Universal Time'
'LONG' | 'LONG' | 'January 5, 1941 at 8:00:00 AM UTC'
'MEDIUM' | 'MEDIUM' | 'Jan 5, 1941, 8:00:00 AM'
null | null | '1/5/41, 8:00 AM'
}

void "test supportsDatePickers"() {
expect:
helper.supportsDatePicker(Date)
helper.supportsDatePicker(LocalDate)
helper.supportsDatePicker(LocalTime)
helper.supportsDatePicker(LocalDateTime)
helper.supportsDatePicker(OffsetDateTime)
helper.supportsDatePicker(OffsetTime)
helper.supportsDatePicker(ZonedDateTime)
}

void "test buildCalendar"() {
//TemporalAccessors without date aren't designed to be supported here
expect:
helper.buildCalendar(new Date()) instanceof GregorianCalendar
helper.buildCalendar(LocalDateTime.now()) instanceof GregorianCalendar
helper.buildCalendar(LocalDate.now()) instanceof GregorianCalendar
helper.buildCalendar(OffsetDateTime.now()) instanceof GregorianCalendar
helper.buildCalendar(ZonedDateTime.now()) instanceof GregorianCalendar
}

void "test format"() {
given:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of('UTC'))
DateTimeFormatter timeOnlyFormatter = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneId.of('UTC'))
DateTimeFormatter dateOnlyFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of('UTC'))

LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime)

OffsetDateTime offsetDateTime = localDateTime.atOffset(ZoneOffset.UTC)

OffsetTime offsetTime = OffsetTime.of(localTime, ZoneOffset.UTC)

ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of('UTC'))

Date javaUtilDate = Date.from(zonedDateTime.toInstant())

Calendar calendar = Calendar.instance
calendar.setTime(javaUtilDate)

Long seconds = javaUtilDate.time

expect:
helper.format(formatter, zonedDateTime) == '1941-01-05 08:00:00'
helper.format(timeOnlyFormatter, offsetTime) == '08:00:00'
helper.format(formatter, offsetDateTime) == '1941-01-05 08:00:00'
helper.format(timeOnlyFormatter, localTime) == '08:00:00'
helper.format(dateOnlyFormatter, localDate) == '1941-01-05'
helper.format(formatter, localDateTime) == '1941-01-05 08:00:00'
helper.format(formatter, javaUtilDate) == '1941-01-05 08:00:00'
helper.format(formatter, calendar) == '1941-01-05 08:00:00'
helper.format(formatter, seconds) == '1941-01-05 08:00:00'

when:
helper.format(formatter, null)

then:
thrown(IllegalArgumentException)
}
}

0 comments on commit 20e6ad4

Please sign in to comment.