我有Service
从网络获取日期字符串,然后我想将其削减到Date
对象。但不知何故应用程序崩溃。
这是我正在解析的字符串:2015-02-05T05:20:02+00:00
onStartCommand()
String datetime = "2015-02-05T05:20:02+00:00";
Date new_date = stringToDate(datetime);
stringToDate()
private Date stringToDate(String s){
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
try{
return df.parse(s);
}catch(ParseException e){
e.printStackTrace();
}
return null;
}
logcat的:
02-06 20:37:02.008: E/AndroidRuntime(28565): FATAL EXCEPTION: main
02-06 20:37:02.008: E/AndroidRuntime(28565): Process: com.dotmav.runescapenotifier, PID: 28565
02-06 20:37:02.008: E/AndroidRuntime(28565): java.lang.RuntimeException: Unable to start service com.dotmav.runescapenotifier.GEService@384655b5 with Intent { cmp=com.dotmav.runescapenotifier/.GEService }: java.lang.IllegalArgumentException: Unknown pattern character 'X'
02-06 20:37:02.008: E/AndroidRuntime(28565): at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2881)
02-06 20:37:02.008: E/AndroidRuntime(28565): at android.app.ActivityThread.access$2100(ActivityThread.java:144)
02-06 20:37:02.008: E/AndroidRuntime(28565): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1376)
02-06 20:37:02.008: E/AndroidRuntime(28565): at android.os.Handler.dispatchMessage(Handler.java:102)
02-06 20:37:02.008: E/AndroidRuntime(28565): at android.os.Looper.loop(Looper.java:135)
02-06 20:37:02.008: E/AndroidRuntime(28565): at android.app.ActivityThread.main(ActivityThread.java:5221)
02-06 20:37:02.008: E/AndroidRuntime(28565): at java.lang.reflect.Method.invoke(Native Method)
02-06 20:37:02.008: E/AndroidRuntime(28565): at java.lang.reflect.Method.invoke(Method.java:372)
02-06 20:37:02.008: E/AndroidRuntime(28565): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
02-06 20:37:02.008: E/AndroidRuntime(28565): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
02-06 20:37:02.008: E/AndroidRuntime(28565): Caused by: java.lang.IllegalArgumentException: Unknown pattern character 'X'
02-06 20:37:02.008: E/AndroidRuntime(28565): at java.text.SimpleDateFormat.validatePatternCharacter(SimpleDateFormat.java:314)
02-06 20:37:02.008: E/AndroidRuntime(28565): at java.text.SimpleDateFormat.validatePattern(SimpleDateFormat.java:303)
02-06 20:37:02.008: E/AndroidRuntime(28565): at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:356)
02-06 20:37:02.008: E/AndroidRuntime(28565): at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:249)
02-06 20:37:02.008: E/AndroidRuntime(28565): at com.dotmav.runescapenotifier.GEService.stringToDate(GEService.java:68)
02-06 20:37:02.008: E/AndroidRuntime(28565): at com.dotmav.runescapenotifier.GEService.onStartCommand(GEService.java:44)
02-06 20:37:02.008: E/AndroidRuntime(28565): at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2864)
02-06 20:37:02.008: E/AndroidRuntime(28565): ... 9 more
编辑: onDestroy()为定期更新设置闹钟...
AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.set(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + (1000 * 60),
PendingIntent.getService(this, 0, new Intent(this, GEService.class), 0)
);
答案 0 :(得分:26)
从
中删除“XXX”DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
一切都会正常。
浏览可在SimpleDateFormat
构造函数中使用的符号列表。这是documentation
可能您正在寻找"yyyy-MM-dd'T'HH:mm:ss.SSSZ"
将您的代码更改为
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
或
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); // if timezone is required
答案 1 :(得分:11)
您使用的是错误的日期格式化程序。
请改用:DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
我认为与Java 7相比,android使用Z(如在Java 6中)用于时区而不是X.因此,使用this作为日期格式。
答案 2 :(得分:8)
由于Z和XXX不同,我实施了以下解决方法:
// This is a workaround from Z to XXX timezone format
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") {
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
StringBuffer rfcFormat = super.format(date, toAppendTo, pos);
return rfcFormat.insert(rfcFormat.length() - 2, ":");
}
@Override
public Date parse(String text, ParsePosition pos) {
if (text.length() > 3) {
text = text.substring(0, text.length() - 3) + text.substring(text.length() - 2);
}
return super.parse(text, pos);
}
}
答案 3 :(得分:6)
Android SimpleDateFormat与Java 7 SDK不同,不支持&#39; X&#39;解析ISO 8601.您可以使用&#39; Z&#39;或者&#39; ZZZZZ&#39;样式格式化并以编程方式将时区设置为UTC。这是一个util类:
public class DateUtil {
public static final String iso8601DatePattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
public static final DateFormat iso8601DateFormat = new SimpleDateFormat(iso8601DatePattern);
public static final TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
static {
iso8601DateFormat.setTimeZone(utcTimeZone);
}
public static String formatAsIso8601(Date date) {
return iso8601DateFormat.format(date);
}
}
答案 4 :(得分:6)
没有人提及牛轧糖前设备上发生的此错误,因此我想与他人分享我的答案,也许这对因为此而到达此线程的人有所帮助。
此answer正确地提到,仅Nougat +设备支持“ X”。我仍然看到documentation建议使用"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
,但不确定为什么他们没有明确指出这一点。
对我来说,yyyy-MM-dd'T'HH:mm:ssXXX
一直运行良好,直到我尝试在6.0设备上对其进行测试,然后它开始崩溃,这促使我对该主题进行了研究。将其替换为yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ
已解决了该问题,并且可以在所有5.0+设备上使用。
答案 5 :(得分:2)
错误在于simpleDateFormat无法识别字符X.如果您要查找毫秒数,则表示字符为S.
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
答案 6 :(得分:2)
API级别24+支持根据X
格式的android documentation区域偏移量
Letter Date or Time Component Supported (API Levels) X Time zone 24+
所以我们不能使用较低的API,我找到了解决此问题的方法:
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(date).let {
StringBuilder(it).insert(it.length - 2, ":").toString()
}
答案 7 :(得分:1)
long millisecondsSinceEpoch = OffsetDateTime.parse( "2015-02-05T05:20:02+00:00" ).plusHour( 1 ).toInstant().toEpochMilli() // Warning: Possible loss of data in truncating nanoseconds to milliseconds. But not in this particular case.
其他答案是正确的,但现在已经过时了。旧的日期时间类现在是遗留的。请改用java.time类。
输入字符串采用标准ISO 8601格式。直接解析,无需定义格式化模式,因为java.time类默认使用ISO 8601格式。
OffsetDateTime
输入包含与+00:00
的UTC偏移量,因此我们可以解析为OffsetDateTime
个对象。
String input = "2015-02-05T05:20:02+00:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input );
如果您想在一小时或一分钟后添加设置闹钟,请调用plus
方法。
OffsetDateTime minuteLater = odt.plusMinutes( 1 );
OffsetDateTime hourLater = odt.plusHours( 1 );
要获得毫秒数,请浏览Instant
课程。 Instant
类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds。要求毫秒意味着可能的数据丢失,因为小数部分的九位数被截断为小数部分的三位数。
long millisecondsSinceEpoch = odt.toInstant().toEpochMilli(); // Warning: Possible loss of data in truncating nanoseconds to milliseconds.
java.time框架内置于Java 8及更高版本中。这些类取代了旧的麻烦日期时间类,例如java.util.Date
,.Calendar
和&amp; java.text.SimpleDateFormat
。
现在位于Joda-Time的maintenance mode项目建议迁移到java.time。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。
大部分java.time功能都被反向移植到Java 6&amp; ThreeTen-Backport中的7,并进一步适应Android中的ThreeTenABP。
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。
答案 8 :(得分:1)
简单的解决方案:
使用此yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ
取代 yyyy-MM-dd'T'HH:mm:ss.SSSXXX
完成。
答案 9 :(得分:1)
基于Alexander K的想法,我对其进行了优化,并支持往返于1970-01-01T00:00:00Z
之类的UTC时区格式的解析,以使所有行为与yyyy-MM-dd'T'HH:mm:ssXXX
完全相同。
public class IsoSimpleDateFormatBeforeNougat extends SimpleDateFormat {
public IsoSimpleDateFormatBeforeNougat() {
super("yyyy-MM-dd'T'HH:mm:ssZ");
}
public IsoSimpleDateFormatBeforeNougat(Locale locale) {
super("yyyy-MM-dd'T'HH:mm:ssZ", locale);
}
public IsoSimpleDateFormatBeforeNougat(DateFormatSymbols formatSymbols) {
super("yyyy-MM-dd'T'HH:mm:ssZ", formatSymbols);
}
@Override
public Date parse(String text, ParsePosition pos) {
if (text.endsWith("Z")) {
return super.parse(text.substring(0, text.length() - 1) + "+0000", pos);
}
if (text.length() > 3 && text.substring(text.length() - 3, text.length() - 2).equals(":")) {
text = text.substring(0, text.length() - 3) + text.substring(text.length() - 2);
}
return super.parse(text, pos);
}
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
StringBuffer rfcFormat = super.format(date, toAppendTo, pos);
if (rfcFormat.substring(rfcFormat.length() - 5).equals("+0000")) {
return rfcFormat.replace(rfcFormat.length() - 5, rfcFormat.length(), "Z");
}
return rfcFormat.insert(rfcFormat.length() - 2, ":");
}
}
测试代码:
@Test
public void test() throws ParseException {
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
SimpleDateFormat sdf = new IsoSimpleDateFormatBeforeNougat();
sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
assertEquals("1970-01-01T08:00:00+08:00", sdf.format(new Date(0)));
assertEquals(0L, sdf.parse("1970-01-01T08:00:00+08:00").getTime());
sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));
assertEquals("1970-01-01T00:00:00Z", sdf.format(new Date(0)));
assertEquals(0L, sdf.parse("1970-01-01T00:00:00Z").getTime());
}
答案 10 :(得分:0)
当pattern不知道时,可以使用以下类将字符串转换为日期。
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* StringToDateFormater is a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows for
* formatting (date → text), parsing (text → date), and normalization.
*
* This class is mainly used for convert the date from string. It should be used only when date pattern doesn't aware of
* it.
*
*/
public class StringToDateFormater extends SimpleDateFormat {
private static final List<String> DATE_SUPPORTED_FORMAT_LIST = Arrays.asList("yyyyMMddHHmmss", "yyyyMMddHHmm",
"yyyyMMddHHmm", "yyyyMMddHH", "yyyyMMdd", "yyyyMMddHHmmssSS");
/**
*
*/
private static final long serialVersionUID = -1732857502488169225L;
/**
* @param pattern
*/
public StringToDateFormater() {
}
@Override
public Date parse(String source) {
Date date = null;
SimpleDateFormat dateFormat = null;
for (String format : DATE_SUPPORTED_FORMAT_LIST) {
dateFormat = new SimpleDateFormat(format);
try {
return dateFormat.parse(source);
} catch (Exception exception) {
}
}
return date;
}
}