Java.util auto correcting invalid date - date

Java.util.date package is auto correcting date. For ex: if we pass date as "2018-02-35", it automatically changes it to "2018-03-07", which is a valid date.
Basically, the requirement is to validate the user entered date but as the date is getting auto corrected the module was never able to find an incorrect date. (Note: UI validation can't be done due to some special restrictions so the validation has to be done by the middleware system).
Is there a way i can handle this with the same util package or can this be handled through any 3rd party jar? pls advise

Even i faced same issue. But after some research i found that there is method (setLenient()) in the DateFormat class to disable this behaviour.
DateFormat df = new SimpleDateFormat(DATE_FORMAT);
df.setLenient(false);
Java Docs:
Specify whether or not date/time parsing is to be lenient. With lenient parsing, the parser may use heuristics to interpret inputs that do not precisely match this object's format. With strict parsing, inputs must match this object's format.

you can write DateDeserializer
public class DateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", getLocale());
format.setLenient(false); // if true, will auto correct the date
// to the next possible valid date
String date = jp.getText();
try {
return format.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
private Locale getLocale() {
Locale locale = (LocaleContextHolder.getLocale() != null) ? LocaleContextHolder.getLocale() : Locale.getDefault();
return locale;
}
}
And then annotate your date property in the media class with this DateDeserializer. You can also use dateSerializer to serialize the object back to Json format. Example below
#JsonSerialize(using=DateSerializer.class)
#JsonDeserialize(using=DateDeserializer.class)
private Date startDate;

Related

java.time.LocalDate not supported in native queries by latest Spring Data/Hibernate?

Problem: Native queries with Spring Data returning dates return java.sql.Date not java.time.LocalDate, despite the setup.
Context: A new project with Spring Boot 2.0.0.M5 (the latest), Hibernate 5.2.11, Hibernate-Java8 5.2.12 (which gives support for JSR310 classes as long as it's on the classpath).
Anonymized example below (the app is not really about birthdays):
public interface BirthdayRepository<T, ID extends Serializable> extends Repository<T, ID> {
#Query(value = "select day from birthdays", nativeQuery = true)
Iterable<java.sql.Date> getBirthdays(); //the return type should ideally be java.time.LocalDate
}
In the database (SQL Server), the day field is DATE and values are like 2017-10-24.
The problem is that at runtime, the Spring Data repository (whose implementation I cannot control, or is there a way?) returns java.sql.Date not java.time.LocalDate (Clarification: the return type appears to be decided by Spring Data and remains java.sql.Date even if I change the return type to be java.time.LocalDate, which is how I started to).
Isn't there a way to get LocalDate directly? I can convert it later, but (1) that's inefficient and (2) the repository methods have to return the old date/time classes, which is something I'd like to avoid. I read the Spring Data documentation, but there's nothing about this.
EDIT: for anyone having the same question, below is the solution, the converter suggested by Jens.
public class LocalDateTypeConverter {
#Converter(autoApply = true)
public static class LocalDateConverter implements AttributeConverter<LocalDate, Date> {
#Nullable
#Override
public Date convertToDatabaseColumn(LocalDate date) {
return date == null ? null : new Date(LocalDateToDateConverter.INSTANCE.convert(date).getTime());
}
#Nullable
#Override
public LocalDate convertToEntityAttribute(Date date) {
return date == null ? null : DateToLocalDateConverter.INSTANCE.convert(date);
}
}
It looks like you found a gap in the converters. Spring Data converts out of the box between java.util.Date and java.time.LocalDate but not between java.time.LocalDate and java.sql.Date and other date and time-related types in the java.sql package.
You can create your own converter to do that. You can use Jsr310JpaConverters as a template.
Also, you might want to create a feature request and if you build a converter for your use, you might even submit a pull request.
I know this is an older question, but my solution to this problem does not require a custom converter.
public interface BirthdayRepository<T, ID extends Serializable> extends Repository<T, ID> {
#Query(value = "select cast(day as date) from birthdays", nativeQuery = true)
Iterable<java.time.LocalDate> getBirthdays();
}
The CAST tells JPQL to use available java date\time types rather than java.sql.Date

Swagger Model Schema Response: alternate label for LocalDate in SpringFox

We are using Swagger 2.x and SpringFox 2.0 to document our REST service created with Spring MVC.
We have a REST response with a property List<LocalDate> dates.
In the Model Schema of the response, the label for dates is shown as 'LocalDate'. That is not intended: we would like to have 'date' or 'yyyy-MM-dd' instead.
We have this class:
public class SayHelloResponse {
private List<LocalDate> dates;
private String message;
public SayHelloResponse(String message, LocalDate... dates) {
this.message = message;
this.dates = ImmutableList.copyOf(dates);
}
public List<LocalDate> getDates() {
return dates;
}
public String getMessage() {
return message;
}
}
That results in this Model Schema:
{
"dates": [
"LocalDate"
],
"message": "string"
}
In the Model Schema, I would like to have LocalDate as 'date' or 'yyyy-MM-dd'. The way to do this seems to be with com.wordnik.swagger.annotations.ApiModelProperty but this does not have any effect (it is being picked up, as when I add #ApiModelProperty(hidden=true) it is hidden).
I created a sample rest project that shows the issue.
Any ideas how I can change LocalDate to 'date' or 'yyyy-MM-dd' in the Model Schema of Swagger?
There is a method in Docket object to replace models called directModelSubstitute(). You can use it like this to substitute LocalDate to Date object:
Docket#directModelSubstitute(LocalDate.class, Date.class)
The only problem with it that I found is that you can't change the date format.
See A/Q section in the official Springfox documentation, specifically question "How do we use Java 8 types esply. LocalDateTime?"
This is recommended in the official Springfox documentation, but doesn't effect:
Docket(DocumentationType.SWAGGER_2)..build().directModelSubstitute(LocalDate.class, java.sql.Date.class)
This effect but change format to date-time instead of date:
Docket(DocumentationType.SWAGGER_2)..build().directModelSubstitute(LocalDate.class, java.util.Date.class);
That's why I use the last one and ignore time part.

How to convert java.util.Date to Java8 java.time.YearMonth

How can I best convert a java.util.Date to a Java 8 java.time.YearMonth?
Unfortunately the following throws a DateTimeException:
YearMonth yearMonth = YearMonth.from(date.toInstant());
results in:
java.time.DateTimeException: Unable to obtain YearMonth from TemporalAccessor: 2015-01-08T14:28:39.183Z of type java.time.Instant
at java.time.YearMonth.from(YearMonth.java:264)
...
I need this functionality since I want to store YearMonth values in a database using JPA. Currently JPA does not support YearMonth's, so I've come up with the following YearMonthConverter (imports omitted):
// TODO (future): delete when next version of JPA (i.e. Java 9?) supports YearMonth. See https://java.net/jira/browse/JPA_SPEC-63
#Converter(autoApply = true)
public class YearMonthConverter implements AttributeConverter<YearMonth, Date> {
#Override
public Date convertToDatabaseColumn(YearMonth attribute) {
// uses default zone since in the end only dates are needed
return attribute == null ? null : Date.from(attribute.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
}
#Override
public YearMonth convertToEntityAttribute(Date dbData) {
// TODO: check if Date -> YearMonth can't be done in a better way
if (dbData == null) return null;
Calendar calendar = Calendar.getInstance();
calendar.setTime(dbData);
return YearMonth.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
}
}
Isn't there a better (cleaner, shorter) solution (for both directions)?
Short answer:
// From Date to YearMonth
YearMonth yearMonth =
YearMonth.from(date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate());
// From YearMonth to Date
// The same as the OP:s answer
final Date convertedFromYearMonth =
Date.from(yearMonth.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
Explanation:
The JavaDoc of the YearMonth.from(TemporalAccessor)-method says:
The conversion extracts the YEAR and MONTH_OF_YEAR fields. The extraction is only permitted if the temporal object has an ISO chronology, or can be converted to a LocalDate.
So, you need to either be able to:
extract the YEAR and MONTH_OF_YEAR fields, or
you should use something that can be converted to a LocalDate.
Lets try it!
final Date date = new Date();
final Instant instant = date.toInstant();
instant.get(ChronoField.YEAR); // causes an error
This is not possible, an exception is thrown:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: Year
at java.time.Instant.get(Instant.java:571)
...
This means that alternative 1 goes out the window. The reason for is explained in this excellent answer about how to convert Date to LocalDate.
Despite its name, java.util.Date represents an instant on the time-line, not a "date". The actual data stored within the object is a long count of milliseconds since 1970-01-01T00:00Z (midnight at the start of 1970 GMT/UTC).
The equivalent class to java.util.Date in JSR-310 is Instant, thus there is a convenient method toInstant() to provide the conversion.
So, a Date can be converted to an Instant but that did not help us, did it?
Alternative 2 however proves to be successful. Convert the Instant to a LocalDate and then use the YearMonth.from(TemporalAccessor)-method.
Date date = new Date();
LocalDate localDate = date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
YearMonth yearMonth = YearMonth.from(localDate);
System.out.println("YearMonth: " + yearMonth);
The output is (since the code was executed in January 2015 ;):
YearMonth: 2015-01

Play 2.0 date format

I'm trying to format a date in a Scala template in Play. So far I've written this:
<p>#DateFormat.getInstance().format(deadline)</p>
Where deadline is the date I'm outputting to the web page. However, this uses the JVM's locale and not the one selected by the user.
My app currently supports two locales, Norwegian (no) and English (en). This works well for messages, but not for Dates. So I tried adding a GlobalSettings to intercept each request as shown below, but apparently it's never invoked:
import java.lang.reflect.Method;
import java.util.Locale;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import play.GlobalSettings;
import play.i18n.Lang;
import play.mvc.Action;
import play.mvc.Http.Request;
public class Global extends GlobalSettings {
#SuppressWarnings("rawtypes")
#Override
public Action onRequest(final Request request, final Method actionMethod) {
LocaleContextHolder.setLocaleContext(new LocaleContext() {
public Locale getLocale() {
Lang preferred = Lang.preferred(request.acceptLanguages());
return preferred.toLocale();
}
});
return super.onRequest(request, actionMethod);
}
}
Does someone have a solution to this problem? Is it a known bug in Play? I'm using version 2.0.4.
Thanks!
I tried estmatic's solution, but it didn't discriminate properly between country variants of the same language, for example if my browser's preferred languages were "en_AU" and "en_US" in that order, then it would only use the "en" part, which resulted in a US-style date (with the month first) rather than an Aussie-style one (with the date first, as is right and proper).
My solution was to create a helper class as follows:
public class Formatter extends Controller {
private static final int DATE_STYLE = LONG;
private static final int TIME_STYLE = SHORT;
/**
* Formats the given Date as a date and time, using the locale of the current
* request's first accepted language.
*
* #param date the date to format (required)
* #return the formatted date
* #see play.mvc.Http.Request#acceptLanguages()
*/
public static String formatDateTime(final Date date) {
final Locale locale = getPreferredLocale();
return DateFormat.getDateTimeInstance(
DATE_STYLE, TIME_STYLE, locale).format(date);
}
private static Locale getPreferredLocale() {
final List<Lang> acceptedLanguages = request().acceptLanguages();
final Lang preferredLanguage = acceptedLanguages.isEmpty() ?
Lang.preferred(acceptedLanguages) : acceptedLanguages.get(0);
return new Locale(preferredLanguage.language(), preferredLanguage.country());
}
}
Then in my Scala templates, all I had to do was use (for example):
#import my.package.Formatter
...
Date = #Formatter.formatDateTime(someDate)
This seems cleaner to me than having a lot of Locale construction logic in the templates.
Well you need to provide the locale when you get your DateFormat instance; otherwise it'll just use the system default locale instead of what Play is getting from the browser.
Something like this seems to work:
#DateFormat.getDateInstance(DateFormat.LONG, (implicitly[Lang]).toLocale).format(deadline)
That implicitly[Lang] bit is basically calling Lang.preferred(request.acceptLanguages() just like you were doing in your onRequest() method.

How to handle optional query parameters in Play framework

Lets say I have an already functioning Play 2.0 framework based application in Scala that serves a URL such as:
http://localhost:9000/birthdays
which responds with a listing of all known birthdays
I now want to enhance this by adding the ability to restrict results with optional "from" (date) and "to" request params such as
http://localhost:9000/birthdays?from=20120131&to=20120229
(dates here interpreted as yyyyMMdd)
My question is how to handle the request param binding and interpretation in Play 2.0 with Scala, especially given that both of these params should be optional.
Should these parameters be somehow expressed in the "routes" specification? Alternatively, should the responding Controller method pick apart the params from the request object somehow? Is there another way to do this?
Encode your optional parameters as Option[String] (or Option[java.util.Date], but you’ll have to implement your own QueryStringBindable[Date]):
def birthdays(from: Option[String], to: Option[String]) = Action {
// …
}
And declare the following route:
GET /birthday controllers.Application.birthday(from: Option[String], to: Option[String])
A maybe less clean way of doing this for java users is setting defaults:
GET /users controllers.Application.users(max:java.lang.Integer ?= 50, page:java.lang.Integer ?= 0)
And in the controller
public static Result users(Integer max, Integer page) {...}
One more problem, you'll have to repeat the defaults whenever you link to your page in the template
#routes.Application.users(max = 50, page = 0)
In Addition to Julien's answer. If you don't want to include it in the routes file.
You can get this attribute in the controller method using RequestHeader
String from = request().getQueryString("from");
String to = request().getQueryString("to");
This will give you the desired request parameters, plus keep your routes file clean.
Here's Julien's example rewritten in java, using F.Option: (works as of play 2.1)
import play.libs.F.Option;
public static Result birthdays(Option<String> from, Option<String> to) {
// …
}
Route:
GET /birthday controllers.Application.birthday(from: play.libs.F.Option[String], to: play.libs.F.Option[String])
You can also just pick arbitrary query parameters out as strings (you have to do the type conversion yourself):
public static Result birthdays(Option<String> from, Option<String> to) {
String blarg = request().getQueryString("blarg"); // null if not in URL
// …
}
For optional Query parameters, you can do it this way
In routes file, declare API
GET /birthdays controllers.Application.method(from: Long, to: Long)
You can also give some default value, in case API doesn't contain these query params it will automatically assign the default values to these params
GET /birthdays controllers.Application.method(from: Long ?= 0, to: Long ?= 10)
In method written inside controller Application these params will have value null if no default values assigned else default values.
My way of doing this involves using a custom QueryStringBindable. This way I express parameters in routes as:
GET /birthdays/ controllers.Birthdays.getBirthdays(period: util.Period)
The code for Period looks like this.
public class Period implements QueryStringBindable<Period> {
public static final String PATTERN = "dd.MM.yyyy";
public Date start;
public Date end;
#Override
public F.Option<Period> bind(String key, Map<String, String[]> data) {
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
try {
start = data.containsKey("startDate")?sdf.parse(data.get("startDate") [0]):null;
end = data.containsKey("endDate")?sdf.parse(data.get("endDate")[0]):null;
} catch (ParseException ignored) {
return F.Option.None();
}
return F.Option.Some(this);
}
#Override
public String unbind(String key) {
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
return "startDate=" + sdf.format(start) + "&" + "endDate=" + sdf.format(end);
}
#Override
public String javascriptUnbind() {
return null;
}
public void applyDateFilter(ExpressionList el) {
if (this.start != null)
el.ge("eventDate", this.start);
if (this.end != null)
el.le("eventDate", new DateTime(this.end.getTime()).plusDays(1).toDate());
}
}
applyDateFilter is just a convienence method i use in my controllers if I want to apply date filtering to the query. Obviously you could use other date defaults here, or use some other default than null for start and end date in the bind method.