【问题标题】:Setting TimeZone works but seems backwards设置时区有效,但似乎倒退
【发布时间】:2021-06-28 18:44:45
【问题描述】:

我收到了一个 UTC 格式的日期,但需要在我的本地时区 (EDT) 中显示它。

偶然发现以下链接:

How to set time zone of a java.util.Date?

提供以下答案:

SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = isoFormat.parse("2010-05-23T09:01:02");

我添加了以下代码行:

isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

你知道它有效吗?

试图了解发生了什么,但似乎有点倒退。

我原以为必须输入 EDT 才能从 UTC 转换为 EDT,但似乎恰恰相反。

isoFormat.setTimeZone(TimeZone.getTimeZone("EDT"));

根据 Java Docs for DateFormat 它读取 ....

基于上述情况,我似乎应该提供我想要的 TimeZone,而不是我要转换的内容。

你能解释一下我遗漏了什么或误解了什么吗?

如果我输入 UTC,如何让 EDT 知道将其正确转换?

任何人都可以填写我应该如何知道他们要求“来自”时区的空白吗?

【问题讨论】:

  • 您正在获得Date - 这只是一个瞬间。当您为此调用 toString() 时,它会将其转换为系统本地时区……但它本质上不是“在”美国东部时间……它只是一个瞬间。
  • 我建议你不要使用SimpleDateFormat、TimeZone`和Date。这些类设计不佳且过时已久,尤其是第一个类是出了名的麻烦。而是使用LocalDateTimeDateTimeFormatterZoneId,均来自java.time, the modern Java date and time API

标签: java timezone


【解决方案1】:

tl;博士

使用 java.time 类。

LocalDateTime
.parse( "2021-07-23T00:00" ) 
.atOffset( ZoneOffset.UTC )
.atZoneSameInstant(  ZoneId.of( "America/Montreal" ) )
.format(
    DateTimeFormatter
    .ofLocalizedDateTime( FormatStyle.FULL )
    .withLocale( Locale.CANADA_FRENCH )
)

详情

您使用的日期时间类在几年前被现代 java.time 类所取代。

显然您的输入字符串采用标准 ISO 8601 格式。 java.time 类在解析或生成文本时默认使用 ISO 8601 格式。因此无需定义自定义格式模式。

String input = "2021-07-23T01:23:45" ;
LocalDateTime ldt = LocalDateTime.parse( input ) ;

显然,您肯定知道输入应该被视为 UTC 中的日期和时间,也就是说,与 UTC 的偏移量为零时分秒。如果是这样,请教育他们发布您的数据,以说服他们在该字符串的末尾提供 +00:00Z 以表达该意图。

同时,我们可以分配零偏移量来实例化OffsetDateTime

OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;

您显然想将该日期时间调整为特定时区。应用ZoneId 以获取ZonedDateTime 对象。

您要求EDT。不幸的是,这样的 2-4 个字母代码不是实时时区。实时时区名称的格式为Continent/Region,例如Europe/Paris。也许您的意思是一个时区,例如America/New_York

ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

【讨论】:

    【解决方案2】:

    java.util.Date 对象仅表示时间轴上的一个瞬间 — 自 UNIX 纪元(格林威治标准时间 1970 年 1 月 1 日 00:00:00)以来的毫秒数的包装。由于它不保存任何时区信息,它的 toString 函数应用 JVM 的时区以返回格式为 EEE MMM dd HH:mm:ss zzz yyyyString,从这个 毫秒 值。要以不同的格式和时区获取java.util.Date 对象的String 表示,您需要使用具有所需格式和适用时区的SimpleDateFormat,例如

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Locale;
    import java.util.TimeZone;
    
    public class Main {
        public static void main(String[] args) throws ParseException {
            SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
            isoFormat.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
            Date date = isoFormat.parse("2010-05-23T09:01:02");
            
            isoFormat.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
            String strDateUtc = isoFormat.format(date);
            System.out.println(strDateUtc);
    
            isoFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
            String strDateNewYork = isoFormat.format(date);
            System.out.println(strDateNewYork);     
        }
    }
    

    输出:

    2010-05-23T09:01:02
    2010-05-23T05:01:02
    

    ONLINE DEMO

    java.time

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

    使用现代日期时间 API java.time 的解决方案:

    现代日期时间 API 基于 ISO 8601,只要日期时间字符串符合 ISO 8601 标准,就不需要明确使用 DateTimeFormatter 对象。

    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    
    public class Main {
        public static void main(String[] args) {
            LocalDateTime ldt = LocalDateTime.parse("2010-05-23T09:01:02");
    
            ZonedDateTime zdtUtc = ldt.atZone(ZoneId.of("Etc/UTC"));
            System.out.println(zdtUtc);
    
            ZonedDateTime zdtNewYork = zdtUtc.withZoneSameInstant(ZoneId.of("America/New_York"));
            System.out.println(zdtNewYork);
        }
    }
    

    输出:

    2010-05-23T09:01:02Z[Etc/UTC]
    2010-05-23T05:01:02-04:00[America/New_York]
    

    ONLINE DEMO

    注意:无论出于何种原因,如果您需要将此ZonedDateTime的对象转换为java.util.Date的对象,您可以这样做:

    Date date = Date.from(zdtUtc.toInstant());
    

    Date date = Date.from(zdtNewYork.toInstant());
    

    通过 Trail: Date Time 了解有关现代日期时间 API 的更多信息。


    * 出于任何原因,如果您必须坚持使用 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

    【讨论】:

      【解决方案3】:

      首先,Java 不支持 EDT 缩写,而是使用“EST5EDT”。如果没有找到任何东西,它属于 EST5EDT。 可以查看full list of available timezones

      在 SimpleDateFormatter 中,如果您使用 parse(),则意味着您尝试“从”某些内容读取。如果您使用的是 format(),则意味着您正在尝试“写入”某些内容。

      结论,在 parse() 的情况下,时区充当输入格式,但在 format() 的情况下,它充当输出格式。下面的程序将 EDT 日期转换为 IST 日期。可能使用下面示例中的 cmets,您将了解究竟发生了什么。

      // EDT Formatter
      SimpleDateFormat edtFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
      edtFormat.setTimeZone(TimeZone.getTimeZone("EST5EDT"));
      
      // IST Formatter
      SimpleDateFormat istFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
      istFormat.setTimeZone(TimeZone.getTimeZone("IST"));
      
      // Convert EDT to IST 
      String edtDate = "2010-05-23T00:00:00";
      Date date = edtFormat.parse(edtDate); //Parse from EDT to Local Timezone
      String istDate = istFormat.format(date); //Parse from Local Timezone to IST
      
      System.out.println("EDT: "+edtDate);
      System.out.print("Local Date: ");
      System.out.println(date);
      System.out.println("IST: "+istDate);
      
      

      我知道 java 最初发布的 java.util.Date 设计得很糟糕,造成了很多混乱,这就是为什么后来他们引入了 java.time api,其名称很清楚,比如 LocalDate、ZonedDate 等。

      【讨论】:

      • 您的回答存在两个严重问题:(1)您使用的SimpleDateFormat 没有Locale。检查Never use SimpleDateFormat or DateTimeFormatter without a Locale (2) 你不应该使用三个字母的abbv。对于时区。查看this page三字母时区ID部分。
      • @ArvindKumarAvinash 我知道语言环境,但这与这里的核心问题无关。我想用简单易懂的最少代码来解释。我不支持复制粘贴文化。谈到良好实践,任何像 Sonar 这样的代码评估工具都可以指出这一点。简而言之,我正在用简单的语言和最少的代码而不是编码标准来帮助理解这个概念
      • @OleV.V.你是对的,问题是java不支持缩写“EDT”检查full list of supported abbreviations here。可能您需要通过“EST5EDT”。由于它无法找到 EDT,它正在回退到 GMT。我会更新我的答案
      猜你喜欢
      • 1970-01-01
      • 2023-04-03
      • 2019-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多