I need to create POSIX format of TimeZone as defined by the following format.
std offset dst [offset],start[/time],end[/time]
For ex for "America/New_York" the POSIX format is EST+5EDT,M3.2.0/2,M11.1.0/2
Now the value M3.2.0/2 is represented in the form Mm.w.d/t.
This specifies day d of week w of month m. The day d must be between 0 (Sunday) and 6. The week w must be between 1 and 5; week 1 is the first week in which day d occurs, and week 5 specifies the last d day in the month. The month m should be between 1 and 12. I borrowed the above explanation from the following link
http://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
So the above example states, the normal offset from UTC is 5 hours; since this is west of the prime meridian, the sign is positive. Summer time begins on March’s second Sunday at 2:00am and ends on November’s first Sunday at 2:00am.
When I check this in Linux timezone file /usr/share/zoneinfo/America/New_York, it matches the above value
EST5EDT,M3.2.0,M11.1.0
However when I construct this in java for timezone "America/New_York" I get the following string
EST-5EDT+1,M2.1.1/2,M10.1.1/2
I constructed the above string by extracting the information from the output of the following code.
TimeZone timezone = TimeZone.getTimeZone("America/New_York");
System.out.println(timezone.toString());
The output is as below
sun.util.calendar.ZoneInfo[id="America/New_York",offset=-18000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/New_York,offset=-18000000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]
Notice the values endMonth=10 which should be 11 as compared to Linux output.
Relying on the output of toString
is not advisable, since there are no contractual guarantees about its format in either the TimeZone or SimpleTimeZone classes.
Obviously, your month numbers are off by one; the week of the month isn’t quite as simple, since you need to take into account the first full week of the month.
I would use Java’s documented public methods to get the information:
static String posixSpecFor(TimeZone tz) {
Formatter posixSpec = new Formatter();
float offset = (float) tz.getRawOffset() / (1000 * 60 * 60) * -1;
posixSpec.format("%s%s%s",
tz.getDisplayName(false, TimeZone.SHORT),
offset >= 0 ? "+" : "",
new DecimalFormat("0.##").format(offset));
if (tz.observesDaylightTime()) {
posixSpec.format("%s", tz.getDisplayName(true, TimeZone.SHORT));
}
ZoneId zone = tz.toZoneId();
TemporalField weekOfMonth =
WeekFields.of(DayOfWeek.SUNDAY, 7).weekOfMonth();
int thisYear = Year.now(zone).getValue();
List<ZoneOffsetTransitionRule> rules =
zone.getRules().getTransitionRules();
if (rules.size() > 2) {
rules = rules.subList(0, 2);
}
for (ZoneOffsetTransitionRule rule : rules) {
posixSpec.format(",M%d.%d.%d/%s",
rule.getMonth().getValue(),
rule.createTransition(thisYear).getDateTimeBefore().get(
weekOfMonth),
rule.getDayOfWeek().getValue() % 7,
rule.getLocalTime());
}
return posixSpec.toString();
}