Java's SimpleDateFormat is used to format a Date object to a string. The formatter supports various pattern letters, which denote textual representation of a Date field. For example, yy is two-letter year, yyyy is four-letter year, and E is day of week.
For example, A SimpleDateFormat initialized with yyyy.MM.dd G 'at' HH:mm:ss z will format a date to something like 2001.07.04 AD at 12:08:56 PDT.
I would like to add some pattern letters to SimpleDateFormat. For example, want C to denote Hebrew weekday (יום ראשון, יום שני, ...).
What's the right way to extend SimpleDateFormat with these new pattern letters? The only online example I could find seems somewhat complicated. I can live with formatting only, without parsing.
E can already be used to get the day of the week. If you want it in hebrew, then initialize the SimpleDateFormat instance with the hebrew locale.
From what I can tell SDF was not build to be extendable so each Calendar field formatting is hardcoded into one method : (. What I would do is I would create a wrapper object and detect special (handled by me chars) and format output by my own in mixed formats i would divide format into whats before and after my format char, and pass them to original SDF and then glue the results together.
java.time
The modern DateTimeFormatter years ago supplanted SimpleDateFormat, with the adoption of JSR 310.
Study that class JavaDoc to see its many formatting codes. While largely similar to the codes used in the SimpleDateFormat class, there are some differences.
This class can automatically localize for you. So you may not need to define any formatting pattern.
If you want just the name of the day of the week localized, use DayOfWeek::getDisplayName method.
Related
I have found myself repeating over and over again the same code pattern. I'm using some sort of date / time object (Java 8 API) and I end up having to roll out my own DateTimeFormatter:
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
DateTimeFormatter.ofPattern("yyyy-MM-dd")
DateTimeFormatter.ofPattern("HH:mm:ss")
which is fine but.. it gets me wondering whether there are not already pre-made ones done for me? I took a glance at the ones provided in the DateTimeFormatter singleton but they don't seem to be of much help.
I think the documentation of DateTimeFormatter is quite clear. The concrete patterns you have mentioned are supported by following predefined constants:
yyyy-MM-dd => ISO_LOCAL_DATE
HH:mm:ss => ISO_LOCAL_TIME
Only your first example "yyyy-MM-dd HH:mm:ss" is not matched by any predefined formatter so you can simply construct it by specifying the pattern (as you have already done). ISO_LOCAL_DATE_TIME is very similar but replaces the space by ISO-literal "T".
yyyy-MM-dd HH:mm:ss: assemble from built-in parts
As a supplement to the good answer by Meno Hochschild.
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
While not built in as it stands (even though occurring regularly), my preference is for assembling it from the two predefined formatters mentioned in that other answer rather than writing my own format pattern string:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.toFormatter();
ISO_LOCAL_TIME accepts and prints other formats than HH:mm:ss, though. On one hand it accepts a format without any seconds (HH:mm), on the other hand seconds with a fraction of up to 9 decimals (for example HH:mm:ss.SSSSSSSSS). It is often an advantage; but if you want to do strict validation of the parsed string or you want string output in a consistent format, it is a drawback, of course.
Or use String.replace(char, char)
Others would format and parse LocalDateTime objects to and from yyyy-MM-dd HH:mm:ss format without any explicit formatter, relying on its resemblance to the ISO 8601 format also mentioned by Meno Hochschild.
String str = "2020-04-17 20:23:30";
LocalDateTime ldt = LocalDateTime.parse(str.replace(' ', 'T'));
System.out.println(ldt);
Output:
2020-04-17T20:23:30
And the other way:
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Tehran"));
String str = now.truncatedTo(ChronoUnit.SECONDS)
.toString()
.replace('T', ' ');
System.out.println(str);
2020-04-18 09:35:38
What is the Java8 java.time equivalent of
org.joda.time.formatDateTimeFormat.shortDate()
I've tried below way, but it fails to parse values such as "20/5/2016" or "20/5/16".
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
You are correct: A Joda-Time DateTimeFormatter (which is the type you get from DateTimeFormat.shortDate()) parses more leniently than a java.time DateTimeFormatter. In the English/New Zealand locale (en-NZ) shortDate uses the format pattern d/MM/yy and parses both 20/5/2016 and 20/5/16 into 2016-05-20.
I frankly find it nasty that it interprets both two-digit and four-digit years into the same year. When the format specifies two-digit year, I would have expected four digits to be an error for stricter input validation. Accepting one-digit month when the format specifies two digits is lenient too, but maybe not so dangerous and more in line with what we might expect.
java.time too uses the format pattern d/MM/yy (tested on jdk-11.0.3). When parsing is accepts one or two digits for day of month, but insist on two-digit month and two-digit year.
You may get the Joda-Time behaviour in java.time, but it requires you to specify the format pattern yourself:
Locale loc = Locale.forLanguageTag("en-NZ");
DateTimeFormatter dateFormatter
= DateTimeFormatter.ofPattern("d/M/[yyyy][yy]", loc);
System.out.println(LocalDate.parse("20/5/2016", dateFormatter));
System.out.println(LocalDate.parse("20/5/16", dateFormatter));
Output is:
2016-05-20
2016-05-20
If you want an advanced solution that works in other locales, I am sure that you can write a piece of code that gets the format pattern from DateTimeFormatterBuilder.getLocalizedDateTimePattern and modifies it by replacing dd with d, MM with M and any number of y with [yyyy][yy]. Then pass the modified format pattern string to DateTimeFormatter.ofPattern.
Edit: I’m glad that you got something to work. In your comment you said that you used:
Stream<String> shortFormPatterns = Stream.of(
"[d][dd]/[M][MM]",
"[d][dd]-[M][MM]",
"[d][dd].[M][MM]",
"[d][dd] [M][MM]",
"[d][dd]/[M][MM]/[yyyy][yy]",
"[d][dd]-[M][MM]-[yyyy][yy]",
"[d][dd].[M][MM].[yyyy][yy]",
"[d][dd] [M][MM] [yyyy][yy]");
It covers more cases that your Joda-Time formatter. Maybe that’s good. Specifically your Joda-Time formatter insists on a slash / between the numbers and rejects either hyphen, dot or space. Also I believe that Joda-Time would object to the year being left out completely.
While you do need [yyyy][yy], you don’t need [d][dd] nor [M][MM]. Just d and M suffice since they also accept two digits (what happens in your code is that for example [d] parses either one or two digits, so [dd] is never used anyway).
If you prefer only one format pattern string, I would expect d[/][-][.][ ]M[/][-][.][ ][yyyy][yy] to work (except in hte cases where the year is omitted) (I haven’t tested).
FormatStyle.SHORT returns shortest format either dd/MM/yy or d/M/yy format, so you need to use pattern to get the customized format
LocalDate date = LocalDate.now();
System.out.println(date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT))); //9/29/19
You can also use DateTimeFormatter.ISO_DATE or DateTimeFormatter.ISO_LOCAL_DATE to get the iso format like yyyy-MM-dd, and also you can see the available formats in DateTimeFormatter
System.out.println(date.format(DateTimeFormatter.ISO_DATE)); //2019-09-29
System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE)); //2019-09-29
If you want the custom format like yyyy/MM/dd the use ofPattern
System.out.println(date.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); //2019/09/29
I am having problems parsing time strings in Java that are in the format of 2013-01-09 09:15:03.000000. In my data, the last three digits are always 0 (meaning the input strings have only millisecond precision), so I passed this format to SimpleDateFormat:
formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS'000'");
but formatter.parse("2013-01-09 09:15:02.500000"); throws an exception:
Unparseable date: "2013-01-09 09:15:02.500000"
at java.text.DateFormat.parse(DateFormat.java:357)
Anyone knows how to do it correctly? I can work around by using format yyyy-MM-dd HH:mm:ss.SSS and using substring to get rid of last three digits but that's really hacky.
EDIT: can anyone explain why the format string yyyy-MM-dd HH:mm:ss.SSS'000' can't be used to parse time "2013-01-09 09:15:02.500000"
try java.sql.Timestamp
Timestamp ts = Timestamp.valueOf("2013-01-09 09:15:03.500000");
Date date = new Date(ts.getTime())
it's also thread-safe and fast as opposed to SimpleDateFormat
java.time
I should like to contribute the modern answer. Use java.time, the modern Java date and time API. One option, you may use a formatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSS");
LocalDateTime dateTime = LocalDateTime.parse(timeString, formatter);
System.out.println(dateTime);
When using the string from your question, "2013-01-09 09:15:02.500000", this printed:
2013-01-09T09:15:02.500
If you want the value printed with six decimals on the seconds even when the last three decimals are 0, use the same formatter to format the time back into a string:
System.out.println(dateTime.format(formatter));
The other option, you may exploit the fact that your string resembles the ISO 8601 format, the format that the modern classes parse as their default, that is, without any explicit formatter. Only ISO 8601 has a T to denote the start of the time part, but we can fix that easily:
LocalDateTime dateTime = LocalDateTime.parse(timeString.replace(' ', 'T'));
It gives the same result, 2013-01-09T09:15:02.500. It’s shorter, but also more tricky.
Why bother?
The classes Date and Timestamp are long outdated, and SimpleDateFormat in particular has proven troublesome. Its surprising behaviour in your situation is just one little story out of very many. The modern API is generally so much nicer to work with.
Why didn’t your formatter work?
While the format pattern strings used by SimpleDateFormat and DateTimeFormatter are similar, there are differences. One is that SimpleDateFormat understands uppercase S as milliseconds no matter of there are one or nine of them, whereas to DateTimeFormatter they mean fraction of second. Your SimpleDateFormat furthermore grabbed all six digits after the decimal point, ignoring the fact that you had typed only three S, so there were no zeroes left to match the '000' (by the way, the apostrophes are not necessary, only letters need them).
Link
Oracle Tutorial
I've figured out myself. Just FYI, Apache commons' FastDateFormat seems accepting the SSS000 format and parses the time correctly.
I have a localized date format. I want to retrieve just the year format in Java.
So if I am given mmddyyyy I would like to extract yyyy.
if I am given mmddyy, i would like to extract yy.
I cannot find a way to get that info using SimpleDateFormat, Date, Calendar etc. classes.
It's important to note that the concept of a "year format" only really applies to SimpleDateFormat. (In the default JDK, anyway.) More specifically, SimpleDateFormat is the only DateFormat implementation provided by the JDK that uses the concept of a "format string" that you can pull out a year format from; the other implementations use more opaque mappings from a Date to a String. For this reason, what you're asking for is only well-defined on the SimpleDateFormat class (again, among the DateFormat implementations available in the stock JDK).
If you're working with a SimpleDateFormat, though, you can just pull the year format out with regular expressions:
SimpleDateFormat df=(something);
final Pattern YEAR_PATTERN=Pattern.compile("^(?:[^y']+|'(?:[^']|'')*')*(y+)");
Matcher m=YEAR_PATTERN.matcher(df.toPattern());
String yearFormat=m.find() ? m.group(1) : null;
// If yearFormat!=null, then it contains the FIRST year format. Otherwise, there is no year format in this SimpleDateFormat.
The regular expression looks so strange because it has to ignore any y's that happen in "fancy" quoted parts of the date format string, like "'Today''s date is 'yyyy-MM-dd". Per the comment in the code above, note that this only pulls out the first year format. If you need to pull out multiple formats, you'll just need to use the Matcher a little differently:
SimpleDateFormat df=(something);
final Pattern YEAR_PATTERN=Pattern.compile("\\G(?:[^y']+|'(?:[^']|'')*')*(y+)");
Matcher m=YEAR_PATTERN.matcher(df.toPattern());
int count=0;
while(m.find()) {
String yearFormat=m.group(1);
// Here, yearFormat contains the count-th year format
count = count+1;
}
This question already has answers here:
SimpleDateFormat and locale based format string
(10 answers)
Closed 8 years ago.
I need to format date to app that has many languages, what is best way to format date, because every country has different kind of date formatting, so is it possible to format date by locale?
Yes, using DateFormat.getDateInstance(int style, Locale aLocale)
This displays the current date in a locale-specific way.
So, for example:
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, yourLocale);
String formattedDate = df.format(yourDate);
See the docs for the exact meaning of the style parameter (SHORT, MEDIUM, etc)
SimpleDateFormat has a constructor which takes the locale, have you tried that?
http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html
Something like
new SimpleDateFormat("your-pattern-here", Locale.getDefault());
Joda-Time
Using the Joda-Time 2.4 library. The DateTimeFormat class is a factory of DateTimeFormatter formatters. That class offers a forStyle method to access formatters appropriate to a Locale.
DateTimeFormatter formatter = DateTimeFormat.forStyle( "MM" ).withLocale( Java.util.Locale.CANADA_FRENCH );
String output = formatter.print( DateTime.now( DateTimeZone.forID( "America/Montreal" ) ) );
The argument with two letters specifies a format for the date portion and the time portion. Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full. A date or time may be ommitted by specifying a style character '-' HYPHEN.
Note that we specified both a Locale and a time zone. Some people confuse the two.
A time zone is an offset from UTC and a set of rules for Daylight Saving Time and other anomalies along with their historical changes.
A Locale is a human language such as Français, plus a country code such as Canada that represents cultural practices including formatting of date-time strings.
We need all those pieces to properly generate a string representation of a date-time value.
Take a look at java.text.DateFormat.
Easier to use (with a bit less power) is the derived class, java.text.SimpleDateFormat
And here is a good intro to Java internationalization: http://java.sun.com/docs/books/tutorial/i18n/index.html (the "Formatting" section addressing your problem, and more).
I agree with Laura and the SimpleDateFormat which is the best way to manage Dates in java. You can set the pattern and the locale. Plus you can have a look at this wikipedia article about Date in the world -there are not so many different ways to use it; typically USA / China / rest of the world -