0
votes

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.

2
Can you post a link to the POSIX doc for this format? I am curious.Basil Bourque
Yes, I mentioned it in the description, its as follows gnu.org/software/libc/manual/html_node/TZ-Variable.htmlJasprit Singh
Thanks, but No, that's not the official POSIX documentation. That is just some library at gnu.org. Googling led me to other sources documenting a different definition of this supposed POSIX date-time format. So I was hoping someone might post an authoritative source.Basil Bourque

2 Answers

0
votes

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();
}
0
votes

Following is the complete code that I am using to construct Posix timezone string

public class PosixTimeZone {

public String toPosixTZ(String timezoneStr) {

    TimeZone timezone = TimeZone.getTimeZone(timezoneStr);
    sop("timezoneStr", timezoneStr);
    String posixTX = "";

    PosixTimeZoneData pTZData = new PosixTimeZoneData(timezone);
    if (timezone.useDaylightTime()) {
        posixTX = getPosixDSString(pTZData);
    } else {
        posixTX = getPosixString(pTZData);
    }
    return posixTX;
}

public static void main(String args[]) {

    System.out.println("Posix TimeZone is " + new PosixTimeZone().toPosixTZ(args[0]));
}

private void sop(String varname, String meesage) {
    System.out.println("**************: " + varname + " = " + meesage);
}

private String getPosixDSString(PosixTimeZoneData pTZData) {
    String posixString = "";

    if ((pTZData.std != null && !pTZData.std.isEmpty())
            && (pTZData.stdOffset != null)//&& !pTZData.stdOffset.isEmpty())
            && (pTZData.dst != null && !pTZData.dst.isEmpty())
            && (pTZData.dstOffset != null)// && !pTZData.dstOffset.isEmpty())
            && (pTZData.start != null && !pTZData.start.isEmpty())
            && (pTZData.end != null && !pTZData.end.isEmpty())) {
        posixString = String.format("%s%s%s%s,%s,%s", pTZData.std, pTZData.stdOffset, pTZData.dst,
                pTZData.dstOffset, pTZData.start, pTZData.end);
    } else {
        sop("Error", "Invalid Parameters");
    }

    return posixString;
}

private String getPosixString(PosixTimeZoneData pTZData) {
    String posixString = "";

    if ((pTZData.std != null && !pTZData.std.isEmpty())
            && (pTZData.stdOffset != null && !pTZData.stdOffset.isEmpty())) {
        posixString = String.format("%s%s", pTZData.std, pTZData.stdOffset);
    } else {
        sop("Error", "Invalid Parameters");
    }

    return posixString;
}

class PosixTimeZoneData {

    String std = "";
    String stdOffset = "";
    String dst = "";
    String dstOffset = "";
    String start = "";
    String end = "";

    private PosixTimeZoneData(TimeZone timeZone) {

        std = timeZone.getDisplayName(false, TimeZone.SHORT);
        int rawOffset = (timeZone.getRawOffset() / 3600000) * -1;
        stdOffset = (rawOffset >= 0)
                ? ((rawOffset == 0) || (rawOffset == 1) ? "" : "+" + rawOffset)
                : "" + rawOffset;
        if (timeZone.useDaylightTime()) {
            dst = timeZone.getDisplayName(true, TimeZone.SHORT);

            int dstRawOffset = timeZone.getDSTSavings() / 3600000;
            dstOffset = (dstRawOffset >= 0)
                    ? ((dstRawOffset == 0) || (dstRawOffset == 1) ? "" : "+" + dstRawOffset)
                    : "" + dstRawOffset;

            ZoneId zone = timeZone.toZoneId();

            TemporalField weekOfMonth
                    = WeekFields.of(DayOfWeek.SUNDAY, 7).weekOfMonth();
            int thisYear = Year.now(zone).getValue();

            List<ZoneOffsetTransitionRule> rules
                    = zone.getRules().getTransitionRules();
            if (rules != null && !rules.isEmpty()) {
                if (rules.size() > 2) {
                    rules = rules.subList(0, 2);
                }

                start = String.format("M%d.%d.%d/%s",
                        rules.get(0).getMonth().getValue(),
                        rules.get(0).createTransition(thisYear).getDateTimeBefore().get(
                        weekOfMonth),
                        rules.get(0).getDayOfWeek().getValue() % 7,
                        rules.get(0).getLocalTime().getHour());

                end = String.format("M%d.%d.%d/%s",
                        rules.get(1).getMonth().getValue(),
                        rules.get(1).createTransition(thisYear).getDateTimeBefore().get(
                        weekOfMonth),
                        rules.get(1).getDayOfWeek().getValue() % 7,
                        rules.get(1).getLocalTime().getHour());

            }
        }
    }

}

}