Java Calendar TimeZone一塌糊涂

时间:2015-07-24 16:03:16

标签: java calendar timezone

这是我的简单代码:

    String defaultSimpleDateFormatPattern = "MMM dd, yyyy HH:mm:ss";
    TimeZone tzNY = TimeZone.getTimeZone("America/New_York");
    TimeZone tzLos = TimeZone.getTimeZone("America/Los_Angeles");
    String dateToTest = "Jan 03, 2015 23:59:59";
    SimpleDateFormat df = new SimpleDateFormat(defaultSimpleDateFormatPattern);
    Calendar c = Calendar.getInstance();
    c.setTime(df.parse(dateToTest)); 
    c.setTimeZone(tzLos);

    System.out.println(c.getTimeZone());
    System.out.println(c.getTime());        
    System.out.println(df.format(c.getTime()));


    Calendar c1 = Calendar.getInstance();
    c1.setTime(df.parse(dateToTest));        
    c1.setTimeZone(tzNY);

    System.out.println(c1.getTimeZone());
    System.out.println(c1.getTime());
    System.out.println(df.format(c1.getTime()));

    System.out.println(c.after(c1)? "after" : (c.before(c1)? "before" : "equal"));

打印输出是"相等"。那个怎么样?对此结果的任何解释?

2 个答案:

答案 0 :(得分:0)

这里有两个问题:

  • 您使用的是无效的时区ID(您想要America/New_York
  • 您正在使用未设置时区的格式化程序进行解析(因此它将使用默认时区),然后在Calendar之后设置时区...那不会t改变表示的时间

所以基本上你要解析相同的Date两次,做一些不影响所表示的Date的事情,然后比较两个相等的Date值。

如果所有可能,您应该使用Joda Time或java.time而不是java.util.Calendar,但如果真的需要使用它,只需创建两个不同的格式化程序,每个时区一个。 (如果您确实需要Calendar,则需要在Calendar中设置时区...)

答案 1 :(得分:0)

java.time

java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*

另外,下面引用的是来自 home page of Joda-Time 的通知:

<块引用>

请注意,从 Java SE 8 开始,要求用户迁移到 java.time (JSR-310) - JDK 的核心部分,取代了该项目。

使用 java.time(现代日期时间 API)的解决方案:您的日期时间字符串没有时区信息,因此可以将其描述为本地日期时间。因此,将其解析为 LocalDateTime 并将时区应用于它以获得 ZonedDateTime

演示:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String defaultSimpleDateFormatPattern = "MMM dd, uuuu HH:mm:ss";
        ZoneId tzNY = ZoneId.of("America/New_York");
        ZoneId tzLos = ZoneId.of("America/Los_Angeles");
        String dateToTest = "Jan 03, 2015 23:59:59";

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(defaultSimpleDateFormatPattern, Locale.ENGLISH);
        LocalDateTime ldt = LocalDateTime.parse(dateToTest, dtf);

        ZonedDateTime zdtNY = ldt.atZone(tzNY);
        ZonedDateTime zdtLos = ldt.atZone(tzLos);

        System.out.println(zdtNY.isAfter(zdtLos) ? "after" : zdtNY.isBefore(zdtLos) ? "before" : "equal");
    }
}

输出:

before

ONLINE DEMO

或者,为每个时区创建单独的 DateTimeFormatter,即要求 Java 解析应用给定时区的本地日期时间字符串。

演示:

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String defaultSimpleDateFormatPattern = "MMM dd, uuuu HH:mm:ss";
        ZoneId tzNY = ZoneId.of("America/New_York");
        ZoneId tzLos = ZoneId.of("America/Los_Angeles");
        String dateToTest = "Jan 03, 2015 23:59:59";

        DateTimeFormatter dtfNY = DateTimeFormatter.ofPattern(defaultSimpleDateFormatPattern, Locale.ENGLISH)
                                    .withZone(tzNY);
        DateTimeFormatter dtfLos = DateTimeFormatter.ofPattern(defaultSimpleDateFormatPattern, Locale.ENGLISH)
                                    .withZone(tzLos);

        ZonedDateTime zdtNY = ZonedDateTime.parse(dateToTest, dtfNY);
        ZonedDateTime zdtLos = ZonedDateTime.parse(dateToTest, dtfLos);

        System.out.println(zdtNY.isAfter(zdtLos) ? "after" : zdtNY.isBefore(zdtLos) ? "before" : "equal");
    }
}

输出:

before

ONLINE DEMO

Trail: Date Time 了解有关现代 Date-Time API 的更多信息。

你的代码有什么问题?

  1. 您尚未为您的 SimpleDateFormat 设置时区:与现代 Date-Time API 不同,您可以通过多种方式创建特定于时区的 Date-Time 对象,您只能用遗留 API 的这种方式来处理这种情况(因为 java.util.Date does not hold timezone information)。它类似于上面显示的替代示例。
  2. 您尚未为您的 Locale 设置 SimpleDateFormat Never use SimpleDateFormat or DateTimeFormatter without a Locale。幸运的是,您的程序没有崩溃,因为您的 JVM 时区必须是英语语言环境。

演示:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) throws ParseException {
        String defaultSimpleDateFormatPattern = "MMM dd, yyyy HH:mm:ss";
        TimeZone tzNY = TimeZone.getTimeZone("America/New_York");
        TimeZone tzLos = TimeZone.getTimeZone("America/Los_Angeles");
        String dateToTest = "Jan 03, 2015 23:59:59";
        SimpleDateFormat df = new SimpleDateFormat(defaultSimpleDateFormatPattern, Locale.ENGLISH);

        df.setTimeZone(tzNY);
        Calendar c = Calendar.getInstance();
        c.setTime(df.parse(dateToTest));

        df.setTimeZone(tzLos);
        Calendar c1 = Calendar.getInstance(tzNY);
        c1.setTime(df.parse(dateToTest));

        System.out.println(c.after(c1) ? "after" : (c.before(c1) ? "before" : "equal"));
    }
}

输出:

before

ONLINE DEMO


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project