Following code does not print "CE" or "Current Era":
System.out.println(IsoEra.CE.getDisplayName(TextStyle.SHORT, Locale.UK)); // output: AD
System.out.println(IsoEra.CE.getDisplayName(TextStyle.FULL, Locale.UK)); // output: Anno Domini
Of course, IsoEra.CE.name() helps but not if the full display name like "common era" or "current era" is required. I consider this a little bit strange because the javadoc of IsoEra explicitly mentions the term "Current era" in its class description. It does not even work for root locale. The use-case here is to serve clients with a non-religious background.
This does not help, too:
LocalDate date = LocalDate.now();
String year = date.format(DateTimeFormatter.ofPattern("G yyyy", Locale.UK)); // AD 2015
System.out.println(year);
The only way I found was:
TextStyle style = ...;
Map<Long,String> eras = new HashMap<>();
long bce = (long) IsoEra.BCE.getValue(); // 0L
long ce = (long) IsoEra.CE.getValue(); // 1L
if (style == TextStyle.FULL) {
eras.put(bce, "Before current era");
eras.put(ce, "Current era");
} else {
eras.put(bce, "BCE");
eras.put(ce, "CE");
}
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendText(ChronoField.ERA, eras)
.appendPattern(" yyyy").toFormatter();
System.out.println(LocalDate.now().format(dtf)); // CE 2015
Is there any better or shorter way?
No, there is no better way to do it!
Explanation: "Current era" (and accoridngly "before current era") is the "name of a field"(abstract/meta) of the ISO standard. Of course there is also no (standardized) country specific translation for these fields and no pattern which prints this output.(By the standard they are referenced in english only, and by jdk respectively only as CE, BCE). So what the original output shows:
AD
Anno Domini
is correct, and the ISO-conform (english) translation of the era (of a date which is "in the current era").
To solve this, I absolutely agree with your approach (of custom date formatting), and going deeper into details: I wouldn't dare to change a single line of it!
The only savings potential I see is in "initialization"(maybe use a EnumMap for the TextStyles...and ... how many languages do you want to support?) ..and "by refactoring".
Thank you for the interesting "problem", and providing a solution to it!
Related
Here's my code:
public static String getStringFormat(Date inputDate, String timeZone){
String strFormat = null;
try{
final TimeZone computedTimeZone = TimeZone.createTimeZone(TimeZoneInfo.buildTimeZoneData(timeZone));
DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.ISO_8601);
strFormat = dateTimeFormat.format(inputDate, computedTimeZone);
Date d = new Date(strFormat);
strFormat = dateTimeFormat.format(d, TimeZone.createTimeZone(0));
String[] s = strFormat.split("\\+");
strFormat = s[0];
}catch(Exception e){
Console.log(e.getMessage());
}
return strFormat;
}
For input, new Date() and Etc/GMT+3 this function returns null. What could be wrong?
Error
Error: NullPointerException: undefined
at NBF_g$.QBF_g$ [as createError_0_g$] (NullPointerException.java:40)
at NBF_g$.ub_g$ [as initializeBackingError_0_g$] (Throwable.java:113)
at NBF_g$.bb_g$ (Throwable.java:61)
at NBF_g$.Ib_g$ (Exception.java:25)
at NBF_g$.avq_g$ (RuntimeException.java:25)
at NBF_g$.gfs_g$ (JsException.java:34)
at new NBF_g$ (NullPointerException.java:27)
at new wou_g$ (JSONString.java:43)
The method TimeZoneInfo.buildTimeZoneData(String tzJSON) doesn't accept the name of the zone, but needs a JSON string full of the details of how that zone works. It turns out that the browser doesn't come to you with all of the details of how all time zones work, so your app has to already be prepared to handle them.
GWT ships with all of the timezones (though they are currently a little out of date, and should be updated in this next release), but you have to tell the compiler which ones you want, or it will compile them out. The full list of all possible timezones and their offsets, etc is not small, so I would encourage you to limit the list.
These are stored in the constants interface TimeZoneConstants. Here is how you might use it:
TimeZoneConstants constants = GWT.create(TimeZoneConstants.class);
// This is the shorthand for TimeZone.createTimeZone(TimeZoneInfo.buildTimeZoneData(...))
TimeZone computedTimeZone = TimeZone.createTimeZone(constants.americaAdak());
//...
If you want to use the timezone string instead, say, passed from the server, you could build a map of the possible timezones that are supported. Be aware though that the full map is very large (200KB just for the timezones in the "America/..." group).
computedTimeZone = TimeZone.createTimeZone(constants.americaAdak());
zones.put(computedTimeZone.getID(), computedTimeZone);
computedTimeZone = TimeZone.createTimeZone(constants.americaAnchorage());
zones.put(computedTimeZone.getID(), computedTimeZone);
computedTimeZone = TimeZone.createTimeZone(constants.americaAnguilla());
zones.put(computedTimeZone.getID(), computedTimeZone);
//...
Then you can read out a specific item from the map as needed:
String tzName = Window.prompt("Enter a timezone name", "America/Chicago");
DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.ISO_8601);
String strFormat = dateTimeFormat.format(inputDate, zones.get(tzName));
//...
In your comment, you clarified the question, that you only need to deal with offsets, not the full TimeZone string format, i.e. Etc/GMT+3, meaning "Offset of +3 hours from GMT". This is easier to handle - simply parse out the +3 into a number, and use the TimeZone.createTimeZone(int timeZoneOffsetInMinutes) method. This will not understand daylight savings time, but that wouldn't be possible without the full name of the timezone or list of offsets, etc (which gets to why that JSON is so large).
//TODO, implement parse(), don't forget about negative numbers
int offsetInHours = parse(timeZone);
TimeZone computedTimeZone = TimeZone.createTimeZone(60 * offsetInHours);
//...
After parsing JSON UTC date-time data from a server, I was presented with
2017-03-27 16:27:45.567
... is there any way to format this without using tedious amount of String manipulation so that the seconds part is rounded up to 46 prior to passing it in as a DateTimeFormat pattern of say, "yyyy-MM-dd HH:mm:ss"?
You can round the second up like this:
DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS")
.withZoneUTC()
.parseDateTime("2017-03-27 16:27:45.567")
.secondOfMinute()
.roundCeilingCopy();
System.out.println(dateTime);
// 2017-03-27T16:27:46.000Z
Have you looked at (and could you use) the MomentJS library? I had issues with reading various date formats from the server and making sense of them in JavaScript code (which led me here). Since then, I've used MomentJS and working with dates/times in JavaScript has been much easier.
Here is an example:
<script>
try
{
var myDateString = "2017-03-27 16:27:45.567";
var d = moment(myDateString);
var result = d.format('YYYY/MM/DD HH:mm:ss');
alert("Simple Format: " + result);
// If we have millliseconds, increment to the next second so that
// we can then get its 'floor' by using the startOf() function.
if(d.millisecond() > 0)
d = d.add(1, 'second');
result = d.startOf('second').format('YYYY/MM/DD HH:mm:ss');
alert("Rounded Format: " + result);
}
catch(er)
{
console.log(er);
}
</script>
But of course, you'll probably want to wrap this logic into a function.
I have a java program where the user can choose the locale which he wants from a checkbox. in order to populate this checkbox I use the Calendar.getAvailableLocales() method. The problem is that this method returns locales which use dates which are not Gregorian and this causes problems in my program which only supports Gregorian dates (ISO). Two examples are the Japanese (Japan, jP) locale and the Thai (Thailand, TH) locale. I do not want these types of locales to appear in the program. How can I filter the list such that only locales with ISO calendar formats are available in the checkbox? Thanks.
EDIT:
Ok. Here is the code for the JLocaleChooser combo box that I use:
locales = Calendar.getAvailableLocales();
localeCount = locales.length;
for (int i = 0; i < localeCount; i++) {
if (locales[i].getCountry().length() > 0) {
addItem(locales[i].getDisplayName());
}
}
After that I set the locale of the system based on the user choice as such:
myLocale2 = theJLocaleChooser.getLocale();
Locale.setDefault(myLocale2);
If the user chooses the Japanese (Japan) locale then everything is fine. if he/she chooses the Japanese (Japan, JP) locale my program does not perform operations like it should. For example, somewhere in the program I get the current month and use the information in a sql statement as such:
Calendar cal0 = Calendar.getInstance();
int month1 = cal0.get(Calendar.MONTH);
month1++; //since 0 is january
int year1 = cal0.get(Calendar.YEAR);
connect.query = "SELECT name1, name2 FROM table1 WHERE MONTH(date) = " + month1 + " AND YEAR(date) = " + year1;
This sql query returns an empty dataset if the user chose Japanese(Japan, JP) but returns the correct dataset if the user chose the Japanese(Japan) locale. The reason is that the value of the year1 variable if I use the Japanese (Japan, JP) locale is 26 (I used a println command) but it is 2014 (like it should be) if I use other locales including the Japanese (Japan) one. Therefore I need locales that use the "regular" years (2012, 2013,...) not other years (like the Islamic Hijra for example).
You can just check whether Calendar.getInstance(Locale) returns a GregorianCalendar:
for (Locale locale : Calendar.getAvailableLocales()) {
if (locale.getCountry().length() > 0
&& Calendar.getInstance(locale) instanceof GregorianCalendar) {
addItem(locale.getDisplayName());
}
}
In my app, I get the user's default locale using Locale.getDefault() and then pass that to Currency.getInstance(Locale). It mostly works, but I have started getting reports from users which show the following IllegalArgumentException in the stack trace:
Caused by: java.lang.IllegalArgumentException: Unsupported ISO 3166
country: en_UK at java.util.Currency.getInstance(Currency.java:81) at
org.
I expected Android to only return valid locales, but that is apparently not the case.
How do I handle such cases to make sure I only get valid ISO 3166 locales? The easy way will be to handle this special case, but I would rather use a generic solution if there is one.
Anyone have experience with this? Thanks.
The ISO 3166 two-letter abbreviation for the UK is not UK, the correct id is GB. UK is there for compatibility reasons (a mistake made in the past).
I did look for other exeptions but did not find any, so for now i would just handle the special case.
Locale loc = new Locale("en","UK"); // test code
if(loc.getCountry().equals("UK")){
loc = new Locale(loc.getLanguage(), "GB");
}
Currency cur = Currency.getInstance(loc);
Currency.getInstance(...) expects a locale of the form "en_US", however, Locale.getDefault() does not always return a locale of this form.
To prevent crashes in your app, you can use something like this:
public static String getCurrencySymbol(){
String cursym;
try {
cursym = Currency.getInstance(Locale.getDefault()).getSymbol();
} catch (IllegalArgumentException e) {
cursym = "?"; // default symbol
}
return cursym;
}
You can try to figure out a better way to get the most appropriate symbol, or just let the user choose one.
Your users (with dev settings enabled) or testers have manually set a weird and invalid country. This bug occurred to us, but it turned out our testers had configured Appium to set the device locale to en-UK :).
A regular user can not select en-UK or any other invalid locale.
String symbolLocale ="";
int localeLength = mLocale.split("-").length;
if (localeLength == 2) {
symbolLocale = new Locale(
mLocale.split("-", 2)[0],
mLocale.split("-", 2)[1]
);
} else if (localeLength == 3) {
symbolLocale = new Locale(
mLocale.split("-", 3)[0] + "-" +
mLocale.split("-", 3)[1],
mLocale.split("-", 3)[2]
);
}
symbolString = Currency.getInstance(symbolLocale).getSymbol(symbolLocale);
I'm using DynamicReports API for building reports.
I'm setting the Locale of the report and format the Date columns of the report, but the Dates are always formatted like 10/12/2009 10:54:44 AM no matter what the Locale is.
The code looks like:
rep.setTemplate(Templates.reportTemplate.setLocale(res.getLocale()));
...
if (rs.getString(i).contains("00:00:00"))
rep.addColumn(col.column(title, name, type.dateType()));
else
rep.addColumn(col.column(title, name, type.dateYearToSecondType()));
Is there a way to automatically format dates regarding to the Locale of the report or have I to use a custom ValueFormatter?
I also tryed the Parameter Map with no success
JasperReportBuilder rep = report()
.setDataSource(query, conn.getConnection())
.setParameter(JRParameter.REPORT_LOCALE, res.getLocale());
.setTemplate(Templates.reportTemplate.setLocale(res.getLocale()));
There is no way to automatically format the dates.
The only way is to use patterns for the date column in respect to the locale.
TextColumnBuilder<Date> column = col.column(title, name, type.dateType());
if (res.getLocale().equals("EN") {
column.setPattern("dd.MM.yyyy");
}
else if (res.getLocale().equals("US") {
column.setPattern("MM/dd/yyyy");
}
else {
...
}
rep.addColumn(column);
I managed to format the date field in dynamic jasper in this way.
ColumnBuilder time = ColumnBuilder.getNew();
time.setTitle("Login Time");
time.setWidth(200);
time.setColumnProperty("emp.logintime", Date.class.getName()).setPattern("dd/MM/yyyy hh:mm:ss a");
drb.addColumn(time.build());