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

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

Related

Java 8 - Hashmap cannot find and return a ZonedDateTime value

I'm working on a example project where we need to store some values in a Hashmap and fetch them via REST services.
POSTing the data as
{"timestamp":"2015-09-01T16:40:00.000Z", "temperature":"27.2"}
Save method:
#PostMapping
public ResponseEntity<?> createCalc(#Valid #RequestBody Calc calc) {
store.add(measurement);
}
...
Store class
private HashMap<ZonedDateTime, Calc> calcHashMap;
...
public void add(Calc calc) {
calcHashMap.put(calc.getTimestamp(), calc);
}
After saving we wanted to get data from Hashmap, none of the below can find it.
http://localhost:8000/measurements/2015-09-01T16:40:00.000Z
or
http://localhost:8000/measurements/2015-09-01T16:40Z
Method we are using is
#GetMapping("/{timestamp}")
public ResponseEntity<Calc> getCalc(#PathVariable ZonedDateTime timestamp) {
Calc calc = process.fetch(timestamp);
Process.java class
public Calc fetch(ZonedDateTime timestamp) {
return calcMap.get(timestamp);
}
Adding that to a Hashmap and Timestamp as the key. We are seeing these differences:
When printing the value in console using System.out.println 2015-09-01T16:40Z
Return from REST GET method in POSTMAN shows this 2015-09-01T16:40:00Z
While the actual value is 2015-09-01T16:40:00.000Z
I need to find the stores timestamp and return the object, but is not being found because of the differences above. How to solve this?
After some research i was able to solve this as below
for (ZonedDateTime zdt : measurementHashMap.keySet()) {
if (timestamp.toInstant().compareTo(measurementHashMap.get(zdt).getTimestamp().toInstant()) == 0) {
return measurementHashMap.get(zdt);
}
}
Option 2:
There is another option to convert ZonedDateTime to OffSetDate and then compare. Ref: https://rextester.com/SIIIW97667
In your Calc class, you can use #JsonFormat from Jackson library and give the required pattern you want. In this case, "yyyy-MM-dd'T'HH:mm:ss.SSSX". Also, I suggest you to use OffsetDateTime directly instead of conversion.

Set range limits on DateTimeItem

What I need specifically is to prevent user from entering date/time that is in the future. That's different than disabling a specific date, since I need to disable ALL dates part a certain date.
Ideally, any future dates should be disabled. For now, I'm just going prevent form submit when user enters invalid time, but disabling unwanted dates would be better.
I went through the javadoc and found nothing. Is it possible? How?
Configuring a range just means disabling the dates outside this range. So the process is the same as the one in the linked answer. You can create a utility method to create filters easier. For example, configureShowRangeHandler admits a Predicate<Date> that will disable the date if the predicate returns false. The enableUntilToday is a simple example to limit selectable dates until today.
{
DatePicker dp = new DatePicker();
Predicate<Date> enableUntilTodayDates = d -> !d.after(new Date());
configureShowRangeHandler(dp, enableUntilTodayDates);
}
static HandlerRegistration configureShowRangeHandler(DatePicker dp, Predicate<Date> fn) {
return dp.addShowRangeHandler(ev -> {
for (Date t = copyDate(ev.getStart()); t.before(ev.getEnd()); addDaysToDate(t, 1)) {
dp.setTransientEnabledOnDates(fn.test(t), t);
}
});
}
copyDate and addDaysToDate are static imported from CalendarUtil

Determining a String based on the value of another String?

Not all that sure how I would describe this question, so I'll jump right into the example code.
I have a Constants.java
package com.t3hh4xx0r.poc;
public class Constants {
//RootzWiki Device Forum Constants
public static final String RWFORUM = "http://rootzwiki.com/forum/";
public static final String TORO = "362-cdma-galaxy-nexus-developer-forum";
public static String DEVICE;
}
In trying to determine the device type, I use this method.
public void getDevice() {
Constants.DEVICE = android.os.Build.DEVICE.toUpperCase();
String thread = Constants.(Constants.DEVICE);
}
Thats not correct though, but thats how I would think it would have worked.
Im setting the Constants.DEVICE to TORO in my case on the Galaxy Nexus. I want to then set the thread String to Constants.TORO.
I dont think I'm explaining this well, but you shoudl be able to understand what I'm trying to do fromt he example code. I want
Constants.(VALUE OF WHAT CONSTANTS.DEVICE IS) set for the String thread.
Another way to put it,
I want to get Constants.(//value of android.os.Build.DEVICE.toUpperCase())
I apologies for the poorly worded question, i dont know of any better way to explain what Im trying to achieve.
Im trying to determine the thread based on the device type. I could go in and do an
if (Constants.DEVICE.equals("TORO"){
String thread = Constants.TORO;
}
But I plan on adding a lot more device options in the future and would like to make it as easy as adding a string to the Constants.java rather than having to add another if clause.
I would suggest using an enum instead of just strings - then you can use:
String name = android.os.Build.DEVICE.toUpperCase();
// DeviceType is the new enum
DeviceType type = Enum.valueOf(DeviceType.class, name);
You can put the value of the string in a field for the enum, and expose it via a property:
public enum DeviceType {
RWFORUM("http://rootzwiki.com/forum/"),
TORO("362-cdma-galaxy-nexus-developer-forum");
private final String forumUrl;
private DeviceType(String forumUrl) {
this.forumUrl = forumUrl;
}
public String getForumUrl() {
return forumUrl;
}
}
(I'm guessing at the meaning of the string value - not a great guess, but hopefully it gives the right idea so you can make your actual code more meaningful.)
EDIT: Or to use a map:
Map<String, String> deviceToForumMap = new HashMap<String, String>();
deviceToForumMap.put("RWFORUM", "http://rootzwiki.com/forum/");
deviceToForumMap.put("TORO", "362-cdma-galaxy-nexus-developer-forum");
...
String forum = deviceToForumMap.get(android.os.Build.DEVICE.toUpperCase());
You can use reflection:
Constants.DEVICE = android.os.Build.DEVICE.toUpperCase();
String thread = (String) Constants.class.getField(Constants.DEVICE).get(null);
Not sure I've understood the question properly, but I feel that it's a right place to use a Map. The outcome will be something like this:
HashMap<String, String> map;
map.put(TORO, DEVICE);
Constants.DEVICE = android.os.Build.DEVICE.toUpperCase();
String thread = map.get(Constants.DEVICE);
Sorry for a possible misunderstanding or your question, but I hope you've got the idea.
P.S. You can find more info about Maps in the Java documentation: Map, HashMap.

jodatime DateTime objects and Locale

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.

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