在将数据转换为设备时区时获得 4 小时的差异?
Getting 4 hours difference while getting while converting data in to device timezone?
我从我设备上的以下代码行中得到 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 );
问题:与上面的代码有 4 小时的差异,因为我将时区设置为波士顿(美国),出现错误。
我的结果 来自下面 @Hugo 解决方案如下
我期待结果如下
请检查一次..我也设置了东部夏令时的时区但没有得到正确的解决方案..请检查一次..并告诉我
SimpleDateFormat
和 Calendar
使用 JVM 默认时区(除非你在它们上设置了不同的时区),每个 device/machine/environment 中的默认时区可以不同。不仅如此,这个默认值 ,因此最好始终明确说明您使用的是哪个。
当您执行以下操作时:
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. Prefer to use IANA timezones names(始终采用 Region/City
格式,例如 America/New_York
或 Europe/Berlin
)。
因此,当您执行 TimeZone.getTimeZone("EDT")
、it usually returns "GMT" (because "EDT" is not recognized, and "GMT" is returned as default). That's because "EDT" is used by more than one timezone 时,您必须具体选择您使用的是哪一个(在这些示例中我使用的是 America/New_York
)。
另一个细节是,在前 2 个格式化程序中,您使用 hh
、which means "hour of am/pm"(值从 1 到 12),但输入没有 AM/PM 指示符来正确解决这个问题。您需要将其更改为 HH
("hour of day",值从 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 默认时区(每个设备可能不同,你无法控制)。
输出为:
My Start date and end date==>>>08:15 AM 09:45 AM
Java新Date/TimeAPI
旧的 类(Date
、Calendar
和 SimpleDateFormat
)有 lots of problems and design issues,它们正在被新的 APIs.
在 Android 中你可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it ).
首先你可以使用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
,并打印全名),语言环境定义了使用的语言。
最后要得到对应的时间,可以再用一个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
这将是您为输出选择的时区中的 date/time。
如果您打算使用 JVM 默认值 (ZoneId.systemDefault()
),只需在之前检查它的值以确保它是您想要的值(可能不是因为它可以在运行时更改,所以最好指定一个)。
我从我设备上的以下代码行中得到 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 );
问题:与上面的代码有 4 小时的差异,因为我将时区设置为波士顿(美国),出现错误。
我的结果 来自下面 @Hugo 解决方案如下
我期待结果如下
请检查一次..我也设置了东部夏令时的时区但没有得到正确的解决方案..请检查一次..并告诉我
SimpleDateFormat
和 Calendar
使用 JVM 默认时区(除非你在它们上设置了不同的时区),每个 device/machine/environment 中的默认时区可以不同。不仅如此,这个默认值
当您执行以下操作时:
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. Prefer to use IANA timezones names(始终采用 Region/City
格式,例如 America/New_York
或 Europe/Berlin
)。
因此,当您执行 TimeZone.getTimeZone("EDT")
、it usually returns "GMT" (because "EDT" is not recognized, and "GMT" is returned as default). That's because "EDT" is used by more than one timezone 时,您必须具体选择您使用的是哪一个(在这些示例中我使用的是 America/New_York
)。
另一个细节是,在前 2 个格式化程序中,您使用 hh
、which means "hour of am/pm"(值从 1 到 12),但输入没有 AM/PM 指示符来正确解决这个问题。您需要将其更改为 HH
("hour of day",值从 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 默认时区(每个设备可能不同,你无法控制)。
输出为:
My Start date and end date==>>>08:15 AM 09:45 AM
Java新Date/TimeAPI
旧的 类(Date
、Calendar
和 SimpleDateFormat
)有 lots of problems and design issues,它们正在被新的 APIs.
在 Android 中你可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it
首先你可以使用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
,并打印全名),语言环境定义了使用的语言。
最后要得到对应的时间,可以再用一个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
这将是您为输出选择的时区中的 date/time。
如果您打算使用 JVM 默认值 (ZoneId.systemDefault()
),只需在之前检查它的值以确保它是您想要的值(可能不是因为它可以在运行时更改,所以最好指定一个)。