Is the following DateFormat able to generate locale indepdent date string - java

My ideal way, of having a locale independent date string, is by using the following code
DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH);
which means, if I run the above code in a Arabic device, or a United States device, both will generate same string.
For the above case, dateFormat.format(date) will produce Dec 26, 2015.
I came across a legacy code, which is using the following way to generate locale independent date string.
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.ENGLISH);
Will the above code be truly locale independent. Will it "mistakenly" generate string like 26 Dec, 2015?
I wrote a simple code, to test both on Windows and Android device.
for (Locale locale : Locale.getAvailableLocales()) {
Locale.setDefault(locale);
DateFormat dateFormat0 = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.ENGLISH);
DateFormat dateFormat1 = new SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH);
if (false == dateFormat0.format(date).equals(dateFormat1.format(date))) {
throw new java.lang.RuntimeException("Opps");
}
}
No exception was being thrown.
I was wondering, can we assume that DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.ENGLISH) will generate same date string too across different devices? Is there any edge case I didn't handle?

Using "MMM dd, yyyy"you retain control of the output, where by using the integer constant DateFormat.DEFAULT you are leaving it up to the locale definitions for the platform. They should be identical across these platforms for any given locale, but there really are no guarantee.
The DateFormat.DEFAULT parameter is only useful if you work with different Locales, as it'll allow DateFormat to create the proper localized date string.
Example:
DateFormat dfEn = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.ENGLISH);
DateFormat dfFr = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.FRENCH);
DateFormat dfGe = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.GERMAN);
System.out.println("ENGLISH: " + dfEn.format(date));
System.out.println("FRENCH : " + dfFr.format(date));
System.out.println("GERMAN : " + dfGe.format(date));
prints:
ENGLISH: Dec 26, 2015
FRENCH : 26 déc. 2015
GERMAN : 26.12.2015

I you want to be sure that the rendered date looks always the same it's the best choice to define the format explicitely as you did in your first code snippet:
DateFormat dateFormat = new SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH);
The given locale here does not define the formatting but the wording to be used, here the name of the month is in english ("Dec"), rather than e.g. in german ("Dez").

Related

SimpleDateFormat print "." for MMM format

I am trying to convert date string from one format to another using SimpleDateFormat.
Conversion works but there is a dot "." after month.
String dateStr = "04/02/1987";
DateFormat df1 = new SimpleDateFormat("dd/MM/yyyy");
Date d = df1.parse(dateStr);
DateFormat df2 = new SimpleDateFormat("dd MMM yyyy");
System.out.println(df2.format(d));
Output is 04 Feb. 1987 instead of 04 Feb 1987.
What is your Locale.getDefault()?
Different output for alphanumeric date parts may be caused by the Locale the formatter is using. In most cases, the system default Locale is used if you don't specify one yourself. I don't know for sure SimpleDateFormat does so, but it seems likely.
I know that a java.time.format.DateTimeFormatter does so, see the following example which uses java.time, the modern and recommended to use datetime API:
public static void main(String[] args) {
String dateStr = "04/02/1987";
LocalDate localDate = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
System.out.println(localDate.format(DateTimeFormatter.ofPattern("dd MMM yyyy",
Locale.ENGLISH)));
System.out.println(localDate.format(DateTimeFormatter.ofPattern("dd MMM yyyy",
Locale.FRENCH)));
}
Output:
04 Feb 1987
04 févr. 1987
The output is (of course) different concerning the name of the month, but using Locale.FRENCH shows a dot after the abbreviated month name. It is possible that your system's default Locale is one that indicates an abbreviation by a dot, too, but is identical to the output format of a Locale.ENGLISH for the numeric parts and the abbreviation of the month.
Please provide Locale.ENGLISH in SimpleDateFormat constructor while creating object as shown below:
String dateStr = "04/02/1987";
DateFormat df1 = new SimpleDateFormat("dd/MM/yyyy");
Date d = df1.parse(dateStr);
DateFormat df2 = new SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH);
System.out.println(df2.format(d));

Java SimpleDateFormat unable to parse "Aug 15, 2017, 4:58 PM ET" with "MMM dd, yyyy, h:mm a z"

I am unable to parse this date. Anyone notice any mistakes? They all seem to fail.
I have tried multiple patterns with multiple Locale types.
Here is my strategy:
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Locale;
public class Test {
static void check(Locale locale){
String dateString = "Aug 15, 2017, 4:58 PM ET";
DateFormat format1 = new SimpleDateFormat("MMM dd, yyyy, h:mm aa zz", locale);
DateFormat format2 = new SimpleDateFormat("MMM dd, yyyy, h:mm a z", locale);
DateFormat format3 = new SimpleDateFormat("MMM dd, yyyy, hh:mm a z", locale);
DateFormat format4 = new SimpleDateFormat("MMM dd, yyyy, K:mm a z", locale);
DateFormat format5 = new SimpleDateFormat("MMM dd, yyyy, KK:mm a z", locale);
for (DateFormat format : Arrays.asList(format1, format2, format3, format4, format5)) {
try {
System.out.println(format.parse(dateString));
} catch (ParseException ex){
System.out.println("Failed");
}
}
}
public static void main(String[] args) {
Arrays.asList(Locale.ENGLISH, Locale.UK, Locale.US, Locale.CANADA, Locale.ROOT, Locale.getDefault()).forEach(Test::check);
}
}
As many have already said, ET is not a timezone. It's an abbreviation commonly used to refer to both EST and EDT (Eastern Standard Time and Eastern Daylight Time), but there are more than one timezone that uses it.
Short names (like EST and EDT) aren't timezones as well, because such abbreviations are ambiguous and not standard. There are more than one timezone that can use the same abbreviations.
The ideal is to use IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
But the use of short names like EST and ET is widespread and common, so we must live with it (and do some workarounds as well).
The first thing is to define which timezone you want to use as ET (and this will be a very arbitrary choice, but there's no other way since ET is ambiguous). In the example below, I've chosen America/New_York. You can see the list of all available timezones (and choose one that fits best to your needs) using the java.util.TimeZone class (calling TimeZone.getAvailableIDs()).
It's possible to overwrite the short names used by SimpleDateFormat, using the java.text.DateFormatSymbols class. So, one solution is to get the current symbols and overwrite just the timezone we want:
SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy, h:mm a z", Locale.ENGLISH);
// get current date symbols
String[][] zoneStrings = sdf.getDateFormatSymbols().getZoneStrings();
for (int i = 0; i < zoneStrings.length; i++) {
// overwrite just America/New_York (my arbitrary choice to be "ET")
if (zoneStrings[i][0].equals("America/New_York")) {
zoneStrings[i][2] = "ET"; // short name for standard time
zoneStrings[i][4] = "ET"; // short name for daylight time
break;
}
}
// create another date symbols and set in the formatter
DateFormatSymbols symbols = new DateFormatSymbols(Locale.ENGLISH);
symbols.setZoneStrings(zoneStrings);
sdf.setDateFormatSymbols(symbols);
String dateString = "Aug 15, 2017, 4:58 PM ET";
System.out.println(sdf.parse(dateString));
This will parse ET as America/New_York, and all other existing built-in zones won't be affected.
Check the javadoc for more details about DateFormatSymbols.
Also note that I used Locale.ENGLISH, because the month name (Aug) is in English. If I don't specify the locale, the system's default will be used, and it's not guaranteed to always be English. Even it the default is correct, it can be changed without notice, even at runtime, so it's better to use an explicit locale.
Java new Date/Time API
If you're using Java 8, you can replace this code with the new java.time API. It's easier, less bugged and less error-prone than the old SimpleDateFormat and Calendar APIs.
All relevant classes are in java.time package. You just need to define a java.util.Set of prefered zones and set it to a java.time.format.DateTimeFormatter. Then you parse it to a java.time.ZonedDateTime - if you still need to work with a java.util.Date, you can easily convert it:
// prefered zones
Set<ZoneId> preferredZones = new HashSet<>();
preferredZones.add(ZoneId.of("America/New_York"));
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// date and time
.appendPattern("MMM dd, yyyy, h:mm a ")
// zone (use set of prefered zones)
.appendZoneText(TextStyle.SHORT, preferredZones)
// create formatter (use English locale for month name)
.toFormatter(Locale.ENGLISH);
String dateString = "Aug 15, 2017, 4:58 PM ET";
// parse string
ZonedDateTime zdt = ZonedDateTime.parse(dateString, fmt);
// convert to java.util.Date
Date date = Date.from(zdt.toInstant());
Daylight Saving Time issues
There are some corner cases. America/New_York timezone has Daylight Saving Time (DST), so when it starts and ends, you can have unexpected results.
If I get the date when DST ends:
String dateString = "Nov 02, 2008, 1:30 AM ET";
At 2 AM, clocks shift 1 hour back to 1 AM, so the local times between 1 AM and 1:59 AM exist twice (in DST and in non-DST offsets).
SimpleDateFormat will get the offset after DST ends (-05:00) so the date will be equivalent to 2008-11-02T01:30-05:00, while ZonedDateTime will get the offset before (-04:00) and the date will be equivalent to 2008-11-02T01:30-04:00.
Fortunately, ZonedDateTime has the withLaterOffsetAtOverlap() method, that returns the corresponding date at the offset after DST ends. So you can emulate SimpleDateFormat's behaviour calling this method.
If I get the date when DST starts, though:
String dateString = "Mar 09, 2008, 2:30 AM ET";
At 2 AM, clocks shift forward to 3 AM, so local times between 2 AM and 2:59 AM don't exist. In this case, both SimpleDateFormat and ZonedDateTime will adjust the time to 3:30 AM and use the DST offset (-04:00) - the date will be equivalent to 2008-03-09T03:30-04:00.
Your format is fine, it is just your date that is wrong. ET is not a valid zone identifier.
With TimeZone.getAvailableIDs() you can look at valid zone IDs.

SimpleDateFormat gives ParseException

I'm fairly new here. I'm used to working with C# but I'm a newbie in java.
I'm trying to get an hour String out of a jSpinner (Date model), but I keep getting errors. I've looked into some answers that are already given here... but it still doesn't work.
uurStr returns "Sat Jan 25 16:09:49 CET 2014", I'm trying to get "16:09" out of it. But no luck so far.... any ideas?
The exception is thrown on the "uurDate = sdf.parse(uurStr);" part.
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
SimpleDateFormat outputFmt = new SimpleDateFormat("HH:mm");
String uurStr = String.valueOf(jSpinner1.getValue());
Date uurDate = null;
try {
uurDate = sdf.parse(uurStr);
} catch (ParseException ex) {
Logger.getLogger(frmBackEnd.class.getName()).log(Level.SEVERE, null, ex);
}
String uur = outputFmt.format(uurDate);
JOptionPane.showMessageDialog(null, uur);
In order to correctly parse the weekday and timezone information, which both are language specific, it is necessary to set the locale as follows:
final SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.US);
If the locale is not set, then the default locale of the JRE is taken. This may be the OS default locale or a user specific locale or the locale may have been overridden by another Java class with Locale.setDefault(Locale.XXXX). In any of these cases, this may be the correct locale or not.
That said, it is usually safer to set the timezone or you may get wrong time information if the timezone is not available in the string to be parsed:
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
In your example the timezone is available and you don't have to worry about that.

Parsing date with SimpleDateFormat

Why does the following give an error:
DateFormat formatter1 = new SimpleDateFormat("E, MMM d");
formatter1.setTimeZone(TimeZone.getTimeZone("America/New_York"));
formatter1.parse("Tue, Nov 26");
I don't get why it isn't working.
You should set a Locale to your formatter where months are spelt in English, otherwise it's using your default Locale :
SimpleDateFormat(String pattern)
Constructs a SimpleDateFormat using the given pattern and the default
date format symbols for the default locale.
I.e :
DateFormat formatter1 = new SimpleDateFormat("E, MMM d", Locale.US);

Java, SimpleDateFormat produces ParseException in German. Works in English

My app is calling up a Twitter feed and a blog feed, both contain a post date of course. When my phone is set to English locale it works, when I switch to Dutch or German it fails. The code in question does not even call upon the locale, and the input values are also independent of the locale.
The offending code:
tweets is a JSONObject containing the complete Twitter feed.
final SimpleDateFormat formatter =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
final SimpleDateFormat parser =
new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
for (int i = 0; i < tweets.length(); i++) {
final JSONObject tweet = tweets.getJSONObject(i);
// The following line is where the failure occurs:
values.put(KEY_TWEET_DATE, formatter.format(parser.parse(tweet
.getString("created_at"))));
}
This works as long as my locale is English.
As soon as I switch to German or Dutch (my app contains translations for those two languages, I haven't tried any other so far) I get an error like this:
WARN/System.err(28273): java.text.ParseException: Unparseable date: Wed Jun 29 10:55:41 +0000 2011
WARN/System.err(28273): at java.text.DateFormat.parse(DateFormat.java:645)
WARN/System.err(28273): at squirrel.DeaddropDroid.DeaddropDB.updateTwitter(DeaddropDB.java:1453)
The "unparseable date" is the correct date, in the expected format. My format string is designed to parse that exact date. And as said, when I switch my phone to English locale, it works just fine. It's the same code: the error occurs even when I switch the locale while the app is running, and disappears when I switch back the locale.
If you need to parse in a particular locale, pass that into the SimpleDateFormat constructor:
final SimpleDateFormat parser =
new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US);
That means it will always use the US locale for day and month names etc.
Have you tried:
SimpleDateFormat parser =
new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy",Locale.getDefault());

Categories