Full date Validation in Java - java

I would like to make a validation for date. the date has many formats:
European and American styles. Also, number, shortcut or even the month full name.
I used Date.parse() but it's not accurate and it has many issues. For example: it doesn't pay attention to the leap year. Also, I added "35/02/2008" without giving me any exception.
I tried regular expression but all of what I found has an issue.
None of them cover the whole possibilities.
Please advise!

You should have a look at the Joda Time library: http://www.joda.org/joda-time/userguide.html#Input_and_Output
For example, you can create a DateTimeFormatter, and parse some text using it:
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMdd");
DateTime dt = fmt.parseDateTime(strInputDateTime);
The library allows for constructing complex date time patterns and it has knowledge of leap years etc.

Date validation does not make sense unless you are validating against a specific date format, or (carefully chosen) set of formats. To illustrate, consider this:
23/11/77
Is that a valid date? It depends!
If you are validating against "dd/MM/YY", then yes it is valid.
If you are validating against "MM/dd/YY", then no it isn't.
If you are validating against "dd/MM/YYYY", then it is valid but it doesn't mean what you think it means.
In short validating "any date" irrespective of format make no sense. The above date could be either valid or invalid, and can mean different things.
The fact that people "know" what these dates mean is worrying. In fact, when most people see a date, they interpret according to their own cultural norms ... without realizing that most dates are ambiguous if the format is not specified / agreed.

If you don't want (or aren't allowed) to use third-party libraries and prefer having manual control over validations, split the string using "/" as separators, then making conditions for all needed cases:
String pattern = "yyyy/MM/dd";
DateFormat df = new SimpleDateFormat(pattern);
String strDate = df.format(date);
String[] arrDate = strDate.split("/");
int year = Integer.parseInt(arrDate[0]);
int month = Integer.parseInt(arrDate[1]);
int day = Integer.parseInt(arrDate[2]);
if (month == 2 && day > 28) {
System.out.println("February has 28 days!");
}
// etc ...

If you are using java 8 then you dont need joda. There is an in-build API LocalDateTime similarly for UTC also there is an API.

The issue that the input may come with different format as soon as my app is an analysis tool.
If I wanna do it manually by splitting the date, I have to create a full class in order to check all the possibilities in all different formats.
There's no full regular expression for this issue. How come?

Related

Java, date from string without pattern

I need to get a Date instance from input file. I don't know the date format, but I want to get it from user profile settings.
Te following code does not working:
DateFormat form = DateFormat.getDa​teInstance(DateF​ormat.SHORT, Locale.getDefaul​t());
try {
Date t = form.parse("6/6/2015");
}
unparseable date error
I want to know if there is any way to get date from string without knowing the date string pattern.
I need this date to create MySQL query. Maybe there is another way to build this query without parsing date? I am using Entity Beans.
No. Consider the date "1/2/2015": is that February 1st or January 2nd. Depends on your locale.
Instead, you should be more specific: rather than getting a date formatter for your locale, use SimpleDateFormat with an explicit pattern.
I want to know if there is any way to get data from string without knowing the data string pattern.
Without any more information, this is very error prone. For example, consider "7/6/2015" - does that mean June 7th, or July 6th?
If you know the user's locale, you can do a lot better - for example, you could obtain DateFormat instances for long, medium, short and full date patterns for that locale, and try them one at a time. Bear in mind, however, that depending on where this code is executing, the default locale (as you're using at the moment) may not be the user's locale. You mention the user profile settings - hopefully that already contains a locale.
One alternative is to ask the user to tell you what the format is - maybe provide lots of different examples, and let them pick the one that matches.
Finally, if the file has lots of dates in and you're confident they'll all be in the same format, you could try to parse all of them in each of several different formats - that's likely to reduce the chances of error, as "7/6/2015" becomes unambigious if you've also seen "13/1/2015" for example.

How to present a date that can be parsed by any DateTimeFormatter pattern in Java?

I'm working with a software that uses a lot of DateTimeFormat parsing, in order to minimize the errors, I wonder if I can present the date String in a certain way that it can be parsed by any DateTimeFormat pattern. Ideally it should work as follows:
String date = "...."
DateTimeFormatter format = DateTimeFormat.forPattern(any pattern I want);
DateTime result = format.parseDateTime(date);
Or does the date have to follow the pattern? Thanks for your help
No, you can not get one size fits all. Think if your string is not a legal date at all, something like "hello", how are you going to parse it?
java.time
Java 8 and later includes the java.time framework (Tutorial). The java.time formatter’s pattern may contain []to mark optional parts. This gives you some flexibility. Say you use format:
M[M]['/']['-']['.']d[d]['/']['-']['.']yyyy[' ']['T'][' ']h[h]:mm:ss
So in this case your string may have one or two digits specifying month, day and hour. Month, day and year may be separated by ., - or / and so forth. For example with format above the following strings will be parsed successfully:
1/10/1995 9:34:45
01-10-1995 09:34:45
01.10.1995T09:34:45
…and so forth.
I wrote a utility that has a set of patterns. Once it gets a String it tries to parse it with all the patterns in the set and sees if it succeeds with one of them. If you write such a set of patterns correctly you may ensure that your util supports any possible String that denotes a valid date.
SimpleDateFromat let you set your own date patters. for example dd/mm/yyyy, mm/dd/yyyy, yyyy-mm-dd etc..
This link can give you a better understanding about date patterns and how to use it
use SimpleDateFormat
SimpleDateFormat sdf=new SimpleDateFormat("dd/MM/yyyy");
Date d=sdf.parse("07/12/2014");

Java printf with date and month

System.out.printf("Time: %d-%d %02d:%02d" +
calendar.get(Calendar.DAY_OF_MONTH),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE);
That is the code a friend showed me, but how do I get the date to appear in a Format like November 1?
This is how to do it:
DateFormat dateFormat = new SimpleDateFormat( "MMMMM d" );
Calendar calendar = new GregorianCalendar(); // The date you want to format
Date dateToFormat = calendar.getTime();
String formattedDate = dateFormat.format( dateToFormat );
System.out.println( formattedDate );
Date d = new Date();
System.out.printf("%s %tB %<td", "Today", d);
// output :
// Today november 01
%tB for Locale-specific full month name, e.g. "January", "February".
%<td d for Day of month, formatted as two digits with leading zeros as necessary, < for reuse the last parameter.
The DateFormat answer is the way to do this. The printf answer is also good although does not provide locale-specific formats (it provides language-specific names but does not use e.g. the day/month/year ordering that the current locale uses).
You asked in a comment:
Can I do it with the calendar.get(Calendar.MONTH) etc method? Or do I have to use date format?
You don't have to use the other methods here, but if you want to use the Calender fields, it is up to you to convert the numeric values they provide to strings like "Tuesday" or "November". For that you can use the built in DateFormatSymbols, which provides internationalized strings from numbers for dates, in the form of String arrays, which you can use the Calendar fields to index in to. See How can I convert an Integer to localized month name in Java? for example.
Note you can use DateFormat.getDateInstance() to retrieve a pre-made format for the current locale (see the rest of those docs, there are also methods for getting pre-made time-only or date+time formats).
Basically you have the following options:
DateFormat (SimpleDateFormat for custom formats)
Locale-specific format (e.g. day/month/year ordering): Yes
Language-specific names (e.g. English "November" vs. Spanish "Noviembre"): Yes
Does the work for you: Yes. This is the best way and will provide a format that the user is used to working with, with no logic needed on your end.
printf date fields
Locale-specific format: No
Language-specific names: Yes
Does the work for you: Partly (up to you to determine field ordering)
Calendar fields with DateFormatSymbols
Locale-specific format: No
Language-specific names: Yes
Does the work for you: No
Calendar fields with your own string conversions (like a big switch statement):
Locale-specific format: No
Language-specific names: No
Does the work for you: No
Another advantage of DateFormat-based formats vs printf date fields is you can still define your own field ordering and formats with the SimpleDateFormat (just like printf) but you can stick to the DateFormat interface which makes it easier to pass around and combine with stock date formats like DateFormat.getDateInstance(DateFormat.MEDIUM).
Check out the documentation for DateFormat for info on the things you can do with it. Check out the documentation for SimpleDateFormat for info on creating custom date formats. Check out this nice example of date formats (archive) for some example output if you want instant gratification.
There's a direct way how to do it using printf, but it's a pain, too:
String.printf("Time: %1$td-%1$tm %1$tH:%1$tM", new Date());
One problem with it is that it uses 4 formatting strings with the same object, so it needs the 1$ prefix to always access the first argument. The other is that I can never remember what letter means what (but maybe that's just me).
Speed could actually be another problem, if you care.
This is documented in the underlying class Formatter.
My preffered way would be something like
myFormatter.format("Time: [d-m HH:MM]", new Date())
where the braces would save us from repeating $1 and make clear where the argument ends.

How do I get localized date pattern string?

It is quite easy to format and parse Java Date (or Calendar) classes using instances of DateFormat.
I could format the current date into a short localized date like this:
DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
String today = formatter.format(new Date());
My problem is that I need to obtain this localized pattern string (something like "MM/dd/yy").
This should be a trivial task, but I just couldn't find the provider.
For SimpleDateFormat, You call toLocalizedPattern()
EDIT:
For Java 8 users:
The Java 8 Date Time API is similar to Joda-time. To gain a localized pattern we can use class
DateTimeFormatter
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
Note that when you call toString() on LocalDate, you will get date in format ISO-8601
Note that Date Time API in Java 8 is inspired by Joda Time and most solution can be based on questions related to time.
For those still using Java 7 and older:
You can use something like this:
DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
String pattern = ((SimpleDateFormat)formatter).toPattern();
String localPattern = ((SimpleDateFormat)formatter).toLocalizedPattern();
Since the DateFormat returned From getDateInstance() is instance of SimpleDateFormat.
Those two methods should really be in the DateFormat too for this to be less hacky, but they currently are not.
It may be strange, that I am answering my own question, but I believe, I can add something to the picture.
ICU implementation
Obviously, Java 8 gives you a lot, but there is also something else: ICU4J. This is actually the source of Java original implementation of things like Calendar, DateFormat and SimpleDateFormat, to name a few.
Therefore, it should not be a surprise that ICU's SimpleDateFormat also contains methods like toPattern() or toLocalizedPattern(). You can see them in action here:
DateFormat fmt = DateFormat.getPatternInstance(
DateFormat.YEAR_MONTH,
Locale.forLanguageTag("pl-PL"));
if (fmt instanceof SimpleDateFormat) {
SimpleDateFormat sfmt = (SimpleDateFormat) fmt;
String pattern = sfmt.toPattern();
String localizedPattern = sfmt.toLocalizedPattern();
System.out.println(pattern);
System.out.println(localizedPattern);
}
ICU enhancements
This is nothing new, but what I really wanted to point out is this:
DateFormat.getPatternInstance(String pattern, Locale locale);
This is a method that can return a whole bunch of locale specific patterns, such as:
ABBR_QUARTER
QUARTER
YEAR
YEAR_ABBR_QUARTER
YEAR_QUARTER
YEAR_ABBR_MONTH
YEAR_MONTH
YEAR_NUM_MONTH
YEAR_ABBR_MONTH_DAY
YEAR_NUM_MONTH_DAY
YEAR_MONTH_DAY
YEAR_ABBR_MONTH_WEEKDAY_DAY
YEAR_MONTH_WEEKDAY_DAY
YEAR_NUM_MONTH_WEEKDAY_DAY
ABBR_MONTH
MONTH
NUM_MONTH
ABBR_STANDALONE_MONTH
STANDALONE_MONTH
ABBR_MONTH_DAY
MONTH_DAY
NUM_MONTH_DAY
ABBR_MONTH_WEEKDAY_DAY
MONTH_WEEKDAY_DAY
NUM_MONTH_WEEKDAY_DAY
DAY
ABBR_WEEKDAY
WEEKDAY
HOUR
HOUR24
HOUR_MINUTE
HOUR_MINUTE_SECOND
HOUR24_MINUTE
HOUR24_MINUTE_SECOND
HOUR_TZ
HOUR_GENERIC_TZ
HOUR_MINUTE_TZ
HOUR_MINUTE_GENERIC_TZ
MINUTE
MINUTE_SECOND
SECOND
ABBR_UTC_TZ
ABBR_SPECIFIC_TZ
SPECIFIC_TZ
ABBR_GENERIC_TZ
GENERIC_TZ
LOCATION_TZ
Sure, there are quite a few. What is good about them, is that these patterns are actually strings (as in java.lang.String), that is if you use English pattern "MM/d", you'll get locale-specific pattern in return. It might be useful in some corner cases. Usually you would just use DateFormat instance, and won't care about the pattern itself.
Locale-specific pattern vs. localized pattern
The question intention was to get localized, and not the locale-specific pattern. What's the difference?
In theory, toPattern() will give you locale-specific pattern (depending on Locale you used to instantiate (Simple)DateFormat). That is, no matter what target language/country you put, you'll get the pattern composed of symbols like y, M, d, h, H, M, etc.
On the other hand, toLocalizedPattern() should return localized pattern, that is something that is suitable for end users to read and understand. For instance, German middle (default) date pattern would be:
toPattern(): dd.MM.yyyy
toLocalizedPattern(): tt.MM.jjjj (day = Tag, month = Monat, year = Jahr)
The intention of the question was: "how to find the localized pattern that could serve as hint as to what the date/time format is". That is, say we have a date field that user can fill-out using the locale-specific pattern, but I want to display a format hint in the localized form.
Sadly, so far there is no good solution. The ICU I mentioned earlier in this post, partially works. That's because, the data that ICU uses come from CLDR, which is unfortunately partially translated/partially correct. In case of my mother's tongue, at the time of writing, neither patterns, nor their localized forms are correctly translated. And every time I correct them, I got outvoted by other people, who do not necessary live in Poland, nor speak Polish language...
The moral of this story: do not fully rely on CLDR. You still need to have local auditors/linguistic reviewers.
You can use DateTimeFormatterBuilder in Java 8. Following example returns localized date only pattern e.g. "d.M.yyyy".
String datePattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.SHORT, null, IsoChronology.INSTANCE,
Locale.GERMANY); // or whatever Locale
The following code will give you the pattern for the locale:
final String pattern1 = ((SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, locale)).toPattern();
System.out.println(pattern1);
Java 8 provides some useful features out of the box for working with and formatting/parsing date and time, including handling locales. Here is a brief introduction.
Basic Patterns
In the simplest case to format/parse a date you would use the following code with a String pattern:
DateTimeFormatter.ofPattern("MM/dd/yyyy")
The standard is then to use this with the date object directly for formatting:
return LocalDate.now().format(DateTimeFormatter.ofPattern("MM/dd/yyyy"));
And then using the factory pattern to parse a date:
return LocalDate.parse(dateString, DateTimeFormatter.ofPattern("MM/dd/yyyy"));
The pattern itself has a large number of options that will cover the majority of usecases, a full rundown can be found at the javadoc location here.
Locales
Inclusion of a Locale is fairly simple, for the default locale you have the following options that can then be applied to the format/parse options demonstrated above:
DateTimeFormatter.ofLocalizedDate(dateStyle);
The 'dateStyle' above is a FormatStyle option Enum to represent the full, long, medium and short versions of the localized Date when working with the DateTimeFormatter. Using FormatStyle you also have the following options:
DateTimeFormatter.ofLocalizedTime(timeStyle);
DateTimeFormatter.ofLocalizedDateTime(dateTimeStyle);
DateTimeFormatter.ofLocalizedDateTime(dateTimeStyle, timeStyle);
The last option allows you to specify a different FormatStyle for the date and the time. If you are not working with the default Locale the return of each of the Localized methods can be adjusted using the .withLocale option e.g
DateTimeFormatter.ofLocalizedTime(timeStyle).withLocale(Locale.ENGLISH);
Alternatively the ofPattern has an overloaded version to specify the locale too
DateTimeFormatter.ofPattern("MM/dd/yyyy",Locale.ENGLISH);
I Need More!
DateTimeFormatter will meet the majority of use cases, however it is built on the DateTimeFormatterBuilder which provides a massive range of options to the user of the builder. Use DateTimeFormatter to start with and if you need these extensive formatting features fall back to the builder.
Please find in the below code which accepts the locale instance and returns the locale specific data format/pattern.
public static String getLocaleDatePattern(Locale locale) {
// Validating if Locale instance is null
if (locale == null || locale.getLanguage() == null) {
return "MM/dd/yyyy";
}
// Fetching the locale specific date pattern
String localeDatePattern = ((SimpleDateFormat) DateFormat.getDateInstance(
DateFormat.SHORT, locale)).toPattern();
// Validating if locale type is having language code for Chinese and country
// code for (Hong Kong) with Date Format as - yy'?'M'?'d'?'
if (locale.toString().equalsIgnoreCase("zh_hk")) {
// Expected application Date Format for Chinese (Hong Kong) locale type
return "yyyy'MM'dd";
}
// Replacing all d|m|y OR Gy with dd|MM|yyyy as per the locale date pattern
localeDatePattern = localeDatePattern.replaceAll("d{1,2}", "dd").replaceAll(
"M{1,2}", "MM").replaceAll("y{1,4}|Gy", "yyyy");
// Replacing all blank spaces in the locale date pattern
localeDatePattern = localeDatePattern.replace(" ", "");
// Validating the date pattern length to remove any extract characters
if (localeDatePattern.length() > 10) {
// Keeping the standard length as expected by the application
localeDatePattern = localeDatePattern.substring(0, 10);
}
return localeDatePattern;
}
Since it's just the locale information you're after, I think what you'll have to do is locate the file which the JVM (OpenJDK or Harmony) actually uses as input to the whole Locale thing and figure out how to parse it. Or just use another source on the web (surely there's a list somewhere). That'll save those poor translators.
You can try something like :
LocalDate fromCustomPattern = LocalDate.parse("20.01.2014", DateTimeFormatter.ofPattern("MM/dd/yy"))
Im not sure about what you want, but...
SimpleDateFormat example:
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yy");
Date date = sdf.parse("12/31/10");
String str = sdf.format(new Date());

How do I create a DateFormat with an optional time argument?

I wish to construct a date format that will optionally have a time argument.
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd [hh:mm]");
Is it also possible to construct a date format object that is capable of parsing different formats? Such as try the current locale but then fall back to ISO-8601 or should I just write multiple date formats if one fails?
UPDATE: Looking back at this question I can see I didn't specify that the reason for multiple date formats was for parsing strings, not for formatting a date, thus ambiguity for formatting date objects wasn't a concern for me. If you take this into account the time portion is or is not included in the parsing string.
SimpleDateFormat won't let you do that. It doesn't support alternatives within a (single) format.
Even if it did, there would a problem. Consider using this
new SimpleDateFormat("yyyy-MM-dd [hh:mm]");
versus using
new SimpleDateFormat("yyyy-MM-dd hh:mm");
new SimpleDateFormat("yyyy-MM-dd ");
In the first case, when I parsed a date against the format, I couldn't tell the difference between "2010-01-01" and "2010-01-01 00:00" by looking at the Date delivered to me. In the 2nd case, I can.
In the first case, when I format a Date with zero in the minutes and seconds fields, it is not clear whether the result should end with "00:00" ... or not. In the second case, this is entirely in the hands of the application.
I guess that what I'm really doing here is raising the issue that dates and date/times mean different things to different people and in different contexts. Sometimes they mean instants and sometimes intervals. Sometimes a lack of expressed precision means imprecision, and sometimes that the precision is implied.
As developers we have to run the line between writing software that is annoyingly picky, and software that makes incorrect assumptions about what the user actually means by a date / time value. The first step in getting it right for the user is understanding the complexity of the problem. Overloading variations into a single format string is (would be) sweeping the problem under the carpet.

Categories

Resources