web-dev-qa-db-ja.com

Android現在の文字列を解析-不明なパターン文字「X」

Webから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)
);
42
Matjaž Mav

から「XXX」を削除

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");

すべてが正常に機能します。

SimpleDateFormatコンストラクター内で使用できるシンボルのリストを確認します。これは ドキュメント です

おそらくあなたは"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
27
Rohit5k2

AndroidバージョンのSimpleDateFormatXパターンをサポートしていないため、XXXは機能しませんが、代わりにZZZZZは同じことを行い、+02:00(またはローカルタイムゾーンに応じて-02:00)の形式でタイムゾーンを出力します。

41
flawyte

間違った日付フォーマッターを使用しています。

代わりにこれを使用してください:DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

Androidとは対照的に、Java 7はXではなくタイムゾーンにZ(Java 6)のように)を使用します。したがって、日付形式には this を使用します。

11
Thanos

pre-nougatデバイスで発生するこのエラーについては誰も言及していませんそれの。

この answer は、「X」がNougat +デバイスでのみサポートされていることを正しく述べています。 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以降のデバイスで機能します。

9
Wahib Ul Haq

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);
    }
}
8
Alexander K

Android SimpleDateFormat はJava 7 SDKとは異なり、ISO 8601を解析するための 'X'をサポートしていません。'Z 'または' ZZZZZ 'スタイルを使用してフォーマットできますタイムゾーンを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);
    }
}
6
Narek

エラーは、simpleDateFormatが文字Xを認識しないと言っています。ミリ秒を探している場合は、文字Sで表されます。

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
2
Adam W

tl; dr

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

入力文字列は標準ISO 8601形式です。 Java.timeクラスはデフォルトでISO 8601形式を使用するため、直接解析し、フォーマットパターンを定義する必要はありません。

OffsetDateTime

入力には+00:00を持つoffset-from-UTCが含まれているため、 OffsetDateTime オブジェクトとして解析できます。

String input = "2015-02-05T05:20:02+00:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input );

数学

1時間または1分後にアラームを設定する場合は、plusメソッドを呼び出します。

OffsetDateTime minuteLater = odt.plusMinutes( 1 );
OffsetDateTime hourLater = odt.plusHours( 1 );

ミリ秒のカウントを取得するには、Instantクラスを調べます。 Instant クラスは、 [〜#〜] utc [〜#〜] のタイムライン上の瞬間を表します nanoseconds の解像度で。ミリ秒を求めるということは、10進数の9桁が3桁の小数点に切り捨てられるため、データ損失の可能性を意味します。

long millisecondsSinceEpoch = odt.toInstant().toEpochMilli();  // Warning: Possible loss of data in truncating nanoseconds to milliseconds.

Java.timeについて

Java.time フレームワークはJava 8以降に組み込まれています。これらのクラスは、Java.util.Date.Calendar、 &Java.text.SimpleDateFormat

Joda-Time プロジェクトは、現在 メンテナンスモード であり、Java.timeへの移行を推奨しています。

詳細については、 Oracleチュートリアル を参照してください。また、Stack Overflowで多くの例と説明を検索してください。

Java.time機能の多くは、 ThreeTen-Backport のJava 6&7にバックポートされ、さらに Android in ThreeTenABP

ThreeTen-Extra プロジェクトは、追加のクラスでJava.timeを拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある証明の場です。

1
Basil Bourque

SimpleDateFormatを使用して、適切にフォーマットされたString出力を生成します。

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

String formattedNow = simpleDateFormat.format(new Date(System.currentTimeMillis()));

出力:2018-02-27T07:36:47.686Z

1
suhasini

簡単なソリューション:

使用 this yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ

代わりに of yyyy-MM-dd'T'HH:mm:ss.SSSXXX

できた.

1
Hiren Patel

次のクラスを使用して、パターンが認識しないときに文字列を日付に変換できます。

    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;
    }
}
0
Sudhakar