我在设备上的以下代码行中获得了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 解决方案的请检查一次..我还设置了 Eastern DayLight Time 的TimeZone但没有得到正确的解决方案..请检查一次..并告诉我
答案 0 :(得分:2)
SimpleDateFormat
和Calendar
使用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:00Z
:Z
最后为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_York
或Europe/Berlin
。
因此,当您执行TimeZone.getTimeZone("EDT")
,it usually returns "GMT"时(因为" EDT"无法识别," GMT"默认返回)。这是因为" EDT"由more than one timezone使用,因此您必须明确选择您正在使用的那个(我在这些示例中使用America/New_York
)。
另一个细节是,在前两个格式化程序中,您使用hh
,which 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
旧类(Date
,Calendar
和SimpleDateFormat
)有lots of problems和design 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.ENGLISH
或javadoc中所述的任何其他值)。
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()
),请先检查其值,以确保它是您想要的值(可能不是因为这可以更改)在运行时,所以指定一个总是更好。