“Each man is always in the middle of the surface of the earth and under the zenith of his own hemisphere, and over the centre of the earth.”
Leonardo da Vinci
Some implementations aren’t based on clever technical tricks, but rather on sheer diligence. That’s why it took FreshMarker Astro quite a while to finally figure out which side of the world a template engine instance is currently located on. The following article describes how FreshMarker Astro managed to get out of this predicament and almost always guesses the hemisphere correctly.
Knowledge of the Northern and Southern Hemispheres is rarely required in software development. This is useful for correctly calculating the seasons, though this is also information that isn’t used very often in many computer systems. FreshMarker Astro is a FreshMarker extension based on the Time4J library. Among other things, FreshMarker Astro can output the corresponding season for a date in a template: ${date?season}. If you want to output the corresponding season for a date, it is necessary to know the hemisphere. After all, a beautiful summer day here in Germany falls in the middle of winter in Australia.
Until now, FreshMarker Astro relied on FreshMarker’s configuration options, and for calculations in the southern hemisphere, this had to be specified using AstroFeatures.AT_SOUTHERN_HEMISPHERE.
Configuration configuration = new Configuration(AstroFeatures.AT_SOUTHERN_HEMISPHERE<);
As mentioned earlier, hemisphere information is not really relevant, which is why the Java API does not provide it. However, it can be derived from the commonly used Locale class. The Locale provides information about the language and country in which the JVM is running. This information is typically used for internationalization (I18N) and localization (L10N); in our case, we use it to determine the hemisphere.
There are currently 195 countries in the world, and most of these countries are located in either the northern or southern hemisphere. So, if we know the country code from the Locale, we almost always know which hemisphere to consider. If the Locale is set to en-AU (Australia) for the template engine, the Southern Hemisphere is selected; if it is set to de-AT (Austria), the Northern Hemisphere is selected.
For a small number of countries, we have to guess whether the Northern Hemisphere or the Southern Hemisphere should be considered. In this case, however, we can still use the existing configuration mechanism from FreshMarker Astro. When it comes to the distribution of countries around the world, there is a fortunate coincidence for our feature: most countries are located in the Northern Hemisphere. Therefore, we only need to look up the ISO country code for about 50 countries and check it against the current country code of the Locale.
Until now, the Southern Hemisphere was selected when AstroFeatures.AT_SOUTHERN_HEMISPHERE was enabled. This remains the case, but the computeHemisphere method would be extended.
private static Hemisphere computeHemisphere(ProcessContext context) {
FeatureSet featureSet = context.getFeatureSet();
if (featureSet.isEnabled(AstroFeatures.AT_SOUTHERN_HEMISPHERE)) {
return Hemisphere.SOUTH;
}
if (featureSet.isEnabled(AstroFeatures.AUTO_HEMISPHERE)) {
try {
Properties properties = new Properties();
properties.load(AstroExtension.class.getResourceAsStream("/southern-countries.properties"));
if (properties.getProperty("entirely").contains(context.getLocale().getCountry())) {
return Hemisphere.SOUTH;
}
if (properties.getProperty("mostly").contains(context.getLocale().getCountry())) {
return Hemisphere.SOUTH;
}
} catch (IOException e) {
// ignored
}
}
return Hemisphere.NORTH;
}
A properties file named southern-countries.properties is loaded, containing three entries. These are lists of countries that are located entirely, mostly, or partially in the Southern Hemisphere. Currently, all countries that are located entirely or mostly in the Southern Hemisphere are assigned to that hemisphere.
If, for any reason, this automatic function does not produce the desired result, it can be turned off.
TemplateBuilder builder = new Configuration().builder().without(AstroFeatures.AUTO_HEMISPHERE);
This completes the implementation of this small feature, and most users no longer need to worry about Hemisphere.