如何解决Android设备系统时间将其转换为Date时更改服务器日期字符串的TimeZone
我必须检查服务器时间与Android设备系统时间之间的时差是否超过1小时。 现在我有来自服务器的这个字符串:
2020-08-24T11:50:18.613+0300
这些是我用于SimpleDateFormat的常量:
private static final String SYSTEM_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
问题是当我尝试将字符串日期转换为Date类时,我得到了这个提示:
Mon Aug 24 13:50:18 GMT+05:00 2020
据我了解,+ 05:00是因为这是在Android设备上默认设置的时区。这就是我得到这个日期的方式:
Locale locale = new Locale("ru","RU");
DateFormat df = new SimpleDateFormat(SYSTEM_FORMAT,locale);
df.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
Date date = df.parse(serverDate);
您可以看到,即使将时区设置为+3(莫斯科时间)也无法达到我期望的日期。我知道我可以只使用字符串进行比较,但要求是比较日期
解决方法
您实际上没有时间 zone ,而是一个偏移量。
要正确转换从服务器获得的String
,应使用java.time
而不是过时的java.util.Date
或java.util.Calendar
。
下面是一个使用适合您的情况的类的示例(java.time.OffsetDateTime
):
public static void main(String[] args) {
String stringFromServer = "2020-08-24T11:50:18.613+0300";
OffsetDateTime timeFromServer = OffsetDateTime.parse(
stringFromServer,DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSZ")
);
System.out.println(timeFromServer.format(
DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss XXX uuuu",Locale.ENGLISH))
);
}
此输出(使用模仿您发布的格式的OffsetDateTime
格式的输出)
Mon Aug 24 11:50:18 +03:00 2020
您也可以使用ZonedDateTime
,但是很可能必须添加ZoneId
,ZoneId.of("Europe/Moscow")
...
可以使用已经创建的OffsetDateTime timeFromServer
这样完成:
ZonedDateTime moscowTime = timeFromServer.atZoneSameInstant(ZoneId.of("Europe/Moscow"));
ZonedDateTime berlinTime = timeFromServer.atZoneSameInstant(ZoneId.of("Europe/Berlin"));
,然后System.out
对它们进行特殊格式化将为您提供以下几行:
2020-08-24T11:50:18.613+03:00[Europe/Moscow]
2020-08-24T10:50:18.613+02:00[Europe/Berlin]
请注意: 由于存在API Desugaring for Android,因此您可以在Android 26以下的API级别中使用Java 8(+)功能。
,在日期时间字符串中,您对Zone-Offset的理解似乎有些差距。日期时间字符串2020-08-24T11:50:18.613+0300
表示它是+0300 hours
的区域偏移处的日期时间,即UTC
上的相应日期时间字符串将为2020-08-24T8:50:18.613+0000
。 / p>
请注意,java.util.Date
缺少时区和时区偏移信息。它仅具有自1970年1月1日UTC时间00:00:00以来的毫秒数。当使用java.util.Date
的实例打印SimpleDateFormat
时,会将表示时区中日期时间的字符串打印到SimpleDateFormat
的实例中。您可以从以下示例中了解它:
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
// Parse the date-time string to java.util.Date
String serverDate = "2020-08-24T11:50:18.613+0300";
final String SYSTEM_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
DateFormat dfSource = new SimpleDateFormat(SYSTEM_FORMAT);
Date date = dfSource.parse(serverDate);
System.out.println(date);
// Get the date-time string for the time-zone of Europe/Moscow from
// java.util.Date
Locale locale = new Locale("ru","RU");
DateFormat dfTarget = new SimpleDateFormat(SYSTEM_FORMAT,locale);
dfTarget.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
System.out.println(dfTarget.format(date));
}
}
我建议您停止使用过时且容易出错的java.util
日期时间API和SimpleDateFormat
。切换到modern java.time
日期时间API和相应的格式API(java.time.format
)。从 Trail: Date Time 了解有关现代日期时间API的更多信息。如果您的Android版本与Java-8不兼容,则可以使用ThreeTen-BackportCheck向后移植。选中How to use ThreeTenABP in Android Project。
使用现代日期时间API:
import java.text.ParseException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) throws ParseException {
// Given date-time string
String serverDate = "2020-08-24T11:50:18.613+0300";
// Date-time formatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
ZonedDateTime zdtServer = ZonedDateTime.parse(serverDate,formatter);
System.out.println("Given date-time: " + zdtServer);
System.out.println("Zone Offset of the given date-time: " + zdtServer.getOffset());
// Convert it to some other time-zone e.g Etc/UTC
ZonedDateTime zdtatUTC = zdtServer.withZoneSameInstant(ZoneId.of("Etc/UTC"));
System.out.println("Equivalent date-time at UTC: " + zdtatUTC);
// Convert it to some other time-zone e.g Europe/London
ZonedDateTime zdtInLondon = zdtServer.withZoneSameInstant(ZoneId.of("Europe/London"));
System.out.println("Equivalent date-time in London: " + zdtInLondon);
}
}
输出:
Given date-time: 2020-08-24T11:50:18.613+03:00
Zone Offset of the given date-time: +03:00
Equivalent date-time at UTC: 2020-08-24T08:50:18.613Z[Etc/UTC]
Equivalent date-time in London: 2020-08-24T09:50:18.613+01:00[Europe/London]
请注意,现代的日期时间API具有一个名为ZonedDateTime
的类,该类具有时区信息以及日期和时间信息。
比较时间时,无需担心与UTC或时区的偏差。偏移量之间的比较比较顺利。
java.time
我正在使用java.time(现代Java日期和时间API)。首先定义几个有用的常量:
private static final Duration TOLERANCE = Duration.ofHours(1);
private static final DateTimeFormatter serverFormatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendPattern("XX")
.toFormatter();
现在我们可以做到:
String serverDateTimeString = "2020-08-24T11:50:18.613+0300";
OffsetDateTime serverTime = OffsetDateTime.parse(serverDateTimeString,serverFormatter);
System.out.println("Server time: " + serverTime);
ZoneId deviceTimeZone = ZoneId.of("Asia/Yekaterinburg");
OffsetDateTime deviceTime = OffsetDateTime.now(deviceTimeZone);
System.out.println("Device time: " + deviceTime);
if (deviceTime.isBefore(serverTime.minus(TOLERANCE)) || deviceTime.isAfter(serverTime.plus(TOLERANCE))) {
System.out.println("Difference between time on server and Android device system time more than 1 hour");
} else {
System.out.println("Difference between time on server and Android device system time 1 hour or less");
}
我已经从问题中使用了您的字符串,因此当我现在运行该代码段时,这段代码告诉我们这两者之间存在很大差异,我们不会感到惊讶:
Server time: 2020-08-24T11:50:18.613+03:00 Device time: 2020-08-24T23:08:57.939565+05:00 Difference between time on server and Android device system time more than 1 hour
为了进行实验,我们还尝试将设备时间设置为您提出问题的时间:
OffsetDateTime deviceTime = OffsetDateTime.of(2020,8,24,13,50,18,ZoneOffset.ofHours(5));
Server time: 2020-08-24T11:50:18.613+03:00 Device time: 2020-08-24T13:50:18+05:00 Difference between time on server and Android device system time 1 hour or less
问题:java.time是否不需要Android API级别26?
java.time在较新和较旧的Android设备上均可正常运行。它只需要至少 Java 6 。
- 在Java 8和更高版本以及更新的Android设备(API级别26以上)中,内置了现代API。
- 在非Android Java 6和7中,获得ThreeTen Backport,这是现代类的backport(JSR 310的ThreeTen;请参见底部的链接)。
- 在较旧的Android上,请使用废除旧书或Android版本的ThreeTen Backport。称为ThreeTenABP。在后一种情况下,请确保使用子包从
org.threeten.bp
导入日期和时间类。
链接
- Oracle tutorial: Date Time解释如何使用java.time。
- Java Specification Request (JSR) 310,其中首先描述了
java.time
。 - ThreeTen Backport project,即
java.time
向Java 6和7(JSR-310的ThreeTen)的反向端口。 - Java 8+ APIs available through desugaring
- ThreeTenABP,Android版本的ThreeTen Backport
- Question: How to use ThreeTenABP in Android Project,其中有非常详尽的解释。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。