Java, change date format. - java

I want to change the format of date from yyyyMM to yyyy-MM.
I have found out that the two ways below works equally well. But which one is the best? I would prefer methodTwo since it is simplier, but is there any advantage using methodOne?
public String date;
public void methodOne()
{
String newDate = date;
DateFormat formatter = new SimpleDateFormat("yyyyMM");
DateFormat wantedFormat = new SimpleDateFormat("yyyy-MM");
Date d = formatter.parse(newDate);
newDate = wantedFormat.format(d);
date = newDate;
}
public void methodTwo()
{
date = date.substring(0, 4) + "-" + date.substring(4, 6);
}

You should prefer method one because it can detect if the input date has an invalid format. Method two could lead to problems, when it's not guaranteed that the input date is always in the same format. Method one is also more easy to adjust, when you later want to change either the input or the output format.
It could make sense to use method two if performance really matters, because it should be slightly faster than method one.

I would say that methodOne is much more generic. If you need to change your wanted format again, you only have to change the argument "yyyy-MM" for whatever new format you want, no matter how exotic it is. I found that when you work with people all across the world, methodOne is much more useful and also easier to read than using substrings. Using substrings means that you have no way to make sure that what is coming has the format you expect. You could be splitting in the wrong order.

Get it in a Easy Way:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
String date = sdf.format(yourDate);

Using the SimpleDateFormat is the industry standard. It is easier to read and maintain in the future as it allows the format to be changed with ease.

java.time.YearMonth
There is a third way: Use the YearMonth class built into Java 8 and later as part of the java.time framework.
You don't have a date and a time, so using a date-time class such as java.util.Date or even the java.time types (Instant, OffsetDateTime, ZonedDateTime) is not a good fit. As you have only a year and a month use, well, YearMonth – a perfect fit.
Rather than pass Strings around your code, pass these YearMonth values. Then you get type-safety and valid values.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuuyy");
YearMonth ym = YearMonth.parse( input );
The default format used by the YearMonth::toString method uses the standard ISO 8601 format you desire.
String output = ym.toString();

Related

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.

Portable way to guarantee that the year field of a DateFormat is only two digits

Currently, I'm having
private ThreadLocal<DateFormat> shortDateFormat = new ThreadLocal<DateFormat>() {
#Override protected DateFormat initialValue() {
final DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT);
return format;
}
};
Using my Android 4.1, this provides me date in format (In my localization. It may look different for other countries)
19/07/2013
However, sometimes I would like to have a much shorter version like 19/07/13
I do not want to hard code as
dd/MM/yy
As the above way would not portable across different countries. Some countries, their month come before date.
Is there any portable way to achieve so?
p/s Not only month/date order. There might be other problem as well. For instance, China is using 19-07-13 or 19-07-2013. There might be more edge cases for other countries, but I don't know.
SimpleDateFormat dateFormat= (SimpleDateFormat) DateFormat.getDateInstance();
dateFormat.applyPattern(dateFormat.toPattern().replaceAll("y{4}", "yy"));
Explanation:
applyPattern(String pattern) applies the given pattern string to this date format.
dateFormat.toPattern() gets the current pattern
dateFormat.toPattern().replaceAll(String regex, String replacement) returns the current pattern, with regex replaced by replacement.
"y{4}" looks through the date format pattern for a series of 4 y's, and
"yy" says that if you see 4 y's, replace them with 2 instead.
Hope that helped. Good luck.
EDIT:
As MH pointed out, since this is for android, it is probably more appropriate to use:
SimpleDateFormat dateFormat = (SimpleDateFormat)
android.text.format.DateFormat.getDateFormat(getApplicationContext());
This should work fine, since the above method call returns a DateFormat of java.text.DateFormat, not of android.text.format.DateFormat.
You should take a look at the functionality android.text.format.DateFormat provides, on top of the java.text.DateFormat.
In particular, the following method will be of interest:
getDateFormatOrder(Context context)
Javadoc:
Gets the current date format stored as a char array. The array will
contain 3 elements (DATE, MONTH, and YEAR) in the order specified by
the user's format preference. Note that this order is only appropriate
for all-numeric dates; spelled-out (MEDIUM and LONG) dates will
generally contain other punctuation, spaces, or words, not just the
day, month, and year, and not necessarily in the same order returned
here.
In other words, the method allows you to determine what order the day, month and year fields are in, according to the user's preference (which triumphs the user's locale, if you ask me). From there it should easy enough to figure out what 'short' format to use; i.e. dd/MM/yy or MM/dd/yy.
As pointed out by the documentation, the return value of the method is only useful in the context of all-numeric date representations. That should be fine in your case.
If you want portable, rather than using the date object, you could instead create an array with month,date, and year. (I would just use the cal object and access each of the three individually)
Calendar cal = Calendar.getInstance();
int year =cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int day= cal.get(Calendar.DAY);
dateArray[0] = month;
dateArray[1] = year;
dateArray[2] = day;
How about creating a map with localized patterns based on country ISO code and a fallback default pattern in case you don't have a specific country defined?

Convert Java Date into XML Date Format (and vice versa)

Is there a nice and easy way to convert a Java Date into XML date string format and vice versa?
Cheers,
Andez
Original answer
I am guessing here that by "XML Date Format" you mean something like "2010-11-04T19:14Z". It is actually ISO 8601 format.
You can convert it using SimpleDateFormat, as others suggested, FastDateFormat or using Joda Time which was I believe especially created for this purpose.
Edit: code samples and more
As earnshae stated in a comment, this answer could be improved with examples.
First, we have to make clear that the original answer is pretty outdated. It's because Java 8 introduced the classes to manipulate date and time - java.time package should be of interest. If you are lucky enough to be using Java 8, you should use one of them. However, these things are surprisingly difficult to get right.
LocalDate(Time) that isn't
Consider this example:
LocalDateTime dateTime = LocalDateTime.parse("2016-03-23T18:21");
System.out.println(dateTime); // 2016-03-23T18:21
At first it may seem that what we're using here is a local (to the user date and time). However, if you dare to ask, you'll get different result:
System.out.println(dateTime.getChronology()); // ISO
This actually, the ISO time. I believe it should read 'UTC' but nonetheless this has no notion of local time zone. So we should consider it universal.
Please notice, that there is no "Z" at the end of the string we are parsing. Should you add anything apart of date and time, you'll be greeted with java.time.format.DateTimeParseException. So it seems that this class is of no use if we want to parse ISO8601 string.
ZonedDateTime to the rescue
Fortunately, there is a class that allows for parsing ISO8601 strings - it's a java.time.ZonedDateTime.
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2016-03-23T18:21+01:00");
System.out.println(zonedDateTime); // 2016-03-23T18:21+01:00
ZonedDateTime zonedDateTimeZulu = ZonedDateTime.parse("2016-03-23T18:21Z");
System.out.println(zonedDateTimeZulu); // 2016-03-23T18:21Z
The only problem here is, you actually need to use time zone designation. Trying to parse raw date time (i.e. "2016-03-23T18:21") will result in already mentioned RuntimeException. Depending on the situation you'd have to choose between LocalDateTime and ZonedDateTime.
Of course you can easily convert between those two, so it should not be a problem:
System.out.println(zonedDateTimeZulu.toLocalDateTime()); // 2016-03-23T18:21
// Zone conversion
ZonedDateTime cetDateTime = zonedDateTimeZulu.toLocalDateTime()
.atZone(ZoneId.of("CET"));
System.out.println(cetDateTime); // 2016-03-23T18:21+01:00[CET]
I recommend using this classes nowadays. However, if your job description includes archeology (meaning you are not lucky enough to be working with more than 2 year old Java 8...), you may need to use something else.
The joy of SimpleDateFormat
I am not a very big fan of https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html, but sometimes you just have no other choice. Problem is, it is not thread-safe and it will throw a checked Exception (namely ParseException) in your face if it dislikes something. Therefore the code snippet is rather ugly:
private Object lock = new Object();
// ...
try {
synchronized (lock) {
// Either "2016-03-23T18:21+01:00" or "2016-03-23T18:21Z"
// will be correctly parsed (mind the different meaning though)
Date date = dateFormat.parse("2016-03-23T18:21Z");
System.out.println(date); // Wed Mar 23 19:21:00 CET 2016
}
} catch (ParseException e) {
LOG.error("Date time parsing exception", e);
}
FastDateFormat
FastDateFormat is synchronized, therefore you can at least get rid of the synchronized block. However, it is an external dependency. But since it's the Apache Commons Lang and it is thoroughly used, I guess it is acceptable. It is actually very similar in usage to SimpleDateFormat:
FastDateFormat fastDateFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mmX");
try {
Date fastDate = fastDateFormat.parse("2016-03-23T18:21+01:00");
System.out.println(fastDate);
} catch (ParseException e) {
LOG.error("Date time parsing exception", e);
}
JodaTime
With Joda-Time you may think that following works:
DateTimeFormatter parser = ISODateTimeFormat.dateTimeParser();
LocalDateTime dateTime = LocalDateTime.parse("2016-03-23T20:48+01:00", parser);
System.out.println(dateTime); // 2016-03-23T20:48:00.000
Unfortunately, no matter what you put at last position (Z, +03:00, ...) the result will be the same. Clearly, it isn't working.
Well, you really should be parsing it directly:
DateTimeFormatter parser = ISODateTimeFormat.dateTimeParser();
DateTime dateTime = parser.parseDateTime("2016-03-23T21:12:23+04:00");
System.out.println(dateTime); // 2016-03-23T18:12:23.000+01:00
Now it will be OK. Please note, that unlike one of other answers, I used dateTimeParser() and not dateTime(). I noticed subtle, but important difference in behavior between them (Joda-Time 2.9.2). But, I leave it to the reader to test it and confirm.
As already suggested use SimpleDateFormat.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
String date = sdf.format(new Date());
System.out.println(date);
Date d = sdf.parse(date);
My guess is that the format/pattern that your looking for is yyyy-MM-dd'T'HH:mm:ss
Also have a look at http://www.w3schools.com/schema/schema_dtypes_date.asp
Using Joda Time you would do the following:
DateTimeFormatter fmt = ISODateTimeFormat.dateTime(); // ISO8601 (XML) Date/time
DateTime dt = fmt.parseDateTime("2000-01-01T12:00:00+100"); // +1hr time zone
System.out.println(fmt.print(dt)); // Prints in ISO8601 format
Thread safe, immutable and simple.
The Perfect method, use XMLGregorianCalendar:
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(v);
DatatypeFactory df = DatatypeFactory.newInstance();
XMLGregorianCalendar dateTime = df.newXMLGregorianCalendar(calendar);
return dateTime.toString();
Just by using SimpleDateFormat in java we can do this...
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date date = sdf.parse("2011-12-31T15:05:50+1000");
I would recommend to use the java built in class javax.xml.bind.DatatypeConverter. It can handle conversion to and from most of the xml simple types. It is a little bit cumbersome for dates that you have to go through a Calendar object but on the other hand it handles all variants of zone information that can occur in a xml datetime field.
From xml:
Calendar c = DatatypeConverter.parseDateTime("2015-10-21T13:25");
Date d = c.getTime();
To xml:
Date yourDate = new Date()
Calendar c = Calendar.getInstance();
c.setTime(yourDate);
String xmlDateTime = DatatypeConverter.printDateTime(c);
EDIT
The DatatypeConverter class is no longer publicly visible in Java 9 and above since it belongs to the javax.xml.bind package. See this question for more information and possible solutions. The solution proposed by loic vaugeois to use XmlGregorianCalendar is much better in this case.
You can parse and format dates to and from any format using SimpleDateFormat
To comply with ISO8601, the timezone must be in the format +HH:MM or - HH:MM
With Simpledateformat you must use XXX instead of Z (see NovelGuy answer)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Without knowing exactly what format you need, the generic response is: you're going to want DateFormat or SimpleDateFormat. There is a nice tutorial on both here.

Categories

Resources