date parsing - support `t` separator, and better doc, recommending yaml "timestamp"
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/c9682116 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/c9682116 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/c9682116 Branch: refs/heads/master Commit: c968211642d9e4c8264469aa34b64f6dc43226de Parents: 0988b6d Author: Alex Heneveld <[email protected]> Authored: Mon Jun 8 16:51:37 2015 +0100 Committer: Alex Heneveld <[email protected]> Committed: Wed Jun 10 18:38:17 2015 +0100 ---------------------------------------------------------------------- .../java/brooklyn/util/flags/TypeCoercions.java | 11 ++++++++-- .../src/main/java/brooklyn/util/time/Time.java | 23 +++++++++++--------- .../test/java/brooklyn/util/time/TimeTest.java | 1 + 3 files changed, 23 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c9682116/core/src/main/java/brooklyn/util/flags/TypeCoercions.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java index dcc00e8..67011b2 100644 --- a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java +++ b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java @@ -106,13 +106,20 @@ public class TypeCoercions { * <li>{@code value.targetTypeValue()} (handy for primitives) * <li>{@code TargetType.valueOf(value)} (for enums) * </ul> - * - * @see #coerce(Object, TypeToken) + * <p> + * A default set of adapters will handle most common Java-type coercions + * as well as <code>String</code> coercion to: + * <ul> + * <li> {@link Set}, {@link List}, {@link Map} and similar -- parses as YAML + * <li> {@link Date} -- parses using {@link Time#parseDate(String)} + * <li> {@link Duration} -- parses using {@link Duration#parse(String)} + * </ul> */ public static <T> T coerce(Object value, Class<T> targetType) { return coerce(value, TypeToken.of(targetType)); } + /** @see #coerce(Object, Class) */ public static <T> Maybe<T> tryCoerce(Object value, TypeToken<T> targetTypeToken) { try { return Maybe.of( coerce(value, targetTypeToken) ); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c9682116/utils/common/src/main/java/brooklyn/util/time/Time.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/brooklyn/util/time/Time.java b/utils/common/src/main/java/brooklyn/util/time/Time.java index ea433a1..1c302d6 100644 --- a/utils/common/src/main/java/brooklyn/util/time/Time.java +++ b/utils/common/src/main/java/brooklyn/util/time/Time.java @@ -472,7 +472,10 @@ public class Time { } } - /** parses dates from string, accepting many formats including 'YYYY-MM-DD', 'YYYY-MM-DD HH:mm:SS', or millis since UTC epoch */ + /** Parses dates from string, accepting many formats including ISO-8601 and http://yaml.org/type/timestamp.html, + * e.g. 2015-06-15 16:00:00 +0000. Millis since eopch UTC is also supported. + * Other formats including locale-specific variants, e.g. recognising month names, + * are supported but this may vary from platform to platform and may change between versions. */ public static Date parseDate(String input) { if (input==null) return null; return parseDateMaybe(input).get(); @@ -639,12 +642,12 @@ public class Time { // patterns with date first String[] DATE_PATTERNS_UNCLOSED = new String[] { // separator before time *required* if date had separators - DATE_ONLY_WITH_INNER_SEPARATORS + "("+getDateTimeSeparatorPattern("T"), + DATE_ONLY_WITH_INNER_SEPARATORS + "("+getDateTimeSeparatorPattern("Tt"), // separator before time optional if date did not have separators - DATE_ONLY_NO_SEPARATORS + "("+optionally(getDateTimeSeparatorPattern("T")), + DATE_ONLY_NO_SEPARATORS + "("+optionally(getDateTimeSeparatorPattern("Tt")), // separator before time required if date has words - DATE_WORDS_2 + "("+getDateTimeSeparatorPattern("T"), - DATE_WORDS_3 + "("+getDateTimeSeparatorPattern("T"), + DATE_WORDS_2 + "("+getDateTimeSeparatorPattern("Tt"), + DATE_WORDS_3 + "("+getDateTimeSeparatorPattern("Tt"), }; for (String tzP: TZ_PATTERNS) for (String dateP: DATE_PATTERNS_UNCLOSED) @@ -684,7 +687,7 @@ public class Time { try { month = new SimpleDateFormat("yyyy-MMM-dd").parse("2015-"+monthS+"-15").getMonth(); } catch (ParseException e) { - return Maybe.absent("Unknown date format '"+input+"': invalid month '"+monthS+"'; try 'yyyy-MM-dd HH:mm:ss.SSS +0000'"); + return Maybe.absent("Unknown date format '"+input+"': invalid month '"+monthS+"'; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); } } @@ -703,7 +706,7 @@ public class Time { if (tzz==null) { Maybe<Matcher> tmm = match("^ ?(?<tzH>(\\+|\\-||)"+DIGIT+optionally(DIGIT)+")"+optionally(optionally(":")+namedGroup("tzM", DIGIT+DIGIT))+"$", tz); if (tmm.isAbsent()) { - return Maybe.absent("Unknown date format '"+input+"': invalid timezone '"+tz+"'; try 'yyyy-MM-dd HH:mm:ss.SSS +0000'"); + return Maybe.absent("Unknown date format '"+input+"': invalid timezone '"+tz+"'; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); } Matcher tm = tmm.get(); String tzM = tm.group("tzM"); @@ -725,7 +728,7 @@ public class Time { String meridian = m.group("meridian"); if (Strings.isNonBlank(meridian) && meridian.toLowerCase().startsWith("p")) { if (hours>12) { - return Maybe.absent("Unknown date format '"+input+"': can't be "+hours+" PM; try 'yyyy-MM-dd HH:mm:ss.SSS +0000'"); + return Maybe.absent("Unknown date format '"+input+"': can't be "+hours+" PM; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); } hours += 12; } @@ -747,7 +750,7 @@ public class Time { // allow ssSSS with no punctuation s = s/=1000; } else { - return Maybe.absent("Unknown date format '"+input+"': invalid seconds '"+secsS+"'; try 'YYYY-MM-DD HH:mm:ss.SSS +0000'"); + return Maybe.absent("Unknown date format '"+input+"': invalid seconds '"+secsS+"'; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); } result.set(Calendar.SECOND, (int)s); result.set(Calendar.MILLISECOND, (int)((s*1000) % 1000)); @@ -756,7 +759,7 @@ public class Time { return Maybe.of(result.getTime()); } - return Maybe.absent("Unknown date format '"+input+"'; try ISO-8601, or 'yyyy-MM-dd' or 'yyyy-MM-dd HH:mm:ss +0000'"); + return Maybe.absent("Unknown date format '"+input+"'; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); } public static TimeZone getTimeZone(String code) { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c9682116/utils/common/src/test/java/brooklyn/util/time/TimeTest.java ---------------------------------------------------------------------- diff --git a/utils/common/src/test/java/brooklyn/util/time/TimeTest.java b/utils/common/src/test/java/brooklyn/util/time/TimeTest.java index 774217e..8ad9caa 100644 --- a/utils/common/src/test/java/brooklyn/util/time/TimeTest.java +++ b/utils/common/src/test/java/brooklyn/util/time/TimeTest.java @@ -244,6 +244,7 @@ public class TimeTest { assertDatesParseToEqual("20150604T080012.345", "2015-06-04-080012.345"); assertDatesParseToEqual("20150604T080012.345Z", "2015-06-04-080012.345+0000"); + assertDatesParseToEqual("20150604t080012.345 Z", "2015-06-04-080012.345+0000"); // accept am and pm assertDatesParseToEqual("20150604 08:00:12.345a", "2015-06-04-080012.345");
