【问题标题】:datetime parsing without changing timezone不更改时区的日期时间解析
【发布时间】:2019-01-20 09:59:07
【问题描述】:

我这里有字符串类型的日期和时间值。

  • appDate = 2/19/2019
  • 开始时间 = 下午 4:15

我想通过 api 在谷歌日历中插入一个事件。

当我解析日期字符串时,它会根据我的本地区域设置进行转换。我住在 GMT+03.00 时区。但我想在字符串中插入日期和时间。

原样输出: {dateTime=2019-01-19T04:15:00.000+03:00, timeZone=America/New_York}

我需要得到这个: {dateTime=2019-01-19T04:15:00.000-05:00, timeZone=America/New_York}

使用下面的代码块,它将晚上 8:15 插入谷歌日历。

SimpleDateFormat formatter = new SimpleDateFormat("mm/dd/yyyy HH:mm a", Locale.US);

String dateInString = appDate + " " + startTime;
Date date = formatter.parse(dateInString);
DateTime startDateTime = new DateTime(date);

EventDateTime start = new EventDateTime().setDateTime(startDateTime).setTimeZone("America/New_York");

【问题讨论】:

  • 您输入的日期/时间是在哪个时区? (不管它在哪里解析或存储在哪里)
  • 我在 GMT-05:00 时区的输入值。我不想更改输入中的任何内容。仅将字符串值按原样解析为日期格式。然后将其发送到 Google 日历。
  • 我没有尝试 Calendar 对象。 Google Calendar api 需要 DateTime 输入类型。
  • 仅供参考,非常麻烦的旧日期时间类,如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 现在是 legacy,被 Java 8 中内置的 java.time 类所取代,之后。见Tutorial by Oracle

标签: java datetime timezone google-calendar-api simpledateformat


【解决方案1】:

java.time

您在遗留java.util.Date 和相关类周围遇到非常 常见的混淆来源。您应该使用 Java 8 中引入的较新的 java.time 类:

System.out.printf("INFO - System default timezone: %s%n", ZoneId.systemDefault().getId());
// INFO - System default timezone: Europe/Moscow

// test data
String appDate = "2/19/2019";
String startTime = "4:15 PM";
String targetTimezone = "America/New_York";

String dateInString = appDate + " " + startTime;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/yyyy h:mm a");
LocalDateTime ldt = LocalDateTime.parse(dateInString, formatter);
ZonedDateTime zdt = ldt.atZone(ZoneId.of(targetTimezone));

System.out.println(zdt);  // 2019-02-19T16:15-05:00[America/New_York]

如您所见,ZonedDateTime 已使用正确的本地时间 (16:15 = 4:15 PM)、正确的偏移量 (-05:00) 和明确指定的时区 (America/纽约)。

如果幸运的话,Google Calendar API 将能够使用 java.time 类。但是,如果您出于某种原因需要恢复为java.util.Date,您始终可以通过调用添加到旧类的新方法来进行转换。从ZonedDateTime 中提取Instant(从区域调整到UTC),然后传递给新的Date.from 方法。

java.util.Date date = java.util.Date.from( zdt.toInstant() );

System.out.println(date);  // Wed Feb 20 00:15:00 MSK 2019
System.out.println(date.getTime());  // 1550610900000

请注意,java.util.Date 是使用 JVM 的当前默认时区 Europe/Moscow 打印的,因此日期和时间已相应调整。但是,这两个值(2019-02-19T16:15-05:00[America/New_York]Wed Feb 20 00:15:00 MSK 2019)表示同一时间点:纪元后的 1550610900000 毫秒(1970-01-01 00:00:00 UTC)。

【讨论】:

  • 如何将 ZonedDateTime 转换为 DateTime 对象?谢谢。
【解决方案2】:

SimpleDateFormat 允许您指定要解析的 TimeZone

例如: formatter.setTimeZone(TimeZone.getTimeZone("America/New_York"));

或者,如果您只是想要一个自定义 GMT-5 时区(即:没有任何特定于位置的 DST 规则): formatter.setTimeZone(TimeZone.getTimeZone("GMT-5"));

编辑:有机会查看问题的第二部分,并查看您正在使用的日历 api - 并假设您在这里使用的 google api 类是那些在com.google.apis groupId、google-api-services-calendar artifactId 中,那么您还想在实例化com.google.api.client.util.DateTime 实例时指定偏移量TimeZone

使用SimpleDateFormat 和指定的纽约时区将字符串“2/19/2019 4:15 PM”解析为日期将得到一个包含长值 1550610900000 的 java.util.Date(无论您的实际的本地时区)。请注意,如果您打印该DatetoString(),它将给出本地计算机时区的日期。例如:对我来说,它说“Wed Feb 20 05:15:00 SGT 2019”。

DateTime 通过 getTime() 从指定的 Date 中提取 long 值,并在内部将其存储为 long。

DateTime(或者至少是我快速拿来试验的 DateTime 类的版本 (v3-rev364-1.25.0))也有一个构造函数,它接受 TimeZone wherece它通过getOffset() 提取与UTC 的偏移量。如果您不通过它,它将在内部使用TimeZone.getDefault().getOffset(value)。稍后当它的 toString() 被调用时,它将使用它存储的偏移量(它从时区获得)呈现它。

例子:

SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy hh:mm a", Locale.US);
formatter.setTimeZone(TimeZone.getTimeZone("America/New_York"));
Date date = formatter.parse("2/19/2019 4:15 PM");

DateTime startDateTimeNY = new DateTime(date,TimeZone.getTimeZone("America/New_York")); 
DateTime startDateTimeSG = new DateTime(date,TimeZone.getTimeZone("Asia/Singapore"));   
DateTime startDateTimeNBO = new DateTime(date,TimeZone.getTimeZone("Africa/Nairobi"));  
System.out.println(startDateTimeNY); //prints 2019-02-19T16:15:00.000-05:00
System.out.println(startDateTimeSG); //prints 2019-02-20T05:15:00.000+08:00
System.out.println(startDateTimeNBO); //prints 2019-02-20T00:15:00.000+03:00

(您可以在输出中看到不同的当地时间)

我还没来得及确认,但看起来EventDateTime 只是使用DateTime 中的toString() 作为日期时间值(如果您没有在其中设置JsonFactory) . (它存储它用于其他目的的 TimeZone 吗?)

TLDR:在 SimpleDateFormat 上指定 TimeZone 可以让您从其源时区正确读取日期,并根据需要格式化输出,您还需要更改对 DateTime 构造函数的调用以使用所需的时区因此:

DateTime startDateTime = new DateTime(date,TimeZone.getTimeZone("America/New_York"));

【讨论】:

  • 我现在已经尝试过了。输出仍然错误。 2019-01-19T12:15:00.000+03:00 解析方法后,它会自动添加 +03:00 。我想防止这种情况发生。
  • @fdemir 你打电话给setTimeZone 之前打电话给parse吗?
  • 没错。解析前。
  • 为什么在日期末尾加上 +03:00?
  • java.util.Date 只是 1970-01-01 00:00:00 UTC 值之后的时区独立毫秒的包装,所以我猜问题的另一半在于如何此处使用的谷歌日历 API 类处理时区?
猜你喜欢
  • 1970-01-01
  • 2020-01-05
  • 2012-05-19
  • 1970-01-01
  • 2011-04-29
  • 2012-08-07
  • 1970-01-01
  • 2011-06-06
  • 2014-01-29
相关资源
最近更新 更多