Java 8 DateTimeFormatter with optional part - time

I have a String representing a date (with or without time) like 13/12/2017 or 13/12/2017 15:39:51
So i'm trying to use java 8 DateTimeFormatter with optional part.
That code works
LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017 15:39:51",DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]"));
System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
13/12/2017
15:39:51
But I don't understand why that one does not
LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017",DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]"));
giving me
Exception in thread "main" java.time.format.DateTimeParseException: Text '13/12/2017' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2017-12-13 of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
...
And even with
LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017",DateTimeFormatter.ofPattern("dd/MM/yyyy"));
it does not work with the same exception.

Use parseBest
When you use an optional component, you should parse using parseBest. Your application may be working using only parse, but then it's only by luck (because you're only parsing full inputs, not partial ones). With parseBest, you can properly handle various TemporalAccessor, which is the whole reason to use optional.
The decision of which TemporalAccessor is returned is rather simple: parseBest will try to match each TemporalQuery in order of argument. When any matches, the method returns that one. So make sure to go from most precise to less precise. Also, if none were matched, an exception will be thrown.
LocalDateTime dateTime;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]");
TemporalAccessor temporalAccessor = formatter.parseBest("13/12/2017", LocalDateTime::from, LocalDate::from);
if (temporalAccessor instanceof LocalDateTime) {
dateTime = (LocalDateTime)temporalAccessor;
} else {
dateTime = ((LocalDate)temporalAccessor).atStartOfDay();
}

Related

NSLocalisation of String

the following localisation string is not working.
this works fine if i use #%i Sides, as the system seems to ignore the #%i, but if i change to #%li, then the localisation string is not found.
i only make the change to silence a warning in Xcode asking me to use a cast to long.
any ideas anyone
thanks
this works but i get a warning
Values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead
numberOfSidesLabel.text = [NSString stringWithFormat:NSLocalizedString(#"%i Sides",%i Sides),polygon.numberOfSides];
this gets rid of the warning but the localisation string fails
numberOfSidesLabel.text = [NSString stringWithFormat:NSLocalizedString(#"%li Sides",%li Sides),(long)polygon.numberOfSides];
this is my localisation string :
/* Class = "IBUILabel"; text = "Sides"; ObjectID = "oYY-ih-zqz"; */
"oYY-ih-zqz.text" = "Sides";

Forcing Java 8 LocalTime toString to report omitted values

I have the following datetime helper method that converts a UTC-zoned Java 8 Date into a datetime string:
public static String dateTimeString(Date date) {
return date.toInstant().atZone(ZoneId.of("UTC")).toLocalDateTime().toString();
}
The desired result is to always have the resultant String be formatted as:
YYYY-MM-dd'T'HH:mm:ss'Z'
Problem is, Java 8 LocalTime#toString() intentionally strips off time components that are zero. So for instance if I have a Date instance that represents June 8, 2018 at 12:35:00 UTC. Then the output of this method above is: 2018-06-08'T'12:35'Z'. Whereas I want it to contain any zeroed-out second/minute/hour components (e.g. 2018-06-08'T'12:35:00'Z').
Any ideas?
private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX");
public static String dateTimeString(Date date) {
return date.toInstant().atOffset(ZoneOffset.UTC).format(formatter);
}
Just use a fixed format pattern string to get your desired format. Let’s try it:
System.out.println(dateTimeString(new Date(0)));
System.out.println(dateTimeString(new Date(1_524_560_255_555L)));
This prints:
1970-01-01T00:00:00Z
2018-04-24T08:57:35Z
In the first example hours, minutes and seconds are printed even if they are 0.
In the second example milliseoncds are omitted even when they are non-zero (you see that the milliseconds value I specified ends in 555).
All of this said, the output conforms to the ISO 8601 format no matter if you have 2018-06-08T12:35Z, 2018-06-08T12:35:00Z or even 2018-06-08T12:35:00.000000000Z. So you may want to check once more whether leaving out the second works for your purpose before you take the trouble of defining your own formatter.
Link: Wikipedia article: ISO 8601
I don't see why you portray the default output to have its letters enclosed in single quotes when Java's actual output instead looks like 2018-06-08T12:35Z, but anyway, here's the code that produces it as desired, with no omissions:
final LocalDateTime ldt = LocalDateTime.of(2018, 6, 8, 12, 35, 0);
final ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.of("Z"));
final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'''T'''HH:mm:ss''X''", Locale.US);
System.err.println(dtf.format(zdt));
Output:
2018-06-08'T'12:35:00'Z'
Personally, I'd probably prefer a format like this, containing the millis, giving time zone information not requiring additional knowledge from the user, and not having imho superfluous characters:
FORMAT: "yyyy-MM-dd HH:mm:ss'.'SSS Z"
OUTPUT: 2018-06-08 12:35:12.345 +0200
It appears that there is no out of the box solution. The simplest way would be to write your own class that extends DateTimeFormatter and override method public String format(TemporalAccessor temporal) where you would call the original method and then if needed modify the output String. See the flags 'Z' for formatter. If that flag is present then you will need to modify the original output.

Why does Java 8 DateTimeFormatter allows an incorrect month value in ResolverStyle.STRICT mode?

Why does this test pass, while the month value is obviously invalid (13)?
#Test
public void test() {
String format = "uuuuMM";
String value = "201713";
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value);
}
When using a temporal query, the expected DateTimeParseException is thrown:
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value, YearMonth::from);
What happens when no TemporalQuery is specified?
EDIT: the 13 value seems to be a special one, as I learned thanks to the answer of ΦXocę 웃 Пepeúpa ツ (see Undecimber).
But the exception is not thrown even with another value, like 50:
#Test
public void test() {
String format = "uuuuMM";
String value = "201750";
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value);
}
I've made some debugging here and found that part of the parsing process is to check the fields against the formatter's chronology.
When you create a DateTimeFormatter, by default it uses an IsoChronology, which is used to resolve the date fields. During this resolving phase, the method java.time.chrono.AbstractChronology::resolveDate is called.
If you look at the source, you'll see the following logic:
if (fieldValues.containsKey(YEAR)) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
if (fieldValues.containsKey(DAY_OF_MONTH)) {
return resolveYMD(fieldValues, resolverStyle);
}
....
return null;
As the input has only the year and month fields, fieldValues.containsKey(DAY_OF_MONTH) returns false, the method returns null and no other check is made as you can see in the Parsed class.
So, when parsing 201750 or 201713 without a TemporalQuery, no additional check is made because of the logic above, and the parse method returns a java.time.format.Parsed object, as you can see by the following code:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuuMM").withResolverStyle(ResolverStyle.STRICT);
TemporalAccessor parsed = fmt.parse("201750");
System.out.println(parsed.getClass());
System.out.println(parsed);
The output is:
class java.time.format.Parsed
{Year=2017, MonthOfYear=50},ISO
Note that the type of the returned object is java.time.format.Parsed and printing it shows the fields that were parsed (year and month).
When you call parse with a TemporalQuery, though, the Parsed object is passed to the query and its fields are validated (of course it depends on the query, but the API built-in ones always validate).
In the case of YearMonth::from, it checks if the year and month are valid using the respective ChronoField's (MONTH_OF_YEAR and YEAR) and the month field accepts only values from 1 to 12.
That's why just calling parse(value) doesn't throw an exception, but calling with a TemporalQuery does.
Just to check the logic above when all the date fields (year, month and day) are present:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuuMMdd").withResolverStyle(ResolverStyle.STRICT);
fmt.parse("20175010");
This throws:
Exception in thread "main" java.time.format.DateTimeParseException: Text '20175010' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 50
As all the date fields are present, fieldValues.containsKey(DAY_OF_MONTH) returns true and now it checks if it's a valid date (using the resolveYMD method).
The month 13 is called : Undecimber
The gregorian calendar that many of us use allows 12 months only but java includes support for calendars which permit thirteen months so it depends on what calendar system you are talking about
For example, the actual maximum value of the MONTH field is 12 in some years, and 13 in other years in the Hebrew calendar system. So the month 13 is valid
It is a little odd that an exception is not thrown when parse is called without a given TemporalQuery. Some of the documentation for the single argument parse method:
This parses the entire text producing a temporal object. It is typically more useful to use parse(CharSequence, TemporalQuery). The result of this method is TemporalAccessor which has been resolved, applying basic validation checks to help ensure a valid date-time.
Note that it says it is "typically more useful to use parse(CharSequence, TemporalQuery)". In your examples, parse is returning a java.time.format.Parsed object, which is not really used for anything other than creating a different TemporalAccessor.
Note that if you try to create a YearMonth from the returned value, an exception is thrown:
YearMonth.from(DateTimeFormatter.ofPattern(format)
.withResolverStyle(ResolverStyle.STRICT).parse(value));
throws
Exception in thread "main" java.time.DateTimeException: Unable to obtain YearMonth from TemporalAccessor: {Year=2017, MonthOfYear=50},ISO of type java.time.format.Parsed
at java.time.YearMonth.from(YearMonth.java:263)
at anl.nfolds.Test.main(Test.java:21)
Caused by: java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 50
at java.time.temporal.TemporalAccessor.get(TemporalAccessor.java:224)
at java.time.YearMonth.from(YearMonth.java:260)
... 1 more
Documentation for Parsed:
A store of parsed data.
This class is used during parsing to collect the data. Part of the parsing process involves handling optional blocks and multiple copies of the data get created to support the necessary backtracking.
Once parsing is completed, this class can be used as the resultant TemporalAccessor. In most cases, it is only exposed once the fields have been resolved.
Since:1.8
#implSpecThis class is a mutable context intended for use from a single thread. Usage of the class is thread-safe within standard parsing as a new instance of this class is automatically created for each parse and parsing is single-threaded

spring validator #Pattern doesn't work on Integer

I am trying a simple pattern validation:
#Min(value = 0, message = "invalid.amount")
#Pattern(regexp = "[0-9]+", message = "invalid.amount")
private double amount;
But it gives the error like:
org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.throwExceptionForNullValidator(ConstraintTree.java:229)
But this works perfectly fine:
#Pattern(regexp = "[\\w|-]{1,5}")
private String data;
It seems regex doesn't work on number types.
You are assuming that the #Pattern annotation will return true if a substring regex match passes. If it isn't working then your assumption may not be true.
#Pattern(regex=,flag=) CharSequence
Checks if the annotated string matches the regular expression regex
considering the given flag match.
You can try change double to String and works with #Pattern normally.
#Pattern(regexp = "[0-9]+", message = "invalid.amount")
private String amount;
Also other way could be:
#DecimalMax("10.0") #DecimalMin("0.0")
private double amount;

Java8 LocalDateTime to XMLGregorianCalender Remove "+05:30" Portion

Did like below,
LocalDateTime currentUTCTime = LocalDateTime.now(ZoneId.of("UTC"));
String reqPattern = currentUTCTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS"));
System.out.println("Required pattern: " + reqPattern);
GregorianCalendar calendar = GregorianCalendar.from(currentUTCTime.atZone(ZoneId.systemDefault()));
XMLGregorianCalendar xcal = DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);
System.out.println("But Showing As :" + xcal);
I want the output as 2015-06-18 11:59:15:135, but when i set the xcal to
a XML tag which takes XMLGregorianCalender, it shows like 2015-06-18T11:59:15.135+05:30.
How can i remove the +05:30 portion?
Use this code:
LocalDateTime currentUTCTime = LocalDateTime.now(); // using system timezone
String iso = currentUTCTime.toString();
if (currentUTCTime.getSecond() == 0 && currentUTCTime.getNano() == 0) {
iso += ":00"; // necessary hack because the second part is not optional in XML
}
XMLGregorianCalendar xml =
DatatypeFactory.newInstance().newXMLGregorianCalendar(iso‌​);
Explanation:
The code makes use of the given factory method expecting a lexicographical representation of a local timestamp in ISO-8601-format. And since a LocalDateTime does not refer to any timezone, its output via toString() cannot contain a timezone offset. Result: XMLGregorianCalendar considers the timezone offset as "not set".
Correction:
The original code did not especially bother about the ISO-variant of formatted output of currentUTCTime.toString(). However, the java.time-API produces an output without seconds or nanoseconds if those parts are equal to zero. This is perfectly legal in ISO, but the W3C-consortium has made the second part non-optional. And the class XMLGregorianCalendar closely follows this deviating spec. Therefore the shown hack above using simple string concatenation in this special edge case. Thanks a lot to #Dave's comment. By the way, using currentUTCTime.format(DateTimeFormatter.ISO_DATE_TIME) as suggested in this comment is also possible (instead of the shown hack).

Resources