How to parse only 4 digit years - java

I'm using Joda-Time to parse years like this:
private DateTime attemptParse(String pattern, String date) {
DateTimeFormatter parser = DateTimeFormat.forPattern(pattern).withLocale(Locale.ENGLISH);
DateTime parsedDateTime = parser.parseLocalDateTime(date).toDateTime(WET);
return parsedDateTime;
}
I'm trying to parse multiple formats: "yyyy-MM-dd", "yyyy-MMM-dd","yyyy MMM dd-dd","yyyy MMM", (etc), "yyyy". When one doesn't work, I try the next one.
And it works like a charm when the string is indeed only 4 digits (e.g: "2016"). The problem is that I sometimes receive things like this: "201400". And Joda-Time matches this with "yyyy" pattern and returns a date with year 201400.
I wanted to avoid the ugly if to check if year > 9999. Is there any way to do this using Joda-Time?

To parse multiple formats, you can create lots of DateTimeParser instances and join all in one single formatter (instead of trying one after another).
This will require a DateTimeFormatterBuilder, which will also be used to enforce a specific number of digits in the input (unfortunately, there's no way to enforce a specific number of digits like you want using just DateTimeFormat.forPattern()).
First you create lots of org.joda.time.format.DateTimeParser instances (one for each possible pattern):
// only yyyy
DateTimeParser p1 = new DateTimeFormatterBuilder()
// year with exactly 4 digits
.appendYear(4, 4).toParser();
// yyyy-MM-dd
DateTimeParser p2 = new DateTimeFormatterBuilder()
// year with exactly 4 digits
.appendYear(4, 4)
// rest of the pattern
.appendPattern("-MM-dd").toParser();
// yyyy MMM
DateTimeParser p3 = new DateTimeFormatterBuilder()
// year with exactly 4 digits
.appendYear(4, 4)
// rest of the pattern
.appendPattern(" MMM").toParser();
Then you create an array with all these patterns and create a DateTimeFormatter with it:
// create array with all the possible patterns
DateTimeParser[] possiblePatterns = new DateTimeParser[] { p1, p2, p3 };
DateTimeFormatter parser = new DateTimeFormatterBuilder()
// append all the possible patterns
.append(null, possiblePatterns)
// use the locale you want (in case of month names and other locale sensitive data)
.toFormatter().withLocale(Locale.ENGLISH);
I also used Locale.ENGLISH (as you're also using it in your question's code). This locale indicates that the month names will be in English (so MMM can parse values like Jan and Sep). With this, you can parse the inputs:
System.out.println(parser.parseLocalDateTime("2014")); // OK
System.out.println(parser.parseLocalDateTime("201400")); // exception
System.out.println(parser.parseLocalDateTime("2014-10-10")); // OK
System.out.println(parser.parseLocalDateTime("201400-10-10")); // exception
System.out.println(parser.parseLocalDateTime("2014 Jul")); // OK
System.out.println(parser.parseLocalDateTime("201400 Jul")); // exception
When the year is 2014, the code works fine. When it's 201400, it throws a java.lang.IllegalArgumentException, such as:
java.lang.IllegalArgumentException: Invalid format: "201400" is malformed at "00"
DateTimeFormatter is immutable and thread-safe, so you don't need to create it every time your validation method is called. You can create it outside of the method (such as in a static final field).
This is better than creating one formatter everytime you perform a validation, and going to the next one when an exception occurs. The formatter created already does it internally, going to the next pattern until it finds one that works (or throwing the exception if all patterns fail).
Java new Date/Time API
Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
If you can't (or don't want to) migrate from Joda-Time to the new API, you can ignore this section.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
This new API is much more strict than the previous ones, so the formatter only works with the exact number of digits (note that some classes are very similar to Joda-Time):
// 4 digits in year
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy", Locale.ENGLISH);
fmt.parse("2014"); // OK
fmt.parse("201400"); // exception
fmt.parse("201"); // exception
This code works with 2014, but with 201400 or 201 (or any other value without exactly 4 digits) it throws an exception:
java.time.format.DateTimeParseException: Text '201400' could not be parsed at index 0
With this, your validation code could work with the array of strings.
There's only one detail: when parsing to a date, Joda-Time sets default values when the input doesn't have some fields (like month becomes January, day becomes 1, hour/minute/second are set to zero, etc).
If you are just validating the input, then you don't need to return anything. Just check if the exception is thrown and you'll know if the input is valid or not.
If you just need the year value, though, you can use the Year class:
DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy", Locale.ENGLISH);
System.out.println(Year.parse("2014", parser)); // ok
System.out.println(Year.parse("201400", parser)); // exception
If you want the year value as an int:
Year year = Year.parse("2014", parser);
int yearValue = year.getValue(); // 2014
But if you want to get a date object, you'll need to set the default values manually - the new API is very strict and don't set those values automatically. In this case, you must set the default values, by using a DateTimeFormatterBuilder.
I also parse it to a LocalDateTime, just as example:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// string pattern
.appendPattern("yyyy")
// default month is January
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
// default day is 1
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
// default hour is zero
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
// default minute is zero
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
// set locale
.toFormatter(Locale.ENGLISH);
// create LocalDateTime
System.out.println(LocalDateTime.parse("2014", fmt)); // 2014-01-01T00:00
System.out.println(LocalDateTime.parse("201400", fmt)); // exception
You can choose whatever values you want as the default for the fields, and use any of the new available date types.

What you are saying is that Jodatime should somehow guess that it should parse "201400" as 2014. I don't think that's reasonably within the scope of that library. You should pre-process the data yourself, for example by using:
String normalizedDate = String.format("%4s", date).trim();

Related

How can I format day and month in the locale-correct order in Java?

Is there a way to format a day and month (in compact form), but not year, in the locale-correct order in Java/Kotlin? So for English it should be "Sep 20" but for Swedish "20 sep.".
For comparison, on Cocoa platforms, I can do the following (in Swift):
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "sv_SE")
formatter.setLocalizedDateFormatFromTemplate("MMM d")
print(formatter.string(from: Date()))
This will correctly turn things around. Is there an equivalent thing to do with the Java SDKs? I've been trying various forms with both DateTimeFormatter and the older SimpleTimeFormat APIs, but no success.
Notes: Unlike this question, I don't want the full medium format that includes the year. I also don't want either DateTimeFormatter.ofPattern("MMM d"), since that gives the incorrect result in Swedish, or DateTimeFormatter.ofPattern("d MMM"), since that gives the incorrect result in English.
No, sorry. I know of no Java library that will automatically turn "MMM d" around into 20 sep. for a locale that prefers the day of month before the month abbreviation.
You may try modifying the answer by Rowi in this way:
DateTimeFormatter ft =
DateTimeFormatter
.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.forLanguageTag("sv-SE"))
;
However the result is:
20 sep. 2019
It includes the year, which you didn’t ask for.
An advanced solution would use the DateTimeFormatterBuilder class to build DateTimeFormatter objects.
DateTimeFormatterBuilder
.getLocalizedDateTimePattern(
FormatStyle.MEDIUM,
null,
IsoChronology.INSTANCE,
Locale.forLanguageTag("sv-SE")
)
This returns d MMM y. Modify this string to delete the y and the space before it. Note that in other languages the y may be yy, yyyy or u and may not come last in the string. Pass your modified format pattern string to DateTimeFormatter.ofPattern.
It may be shaky. Even if you look through the format pattern strings for all available locales, the next version of CLDR (where the strings come from) might still contain a surprise. But I think it’s the best we can do. If it were me, I’d consider throwing an exception in case I can detect that the string from getLocalizedDateTimePattern doesn’t look like one I know how to modify.
You can do it in Java using LocalDate:
LocalDate dt = LocalDate.parse("2019-09-20");
System.out.println(dt);
DateTimeFormatter ft = DateTimeFormatter.ofPattern("dd MMM", new Locale("sv","SE"));
System.out.println(ft.format(dt));
You could get the DateFormat's pattern and remove the year:
val locale: Locale
val datePattern = (DateFormat.getDateInstance(DateFormat.MEDIUM, locale) as SimpleDateFormat).toPattern()
.replace("y", "").trim { it < 'A' || it > 'z' }

Parse java.time trying multiple patterns

We have a library where users can pass in dates in multiple formats. They follow the ISO but are abbreviated at times.
So we get things like "19-3-12" and "2019-03-12T13:12:45.1234" where the fractional seconds can be 1 - 7 digits long. It's a very large number of combinations.
DateTimeFormatter.parseBest doesn't work because it won't accept "yy-m-d" for a local date. The solutions here won't work because it assumes we know the pattern - we don't.
And telling people to get their string formats "correct" won't work as there's a ton of existing data (these are mostly in XML & JSON files).
My question is, how can I parse strings coming in in these various pattersn without have to try 15 different explicit patterns?
Or even better, is there some way to parse a string and it will try everything possible and return a Temporal object if the string makes sense for any date[time]?
Without a full specification it is hard to give a precise recommendation. The techniques generally used for variable formats include:
Trying a number of known formats in turn.
Optional parts in the format pattern.
DateTimeFormatterBuilder.parseDefaulting() for parts that may be absent from the parsed string.
As you are aware, parseBest.
I am assuming that y-M-d always come in this order (never M-d-y or d-M-y, for example). 19-3-12 conflicts with ISO 8601 since the standard requires (at least) 4 digit year and 2 digit month. A challenge with 2-digit year is guessing the century: is this 1919 or 2019 or might it be 2119?
The good news: presence and absence of seconds and varying number of fractional digits are all built-in and pose no problem.
From what you have told us it seems to me that the following is a fair shot.
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("[uuuu][uu]-M-d")
.optionalStart()
.appendLiteral('T')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.optionalEnd()
.toFormatter();
TemporalAccessor dt = formatter.parseBest("19-3-12", LocalDateTime::from, LocalDate::from);
System.out.println(dt.getClass());
System.out.println(dt);
Output:
class java.time.LocalDate
2019-03-12
I figure that it should work with the variations of formats that you describe. Let’s just try your other example:
dt = formatter.parseBest( "2019-03-12T13:12:45.1234", LocalDateTime::from, LocalDate::from);
System.out.println(dt.getClass());
System.out.println(dt);
class java.time.LocalDateTime
2019-03-12T13:12:45.123400
To control the interpretation of 2-digit year you may use one of the overloaded variants of DateTimeFormatterBuilder.appendValueReduced(). I recommend that you consider a range check on top of it.
Trying all the possible formats would perform worse than trying only 15.
You can try to "normalize" to a single format but then you would be doing the work those 15 formats are supposed to do.
I think the best approach is the one described by #JB Nizet, to try only patterns that match string length.
public Date parse(String openFormat) {
String[] formats = {"YYY-MM-DD"};
switch(openFormat.length()) {
case 24: // 2019-03-12T13:12:45.1234
formats = new String[]{"YYY-MM-DDThh:mm:ssetcetc", }; // all the formats for length 24
break;
...
case 6: //YYY-MM-DD, DD-MM-YYYY
formats = new String[]{YYY-MM-DD", "DD-MM-YYYY", }; // all the formats for length 6
break;
}
Date myDate
// now try the reduced number of formats, possibly only 1 or 2
for( String format : formats) try {
myDate = date parse ( format ) etcetc
} catch (DateFormatException d) {
continue;
}
if (myDate == null){
throw InvalidDate
} else {
return myDate
}
}

Formatting date in Java using SimpleDateFormat

I am trying to parse a date into an appropriate format, but I keep getting the error
Unparseable date
Can anyone tell me what the mistake is?
try {
System.out.println(new SimpleDateFormat("d-MMM-Y").parse("05-03-2018").toString());
} catch (ParseException e) {
e.printStackTrace();
}
I want the date to have this format:
05-Mar-18
Since you want to change the format, first read and parse the date (from String) of your own format in a Date type object. Then use that date object by formatting it into a new (desired) format using a SimpleDateFormat.
The error in your code is with the MMM and Y. MMM is the month in string while your input is a numeric value. Plus the Y in your SimpleDateFormat is an invalid year. yy is what needs to be added.
So here is a code that would fix your problem.
SimpleDateFormat dateFormat = new SimpleDateFormat("d-MM-yyyy");
Date date = dateFormat.parse("05-03-2018");
dateFormat = new SimpleDateFormat("dd-MMM-yy");
System.out.println(dateFormat.format(date));
I hope this is what you're looking for.
There are some concepts about dates you should be aware of.
There's a difference between a date and a text that represents a date.
Example: today's date is March 9th 2018. That date is just a concept, an idea of "a specific point in our calendar system".
The same date, though, can be represented in many formats. It can be "graphical", in the form of a circle around a number in a piece of paper with lots of other numbers in some specific order, or it can be in plain text, such as:
09/03/2018 (day/month/year)
03/09/2018 (monty/day/year)
2018-03-09 (ISO8601 format)
March, 9th 2018
9 de março de 2018 (in Portuguese)
2018年3月5日 (in Japanese)
and so on...
Note that the text representations are different, but all of them represent the same date (the same value).
With that in mind, let's see how Java works with these concepts.
a text is represented by a String. This class contains a sequence of characters, nothing more. These characters can represent anything; in this case, it's a date
a date was initially represented by java.util.Date, and then by java.util.Calendar, but those classes are full of problems and you should avoid them if possible. Today we have a better API for that.
With the java.time API (or the respective backport for versions lower than 8), you have easier and more reliable tools to deal with dates.
In your case, you have a String (a text representing a date) and you want to convert it to another format. You must do it in 2 steps:
convert the String to some date-type (transform the text to numerical day/month/year values) - that's called parsing
convert this date-type value to some format (transform the numerical values to text in a specific format) - that's called formatting
For step 1, you can use a LocalDate, a type that represents a date (day, month and year, without hours and without timezone), because that's what your input is:
String input = "05-03-2018";
DateTimeFormatter inputParser = DateTimeFormatter.ofPattern("dd-MM-yyyy");
// parse the input
LocalDate date = LocalDate.parse(input, inputParser);
That's more reliable than SimpleDateFormat because it solves lots of strange bugs and problems of the old API.
Now that we have our LocalDate object, we can do step 2:
// convert to another format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yy", Locale.ENGLISH);
String output = date.format(formatter);
Note that I used a java.util.Locale. That's because the output you want has a month name in English, and if you don't specify a locale, it'll use the JVM's default (and who guarantees it'll always be English? it's better to tell the API which language you're using instead of relying on the default configs, because those can be changed anytime, even by other applications running in the same JVM).
And how do I know which letters must be used in DateTimeFormatter? Well, I've just read the javadoc. Many developers ignore the documentation, but we must create the habit to check it, specially the javadoc, that tells you things like the difference between uppercase Y and lowercase y in SimpleDateFormat.

Formatting MonthDay using DateTimeFormatter.ofLocalizedDate [duplicate]

This question already has answers here:
How to format YearMonth and MonthDay depending on a Locale?
(3 answers)
Closed 5 years ago.
I am trying to format a MonthDay object in a way that I do not have to specify the order. I am trying to use a localized DateTimeFormatter.
I have this code:
LocalDate datetime = LocalDate.parse("2017-08-11", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
MonthDay monthday = MonthDay.from(datetime);
System.out.println(monthday.format(DateTimeFormatter.ofPattern("MMMM dd").withLocale(Locale.ENGLISH)));
System.out.println(monthday.format(DateTimeFormatter.ofPattern("MMMM dd").withLocale(Locale.GERMANY)));
System.out.println(monthday.format(DateTimeFormatter.ofPattern("MMMM dd").withLocale(Locale.forLanguageTag("UK"))));
System.out.println(datetime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.ENGLISH)));
System.out.println(datetime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.forLanguageTag("UK"))));
// next line throws exception for java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra
System.out.println(monthday.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.forLanguageTag("UK"))));
The first 3 prints will print as expected the translated Month and day, but it is always month and then day. It does not change the order because I am explicitly telling it the order.
The next two (before the exception) would print respectively:
Aug 11, 2017
11 серп. 2017
Notice how the day is either before or after the month depending on the locale passed to the function. How do I do this with a MonthDay object as the last line throws an exception when done in this way.
The other answers given so far describe the limitations of the standard DateTimeFormatter, see also the unsolved related JDK-issue. The suggested workaround to edit the localized date pattern by removing "y" etc. is tricky and might not work for all locales due to the existence of other localized literals inside the pattern.
However, you might also consider using external libraries which have a stronger focus on internationalization issues and have the capability to format a month-day-object using just the locale information. So the locale determines the order of field components and also dots, spaces or other special literals (like in Chinese).
Here two options with the necessary type conversions related to your system timezone:
ICU4J
MonthDay md = MonthDay.now();
GregorianCalendar gcal =
new GregorianCalendar(
2000, // avoids possible leap year problems
md.getMonthValue() - 1,
md.getDayOfMonth()
);
DateFormat df =
DateFormat.getInstanceForSkeleton(
DateFormat.ABBR_MONTH_DAY,
Locale.forLanguageTag("en")
);
System.out.println(df.format(gcal.getTime())); // Aug 15
DateFormat df2 =
DateFormat.getInstanceForSkeleton(
DateFormat.ABBR_MONTH_DAY,
Locale.forLanguageTag("de")
);
System.out.println(df2.format(gcal.getTime())); // 15. Aug.
DateFormat df3 =
DateFormat.getInstanceForSkeleton(DateFormat.MONTH_DAY, Locale.forLanguageTag("zh"));
System.out.println(df3.format(gcal.getTime())); // 8月15日
Time4J
MonthDay md = MonthDay.now();
ChronoFormatter<AnnualDate> cf1 =
ChronoFormatter.ofStyle(DisplayMode.SHORT, Locale.GERMAN, AnnualDate.chronology());
System.out.println(cf1.format(AnnualDate.from(md))); // 15.8.
ChronoFormatter<AnnualDate> cf2 =
ChronoFormatter.ofStyle(DisplayMode.MEDIUM, Locale.GERMAN, AnnualDate.chronology());
System.out.println(cf2.format(AnnualDate.from(md))); // 15.08.
ChronoFormatter<AnnualDate> cf3 =
ChronoFormatter.ofStyle(DisplayMode.LONG, Locale.ENGLISH, AnnualDate.chronology());
System.out.println(cf3.format(AnnualDate.from(md))); // Aug 15
ChronoFormatter<AnnualDate> cf4 =
ChronoFormatter.ofStyle(DisplayMode.FULL, Locale.GERMAN, AnnualDate.chronology());
System.out.println(cf4.format(AnnualDate.from(md))); // 15. August
ChronoFormatter<AnnualDate> cf5 =
ChronoFormatter.ofStyle(DisplayMode.FULL, Locale.CHINESE, AnnualDate.chronology());
System.out.println(cf5.format(AnnualDate.from(md))); // 8月15日
Disclaimer: Time4J has been written by myself to fill gaps or to improve other features of JSR-310 (java.time-package).
MonthDay does not store the year information and the FormatStyle.MEDIUM requires a year value, thats why the formatter is not finding the field YearOfEra same occurs if you use YearMonth with same FormatStyle but now for the missing field DayOfMonth.
MonthDay API
This class does not store or represent a year, time or time-zone. For example, the value "December 3rd" can be stored in a MonthDay.
You could transform monthday to an Instant or simply use the datetime.
ofLocalizedDate returns a formatter for the date, so it formats the day, month and year fields, so the object being formatted needs to have all the three fields. MonthDay doesn't have the year field, that's why it throws a UnsupportedTemporalTypeException.
If you want to print the whole date (with day, month and year), you must add the year to the MonthDay object. You can use the atYear method for that:
System.out.println(monthday.atYear(2017).format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.forLanguageTag("UK"))));
This will output:
11 серп. 2017
If you want the current year, just use Year.now().getValue() instead of 2017.
You can also use datetime.getYear(), if you want the same year of the LocalDate - or use the datetime instead of the monthday.
If you want to print just the day and month, you'll have to do some workarounds.
As the localized formatters are built-in in the JDK (and there seems to be no way to change them), you don't have a direct way of doing it, though there are some alternatives.
One (ugly) solution is to set a year and then remove it from the formatted String:
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.forLanguageTag("UK"));
// set year to 2017, then remove "2017" from the formatted string
System.out.println(monthday.atYear(2017).format(formatter).replace("2017", "").trim());
The output is:
11 серп.
The boring part is to remove all extra characters that might exist in each locale. For the English locale, I also had to remove the ,:
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.ENGLISH);
// remove the year (2017) and the "," from the output
System.out.println(monthday.atYear(2017).format(formatter).replace("2017", "").replace(",", "").trim());
The output is:
Aug 11
You'll have to check all the extra characters (such as ,) for all locales, and remove them from the output. Or just use ofPattern with fixed non-locale specific patterns (as you did in your first 3 tests).
As #BasilBourque's noticed in the comments, I'm assuming that the year is at the beggining or end of the pattern. If the year is in the middle, there will be some extra spaces in the final result, which can be removed with .replaceAll("\\s{2,}", " ") (2 or more spaces are replaced by just one).
Another alternative (as suggested by #JodaStephen comment) is to use a DateTimeFormatterBuilder to get the localized date pattern.
Then I remove the year from the pattern, replacing y and u (the patterns used for the year), and also remove some other characters (like , and extra spaces).
With the resulting pattern (without the year), I create a DateTimeFormatter with the specified locale and format the MonthDay:
// get date pattern for the specified locale
String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.forLanguageTag("UK"));
pattern = pattern
// remove the year (1 or more occurrences of "y" or "u")
.replaceAll("[yu]+", "")
// replace "," (you can change this to remove any other characters you want)
.replaceAll(",", "")
// replace 2 or more spaces with just one space and trim to remove spaces in the start or end
.replaceAll("\\s{2,}", " ").trim();
// create formatter for the pattern and locale
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern, Locale.forLanguageTag("UK"));
System.out.println(monthday.format(fmt));
The output will be:
11 серп.
Just reminding that this example might be incomplete, because there are some locales that uses /, - and other characters as separators and you must remove them from the final result. Check all the locales you're working with and remove the characters accordingly.
Another corner-case not covered by this is when you have y or u as literals (inside '): in this case they shouldn't be removed. Anyway, you'll have to check the formats for all locales you're working with and handle each case accordingly.

Java Date changing format [duplicate]

This question already has answers here:
java.util.Date format conversion yyyy-mm-dd to mm-dd-yyyy
(8 answers)
Closed 5 years ago.
I am trying to change the format of Date objects, I am trying to do it in this way:
for(Date date : dates){
DateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
String formatterDate = formatter.format(date);
Date d = formatter.parse(formatter.format(date));
}
But this does not have any effect on the d object, it is still with the old format, can't really understand why it is like that.
Please try to keep two concepts apart: your data and the presentation of the data to your user (or formatting for other purposes like inclusion in JSON). An int holding the value 7 can be presented as (formatted into) 7, 07, 007 or +7 while still just holding the same value without any formatting information — the formatting lies outside the int. Just the same, a Date holds a point in time, it can be presented as (formatted into) “June 1st 2017, 12:46:01.169”, “2017/06/01” or “1 Jun 2017” while still just holding the same value without any formatting information — the formatting lies outside the Date.
Depending on your requirements, one option is you store your date as a Date (or better, an instance of one of the modern date and time classes like LocalDate) and keep a formatter around so you can format it every time you need to show it to the user. If this won’t work and you need to store the date in a specific format, then store it as a String.
Java 8 (7, 6) date and time API
Now I have been ranting about using the newer Java date and time classes in the comments, so it might be unfair not to show you that they work. The question tries to format as yyyy-MM-dd, which we may do with the following piece of code.
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu/MM/dd");
for (LocalDate date : localDates) {
String formatterDate = date.format(dateFormatter);
System.out.println(formatterDate);
}
In one run I got
2017/05/23
2017/06/01
Should your objects in the list have other types than LocalDate, most other newer date and time types can be formatted in exactly the same way using the same DateTimeFormatter. Instant is a little special in this respect because it doesn’t contain a date, but you may do for example myInstant.atZone(ZoneId.of("Europe/Oslo")).format(dateFormatter) to obtain the date it was/will be in Oslo’s time zone at that instant.
The modern classes were introduced in Java 8 and are enhanced a bit in Java 9. They have been backported to Java 6 and 7 in the ThreeTen Backport with a special edition for Android, ThreeTenABP. So I really see no reason why you should not prefer to use them in your own code.
Try this one.
String formattedDate = null;
SimpleDateFormat sdf = new SimpleDateFormat(format you want);
formattedDate = sdf.format( the date you want to format );
return formattedDate;
some not best solution, but it works: this method will convert Date object to String of any pattern you need
public static void format(Date date){
String pattern = "MMM d yyyy";
LocalDateTime localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String result = formatter.format(localDate);
// new Date() -> Jun 1 2017
}
SimpleDateFormat is useful while converting Date to String or vice-versa. java.util.Date format will always remain same. If you want to display it in standalone application then use date.getxxx() methods and choose your design.

Categories

Resources