「ThreeTenABP」で捨てれないのか android.text.format.DateUtils

Androidで日付表記をお手軽に国際化する « LINE Engineers' Blog

読んでみると, なんかいろいろややこしそう.

イギリス式: Fri, 18 Nov 2016
アメリカ式: Fri, Nov 18, 2016

イギリス英語: Fri, 18 Nov 2016
アメリカ英語: Fri, Nov 18, 2016
日本語: 2016年11月18日(金)

イギリス英語: 18/11/2016
アメリカ英語: 11/18/2016
日本語: 2016/11/18

Android 6.0: 2016年11月18日(金)
Android 4.1: 2016/11/18 (金)

Android 6.0: 2016年11月18日金曜日
Android 4.1: 2016年11月18日 (金)

日本語: 3 日前
英語: 3 days ago
フランス語: Il y a 3 jours

日本語: 11月18日
英語: on Nov 18
フランス語: le 18 nov.

挙動にぶれがあります(エミュレータにて動作確認)。小さいTextViewに表示する際にはOS versionによって見切れてしまっていないかなどの検証をしましょう。

OS version間での挙動差分や、ビジネス上アプリで必要な言語をAPIが対応してくれるかの検証は必要

Android 7.0でのICU4Jの導入と共に下記クラスがICU4JでのDateUtilsに対応するクラスとして案内されています。

うーむ...「日付表示」て, 書いときゃ世界のどこでもまあまあいけるやつはねえのかなあ, と.

ThreeTenABP で DateTimeFormatter を使う

「ThreeTenABP」て何なのかは, 作者であるぼくらの Jake Wharton神 の書いていることを読めば分かります.

JakeWharton/ThreeTenABP: An adaptation of the JSR-310 backport for Android.

なぜ JSR-310 なのか?

JSR-310 は, java.time.* パッケージとして Java8 に含まれています. これは, Java と Android の両方で Date/Calendar API群をすべて置き換えるものです.
Java6 の開発者であるStepahen Colebourne によって調整され, それにバックポートされました.

なぜ ThreeTenBP を使わないのか?

Android 上で Joda-Time を使う問題のように ThreeTenBP はタイムゾーン情報の読込みにjar リソースを利用しています. これが Android 上では非常に非効率です.
このライブラリでは, タイムゾーン情報を Android標準で使えるようして, 効率的にパースできるローダーを提供しています.

なぜ Joda-Time を使わないのか?

Joda-Time は非常に大きいバイナリサイズと多くのメソッド数をもつ非常に大きいAPIです. また, JSR-310 と Joda-Time の作者は「壊れてはいないが, 設計に欠陥がある」と言っています.
すでに Joda-Time を使っているなら, サイズとメソッド数を除けば, 置き換える理由はほとんどありません. 新しいプロジェクトのために, このライブラリはバイナリサイズとメソッド数だけでなくAPIの数をかなり削減したJava8の標準APIの形 で提供しています.

Joda Time's Memory Issue in Android

Date/Calendar API + JodaTime の機能をJava8 で合成した java.time.* (JSR-310) を 現Android開発向けに適正化し利用できるようにしたものが「TheeTenABP」ということになります.

コードの記述は Java8 の記述と同じなので Java8 Date and Time APIドキュメントや記事が参考にできます.

続・今日から始めるJava8 - JSR-310 Date and Time API - Taste of Tech Topics

13 章 : Date and Time API · Java Study

Java 8 の DateTimeFormatter の曜日等のフォーマットについて - tokuhirom's blog

Javaで日時を扱う(Java8) - Qiita

同梱されている国際化可能そうな日時フォーマットを試してみる.

...
DateTimeFormatter formatter =
    DateTimeFormatter
        .ofLocalizedDateTime(formatStyle, formatStyle)
        .withLocale(Locale.getDefault());
Timber.d(zonedDateTime.format(formatter) + " : " + formatStyle.name());
...

日付と時刻, それぞれ FormatStyle.SHORTからFULLまでの4種類ある.


2016年12月14日水曜日 10時13分36秒 日本標準時 : FULL
2016年12月14日 10:13:36 JST : LONG
2016/12/14 10:13:36 : MEDIUM
2016/12/14 10:13 : SHORT

Wednesday, 14 December 2016 01:13:36 Greenwich Mean Time : FULL
14 December 2016 01:13:36 GMT+00:00 : LONG
14 Dec 2016 01:13:36 : MEDIUM
14/12/2016 01:13 : SHORT

Tuesday, December 13, 2016 8:13:36 PM Eastern Standard Time : FULL
December 13, 2016 8:13:36 PM EST : LONG
Dec 13, 2016 20:13:36 : MEDIUM
12/13/16 20:13 : SHORT

2016年12月14日星期三 香港標準時間上午9時13分36秒 : FULL
2016年12月14日 GMT+08:00上午9時13分36秒 : LONG
2016年12月14日 09:13:36 : MEDIUM
2016/12/14 09:13 : SHORT

2016년 12월 14일 수요일 오전 10시 13분 36초 대한민국 표준시 : FULL
2016년 12월 14일 오전 10시 13분 36초 GMT+09:00 : LONG
2016. 12. 14. 10시 13분 36초 : MEDIUM
16. 12. 14. 10:13 : SHORT

なんか微妙...

FormatStyle.MEDIUM くらいが良さげだが, 曜日が含まれてないし.

別個, 曜日の文字列としては別に


localDateTime.getDayOfWeek()
    .getDisplayName(TextStyle.FULL, Locale.getDefault());

のようにして文字列として取れるっちゃあとれるが並び順やセパレータの件がある.

カスタムで言語別にフォーマットつくるのもなあ.

まとめ

android.text.format.DateUtils で.


DateUtils.formatDateTime(
       context,
       timestamp, 
       FORMAT_SHOW_YEAR | FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL);


2016年12月14日(水) 10:13
Wed, 14 Dec 2016 00:13
Tue, Dec 13, 2016, 20:13
2016年12月14日週三 09:13
2016년 12월 14일 (수) 10:13

結局, これが簡単で自然,

表示言語やタイムゾーンの設定は context から取得してくれるし.

モバイル画面上の表示としてその他海外で自然かどうかは実際知らんけども.

日時を多言語化するというのは思ったより複雑.

モバイル端末というのは, いろいろな制限の中で動いているのだなあと実感.

Date/Calendar のことはもう忘れていいのだよな?

(つづく)


Android開発のための Effective Java

ask_hn__why_is_advice_in_effective_java_considered_inappropriate_for_android____hacker_news

「Effective Java」は素晴らしい本であるが, 「Android開発では少し当てはまらないとこもあるよ」という話.

Effective Java for Android (cheatsheet). – RockNNull – Medium

インスタンス化させない

new を使ってオブジェクトを生成させたくない場合, private なコンストラクタを使いましょう. 特に, 静的なメソッドのみのユーティリティクラスで使うといいでしょう.


class MovieUtils {
    private MovieUtils() {}
    static String titleAndYear(Movie movie) {
        [...]
    }
}

静的なFactory

new を使う代わりに, private なコンストラクタを静的な Factory メソッドを使いましょう. これらの Factory メソッドは名前をつけることができ、毎回オブジェクトの新しいインスタンスを返す必要はなく、必要なものに応じて異なるサブタイプを返すことができます.


class Movie {
    [...]
    public static Movie create(String title) {
        return new Movie(title);
    }
}

Builder

3つを超えるコンストラクタパラメータを必要とするオブジェクトは, Builder を使ってオブジェクトを生成しましょう. すこし冗長かもしれませんが, 拡張しやすく読みやすいです. 値にクラスを使う場合は AutoValue を考慮するといいです.


class Movie {
    static Builder newBuilder() {
        return new Builder();
    }
    static class Builder {
        String title;
        Builder withTitle(String title) {
            this.title = title;
            return this;
        }
        Movie build() {
            return new Movie(title);
        }
    }

    private Movie(String title) {
    [...]    
    }
}
// Use like this:
Movie matrix = Movie.newBuilder().withTitle("The Matrix").build();

イミュータブル化

「イミュータブル」とは, 生存している間は変化しないことです. オブジェクト内のすべての必要なデータは作成時に提供されます. これは, 簡単, スレッドセーフ, 共有性などさまざまな利点があります.


class Movie {
    [...]
    Movie sequel() {
        return Movie.create(this.title + " 2");
    }
}
// Use like this:
Movie toyStory = Movie.create("Toy Story");
Movie toyStory2 = toyStory.sequel();

すべてのクラスをイミュータブルにすることは難しいかもしれませんが可能な限りイミュータブルにします. (例えば, フィールドを private final に, クラスを final に). オブジェクトの生成はモバイル上では, コストが高くなる可能性があります.

静的なメンバークラス

外部に依存しないインナークラスを定義する場合, それを static にすることを忘れてはなりません. そうしないと, それぞれのインナークラスのインスタンスが外部のクラスの参照をもつことになります.


class Movie {
    [...]
    static class MovieAward {
        [...]
    }
}

すべてにジェネリクスを

Java は, タイプセーフティ(型安全) を提供しています. 可能であれば, プリミティブ型やObject型は避けましょう. ジェネリックスは、コンパイル時にコード・タイプを安全にしてくれるメカニズムです.


// DON'T
List movies = Lists.newArrayList();
movies.add("Hello!");
[...]
String movie = (String) movies.get(0);

// DO
List<String> movies = Lists.newArrayList();
movies.add("Hello!");
[...]
String movie = movies.get(0);

メソッドのパラメータや戻り値についても使うことができることを忘れないでください.


// DON'T
List sort(List input) {
    [...]
}

// DO
<T> List<T> sort(List<T> input) {
    [...]
}

すこし柔軟にするには, バインドされたワイルドカードを利用して, 受け入れる範囲を広げることができます.


// Read stuff from collection - use "extends"
void readList(List<? extends Movie> movieList) {
    for (Movie movie : movieList) {
        System.out.print(movie.getTitle());
        [...]
    }
}

// Write stuff to collection - use "super"
void writeList(List<? super Movie> movieList) {
    movieList.add(Movie.create("Se7en"));
    [...]
}

空の返却

リストやコレクションを結果なしで返却するときの null を避けます.
空のコレクションを返すことで, 簡単なインターフェースとなり, 偶発的な NPE を避けることができます. 新しいコレクションを作成するのではなく、同じコレクションを返すことをお勧めします。


List<Movie> latestMovies() {
    if (db.query().isEmpty()) {
        return Collections.emptyList();
    }
    [...]
}

String の連結

文字列を連結しなければならない場合、+演算子を使う場合があります.
多くの文字列の連結には使用しないでくだい. パフォーマンスは本当に悪いです.
代わりに StringBuilder を推奨します.


String latestMovieOneLiner(List<Movie> movies) {
    StringBuilder sb = new StringBuilder();
    for (Movie movie : movies) {
        sb.append(movie);
    }
    return sb.toString();
}

例外を使った修正

私はエラーを示すための例外を投げることに賛成ではありませんが, 例外をチェックすることで、エラーを修正することができます.


List<Movie> latestMovies() throws MoviesNotFoundException {
    if (db.query().isEmpty()) {
        throw new MoviesNotFoundException();
    }
    [...]
}

再度, 見直してみるといいのかもしれません.



Ask HN: Why is “advice in Effective Java considered inappropriate for Android”? | Hacker News

Radio Hacker News -Translation - Android Apps on Google Play


日本語設定では見ることができない本当のAndroidバージョン別のシェア率

Android のバージョン別の利用割合をみるとき,

みますよね, ここ.

ダッシュボード | Android Developers

%e3%82%bf%e3%82%99%e3%83%83%e3%82%b7%e3%83%a5%e3%83%9b%e3%82%99%e3%83%bc%e3%83%88%e3%82%99___android_developers

ところが, ここにアクセスすると,

Dashboards | Android Developers

dashboards___android_developers

当然数字も違いますよね.

URLを見てみると, 微妙に違ったりします.

../dashboards/index.html?hl=ja

../dashboards/index.html?hl=en

パラメータだけでなく, 他の何かも影響してる雰囲気.

../dashboards/index.html

画面の下にあるプルダウンから言語変えてみるのもいいみたいです.

%e3%82%bf%e3%82%99%e3%83%83%e3%82%b7%e3%83%a5%e3%83%9b%e3%82%99%e3%83%bc%e3%83%88%e3%82%99___android_developers

なんだか気持ち悪いので, スクリプトにしておきますね.

version_android

Android バージョンやコードネームなどからのシェアの一覧取得