例如,我以date
的形式检索名为2018-09-20T17:00:00Z
的字符串,并使用
Thu Oct 20 17:00:00 GMT+01:00 2018
格式的Date。
SimpleDateFormat dateConvert = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US);
convertedDate = new Date();
try {
convertedDate = dateConvert.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
但是在不同的设备上我得到了不同的结果。一个给Thu Oct 20 17:00:00 BST 2018
(英国夏令时,相当于格林尼治标准时间+01:00),但是后来证明这是有问题的。是否有办法确保日期采用GMT偏移量格式设置,即GMT + 01:00而不是BST?
答案 0 :(得分:3)
Instant convertInstant = Instant.parse(date);
Instant
(就像Date
一样)代表一个时间点,与时区无关。很好。另外,您的2018-09-20T17:00:00Z
的String瞬时处于ISO 8601格式,因此Instant
类无需指定格式即可对其进行解析。
编辑:例如,要在英国夏令时以明确的UTC偏移量将其格式化为人类可读的字符串,请使用:
DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss Z yyyy", Locale.UK);
ZonedDateTime dateTime = convertInstant.atZone(ZoneId.of("Europe/London"));
String formatted = dateTime.format(formatter);
System.out.println(formatted);
此代码段已打印:
2018年9月20日星期四18:00:00 +0100
18:00是偏移+01:00的正确时间。原始字符串末尾的Z
表示偏移量为零,也称为“ Zulu时区”,偏移量为零的17与偏移量+01:00的18:00是同一时间点。我从您自己的答案中接过了格式模式字符串。
编辑2
我想向您提出从您自己的答案中重写Fixture
类的建议:
public class Fixture implements Comparable<Fixture> {
private static DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss Z yyyy", Locale.UK);
public Instant date;
/** @param date Date string from either web service or persistence */
public Fixture(String date) {
this.date = Instant.parse(date);
}
/** @return a string for persistence, e.g., Firebase */
public String getDateForPersistence() {
return date.toString();
}
/** @return a string for the user in the default time zone of the device */
public String getFormattedDate() {
return date.atZone(ZoneId.systemDefault()).format(formatter);
}
@Override
public int compareTo(Fixture other) {
return date.compareTo(other.date);
}
@Override
public String toString() {
return "Fixture [date=" + date + "]";
}
}
此类具有自然的顺序(即按日期和时间排序),因为它实现了Comparable
,这意味着您不再需要DateSorter
类。以下几行代码演示了如何使用新的getXx
方法:
String date = "2018-09-24T11:30:00Z";
Fixture fixture = new Fixture(date);
System.out.println("Date for user: " + fixture.getFormattedDate());
System.out.println("Date for Firebase: " + fixture.getDateForPersistence());
当我在欧洲/伦敦时区运行此代码片段时,我得到了:
Date for user: Mon Sep 24 12:30:00 +0100 2018
Date for Firebase: 2018-09-24T11:30:00Z
因此,按照我认为您的要求,用户可以从UTC中获得自己的日期和时间,以及自己的偏移量。在欧洲/柏林时区尝试相同的代码段:
Date for user: Mon Sep 24 13:30:00 +0200 2018
Date for Firebase: 2018-09-24T11:30:00Z
我们看到德国用户被告知比赛是13:30而不是12:30,这与他或她的时钟一致。在Firebase中保留的日期不变,这也是您想要的。
您的格式模式字符串yyyy-MM-dd'T'hh:mm:ss'Z'
中有两个错误:
hh
在01到12的AM或PM中持续一个小时,并且仅对AM / PM标记有意义。实际上,您将获得正确的结果,除非解析一个小时的12 ,这将被理解为00。Z
解析为文字,您不会从字符串中获取UTC偏移量信息。而是SimpleDateFormat
将使用JVM的时区设置。显然,一台设备之间存在差异,并解释了为什么在不同的设备上获得不同且相互矛盾的结果。代码中发生的另一件事是Date.toString
的特殊行为:此方法获取JVM的时区设置并将其用于生成字符串。因此,当一台设备设置为Europe / London且另一台设备设置为GMT + 01:00时,相等的Date
对象将在这些设备上呈现不同的效果。这种行为使很多人感到困惑。
是的,java.time
在较新的Android设备上都能很好地工作。它只需要至少Java 6 。
org.threeten.bp.Duration
开发和运行的。org.threeten.bp
导入日期和时间类。java.time
。java.time
。java.time
向Java 6和7(JSR-310的ThreeTen)的反向端口。答案 1 :(得分:2)
您正在执行两步过程的第一步:
Date
对象Date
对象,然后再次使用format
来SimpleDateFormat
。 因此,您已经正确完成了第一步,这是您必须执行的第2步,请尝试以下操作:
final String formattedDateString = new SimpleDateFormat("EEE MMM dd HH:mm:ss 'GMT'XXX yyyy").format(convertedDate);
答案 2 :(得分:0)
因此,只需在此处添加一些更新即可。人们提供的答案极大地帮助了我,而我现在拥有的代码可以完全满足我的要求,但是其中一项注释中的“旧版”一词使我感到可能会有更好,更持久的方式。这是代码中当前发生的情况。
1)我以2018-09-22T11:30:00Z
的形式获取一个带有字符串utc日期的足球器材
2)然后我使用SimpleDateFormat convertUtcDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
和convertUtcDate.setTimeZone(TimeZone.getTimeZone("GMT"));
3)然后,我使用currentTime = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime();
获取当前时间,并使用if(convertedDate.after(currentTime))
比较两者,以找到球队的下一个比赛。至此,我发现设备将使用BST或GMT + 01:00以相同的形式显示这两个日期,但是无论哪种方式,都可以准确比较这两个日期。
4)然后我使用SimpleDateFormat convertToGmt = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
和String dateString = convertToGmt.format(convertedDate);
5)对于1)中的utc日期,无论设备是什么,它都返回Sat Sep 22 12:30:00 GMT+01:00 2018
。请注意,时间与utc日期不同。不太确定为什么会这样(可能是因为运行API的人位于德国,比我在英格兰这里要晚一个小时),但重要的是这次是正确的(它指的是富勒姆-沃特福德明天是格林尼治标准时间12:30(格林尼治标准时间+01:00)。
6)然后,我将此字符串以及有关灯具的其他一些信息发送到Firebase。重要的是,此时的日期格式应为GMT + 01:00而不是BST,因为其他设备在读取该信息时可能无法识别BST格式。
7)当涉及从Firebase回调该信息时,我可以使用SimpleDateFormat String2Date = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US);
进行解析,将其转换为日期,然后可以通过比较它们的日期按时间顺序排列。
我只想重申一下这种方法有效。当英国的时区更改回GMT + 00:00时,我已经检查了灯具,它仍然可以正常工作。我试图确保一切都按照格林尼治标准时间(GMT)进行,以便可以在任何地方使用。我不确定是否会是这种情况。有人看到这种方法有什么缺陷吗?可以改善吗?
编辑:这是我希望简单,准确地表示我在做什么的代码片段。
public class FragmentFixture extends Fragment {
SimpleDateFormat convertUtcDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
SimpleDateFormat String2Date = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US);
SimpleDateFormat convertToGmt = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
private List<Fixture> fixtureList;
Date date1;
Date date2;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
convertUtcDate.setTimeZone(TimeZone.getTimeZone("GMT"));
fixtureList = new ArrayList<>();
// retrieve fixtures from API and get date for a certain fixture. I will provide an example
String date = "2018-09-22T11:30:00Z";
Date convertedDate = new Date();
try {
convertedDate = convertUtcDate.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
Date currentTime = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime();
if (convertedDate.after(currentTime)) {
String dateString = convertToGmt.format(convertedDate);
Fixture fixture = new Fixture(dateString);
fixtureList.add(fixture);
Collections.sort(fixtureList, new DateSorter());
}
}
public class DateSorter implements Comparator<Fixture> {
@Override
public int compare(Fixture fixture, Fixture t1) {
try {
date1 = String2Date.parse(fixture.getDate());
} catch (ParseException e) {
e.printStackTrace();
}
try {
date2 = String2Date.parse(t1.getDate());
} catch (ParseException e) {
e.printStackTrace();
}
return date1.compareTo(date2);
}
}
public class Fixture {
public String date;
public Fixture() {
}
public Fixture(String date) {
this.date = date;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
}