【问题标题】:Java TimeZone and Linux TimeZone Daylight Savings doesnot matchJava TimeZone 和 Linux TimeZone 夏令时不匹配
【发布时间】:2016-11-17 18:28:54
【问题描述】:

我需要创建由以下格式定义的 POSIX 格式的 TimeZone。

std offset dst [offset],start[/time],end[/time]

例如“America/New_York”的 POSIX 格式是 EST+5EDT,M3.2.0/2,M11.1.0/2

现在值 M3.2.0/2 以 Mm.w.d/t 的形式表示。

这指定了 m 月 w 周的第 d 天。 d 天必须介于 0(星期日)和 6 之间。周 w 必须介于 1 和 5 之间;第 1 周是第 d 天出现的第一周,第 5 周指定该月的最后 d 天。 m 月份应该在 1 到 12 之间。我从下面的链接中借用了上面的解释

http://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html

所以上面的例子说明,与 UTC 的正常偏移量是 5 小时;因为这是本初子午线以西,所以符号为正。夏季时间从 3 月的第二个星期日凌晨 2:00 开始,到 11 月的第一个星期日凌晨 2:00 结束。

当我在 Linux 时区文件 /usr/share/zoneinfo/America/New_York 中检查时,它与上述值匹配

EST5EDT,M3.2.0,M11.1.0

但是,当我在 java 中为时区“America/New_York”构造它时,我得到以下字符串

EST-5EDT+1,M2.1.1/2,M10.1.1/2

我通过从以下代码的输出中提取信息来构造上述字符串。

TimeZone timezone = TimeZone.getTimeZone("America/New_York");
System.out.println(timezone.toString());

输出如下

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]

注意值 endMonth=10 与 Linux 输出相比应该是 11。

【问题讨论】:

  • 你能发布一个指向这种格式的 POSIX 文档的链接吗?我很好奇。
  • 是的,我在描述中提到过,如下gnu.org/software/libc/manual/html_node/TZ-Variable.html
  • 谢谢,但不,这不是官方的 POSIX 文档。那只是 gnu.org 上的一些库。谷歌搜索让我找到了其他来源,记录了这种假定的 POSIX 日期时间格式的不同定义。所以我希望有人可以发布一个权威来源。

标签: java timezone posix dst


【解决方案1】:

不建议依赖toString 的输出,因为在 TimeZone 或 SimpleTimeZone 类中没有关于其格式的合同保证。

显然,您的月份数字差了一位;一个月中的一周并不那么简单,因为您需要将一个月的第一周考虑在内。

我会使用 Java 记录的公共方法来获取信息:

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

【讨论】:

  • 太棒了!!!!我使用了您代码中的输入,并且能够构造以下字符串 EST+5EDT,M3.2.0/2,M11.1.0/2 非常感谢您的帮助。
【解决方案2】:

以下是我用来构造 Posix 时区字符串的完整代码

公共类 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());

            }
        }
    }

}

}

【讨论】:

  • 无需检查rules 是否为空。 method’s contract 声明它返回“一个不可变的转换规则列表,而不是 null。”
  • 好的。会改变它。谢谢。
猜你喜欢
  • 2019-08-04
  • 1970-01-01
  • 2021-07-04
  • 2010-10-25
  • 2012-04-11
  • 1970-01-01
  • 2014-12-27
  • 2012-07-09
  • 1970-01-01
相关资源
最近更新 更多