在将数据转换为设备时区时获得4小时的差异?

时间:2017-09-13 15:04:51

标签: android calendar timezone simpledateformat

我在设备上的以下代码行中获得了4小时的时区差异:

我正在以2018-09-30T13:45:00Z

这样的方式获得时间

我的开始和结束日期如下: -

  

“起始日期”: “2017-09-13T12:15:00Z”,
      “END_DATE”: “2018-09-30T13:45:00Z”,

Calendar c = Calendar.getInstance(Locale.getDefault());
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Date localStartDate = formatter.parse(startTime);
Date localEndDate = formatter.parse(endTime);
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm");

dateFormatter.setTimeZone(c.getTimeZone());
localStartDate = dateFormatter.parse(startTime);
localEndDate = dateFormatter.parse(endTime);
SimpleDateFormat monthFormat = new SimpleDateFormat("MMM");
monthFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));


String monthName = monthFormat.format(localStartDate);
eventDate.setMonth(monthName);
SimpleDateFormat dateFormat = new SimpleDateFormat("dd");
dateFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));


String dateName = dateFormat.format(localStartDate);
eventDate.setDate(dateName);
SimpleDateFormat dayNameFormat = new SimpleDateFormat("EEEE");
dayNameFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));


String dayName1 = dayNameFormat.format(localStartDate);
String dayName2 = dayNameFormat.format(localEndDate);
eventDate.setDayName1(dayName1);
eventDate.setDayName2(dayName2);
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm a");
timeFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));



String startTimeName = timeFormat.format(localStartDate);
String endTimeName = timeFormat.format(localEndDate);


System.out.println("My Start date and end date==>>>"+startTimeName+"  " +endTimeName );

问题:与上面的代码有4个小时的差异,因为我将时区设置为波士顿(美国),收到错误。

来自以下 @Hugo 解决方案的

我的结果如下 enter image description here

期待结果如下所示 enter image description here

请检查一次..我还设置了 Eastern DayLight Time 的TimeZone但没有得到正确的解决方案..请检查一次..并告诉我

1 个答案:

答案 0 :(得分:2)

SimpleDateFormatCalendar使用JVM默认时区(除非您在其上设置不同的时区),并且每个设备/计算机/环境中的默认时区可以不同。不仅如此,这是默认的can be changed without notice, even at runtime,因此最好始终明确指出您正在使用哪一个。

当你做以下事情时:

Calendar c = Calendar.getInstance(Locale.getDefault());
dateFormatter.setTimeZone(c.getTimeZone());
monthFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));

使用默认时区创建Calendar,因此dateFormatter也将具有相同的区域。 monthFormat以及您创建的其他格式化程序也是如此。设置为不同区域的唯一格式化程序是第一个(设置为UTC)。

此外,第二个格式化程序是冗余的(它与第一个格式化程序执行的操作相同:将String解析为Date),因此您可以将其删除。

假设您的输入为String,其值为2018-09-30T13:45:00ZZ最后为indicates that this date is in UTC。所以你应该使用设置为UTC的格式化程序解析它。因此,您应该只使用c.getTimeZone()而不是TimeZone.getDefault()TimeZone.getTimeZone("UTC")

对于输出,您必须使用要转换为的时区设置格式化程序。如果时区是" EDT",则设置为它(但不要完全使用" EDT",见下文)。如果您想使用JVM默认值,请使用TimeZone.getDefault() - 之前检查此值,以确保默认值符合您的需要

请记住像" EDT"等短名称。和" EST"不是真正的时区。这些缩写是ambiguous and not standard。更喜欢使用IANA timezones names(始终采用Region/City格式,例如America/New_YorkEurope/Berlin

因此,当您执行TimeZone.getTimeZone("EDT")it usually returns "GMT"时(因为" EDT"无法识别," GMT"默认返回)。这是因为" EDT"由more than one timezone使用,因此您必须明确选择您正在使用的那个(我在这些示例中使用America/New_York)。

另一个细节是,在前两个格式化程序中,您使用hhwhich means "hour of am/pm"(值从1到12),但输入没有AM / PM指示符来正确解析此问题。您需要将其更改为HH("一天中的小时",值为0到23)。

    // input is in UTC
TimeZone inputZone = TimeZone.getTimeZone("UTC");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
formatter.setTimeZone(inputZone);
Date localStartDate = formatter.parse(startTime);
Date localEndDate = formatter.parse(endTime);
...

// removed the second formatter (it was redundant)

// output is in EST (America/New_York)
// or use TimeZone.getDefault() to get JVM default timezone
TimeZone outputZone = TimeZone.getTimeZone("America/New_York");
SimpleDateFormat monthFormat = new SimpleDateFormat("MMM");
monthFormat.setTimeZone(outputZone);
...

SimpleDateFormat dateFormat = new SimpleDateFormat("dd");
dateFormat.setTimeZone(outputZone);
...

SimpleDateFormat dayNameFormat = new SimpleDateFormat("EEEE");
dayNameFormat.setTimeZone(outputZone);
...

SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm a");
timeFormat.setTimeZone(outputZone);
...

System.out.println("My Start date and end date==>>>" + startTimeName + "  " + endTimeName);

通过这种方式,您可以明确地使用UTC作为输入,使用特定的时区进行输出,而不是依赖于JVM默认时区(在每个设备中可以有所不同,并且您无法控制)。 / p>

输出结果为:

  

我的开始日期和结束日期==>>> 08:15 AM 09:45 AM

Java新日期/时间API

旧类(DateCalendarSimpleDateFormat)有lots of problemsdesign issues,并且它们被新API取代

在Android中,您可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。为了使其有效,您还需要ThreeTenABP(更多关于如何使用它here)。

首先,您可以使用org.threeten.bp.Instant来解析输入,因为它是UTC(最后由Z指定)。然后,您使用org.threeten.bp.ZoneId将其转换为org.threeten.bp.ZonedDateTime

// output timezone
// or use ZoneId.systemDefault() to get JVM default timezone
ZoneId zone = ZoneId.of("America/New_York");
// parse the inputs
ZonedDateTime startDate = Instant.parse(startTime).atZone(zone);
ZonedDateTime endDate = Instant.parse(endTime).atZone(zone);

然后您可以使用这些对象来获取其他字段:

// get month name
System.out.println(startDate.getMonth().getDisplayName(TextStyle.SHORT, Locale.getDefault()));

这相当于MMM模式,它将在默认语言环境中打印月份名称。如果您希望使用特定语言的月份名称,只需使用其他java.util.Locale值(例如Locale.ENGLISHjavadoc中所述的任何其他值)。

org.threeten.bp.format.TextStyle定义月份名称是狭义(通常只有一个字母),短(通常是2或3个字母)还是满(全名)。输出根据使用的区域设置而变化。

我个人更喜欢不使用默认语言环境,因为即使在运行时也可以在不事先通知的情况下进行更改。指定所需的区域设置总是更好。

要获取月中的某一天,您可以选择将其作为int或格式String(使用org.threeten.bp.format.DateTimeFormatter)获取:

// get day of month as int
int day = startDate.getDayOfMonth(); // 30
// get day of month as formatted string
String dayStr = DateTimeFormatter.ofPattern("dd").format(startDate); // 30

要获取星期几,它与用于获得月份的代码相似:

// get day of week
System.out.println(startDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault()));

此处适用相同的逻辑:TextStyle定义名称的方式(在这种情况下,FULL等同于EEEE,并打印全名),以及locale定义了使用的语言。

最后,要获得相应的时间,您可以使用其他DateTimeFormatter

// get time
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("hh:mm a");
System.out.println(fmt.format(startDate)); // 08:15 AM
System.out.println(fmt.format(endDate)); // 09:45 AM

这将是您为输出选择的时区中的日期/时间。

如果您要使用JVM默认值(ZoneId.systemDefault()),请先检查其值,以确保它是您想要的值(可能不是因为这可以更改)在运行时,所以指定一个总是更好。