Related
I am trying to check the date is in required format and if its not in required format(yyyy-MM-dd'T'HH:mm:sss) i need to add zero at the end to return a date in required format
For example if i get String inputDate= 2018-08-04T09:07:12.42 and i need to convert inputdate to 2018-08-04T09:07:12.420.
For convertStringToDate i am passing inputDate = 2018-08-04T09:07:12.42 and dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS". I tried the below code, but im not sure where i am missing . Pls suggest
public Date convertStringToDate(String inputDate, String dateFormat) {
String formattedInput = inputDate;
try {
// add milliseconds if missing from date
if (validateDateFormat(inputDate, "yyyy-MM-dd'T'HH:mm:ss") && !validateDateFormat(inputDate, dateFormat)) {
formattedInput = inputDate + "0";
}
Log.logInfo(this, "formattedInput: " + formattedInput);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
return simpleDateFormat.parse(formattedInput);
} catch (Exception e) {
Log.logError(this, "error in inputDate: " + formattedInput + " - convertStringToDate: " + e.getMessage());
return null;
}
}
public boolean validateDateFormat(String strDate, String dateFormat) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
simpleDateFormat.setLenient(false);
Date javaDate = null;
try {
javaDate = simpleDateFormat.parse(strDate);
Log.logInfo(this, "formattedInput Date: " + javaDate);
return true;
}
/* Date format is invalid */
catch (Exception e) {
Log.logInfo(this, strDate + " is Invalid Date format");
return false;
}
/* Return true if date format is valid */
}
tl;dr
Parse your date-time strings using LocalDateTime#parse(CharSequence text) and use DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH) to format the obtained LocalDateTime.
Details
Irrespective of whether you have one/two/three digits in the fraction-of-second part of your date-time strings, they are compliant with ISO 8601 standards and therefore, you do not need a DateTimeFormatter to parse your date-time strings into a LocalDateTime by using LocalDateTime#parse(CharSequence text).
However, to keep three digits always in the string resulting from formatting the LocalDateTime, you need to use .SSS with the formatter i.e. DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH).
Demo:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream.of(
"2018-08-04T09:07:12.421",
"2018-08-04T09:07:12.42",
"2018-08-04T09:07:12.4"
)
.map(LocalDateTime::parse)
.map(dt -> dt.format(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH)))
.forEach(System.out::println);
}
}
Output:
2018-08-04T09:07:12.421
2018-08-04T09:07:12.420
2018-08-04T09:07:12.400
Learn more about the modern Date-Time API from Trail: Date Time.
Note: In March 2014, java.time API supplanted the error-prone legacy date-time API. Since then, it has been strongly recommended to use this modern date-time API.
I find it curious that the most obvious way to create Date objects in Java has been deprecated and appears to have been "substituted" with a not so obvious to use lenient calendar.
How do you check that a date, given as a combination of day, month, and year, is a valid date?
For instance, 2008-02-31 (as in yyyy-mm-dd) would be an invalid date.
Key is df.setLenient(false);. This is more than enough for simple cases. If you are looking for a more robust (I doubt that) and/or alternate libraries like joda-time, then look at the answer by user "tardate"
final static String DATE_FORMAT = "dd-MM-yyyy";
public static boolean isDateValid(String date)
{
try {
DateFormat df = new SimpleDateFormat(DATE_FORMAT);
df.setLenient(false);
df.parse(date);
return true;
} catch (ParseException e) {
return false;
}
}
As shown by #Maglob, the basic approach is to test the conversion from string to date using SimpleDateFormat.parse. That will catch invalid day/month combinations like 2008-02-31.
However, in practice that is rarely enough since SimpleDateFormat.parse is exceedingly liberal. There are two behaviours you might be concerned with:
Invalid characters in the date string
Surprisingly, 2008-02-2x will "pass" as a valid date with locale format = "yyyy-MM-dd" for example. Even when isLenient==false.
Years: 2, 3 or 4 digits?
You may also want to enforce 4-digit years rather than allowing the default SimpleDateFormat behaviour (which will interpret "12-02-31" differently depending on whether your format was "yyyy-MM-dd" or "yy-MM-dd")
A Strict Solution with the Standard Library
So a complete string to date test could look like this: a combination of regex match, and then a forced date conversion. The trick with the regex is to make it locale-friendly.
Date parseDate(String maybeDate, String format, boolean lenient) {
Date date = null;
// test date string matches format structure using regex
// - weed out illegal characters and enforce 4-digit year
// - create the regex based on the local format string
String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");
reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");
if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {
// date string matches format structure,
// - now test it can be converted to a valid date
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();
sdf.applyPattern(format);
sdf.setLenient(lenient);
try { date = sdf.parse(maybeDate); } catch (ParseException e) { }
}
return date;
}
// used like this:
Date date = parseDate( "21/5/2009", "d/M/yyyy", false);
Note that the regex assumes the format string contains only day, month, year, and separator characters. Aside from that, format can be in any locale format: "d/MM/yy", "yyyy-MM-dd", and so on. The format string for the current locale could be obtained like this:
Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();
Joda Time - Better Alternative?
I've been hearing about joda time recently and thought I'd compare. Two points:
Seems better at being strict about invalid characters in the date string, unlike SimpleDateFormat
Can't see a way to enforce 4-digit years with it yet (but I guess you could create your own DateTimeFormatter for this purpose)
It's quite simple to use:
import org.joda.time.format.*;
import org.joda.time.DateTime;
org.joda.time.DateTime parseDate(String maybeDate, String format) {
org.joda.time.DateTime date = null;
try {
DateTimeFormatter fmt = DateTimeFormat.forPattern(format);
date = fmt.parseDateTime(maybeDate);
} catch (Exception e) { }
return date;
}
tl;dr
Use the strict mode on java.time.DateTimeFormatter to parse a LocalDate. Trap for the DateTimeParseException.
LocalDate.parse( // Represent a date-only value, without time-of-day and without time zone.
"31/02/2000" , // Input string.
DateTimeFormatter // Define a formatting pattern to match your input string.
.ofPattern ( "dd/MM/uuuu" )
.withResolverStyle ( ResolverStyle.STRICT ) // Specify leniency in tolerating questionable inputs.
)
After parsing, you might check for reasonable value. For example, a birth date within last one hundred years.
birthDate.isAfter( LocalDate.now().minusYears( 100 ) )
Avoid legacy date-time classes
Avoid using the troublesome old date-time classes shipped with the earliest versions of Java. Now supplanted by the java.time classes.
LocalDate & DateTimeFormatter & ResolverStyle
The LocalDate class represents a date-only value without time-of-day and without time zone.
String input = "31/02/2000";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu" );
try {
LocalDate ld = LocalDate.parse ( input , f );
System.out.println ( "ld: " + ld );
} catch ( DateTimeParseException e ) {
System.out.println ( "ERROR: " + e );
}
The java.time.DateTimeFormatter class can be set to parse strings with any of three leniency modes defined in the ResolverStyle enum. We insert a line into the above code to try each of the modes.
f = f.withResolverStyle ( ResolverStyle.LENIENT );
The results:
ResolverStyle.LENIENTld: 2000-03-02
ResolverStyle.SMARTld: 2000-02-29
ResolverStyle.STRICTERROR: java.time.format.DateTimeParseException: Text '31/02/2000' could not be parsed: Invalid date 'FEBRUARY 31'
We can see that in ResolverStyle.LENIENT mode, the invalid date is moved forward an equivalent number of days. In ResolverStyle.SMART mode (the default), a logical decision is made to keep the date within the month and going with the last possible day of the month, Feb 29 in a leap year, as there is no 31st day in that month. The ResolverStyle.STRICT mode throws an exception complaining that there is no such date.
All three of these are reasonable depending on your business problem and policies. Sounds like in your case you want the strict mode to reject the invalid date rather than adjust it.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
You can use SimpleDateFormat
For example something like:
boolean isLegalDate(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
return sdf.parse(s, new ParsePosition(0)) != null;
}
The current way is to use the calendar class. It has the setLenient method that will validate the date and throw and exception if it is out of range as in your example.
Forgot to add:
If you get a calendar instance and set the time using your date, this is how you get the validation.
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
cal.getTime();
}
catch (Exception e) {
System.out.println("Invalid date");
}
java.time
With the Date and Time API (java.time classes) built into Java 8 and later, you can use the LocalDate class.
public static boolean isDateValid(int year, int month, int day) {
try {
LocalDate.of(year, month, day);
} catch (DateTimeException e) {
return false;
}
return true;
}
Building on Aravind's answer to fix the problem pointed out by ceklock in his comment, I added a method to verify that the dateString doesn't contain any invalid character.
Here is how I do:
private boolean isDateCorrect(String dateString) {
try {
Date date = mDateFormatter.parse(dateString);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return matchesOurDatePattern(dateString); //added my method
}
catch (ParseException e) {
return false;
}
}
/**
* This will check if the provided string matches our date format
* #param dateString
* #return true if the passed string matches format 2014-1-15 (YYYY-MM-dd)
*/
private boolean matchesDatePattern(String dateString) {
return dateString.matches("^\\d+\\-\\d+\\-\\d+");
}
An alternative strict solution using the standard library is to perform the following:
1) Create a strict SimpleDateFormat using your pattern
2) Attempt to parse the user entered value using the format object
3) If successful, reformat the Date resulting from (2) using the same date format (from (1))
4) Compare the reformatted date against the original, user-entered value. If they're equal then the value entered strictly matches your pattern.
This way, you don't need to create complex regular expressions - in my case I needed to support all of SimpleDateFormat's pattern syntax, rather than be limited to certain types like just days, months and years.
I suggest you to use org.apache.commons.validator.GenericValidator class from apache.
GenericValidator.isDate(String value, String datePattern, boolean strict);
Note: strict - Whether or not to have an exact match of the datePattern.
I think the simpliest is just to convert a string into a date object and convert it back to a string. The given date string is fine if both strings still match.
public boolean isDateValid(String dateString, String pattern)
{
try
{
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
if (sdf.format(sdf.parse(dateString)).equals(dateString))
return true;
}
catch (ParseException pe) {}
return false;
}
Assuming that both of those are Strings (otherwise they'd already be valid Dates), here's one way:
package cruft;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateValidator
{
private static final DateFormat DEFAULT_FORMATTER;
static
{
DEFAULT_FORMATTER = new SimpleDateFormat("dd-MM-yyyy");
DEFAULT_FORMATTER.setLenient(false);
}
public static void main(String[] args)
{
for (String dateString : args)
{
try
{
System.out.println("arg: " + dateString + " date: " + convertDateString(dateString));
}
catch (ParseException e)
{
System.out.println("could not parse " + dateString);
}
}
}
public static Date convertDateString(String dateString) throws ParseException
{
return DEFAULT_FORMATTER.parse(dateString);
}
}
Here's the output I get:
java cruft.DateValidator 32-11-2010 31-02-2010 04-01-2011
could not parse 32-11-2010
could not parse 31-02-2010
arg: 04-01-2011 date: Tue Jan 04 00:00:00 EST 2011
Process finished with exit code 0
As you can see, it does handle both of your cases nicely.
This is working great for me. Approach suggested above by Ben.
private static boolean isDateValid(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
try {
Date d = asDate(s);
if (sdf.format(d).equals(s)) {
return true;
} else {
return false;
}
} catch (ParseException e) {
return false;
}
}
looks like SimpleDateFormat is not checking the pattern strictly even after setLenient(false); method is applied on it, so i have used below method to validate if the date inputted is valid date or not as per supplied pattern.
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public boolean isValidFormat(String dateString, String pattern) {
boolean valid = true;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
try {
formatter.parse(dateString);
} catch (DateTimeParseException e) {
valid = false;
}
return valid;
}
Two comments on the use of SimpleDateFormat.
it should be declared as a static instance
if declared as static access should be synchronized as it is not thread safe
IME that is better that instantiating an instance for each parse of a date.
Above methods of date parsing are nice , i just added new check in existing methods that double check the converted date with original date using formater, so it works for almost each case as i verified. e.g. 02/29/2013 is invalid date.
Given function parse the date according to current acceptable date formats. It returns true if date is not parsed successfully.
public final boolean validateDateFormat(final String date) {
String[] formatStrings = {"MM/dd/yyyy"};
boolean isInvalidFormat = false;
Date dateObj;
for (String formatString : formatStrings) {
try {
SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
sdf.applyPattern(formatString);
sdf.setLenient(false);
dateObj = sdf.parse(date);
System.out.println(dateObj);
if (date.equals(sdf.format(dateObj))) {
isInvalidFormat = false;
break;
}
} catch (ParseException e) {
isInvalidFormat = true;
}
}
return isInvalidFormat;
}
Here's what I did for Node environment using no external libraries:
Date.prototype.yyyymmdd = function() {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
var dd = this.getDate().toString();
return zeroPad([yyyy, mm, dd].join('-'));
};
function zeroPad(date_string) {
var dt = date_string.split('-');
return dt[0] + '-' + (dt[1][1]?dt[1]:"0"+dt[1][0]) + '-' + (dt[2][1]?dt[2]:"0"+dt[2][0]);
}
function isDateCorrect(in_string) {
if (!matchesDatePattern) return false;
in_string = zeroPad(in_string);
try {
var idate = new Date(in_string);
var out_string = idate.yyyymmdd();
return in_string == out_string;
} catch(err) {
return false;
}
function matchesDatePattern(date_string) {
var dateFormat = /[0-9]+-[0-9]+-[0-9]+/;
return dateFormat.test(date_string);
}
}
And here is how to use it:
isDateCorrect('2014-02-23')
true
// to return valid days of month, according to month and year
int returnDaysofMonth(int month, int year) {
int daysInMonth;
boolean leapYear;
leapYear = checkLeap(year);
if (month == 4 || month == 6 || month == 9 || month == 11)
daysInMonth = 30;
else if (month == 2)
daysInMonth = (leapYear) ? 29 : 28;
else
daysInMonth = 31;
return daysInMonth;
}
// to check a year is leap or not
private boolean checkLeap(int year) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
Here is I would check the date format:
public static boolean checkFormat(String dateTimeString) {
return dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}") || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}")
|| dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}") || dateTimeString
.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z") ||
dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}Z");
}
public static String detectDateFormat(String inputDate, String requiredFormat) {
String tempDate = inputDate.replace("/", "").replace("-", "").replace(" ", "");
String dateFormat;
if (tempDate.matches("([0-12]{2})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMddyyyy";
} else if (tempDate.matches("([0-31]{2})([0-12]{2})([0-9]{4})")) {
dateFormat = "ddMMyyyy";
} else if (tempDate.matches("([0-9]{4})([0-12]{2})([0-31]{2})")) {
dateFormat = "yyyyMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([0-12]{2})")) {
dateFormat = "yyyyddMM";
} else if (tempDate.matches("([0-31]{2})([a-z]{3})([0-9]{4})")) {
dateFormat = "ddMMMyyyy";
} else if (tempDate.matches("([a-z]{3})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMMddyyyy";
} else if (tempDate.matches("([0-9]{4})([a-z]{3})([0-31]{2})")) {
dateFormat = "yyyyMMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([a-z]{3})")) {
dateFormat = "yyyyddMMM";
} else {
return "Pattern Not Added";
//add your required regex
}
try {
String formattedDate = new SimpleDateFormat(requiredFormat, Locale.ENGLISH).format(new SimpleDateFormat(dateFormat).parse(tempDate));
return formattedDate;
} catch (Exception e) {
//
return "";
}
}
setLenient to false if you like a strict validation
public boolean isThisDateValid(String dateToValidate, String dateFromat){
if(dateToValidate == null){
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
sdf.setLenient(false);
try {
//if not valid, it will throw ParseException
Date date = sdf.parse(dateToValidate);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
return false;
}
return true;
}
With 'legacy' date format, we can format the result and compare it back to the source.
public boolean isValidFormat(String source, String pattern) {
SimpleDateFormat sd = new SimpleDateFormat(pattern);
sd.setLenient(false);
try {
Date date = sd.parse(source);
return date != null && sd.format(date).equals(source);
} catch (Exception e) {
return false;
}
}
This execerpt says 'false' to source=01.01.04 with pattern '01.01.2004'
We can use the org.apache.commons.validator.GenericValidator's method directly without adding the whole library:
public static boolean isValidDate(String value, String datePattern, boolean strict) {
if (value == null
|| datePattern == null
|| datePattern.length() <= 0) {
return false;
}
SimpleDateFormat formatter = new SimpleDateFormat(datePattern, Locale.ENGLISH);
formatter.setLenient(false);
try {
formatter.parse(value);
} catch(ParseException e) {
return false;
}
if (strict && (datePattern.length() != value.length())) {
return false;
}
return true;
}
A simple and elegant way for Android developers (Java 8 not required):
// month value is 1-based. e.g., 1 for January.
public static boolean isDateValid(int year, int month, int day) {
Calendar calendar = Calendar.getInstance();
try {
calendar.setLenient(false);
calendar.set(year, month-1, day);
calendar.getTime();
return true;
} catch (Exception e) {
return false;
}
}
Below code works with dd/MM/yyyy format and can be used to check NotNull,NotEmpty as well.
public static boolean validateJavaDate(String strDate) {
if (strDate != null && !strDate.isEmpty() && !strDate.equalsIgnoreCase(" ")) {
{
SimpleDateFormat date = new SimpleDateFormat("dd/MM/yyyy");
date.setLenient(false);
try {
Date javaDate = date.parse(strDate);
System.out.println(strDate + " Valid Date format");
}
catch (ParseException e) {
System.out.println(strDate + " Invalid Date format");
return false;
}
return true;
}
} else {
System.out.println(strDate + "----> Date is Null/Empty");
return false;
}
}
This question already has answers here:
Get first date of current month in java
(11 answers)
Closed 4 years ago.
I can convert a String to a Date:
private static final SimpleDateFormat CARD_DATE_FORMAT = new SimpleDateFormat("yyMMdd", Locale.getDefault());
public static Date toCardDateFormat(String date){
try {
return CARD_DATE_FORMAT.parse(date);
} catch (ParseException e) {
return null;
}
}
For example I have 200908 - it will convert to 2020-09-08, but I need set the day always to 1st day of month. I need 2020-09-01. How can I make this?
According to your need,you can use this method:
private static final SimpleDateFormat CARD_DATE_FORMAT = new SimpleDateFormat("yyMMdd", Locale.getDefault());
public static String toCardDateFormat(String date) {
try {
Date value = CARD_DATE_FORMAT.parse(date);
SimpleDateFormat dateFormatter = new SimpleDateFormat("yy-MM", Locale.getDefault());
String datetimeLocale = dateFormatter.format(value);
String newDate = datetimeLocale + "-01";
return newDate;
} catch (ParseException e) {
return null;
}
}
for date object you can use this:
public static Date toCardDateFormat(String date) {
try {
Date value = CARD_DATE_FORMAT.parse(date);
SimpleDateFormat dateFormatter = new SimpleDateFormat("yy-MM", Locale.getDefault());
String datetimeLocale = dateFormatter.format(value);
String newDate = datetimeLocale + "-01";
SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd",Locale.getDefault());
Date d = dateFormat.parse(newDate);
return d;
} catch (ParseException e) {
return null;
}
}
Maciej's answer is correct, but if you use Java 8 or higher, it's better to use the java.time classes:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMdd");
// parse and change the day of month
LocalDate d = LocalDate.parse("200908", formatter).withDayOfMonth(1);
System.out.println(d); // 2020-09-01
Note that the LocalDate is printed in the format you want - which is ISO8601 compliant. If you want a different format, just use another DateTimeFormatter and call the format method.
Manually changing the string, as suggested by others, might also work, but if you're dealing with dates, why not use a proper date-handling API? Direct string manipulation won't help you in cases like invalid dates (the formatter will throw an exception for invalid inputs), or if you try to change the day to invalid values (such as day 31 for April, or 29 for February in a non-leap year, which are checked by the API and throw an exception if the value is invalid).
Your code has multi threading issue, best create locally in your convert function to avoid it (otherwise you will have troubles with multiple thread as this class is not thread-safe)
Answer to your question is (using old Date api) - you can use Calendar to do it:
public static Date toCardDateFormat(String date){
Date result = null;
try {
result = new SimpleDateFormat("yyMMdd", Locale.getDefault()).parse(date);
} catch (ParseException e) {
return null; // or better throw exception or return Optional.empty()
}
Calendar cal = Calendar.getInstance();
cal.setTime(result);
cal.set(Calendar.DAY_OF_MONTH, 1);
return cal.getTime();
}
you could try something like this:
String[] arr = date.split("-");
String newDate = arr[0] + "-" + arr[1] + "-01";
private static final DateTimeFormatter CARD_DATE_FORMAT
= DateTimeFormatter.ofPattern("yyMMdd", Locale.getDefault());
public static Optional<YearMonth> toCardDateFormat(String date) {
try {
return Optional.of(YearMonth.parse(date, CARD_DATE_FORMAT));
} catch (DateTimeParseException e) {
return Optional.empty();
}
}
Don’t return null from a method. The risk of a NullPointerException on the caller’s side would be great. If you believe that not returning a value is the right thing in case of a string in the wrong format or containing an invalid date, use Optional to force the caller to take the possibility of no return value into account. Another obvious option is to leave any parsing exception to the caller:
public static YearMonth toCardDateFormat(String date) {
return YearMonth.parse(date, CARD_DATE_FORMAT);
}
DateTimeParseException is an unchecked exception, so needs not be declared in the method signature. Let’s try it:
System.out.println(toCardDateFormat("200908"));
This prints:
2020-09
Other messages:
I am using and warmly recommending java.time, the modern Java date and time API. The old date-time classes from Java 1.0 and 1.1 are now long outdated, and SimpleDateFormat in particular is notoriously troublesome. I think you should avoid them. The modern API is so much nicer to work with.
It seems you would really prefer to remove the day of month so you only have the month and year? The YearMonth class from java.time does exactly that for you. If instead you wanted a full date, use the LocalDate class as in xunts’ answer.
Link: Oracle tutorial: Date Time explaining how to use java.time.
I am trying to parse some dates that are coming out of a document. It would appear users have entered these dates in a similar but not exact format.
here are the formats:
9/09
9/2009
09/2009
9/1/2009
9-1-2009
What is the best way to go about trying to parse all of these? These seem to be the most common, but I guess what is hanging me up is that if i have a pattern of "M/yyyy" wont that always catch before "MM/yyyy" Do I have to set up my try/catch blocks nested in a least restrictive to most restrictive way? it seems like it sure is going to take a lot of code duplication to get this right.
You'll need to use a different SimpleDateFormat object for each different pattern. That said, you don't need that many different ones, thanks to this:
Number: For formatting, the number of pattern letters is the minimum number of digits, and shorter numbers are zero-padded to this amount. For parsing, the number of pattern letters is ignored unless it's needed to separate two adjacent fields.
So, you'll need these formats:
"M/y" (that covers 9/09, 9/2009, and 09/2009)
"M/d/y" (that covers 9/1/2009)
"M-d-y" (that covers 9-1-2009)
So, my advice would be to write a method that works something like this (untested):
// ...
List<String> formatStrings = Arrays.asList("M/y", "M/d/y", "M-d-y");
// ...
Date tryParse(String dateString)
{
for (String formatString : formatStrings)
{
try
{
return new SimpleDateFormat(formatString).parse(dateString);
}
catch (ParseException e) {}
}
return null;
}
What about just defining multiple patterns? They might come from a config file containing known patterns, hard coded it reads like:
List<SimpleDateFormat> knownPatterns = new ArrayList<SimpleDateFormat>();
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm.ss'Z'"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
for (SimpleDateFormat pattern : knownPatterns) {
try {
// Take a try
return new Date(pattern.parse(candidate).getTime());
} catch (ParseException pe) {
// Loop on
}
}
System.err.println("No known Date format found: " + candidate);
return null;
Matt's approach above is fine, but please be aware that you will run into problems if you use it to differentiate between dates of the format y/M/d and d/M/y. For instance, a formatter initialised with y/M/d will accept a date like 01/01/2009 and give you back a date which is clearly not what you wanted. I fixed the issue as follows, but I have limited time and I'm not happy with the solution for 2 main reasons:
It violates one of Josh Bloch's quidelines, specifically 'don't use exceptions to handle program flow'.
I can see the getDateFormat() method becoming a bit of a nightmare if you needed it to handle lots of other date formats.
If I had to make something that could handle lots and lots of different date formats and needed to be highly performant, then I think I would use the approach of creating an enum which linked each different date regex to its format. Then use MyEnum.values() to loop through the enum and test with if(myEnum.getPattern().matches(date)) rather than catching a dateformatexception.
Anway, that being said, the following can handle dates of the formats 'y/M/d' 'y-M-d' 'y M d' 'd/M/y' 'd-M-y' 'd M y' and all other variations of those which include time formats as well:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static final String[] timeFormats = {"HH:mm:ss","HH:mm"};
private static final String[] dateSeparators = {"/","-"," "};
private static final String DMY_FORMAT = "dd{sep}MM{sep}yyyy";
private static final String YMD_FORMAT = "yyyy{sep}MM{sep}dd";
private static final String ymd_template = "\\d{4}{sep}\\d{2}{sep}\\d{2}.*";
private static final String dmy_template = "\\d{2}{sep}\\d{2}{sep}\\d{4}.*";
public static Date stringToDate(String input){
Date date = null;
String dateFormat = getDateFormat(input);
if(dateFormat == null){
throw new IllegalArgumentException("Date is not in an accepted format " + input);
}
for(String sep : dateSeparators){
String actualDateFormat = patternForSeparator(dateFormat, sep);
//try first with the time
for(String time : timeFormats){
date = tryParse(input,actualDateFormat + " " + time);
if(date != null){
return date;
}
}
//didn't work, try without the time formats
date = tryParse(input,actualDateFormat);
if(date != null){
return date;
}
}
return date;
}
private static String getDateFormat(String date){
for(String sep : dateSeparators){
String ymdPattern = patternForSeparator(ymd_template, sep);
String dmyPattern = patternForSeparator(dmy_template, sep);
if(date.matches(ymdPattern)){
return YMD_FORMAT;
}
if(date.matches(dmyPattern)){
return DMY_FORMAT;
}
}
return null;
}
private static String patternForSeparator(String template, String sep){
return template.replace("{sep}", sep);
}
private static Date tryParse(String input, String pattern){
try{
return new SimpleDateFormat(pattern).parse(input);
}
catch (ParseException e) {}
return null;
}
}
If working in Java 1.8 you can leverage the DateTimeFormatterBuilder
public static boolean isTimeStampValid(String inputString)
{
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("" + "[yyyy-MM-dd'T'HH:mm:ss.SSSZ]" + "[yyyy-MM-dd]"));
DateTimeFormatter dateTimeFormatter = dateTimeFormatterBuilder.toFormatter();
try {
dateTimeFormatter.parse(inputString);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
See post: Java 8 Date equivalent to Joda's DateTimeFormatterBuilder with multiple parser formats?
In Apache commons lang, DateUtils class we have a method called parseDate. We can use this for parsing the date.
Also another library Joda-time also have the method to parse the date.
Here is the complete example (with main method) which can be added as a utility class in your project. All the format mentioned in SimpleDateFormate API is supported in the below method.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
public class DateUtility {
public static Date parseDate(String inputDate) {
Date outputDate = null;
String[] possibleDateFormats =
{
"yyyy.MM.dd G 'at' HH:mm:ss z",
"EEE, MMM d, ''yy",
"h:mm a",
"hh 'o''clock' a, zzzz",
"K:mm a, z",
"yyyyy.MMMMM.dd GGG hh:mm aaa",
"EEE, d MMM yyyy HH:mm:ss Z",
"yyMMddHHmmssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
"YYYY-'W'ww-u",
"EEE, dd MMM yyyy HH:mm:ss z",
"EEE, dd MMM yyyy HH:mm zzzz",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSzzzz",
"yyyy-MM-dd'T'HH:mm:sszzzz",
"yyyy-MM-dd'T'HH:mm:ss z",
"yyyy-MM-dd'T'HH:mm:ssz",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HHmmss.SSSz",
"yyyy-MM-dd",
"yyyyMMdd",
"dd/MM/yy",
"dd/MM/yyyy"
};
try {
outputDate = DateUtils.parseDate(inputDate, possibleDateFormats);
System.out.println("inputDate ==> " + inputDate + ", outputDate ==> " + outputDate);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return outputDate;
}
public static String formatDate(Date date, String requiredDateFormat) {
SimpleDateFormat df = new SimpleDateFormat(requiredDateFormat);
String outputDateFormatted = df.format(date);
return outputDateFormatted;
}
public static void main(String[] args) {
DateUtility.parseDate("20181118");
DateUtility.parseDate("2018-11-18");
DateUtility.parseDate("18/11/18");
DateUtility.parseDate("18/11/2018");
DateUtility.parseDate("2018.11.18 AD at 12:08:56 PDT");
System.out.println("");
DateUtility.parseDate("Wed, Nov 18, '18");
DateUtility.parseDate("12:08 PM");
DateUtility.parseDate("12 o'clock PM, Pacific Daylight Time");
DateUtility.parseDate("0:08 PM, PDT");
DateUtility.parseDate("02018.Nov.18 AD 12:08 PM");
System.out.println("");
DateUtility.parseDate("Wed, 18 Nov 2018 12:08:56 -0700");
DateUtility.parseDate("181118120856-0700");
DateUtility.parseDate("2018-11-18T12:08:56.235-0700");
DateUtility.parseDate("2018-11-18T12:08:56.235-07:00");
DateUtility.parseDate("2018-W27-3");
}
}
Best and Simple Java 8 answer (from https://stackoverflow.com/a/59546290/2131040)
final DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"))
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
This solution checks all the possible formats before throwing an exception. This solution is more convenient if you are trying to test for multiple date formats.
Date extractTimestampInput(String strDate){
final List<String> dateFormats = Arrays.asList("yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd");
for(String format: dateFormats){
SimpleDateFormat sdf = new SimpleDateFormat(format);
try{
return sdf.parse(strDate);
} catch (ParseException e) {
//intentionally empty
}
}
throw new IllegalArgumentException("Invalid input for date. Given '"+strDate+"', expecting format yyyy-MM-dd HH:mm:ss.SSS or yyyy-MM-dd.");
}
For the modern answer I am ignoring the requirement to use SimpleDateFormat. While using this class for parsing was a good idea in 2010 when this question was asked, it is now long outdated. The replacement, DateTimeFormatter, came out in 2014. The idea in the following is pretty much the same as in the accepted answer.
private static DateTimeFormatter[] parseFormatters = Stream.of("M/yy", "M/y", "M/d/y", "M-d-y")
.map(DateTimeFormatter::ofPattern)
.toArray(DateTimeFormatter[]::new);
public static YearMonth parseYearMonth(String input) {
for (DateTimeFormatter formatter : parseFormatters) {
try {
return YearMonth.parse(input, formatter);
} catch (DateTimeParseException dtpe) {
// ignore, try next format
}
}
throw new IllegalArgumentException("Could not parse " + input);
}
This parses each of the input strings from the question into a year-month of 2009-09. It’s important to try the two-digit year first since "M/y" could also parse 9/09, but into 0009-09 instead.
A limitation of the above code is it ignores the day-of-month from the strings that have one, like 9/1/2009. Maybe it’s OK as long as most formats have only month and year. To pick it up, we’d have to try LocalDate.parse() rather then YearMonth.parse() for the formats that include d in the pattern string. Surely it can be done.
I'm solved this problem more simple way using regex
fun parseTime(time: String?): Long {
val longRegex = "\\d{4}+-\\d{2}+-\\d{2}+\\w\\d{2}:\\d{2}:\\d{2}.\\d{3}[Z]\$"
val shortRegex = "\\d{4}+-\\d{2}+-\\d{2}+\\w\\d{2}:\\d{2}:\\d{2}Z\$"
val longDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sssXXX")
val shortDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
return when {
Pattern.matches(longRegex, time) -> longDateFormat.parse(time).time
Pattern.matches(shortRegex, time) -> shortDateFormat.parse(time).time
else -> throw InvalidParamsException(INVALID_TIME_MESSAGE, null)
}
}
Implemented the same in scala, Please help urself with converting to Java, the core logic and functions used stays the same.
import java.text.SimpleDateFormat
import org.apache.commons.lang.time.DateUtils
object MultiDataFormat {
def main(args: Array[String]) {
val dates =Array("2015-10-31","26/12/2015","19-10-2016")
val possibleDateFormats:Array[String] = Array("yyyy-MM-dd","dd/MM/yyyy","dd-MM-yyyy")
val sdf = new SimpleDateFormat("yyyy-MM-dd") //change it as per the requirement
for (date<-dates) {
val outputDate = DateUtils.parseDateStrictly(date, possibleDateFormats)
System.out.println("inputDate ==> " + date + ", outputDate ==> " +outputDate + " " + sdf.format(outputDate) )
}
}
}
Using DateTimeFormatter it can be achieved as below:
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.TimeZone;
public class DateTimeFormatTest {
public static void main(String[] args) {
String pattern = "[yyyy-MM-dd[['T'][ ]HH:mm:ss[.SSSSSSSz][.SSS[XXX][X]]]]";
String timeSample = "2018-05-04T13:49:01.7047141Z";
SimpleDateFormat simpleDateFormatter = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
TemporalAccessor accessor = formatter.parse(timeSample);
ZonedDateTime zTime = LocalDateTime.from(accessor).atZone(ZoneOffset.UTC);
Date date=new Date(zTime.toEpochSecond()*1000);
simpleDateFormatter.setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));
System.out.println(simpleDateFormatter.format(date));
}
}
Pay attention at String pattern, this is the combination of multiple patterns. In open [ and close ] square brackets you can mention any kind of patterns.
I was having multiple date formats into json, and was extracting csv with universal format. I looked multiple places, tried different ways, but at the end I'm able to convert with the following simple code.
private String getDate(String anyDateFormattedString) {
#SuppressWarnings("deprecation")
Date date = new Date(anyDateFormattedString);
SimpleDateFormat dateFormat = new SimpleDateFormat(yourDesiredDateFormat);
String convertedDate = dateFormat.format(date);
return convertedDate;
}
I have a string containing a date in the format YYYY-MM-DD.
How would you suggest I go about converting it to the format DD-MM-YYYY in the best possible way?
This is how I would do it naively:
import java.util.*;
public class test {
public static void main(String[] args) {
String date = (String) args[0];
System.out.println(date); //outputs: YYYY-MM-DD
System.out.println(doConvert(date)); //outputs: DD-MM-YYYY
}
public static String doConvert(String d) {
String dateRev = "";
String[] dateArr = d.split("-");
for(int i=dateArr.length-1 ; i>=0 ; i--) {
if(i!=dateArr.length-1)
dateRev += "-";
dateRev += dateArr[i];
}
return dateRev;
}
}
But are there any other, more elegant AND effective way of doing it? Ie. using some built-in feature? I have not been able to find one, while quickly searching the API.
Anyone here know an alternative way?
Use java.util.DateFormat:
DateFormat fromFormat = new SimpleDateFormat("yyyy-MM-dd");
fromFormat.setLenient(false);
DateFormat toFormat = new SimpleDateFormat("dd-MM-yyyy");
toFormat.setLenient(false);
String dateStr = "2011-07-09";
Date date = fromFormat.parse(dateStr);
System.out.println(toFormat.format(date));
Here’s the modern answer.
private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-uuuu");
public static String doConvert(String d) {
return LocalDate.parse(d).format(formatter);
}
With this we may do for example:
System.out.println(doConvert("2017-06-30"));
This prints
30-06-2017
I am exploiting the fact that the format you have, YYYY-MM-DD, conforms with the ISO 8601 standard, a standard that the modern Java date and time classes “understand” natively, so we need no explicit formatter for parsing, only one for formatting.
When this question was asked in 2011, SimpleDateFormat was also the answer I would have given. The newer date and time API came out with Java 8 early in 2014 and has also been backported to Java 6 and 7 in the ThreeTen-Backport project. For Android, see the ThreeTenABP project. So these days honestly I see no excuse for still using SimpleDateFormat and Date. The newer classes are much more programmer friendly and nice to work with.
Best to use a SimpleDateFormat (API) object to convert the String to a Date object. You can then convert via another SimpleDateFormat object to whatever String representation you wish giving you tremendous flexibility.
If you're not looking for String to Date conversion and vice-versa, and thus don't need to handle invalid dates or anything, String manipulation is the easiest and most efficient way. But i's much less readable and maintainable than using DateFormat.
String dateInNewFormat = dateInOldFormat.substring(8)
+ dateInOldFormat.substring(4, 8)
+ dateInOldFormat.substring(0, 4)
import java.util.DateFormat;
// Convert String Date To Another String Date
public String convertStringDateToAnotherStringDate(String stringdate, String stringdateformat, String returndateformat){
try {
Date date = new SimpleDateFormat(stringdateformat).parse(stringdate);
String returndate = new SimpleDateFormat(returndateformat).format(date);
return returndate;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
//-------
String resultDate = convertStringDateToAnotherStringDate("1997-01-20", "yyyy-MM-dd", "MM/dd/yyyy")
System.out.println(resultDate);
Result (date string) : 01/20/1997