android - 在将数据转换为设备时区的同时获得4个小时的时间差?

标签 android calendar timezone simpledateformat

我从设备上的以下代码行中获取了时区4小时的时差:

我正在以2018-09-30T13:45:00Z这样的方式度过时间

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


  “ start_date”:“ 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 );


问题:当我将时区设置为BOSTON(US)时,从上述代码获取了4个小时的时间差,出现错误。

我从下面的@Hugo解决方案得到的结果如下
enter image description here

我期待如下结果
enter image description here

请检查一次。.我还设置了美国东部夏令时间的时区,但未获得适当的解决方案。请检查一次。并让我知道

最佳答案

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”)。这是因为more than one timezone使用了“ EDT”,因此必须特别选择要使用的那个(在这些示例中,我使用的是America/New_York)。

另一个细节是,在前2个格式化程序中,您使用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默​​认时区(每个设备中的时区可能不同,您无法控制)。

输出为:


  我的开始日期和结束日期== >>> 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,并打印全名),而语言环境则定义所使用的语言。

最后,要获取相应的时间,可以使用另一个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()),只需在检查它的值之前确保它是您想要的值(这可能不是因为可以在运行时更改它,所以始终最好指定一个值) 。

关于android - 在将数据转换为设备时区的同时获得4个小时的时间差?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46200941/

相关文章:

Android Market - 应用程序不适用于设备

c# - 如何使用 Mailgun Rest API (C#) 发送 iCal 邀请

JavaScript 渲染逻辑 : Horizontal Dynamic Booking Calendar

java - 如何在不同时区以秒为单位查找两个日期之间的差异

mongodb - Ubuntu 时区已更改,但 mongodb 仍根据以前的时区打印日期时间

Android 在短按时同时触发 onclick 和 onlongclick

android - 第一个Android项目,以及第一次使用Mercurial,需要选择我的IDE

python - 使用python-twitter库,如何设置时区?

android - 如何从 Mavericks 获取 SHA1 指纹

android - 世博会保存日历事件