jodatime DateTime objects and Locale - java

Given that this field locale have been set for norwegian bokmål and norway:
Locale locale = new Locale("nb","no");
What's missing from this code fragment inside a method to return the proper string for the language bokmål?
Assert.assertNotNull(locale);//Is asserted
MutableDateTime start = new MutableDateTime(2012,1, 10,10,0,0,0 );
start.setDayOfWeek(DateTimeConstants.SATURDAY);
System.out.println(start.dayOfWeek().getAsText(locale));
System.out.println(locale.getISO3Language().toString());
The output is "Saturday, nob"
Do I need to implement locale specific weekday name strings myself? If so is there some base object or interface to override in jodatime? I can't seem to find one.

Your code works correctly for me:
[system:/tmp]$ cat Loc.java
import org.joda.time.*;
import java.util.Locale;
class Loc {
public static void main(String[] args) {
Locale locale = new Locale("nb","no");
MutableDateTime start = new MutableDateTime(2012,1, 10,10,0,0,0 );
start.setDayOfWeek(DateTimeConstants.SATURDAY);
System.out.println(start.dayOfWeek().getAsText(locale));
System.out.println(locale.getISO3Language().toString());
}
}
[system:/tmp]$ java Loc
lørdag
nob
A few things to try:
Try using new Locale("nb", "NO") (country/region is supposed to be case-insentive, but it's worth a shot)
Try using new Locale("no", "NO") or new Locale("nn", "NO") - Nyorsk may not be what you're looking for, but does it work? From some Googling, it seems like some platforms might treat nb_NO as an alias for no_NO? It might be useful just to know if that does or doesn't work.
Make sure the nb_NO locale is available. It probably is since getISO3Language() seems to work.

Related

How can I create a jollyday HolidayManager instance from a given Locale?

In my application the user can set her Locale from a list of available locales. The application needs to determine if a given date is a holiday and I am using jollyday for this task.
Here is my current code:
public boolean isHoliday(Calendar cal) {
HolidayManager m = HolidayManager.getInstance();
return m.isHoliday(cal);
}
I can change this to the following code to set a specific HolidayCalendar:
public boolean isHoliday(Calendar cal) {
HolidayManager m = HolidayManager.getInstance(HolidayCalendar.GERMANY);
return m.isHoliday(cal);
}
But this method is deprecated and I would need to figure out which HolidayCalendar I need based on the user locale. What I want to do is something like this:
public boolean isHoliday(Calendar cal, Locale loc) {
HolidayManager m = HolidayManager.getInstance(loc);
return m.isHoliday(cal);
}
I know that HolidayManager is using Locale.getDefault() and there are public methods in Holiday too (see here) but I can not figure out how to do this and the documentation is very brief.
My question: Can I get the holidays based on a particular locale? Do I have to write a major switch statement for all the locales to match a HolidayCalendar?
Update I created a PR to the project and added the functionality myself. The PR already got merged
In the jollyday/util/ResourceUtil.java I see a hashmap for locale-holiday_description pair, but it's private static and not being populated anywhere.
https://github.com/svendiedrichsen/jollyday/blob/master/src/main/java/de/jollyday/util/ResourceUtil.java#L61
private static final Map<Locale, ResourceBundle> HOLIDAY_DESCRIPTION_CACHE = new HashMap<>();
So, I believe, there isn't any direct method to do what you are looking for. And it would default to default Locale as you figured out. But you can fork and modify that package to your needs and use it in your code.
Also, trying opening a issue-request https://github.com/svendiedrichsen/jollyday/issues

Converter for Joda-Time `DateTime` object to String in `org.simpleframework.xml` ("Simple XML Serialization" library)

How to build a converter for the org.simpleframework.xml libary?
I am using the Simple XML Serialization library (org.simpleframework.xml package) from SimpleFramework.org.
I want Joda-Time DateTime objects to be serialized as an ISO 8601 string such as 2014-07-16T00:20:36Z. Upon re-constituting the Java object, I want a DateTime to be constructed from that string. The documentation does not really explain how to build a converter.
I know it has something to do with a Transform and a Matcher. In the MythTV-Service-API project, I found implementations of both Transform and Matcher. But I have not determined how to put it together.
You may choose between two approaches, as discussed in this similar Question, Serialization third-party classes with Simple XML (org.simpleframework.xml):
Converter
Transform
I do not know the pros and cons of each in comparison. But I do know how to implement the Transform approach. For that, read on.
Transform Approach
Three pieces are needed:
An implementation of the Transform interface.
An implementation of the Matcher interface.
A RegistryMatcher instance where the Transform implementation is mapped to the data-type it handles.
All three of these are a part of the transform package.
I suggest putting your implementations in a "converters" package in your project.
Transform Implementation
Your Transform implementation may look like this.
This implementation here is a simplistic. It assumes you want the output to be the default ISO 8601 string generated by a DateTime’s toString method. And it assumes every text input will be compatible with the default parser in a DateTime constructor. To handle other formats, define a bunch of DateTimeFormatter instances, calling the parseDateTime method on each one in succession until a formatter succeeds without throwing an IllegalArgumentException. Another thing to consider is time zone; you may want to force the time zone to UTC or some such.
package com.your.package.converters.simplexml;
import org.joda.time.DateTime;
import org.simpleframework.xml.transform.Transform;
import org.slf4j.LoggerFactory;
/**
*
* © 2014 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for such usage and its consequences.
*/
public class JodaTimeDateTimeTransform implements Transform<DateTime>
{
//static final org.slf4j.Logger logger = LoggerFactory.getLogger( JodaTimeDateTimeTransform.class );
#Override
public DateTime read ( String input ) throws Exception
{
DateTime dateTime = null;
try {
dateTime = new DateTime( input ); // Keeping whatever offset is included. Not forcing to UTC.
} catch ( Exception e ) {
//logger.debug( "Joda-Time DateTime Transform failed. Exception: " + e );
}
return dateTime;
}
#Override
public String write ( DateTime dateTime ) throws Exception
{
String output = dateTime.toString(); // Keeping whatever offset is included. Not forcing to UTC.
return output;
}
}
Matcher Implementation
The Matcher implementation is quick and easy.
package com.your.package.converters.simplexml;
import org.simpleframework.xml.transform.Transform;
import org.simpleframework.xml.transform.Matcher;
/**
*
* © 2014 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for such usage and its consequences.
*/
public class JodaTimeDateTimeMatcher implements Matcher
{
#Override
public Transform match ( Class classType ) throws Exception
{
// Is DateTime a superclass (or same class) the classType?
if ( org.joda.time.DateTime.class.isAssignableFrom( classType ) ) {
return new JodaTimeDateTimeTransform();
}
return null;
}
}
Registry
Putting these into action means the use of a registry.
RegistryMatcher matchers = new RegistryMatcher();
matchers.bind( org.joda.time.DateTime.class , JodaTimeDateTimeTransform.class );
// You could add other data-type handlers, such as the "YearMonth" class in Joda-Time.
//matchers.bind( org.joda.time.YearMonth.class , JodaTimeYearMonthTransform.class );
Strategy strategy = new AnnotationStrategy();
Serializer serializer = new Persister( strategy , matchers );
And continue on the usual way with a serializer that understands the Joda-Time type(s).

How to get the pattern of Number Format of a specific locale?

I have a simple question:
How to get the pattern used to format a number using NumberFormat created for a specific locale as shown below:
import java.util.Locale;
Locale aLocale = new Locale("fr","CA");
NumberFormat numberFormat=NumberFormat.getNumberInstance(aLocale);
Here I want to know the pattern used to format a number in French language and the country of Canada.
For e.g. :
a number 123456.7890 is converted into 123 456,789 after formatting it means pattern may be # ###,### for above mentioned locale.
The subclasses DecimalFormat and ChoiceFormat have a method toPattern(), so you must check using instanceof and call toPattern()
String pattern = null;
if (numberFormat instanceof DecimalFormat) {
pattern = ((DecimalFormat)numberFormat).toPattern();
}
Consider DecimalFormat.toLocalizedPattern() too
NumberFormat is an interface so there can be multiple implementations.
public String getPattern(NumberFormat numberFormat) {
if(numberFormat instanceof java.text.DecimalFormat)
return ((java.text.DecimalFormat)numberFormat).toPattern();
if(numberFormat instanceof java.text.ChoiceFormat)
return ((java.text.ChoiceFormat)numberFormat).toPattern();
throw new IllegalArgumentException("Unknown NumberFormat implementation");
}
Please note that this will work today, but may break in the future when other implementations are added.

NumberFormatException in String.Format("%05.2f", 8.00)

I'm using italian locale for my program so Float.parseFloat("8,00") must function well.
But I have encountered a very bad NFE in the following line:
this.cuSurfaceJTextField1.setValue(
String.format("%05.2f",info.getCuSurface()));
I note that the above code used to work well up to some changes I made to the listeners that don't look to be related to this line of the code.(Now I have a propertyChangeListener that updates the model when the value is changed.
this.cuSurfaceJTextField1.addPropertyChangeListener("value",
new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
info.setCuSurface(Float.parseFloat(
(String)cuSurfaceJTextField1.getValue()));
updateCuSurface();
}
});
The useful part of the exception:
Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: "08,00"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1241)
at java.lang.Float.parseFloat(Float.java:452)
at View.bars.QuadrateJPanel$11.propertyChange(QuadrateJPanel.java:348)
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:328)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
at java.awt.Component.firePropertyChange(Component.java:8382)
at javax.swing.JFormattedTextField.setValue(JFormattedTextField.java:799)
at javax.swing.JFormattedTextField.setValue(JFormattedTextField.java:502)
I'm using italian locale for my program so Float.parseFloat("8,00") must function well.
No. Float.parseFloat is not locale dependent. So, there are two ways to fix your issue:
Format like this:
String.format(Locale.US, "%05.2f",info.getCuSurface())
Replace the comma when you parse:
info.setCuSurface(Float.parseFloat(
((String) cuSurfaceJTextField1.getValue()).replace(',','.')));
Looks like there may be some confusion over your Locale? I've seen this myself - in America and other places numbers are written as "8.00" but in parts of Europe (France was what gave me trouble) it is written as "8,00". Seems like when String.format() is called it is in a Locale that uses commas... but setting the value on the JTextField is automatically parsing the passed-in string with Float.parseFloat() which is not sensitive to Locale matters - it looks for . and that's it.
parseFloat doesn't use the default locale, it calls FloatingDecimal.readJavaFormatString(s).floatValue().
Instead use NumberFormat which will honour the Locale set
public class NFE {
public static void main(String[] args) throws Exception
{
Locale.setDefault(Locale.ITALIAN);
NumberFormat format = NumberFormat.getInstance();
//Float.parseFloat("8,00");
System.out.println(format.parse("8,00"));
}
}

Can you make a TextField<BigDecimal> accept both , and . as decimal separator?

In a Wicket app, I have a decimal number text field:
TextField<BigDecimal> f =
new TextField<BigDecimal>("f", new PropertyModel<BigDecimal>(model, "share"));
I want it to always accept both . (dot) and , (comma) as decimal separator (regardless of browser's locale settings).
For showing the value, session's locale is used [which in our case is forced to be "fi" (-> comma)], but here I'm interested in what the field accepts as input.
My question is, do I have to change the field to TextField<String>, and convert to domain object's type (BigDecimal) manually? Or is there some way to use TextField<BigDecimal> (which allows e.g. making use of Wicket's MinimumValidator or RangeValidator), and still have it accept both decimal separators?
Thanks to #bert's comment, and the Wicket in Action book, I found an approach that works. In the Application class specify a custom converter for BigDecimals:
#Override
protected IConverterLocator newConverterLocator() {
ConverterLocator converterLocator = new ConverterLocator();
converterLocator.set(BigDecimal.class, new CustomBigDecimalConverter());
return converterLocator;
}
And in the custom converter, convertToObject needs to be overridden. NB: this is sufficient for our needs; think about your requirements and adapt as needed!
public class CustomBigDecimalConverter extends BigDecimalConverter {
#Override
public BigDecimal convertToObject(String value, Locale locale) {
// NB: this isn't universal & your mileage problably varies!
// (Specifically, this breaks if '.' is used as thousands separator)
if ("fi".equals(locale.getLanguage())) {
value = value.replace('.', ',');
}
return super.convertToObject(value, locale);
}
}
Edit: Offtopic, but I want to document this too. We needed our app to support a scale of 4 decimal places, and our custom BigDecimal converter nicely solves that problem too.
#Override
public String convertToString(Object value, Locale locale) {
NumberFormat fmt = getNumberFormat(locale);
fmt.setMaximumFractionDigits(4); // By default this is 3.
return fmt.format(value);
}
After this customisation, a decimal number like 2.0005 will be shown as 2.0005 instead of 2.

Categories