Formatting date in Java using SimpleDateFormat - java

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.

Related

Is there a simple way to change a timestamp value containing seconds and ms to a timestamp value having hours and minutes?

So I have an object ('Task') that has an attribute 'Start Date' which is basically a Timestamp object. So this date is in this format 'YYYY/MM/dd hh:mm:ss:ms'. But for a test case I am authoring, I need this date to be in this format 'YYYY/MM/dd hh:mm'. Also it needs to be a timestamp object as I have to set this value back to the 'Task' object.
I have tried several approaches including the snippet shown below:
SimpleDateFormat formatter = new SimpleDateFormat("YYYY-MM-dd hh:mm");
if (task.getStartDate() != null) {
String newDate = formatter.format(task.getStartDate());
Date date = formatter.parse(newDate);
task.setStartDate(new Timestamp(date.getTime()));
}
I expected the value of the timestamp to be in the format '2018-12-30 09:54' but it resulted in '2018-12-30 09:54:00.0'. So the questions that I have in mind is:
Is there a way to not consider the seconds and millis in the Timestamp object?
If no, then, is the snippet provided an efficient way to update the Timestamp object?
TL;DR
Avoid the Timestamp class if you can. It’s poorly designed and long outdated.
To answer your questions, no, a Timestamp hasn’t got, as in cannot have a format (the same holds true for its modern replacement, Instant (or LocalDateTime)).
Under all circumstances avoid SimpleDateFormat and Date. The former in particular is notoriously troublesome, and both are long outdated too.
Don’t put a format into your model class
You should not want an Instant nor a Timestamp with a specific format. Good practice in all but the simplest throw-away programs is to keep your user interface apart from your model and your business logic. The value of the Instant object belongs in your model, so keep your Instant or Timestamp there and never let the user see it directly. I hope that it’s clear to you that 2018-12-30 09:54 and 2018-12-30 09:54:00.0 represent the same value, the same Timestamp. Just like 17, 0017 and 0x11 represent the same integer value. When you adhere to what I said, it will never matter which format the Instant has got.
Whenever the user should see the date and time, this happens in the UI, not in the model. Format it into a String and show the string to the user. Similarly if you need a specific format for persistence or exchange with another system, format the Instant into a string for that purpose.
java.time and JDBC 4.2
Also for exchange with your database over JDBC, provided that you’ve got a JDBC 4.2 compliant driver, prefer to use a type from java.time over Timestamp. If the datatype on the database side is timestamp with time zone, very clearly recommended for a timestamp, pass an OffsetDateTime like
OffsetDateTime dateTime = yourInstant.atOffset(ZoneOffset.UTC);
yourPreparedStatement.setObject(4, dateTime);
Use setObject, not setTimestamp. Some drivers accept the Instant directly, without conversion to OffsetDateTime. If on the database side you need a mere timestamp (without time zone), use LocalDateTime in Java instead and pass one to setObject in the same way as above.
PS There are errors in your format pattern string
In a format pattern string, uppercase YYYY is for week based year and only useful with a week number. For year use either uuuu or lowercase yyyy. Similarly lowercase hh is for hour within AM or PM from 01 through 12 and only useful with an AM or PM marker. For hour of day from 00 through 23 you need uppercase HH. These errors will give you incorrect dates and times in most cases. Using the wrong case of format pattern letters is a very common mistake. SimpleDateFormat generally doesn’t mind, it just gives incorrect results. The modern DateTimeFormatter does a somewhat better job of notifying you of such errors.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related questions
Formatting timestamp in Java about getting rid of the .0 decimal on the second of a Timestamp.
timestamp formatting in scala [duplicate] about getting a Timestamp with only date and hour (no minute, second or fraction of second).
java parsing string to date about uppercase Y for year in a format pattern string.
Comparing two times in android about lowercase h for hour of day in a format pattern string.

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.

Converting a time String to ISO 8601 format

I am trying to create a String in a format like 2015-08-20T08:26:21.000Z
to 2015-08-20T08:26:21Z
I know it can be done with some String splitting techniques, but i am wondering if there is an elegant solution for that (with minimal code changes).
Both of the above are time strings, the final one which i need is Date in ISO 8601 . https://www.rfc-editor.org/rfc/rfc3339#section-5.6
I have tried a few similar questions like converting a date string into milliseconds in java but they dont actually solve the purpose.
Also tried using :
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ");
String nowAsString = df.format(new Date());
But it still does not do any String to String conversions. Getting the following error:
23:04:13,829 WARN [RuntimeExceptionMapper] caught RuntimeException: {}: java.lang.IllegalArgumentException: Cannot format given Object as a Date
Is there some library which someone can suggest ?
Thanks.
tl;dr
Instant.parse( "2015-08-20T08:26:21.000Z" )
.toString()
2015-08-20T08:26:21Z
Date-Time Formatter
If all you want to do is eliminate the .000, then use date-time objects to parse your input string value, then generate a new string representation of that date-time value in a different format.
ISO 8601
By the way, if that is your goal, the Question’s title make no sense as both strings mentioned in the first sentence are valid ISO 8601 formatted strings.
2015-08-20T08:26:21.000Z
2015-08-20T08:26:21Z
java.time
Java 8 and later has the new java.time package. These new classes supplant the old java.util.Date/.Calendar & java.text.SimpleDateFormat classes. Those old classes were confusing, troublesome, and flawed.
Instant
If all you want is UTC time zone, then you can use the Instant class. This class represents a point along the timeline without regard to any particular time zone (basically UTC).
DateTimeFormatter.ISO_INSTANT
Calling an Instant’s toString generates a String representation of the date-time value using a DateTimeFormatter.ISO_INSTANT formatter instance. This formatter is automatically flexible about the fractional second. If the value has a whole second, no decimal places are generated (apparently what the Question wants). For a fractional second, digits appear in groups of 3, 6, or 9, as needed to represent the value up to nanosecond resolution. Note: this format may exceed ISO 8601 limit of milliseconds (3 decimal places).
Example code
Here is some example code in Java 8 Update 51.
String output = Instant.parse( "2015-08-20T08:26:21.000Z" ).toString( );
System.out.println("output: " + output );
output: 2015-08-20T08:26:21Z
Changing to a fractional second, .08
String output = Instant.parse( "2015-08-20T08:26:21.08Z" ).toString( );
output: 2015-08-20T08:26:21.080Z
If interested in any time zone other than UTC, then make a ZonedDateTime object from that Instant.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , ZoneId.of( "America/Montreal" ) ) ;
Your format is just not right try this :-
try {
String s = "2015-08-20T08:26:21.000Z";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
Date d = df.parse(s);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
System.out.println(sdf.format(d));
} catch (ParseException e) {
e.printStackTrace();
}
Conversion of a date String of unknown formatting into a date String that uses known formatting can be accomplished using two DateFormat objects- one dynamically configured to parse the format of the input String, and one configured to generate the formatted output String. For your situation the input String formatting is unspecified and must be provided by the caller, however, the output String formatting can be configured to use ISO 8601 formatting without additional input. Essentially, generating an ISO 8601 formatted date String output requires two inputs provided by the caller- a String containing the formatted date and another String that contains the SimpleDateFormat format.
Here is the described conversion as Java code (I deliberately have left out null checks and validations, add these as appropriate for your code):
private String formatDateAsIso8601(final String inputDateAsString, final String inputStringFormat) throws ParseException {
final DateFormat iso8601DateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.ENGLISH);
iso8601DateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
final DateFormat inputDateFormatter = new SimpleDateFormat(inputStringFormat, Locale.ENGLISH);
final Date inputDate = inputDateFormatter.parse(inputDateAsString);
return iso8601DateFormatter.format(inputDate);
}
If you want to modify that method please note that SimpleDateFormat is not thread-safe, and that you should not use it from a static context without a workaround for multi-threaded code (ThreadLocal is commonly used to do just such a workaround for SimpleDateFormat).
An additional "gotcha" is the use of a Locale during the construction of the SimpleDateFormat objects- do not remove the Locale configuration. It is not safe to allow the system to choose to use the default Locale because that is user/machine specific. If you do allow it to use the default Locale, you run the risk of transient bugs because your development machine uses a Locale different than the Locale of your end-user. You do not have to use my selected ENGLISH Locale, it is perfectly fine to use a different Locale (you should understand the rules of that Locale and modify the code as appropriate however). Specification of no Locale and utilization of the system default is incorrect however, and likely will lead to many frustrating hours trying to diagnose an elusive bug.
Please understand this solution is not ideal as of Java 8 and the inclusion of the JodaTime based classes, like Instant. I chose to answer using the outdated API's because those were what you seemed concerned with in your question. If you are using Java 8 I strongly urge to learn and utilize the new classes as they are an improvement in almost every conceivable way.

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.

Categories

Resources