Androidアプリ開発で国民の祝日を表示するのに試行錯誤。後編

　どうも！ヨースケです。なかなか良いタイトルが思いつきませんw簡潔にかつ具体的でなんかいい感じ(笑)のタイトルは難しいですね。ブログとかとはちょっと違う気がしますし、僕も内容もそうですがいい感じになるように精進します！

　[前編](https://qiita.com/ysk75/items/90151559e08551ed58e8)は国民の祝日の日付について説明しました。後編ではソースコードを組むにあたっての考え方(と言っても僕はこう考えましたよ。と示すものですが...)やソースコード、そしてまぁまぁの欠点があったのでそれも含めてご紹介します。
　前編でのおさらいとして祝日の曜日と備考をもう一度載せておきます。

|祝日名|日付|備考|
|:---:|:---:|:--:|
|元日|1月1日|-|
|成人の日|1月の第2月曜日|-|
|建国記念の日|2月11日|-|
|天皇誕生日|2月23日|-|
|春分の日|3月の19～22日のうち1日|西暦年を4で割った余りで日にちを決定|
|昭和の日|4月29日|-|
|憲法記念日|5月3日|-|
|みどりの日|5月4日|-|
|こどもの日|5月5日|-|
|海の日|7月の第3月曜日|2020年は7月23日|
|山の日|8月11日|2020年は8月10日|
|敬老の日|9月の第3月曜日|-|
|秋分の日|9月の22～24日のうち1日|西暦年を4で割った余りで日にちを決定|
|スポーツの日|10月の第2月曜日|2020年は7月24日|
|文化の日|11月3日|-|
|勤労感謝の日|11月23日|-|

- 考え方
- ソースコード
- 実行結果
- 欠点

### 考え方
　ソースコードを組む前に僕の考えを説明します。まず使うのは**Calendarクラス**です。これにより年、月、日、曜日、週を取得しそれによって祝日を設定します....って当たり前ですね(笑)。僕は祝日をTextViewでアンダーライン付きで表示し、タップすると詳細をダイアログで表示したいのでデータベースを使います。

|id|name|detail|adress|
|:--:|:--:|:----:|:-----:|
|1|元日|一年の...|https....|
|2|成人の日|祝日法に...|https...|
|...|...|...|...|
|15|文化の日|明治天皇の...|https...|
|16|勤労感謝の日|令和時代では...|https....|

と、途中省略しましたが各祝日にidを割り振ってそのidを基に祝日とその詳細を表示します...うーんと
![holiday.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534506/491e2aa9-8f98-ad5c-0685-18daaf5bb75e.png)
↑こんな感じでわかりますかね...説明が下手ですみませんm(. .)m
　次に振替休日なんですが、こちらもidとflagのみのデータベース

|id|flag|
|:--:|:--:|
|100|0|
を用意し日曜日に祝日が来たらflagを1にしその日が振替休日なら0に戻す。というようなことをしています。だから欠点があるんですよ...

### ソースコード
　国民の祝日を判断するHoliday.java、データベースのDateDatabeseHelper.java、そしてメインのMainActivity.javaに分けてソースコードを載せます。

```java:Holiday.java
public class Holiday {
    //今日の日付や曜日、年、週を取得
    int year,month,day,youbi,week;
    Calendar calendar = Calendar.getInstance();
    //データベース
    DateDatabaseHelper helper = null;
    //国民の祝日を表示するか否かを判定する
    boolean holidayFlag;
    //祝日の名前や詳細、idを取得
    String holidayName,holidayDetail;
    int id;
    //振替休日を判断
    boolean transHolidayFlag;


    public void holiday(){
        year = calendar.get(Calendar.YEAR); //年
        month = calendar.get(Calendar.MONTH) + 1; //月
        day = calendar.get(Calendar.DAY_OF_MONTH); //日
        youbi = calendar.get(Calendar.DAY_OF_WEEK); //曜日
        week = calendar.get(Calendar.WEEK_OF_MONTH); //週
        if(month == 1 && day == 1){
            //元日
            id = 1;
            holidayFlag = true;
        }
        else if(month == 1 && week == 2 && youbi == 2){
            //成人の日
            id = 2;
            holidayFlag = true;
            }
        else if(month == 2 && day == 11){
            //建国記念の日
            id = 3;
            holidayFlag = true;
        }
        else if(month == 2 && day == 23){
            //天皇誕生日
            id = 4;
            holidayFlag = true;
        }
        else if(month == 3){
            //春分の日
            switch (year % 4){
                case 0:
                    //1992-2091年まで20日
                    if(year >= 1992 && year <= 2091){
                        if(day == 20){
                            id = 5;
                            holidayFlag = true;
                        }
                        else {
                            holidayFlag = false;
                        }
                    }
                    //2092-2099年までは19日
                    else{
                        if(day == 19){
                            id = 5;
                            holidayFlag = true;
                        }
                        else {
                            holidayFlag = false;
                        }
                    }
                    break;
                case 1:
                    //1992-2099年まで20日
                    if(day == 20){
                        id = 5;
                        holidayFlag = true;
                    }
                    else {
                        holidayFlag = false;
                    }
                    break;
                case 2:
                    //1992-2023年までは21日
                    if(year >= 1992 && year <= 2023){
                        if(day == 21){
                            id = 5;
                            holidayFlag = true;
                        }
                        else{
                            holidayFlag = false;
                        }
                    }
                    //2024-2099年まで20日
                    else{
                        if(day == 20){
                            id = 5;
                            holidayFlag = true;
                        }
                        else{
                            holidayFlag = false;
                        }
                    }
                    break;
                case 3:
                    //1992-2055年までは21日
                    if(year >= 1992 && year <= 2055){
                        if(day == 21){
                            id = 5;
                            holidayFlag = true;
                        }
                        else{
                            holidayFlag = false;
                        }
                    }
                    //2056-2099年までは20日
                    else{
                        if(day == 20){
                            id = 5;
                            holidayFlag = true;
                        }
                        else{
                            holidayFlag = false;
                        }
                    }
                    break;
            }
        }
        else if(month == 4 && day == 29){
            //昭和の日
            id = 6;
            holidayFlag = true;
        }
        else if(month == 5 && day == 3){
            //憲法記念日
            id = 7;
            holidayFlag = true;
        }
        else if(month == 5 && day == 4){
            //みどりの日
            id = 8;
            holidayFlag = true;
        }
        else if(month == 5 && day == 5){
            //こどもの日
            id = 9;
            holidayFlag = true;
        }
        else if(year != 2020 && month == 7 && week == 3 && youbi == 2){
            //海の日
            id = 10;
            holidayFlag = true;
        }
        else if(year == 2020 && month == 7 && day == 23){
            //2020年だけ海の日が移動する
            id = 10;
            holidayFlag = true;
        }
        else if(year != 2020 && month == 8 && day == 11){
            //山の日
           id = 11;
           holidayFlag = true;
        }
        else if(year == 2020 && month == 8 && day == 10){
            //2020年だけ山の日が移動する
            id = 11;
            holidayFlag = true;
        }
        else if(month == 9 && week == 3 && youbi == 2){
            //敬老の日
            id = 12;
            holidayFlag = true;
        }
        else if(month == 9){
            //秋分の日
            switch (year % 4){
                case 0:
                    //2012-2099年まで22日
                    if(day == 22){
                        id = 13;
                        holidayFlag = true;
                    }
                    else {
                        holidayFlag = false;
                    }
                    break;
                case 1:
                    //2012-2043年までは23日
                    if(year >= 2012 && year <= 2043){
                        if(day == 23){
                            id = 13;
                            holidayFlag = true;
                        }
                        else{
                            holidayFlag = false;
                        }
                    }
                    //2044-2099年までは22日
                    else{
                        if(day == 22){
                            id = 13;
                            holidayFlag = true;
                        }
                        else{
                            holidayFlag = false;
                        }
                    }
                    break;
                case 2:
                    //2012-2075年までは23日
                    if(year >= 2012 && year <= 2075){
                        if(day == 23){
                            id = 13;
                            holidayFlag = true;
                        }
                        else{
                            holidayFlag = false;
                        }
                    }
                    //2076-2099年までは22日
                    else{
                        if(day == 22){
                            id = 13;
                            holidayFlag = true;
                        }
                        else{
                            holidayFlag = false;
                        }
                    }
                    break;
                case 3:
                    //2012-2099年までは23日
                    if(day == 23){
                        id = 13;
                        holidayFlag = true;
                    }
                    else{
                        holidayFlag = false;
                    }
                    break;
            }
        }
        else if(year != 2020 && month == 10 && week == 2 && youbi == 2){
            //スポーツの日
            id = 14;
            holidayFlag = true;
        }
        else if(year == 2020 && month == 7 && day == 24){
            //2020年だけスポーツの日が移動する
            id = 14;
            holidayFlag = true;
        }
        else if(month == 11 && day == 3){
            //文化の日
            id = 15;
            holidayFlag = true;
        }
        else if(month == 11 && day == 23){
            //勤労感謝の日
            id = 16;
            holidayFlag = true;
        }
        else{
            holidayFlag = false;
        }
        }
        //mainに休日か否かを渡す
    public boolean getHoliday(){
        return holidayFlag;
    }
    //idをmainに渡す
    public int holiday_info(){
       return id;
    }
    //日曜に祝日があるかどうかをmainに渡す
    public boolean getTransferHoliday(){
        //祝日でその日が日曜なら
        if(holidayFlag == true && youbi == 1){
            transHolidayFlag = true;
        }
        else{
            transHolidayFlag = false;
        }
        return transHolidayFlag;
    }
}
```
冗長ではありますが、基本はその日が祝日かどうかの判断、また日曜に祝日があるかどうかの判断をし判断結果をそれぞれMainに渡しています。

```java:DateDatabaseHelper.java
public class DateDatabaseHelper extends SQLiteOpenHelper {
    static final private String DBNAME = "OneDayDiary.sqlite";
    static final private int VERSION = 16;

    public DateDatabaseHelper(Context context) {
        super(context, DBNAME, null, VERSION);
    }

    @Override
    public void onOpen(SQLiteDatabase db) {
        super.onOpen(db);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
----中略-----
//祝日用データベース
        db.execSQL("CREATE TABLE holiday(" +
                "id INTEGER PRIMARY KEY, name TEXT, detail TEXT, adress TEXT)");
        int[] id_holiday = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
        String[] name_holiday = {
                "元日","成人の日","建国記念の日","天皇誕生日","春分の日","昭和の日","憲法記念日","みどりの日","こどもの日",
                "海の日","山の日","敬老の日","秋分の日","スポーツの日","文化の日","勤労感謝の日"
        };
        String[] detail_holiday = {
                "一年の始まりの祝日で祝日法では「年のはじめを祝う」ことを趣旨としています。",
                        "祝日法によると「おとなになったことを自覚し、みずから生き抜こうとする青年を祝いはげます」ことを趣旨とした日で、各地で成人式が行われます。",
                        "建国を祝う日として制定されました。「建国をしのび、国を愛する心を養う。」と祝日法には規定されています。",
                        "「天皇の誕生を祝う」ことを祝日法では趣旨とし、一般参賀や各地の神道神社では天長祭などさまざまな行事が行われます。",
                        "「自然をたたえ、生物をいつくしむ」と祝日法に規定されており、昼と夜の長さが等しくなる日と言われています。",
                        "昭和天皇の誕生日であるこの日を「昭和の日」として制定しています。祝日法では「激動の日々を経て、復興を遂げた昭和の時代を顧み、国の将来に思いをいたす」と規定されています。",
                        "祝日法ではこの日を「日本国憲法の施行を記念し、国の成長を期する」と規定しています。",
                        "名前の由来は昭和天皇がこよなく自然を愛していたことから「みどりの日」となりました。他には「科学の日」などの意見がありました。祝日法によると「自然に親しむとともにその恩恵に感謝し、豊かな心をはぐくむ」ことを趣旨としています。",
                        "端午の節句であるこの日の趣旨は「こどもの人権を重んじ、こどもの幸福をはかるとともに、母に感謝する」と祝日法に規定されています。",
                        "祝日法によると海の日は「海の恩恵に感謝するとともに、海洋国日本の繁栄を願う」ことを趣旨としています。",
                        "この日は2014年に制定され、2016年に新設されました。祝日の新設は1996年の「海の日」以来20年ぶりとなりました。祝日法の趣旨として「山に親しむ機会を得て、山の恩恵に感謝する」とされています。",
                        "祝日法では「多年にわたり社会につくしてきた老人を敬愛し、長寿を祝う」ことを趣旨としています。",
                        "昼の時間が短くなって昼と夜の長さが同じになる日とされています。「祖先をうやまい、なくなった人々をしのぶ」ことを祝日法では趣旨としています。",
                        "2020年から「体育の日」から「スポーツの日」と名称が変更されました。また、2020年では7月24日に移動します。祝日法では「スポーツにしたしみ、健康な心身をつちかう」としています。",
                        "明治天皇の誕生日にあたるこの日、祝日法によると「自由と平和を愛し、文化をすすめる」ことを趣旨としています。",
                        "令和時代ではこの日が1年で最後の祝日となります。「勤労をたつとび、生産を祝い、国民たがいに感謝しあう」ことを祝日法では趣旨としています。"
        };
        String[] url_holiday = {
                "https://ja.wikipedia.org/wiki/%E5%85%83%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E6%88%90%E4%BA%BA%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E5%BB%BA%E5%9B%BD%E8%A8%98%E5%BF%B5%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E5%A4%A9%E7%9A%87%E8%AA%95%E7%94%9F%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E6%98%A5%E5%88%86%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E6%98%AD%E5%92%8C%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E6%86%B2%E6%B3%95%E8%A8%98%E5%BF%B5%E6%97%A5_(%E6%97%A5%E6%9C%AC)",
                        "https://ja.wikipedia.org/wiki/%E3%81%BF%E3%81%A9%E3%82%8A%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E3%81%93%E3%81%A9%E3%82%82%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E6%B5%B7%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E5%B1%B1%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E6%95%AC%E8%80%81%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E7%A7%8B%E5%88%86%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E4%BD%93%E8%82%B2%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E6%96%87%E5%8C%96%E3%81%AE%E6%97%A5",
                        "https://ja.wikipedia.org/wiki/%E5%8B%A4%E5%8A%B4%E6%84%9F%E8%AC%9D%E3%81%AE%E6%97%A5"
        };
        db.beginTransaction();
        try{
            SQLiteStatement sql_holiday = db.compileStatement(
                    "INSERT INTO holiday(id, name, detail, adress) VALUES(?,?,?,?)");
            for(int i = 0; i < id_holiday.length; i++){
                sql_holiday.bindLong(1,id_holiday[i]);
                sql_holiday.bindString(2,name_holiday[i]);
                sql_holiday.bindString(3,detail_holiday[i]);
                sql_holiday.bindString(4,url_holiday[i]);
                sql_holiday.executeInsert();
            }
            db.setTransactionSuccessful();
        } catch (SQLException e){
            e.printStackTrace();
        } finally {
            db.endTransaction();
        }
        db.execSQL("CREATE TABLE transHoliday(" +
                "id INTEGER PRIMARY KEY, flag INTEGER)");
        db.execSQL("INSERT INTO transHoliday(" +
                "id, flag) VALUES(100,0)");

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int old_v, int new_v) {
        db.execSQL("DROP TABLE IF EXISTS holiday");
        db.execSQL("DROP TABLE IF EXISTS transHoliday");
        onCreate(db);

    }


}   
```
データベースにはあらかじめ祝日のデータ、振替休日のオンオフだけのものを用意しています。ちなみに、URLをデータとして入れている理由は別画面にて使えるかなと思っています。URLはこの祝日だけでなく福岡の情報とかその日の記念日のURLとかも入れています。

```java:MainActivity.java
public class MainActivity extends AppCompatActivity{
//日付と曜日を表示する準備
    private TextView today;
    Calendar calendar = Calendar.getInstance();
    int year,month,day;

---中略---

//国民の祝日
    private TextView holiday;
    String holidayName,holidayDetail; //祝日名と詳細
    boolean holidayFlag,transHolidayFlag; //祝日、振替休日があるかどうか
    int holidayId; //DBで検索するためのID
    long transHoliday = 0; //振替休日フラグ
    Holiday Hday = new Holiday(); //Holidayクラス

---中略---

//データベースに接続する準備
    private DateDatabaseHelper helper = null;
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //各ウィジットの宣言

---中略---

 holiday = findViewById(R.id.holiday);

---中略---

//今日の年月日を取得
        year = calendar.get(calendar.YEAR);
        month = calendar.get(calendar.MONTH) + 1;
        day = calendar.get(calendar.DAY_OF_MONTH);

---中略---

    //国民の祝日
        //holidayメソッドを動かす
        Hday.holiday();
        //祝日があるかどうかの判定
        holidayFlag = Hday.getHoliday();
        //振替休日の有無を得る
        transHolidayFlag = Hday.getTransferHoliday();
        //振替休日id
        String[] transHdayId = {"100"};
        //transHolidayFlagがtrueだったらDBのフラグを1に更新

            SQLiteDatabase db_transUp1 = helper.getWritableDatabase();
            try{
                if(transHolidayFlag == true) {
                ContentValues cv_transUp1 = new ContentValues();
                cv_transUp1.put("flag",1);
                db_transUp1.update("transHoliday",cv_transUp1,"id = ?",transHdayId);
                }
                Cursor cursor_transUp1 = db_transUp1.rawQuery("select flag from transHoliday",null);
                cursor_transUp1.moveToFirst();
                transHoliday = cursor_transUp1.getLong(0);
            } finally {
                db_transUp1.close();
            }


        if(holidayFlag == true){
            //祝日があったら名前と詳細を取得
           holidayId = Hday.holiday_info();
            SQLiteDatabase db_holiday = helper.getWritableDatabase();
            try{
                Cursor cursor_holiday = db_holiday.query(
                        "holiday",
                        new String[] {"name","detail"},
                        "id = ?",
                        new String[] {"" + holidayId},
                        null,
                        null,
                        null
                );
                cursor_holiday.moveToFirst();
                holidayName = cursor_holiday.getString(0);
                holidayDetail = cursor_holiday.getString(1);
                //holidayNameにアンダーラインを引く
                SpannableString uHoliday = new SpannableString(holidayName);
                uHoliday.setSpan(new UnderlineSpan(),0,holidayName.length(),0);
                holiday.setText(uHoliday);
                //曜日の字を赤くする
                day_of_the_week.setTextColor(Color.RED);
            } finally {
                db_holiday.close();
            }
        }
        else if(transHoliday == 1 && holidayFlag == false ){
            //振替休日フラグがtrueでかつ、国民の祝日フラグもfalseならその日は振替休日
            holiday.setText("振替休日");
            holidayDetail = "今日は振替休日です";
            //DBの振替休日フラグを0に戻す
            SQLiteDatabase db_transUp2 = helper.getWritableDatabase();
            try{
                ContentValues cv_transUp2 = new ContentValues();
                cv_transUp2.put("flag",0);
                db_transUp2.update("transHoliday",cv_transUp2,"id = ?",transHdayId);
            } finally {
                db_transUp2.close();
            }
        }
        else{
            //祝日じゃなかったら表示しない
            holiday.setText("");
        }
        //詳細をダイアログで表示
        holiday.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle(holidayName);
                builder.setMessage(holidayDetail);
                builder.setCancelable(false);
                builder.setPositiveButton("OK",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {

                            }
                        });
                builder.create().show();
            }
        });

---中略---

//onCreateはココまで
    }
}
```
MainではHoliday.javaからもらったholidayFlag,transHolidayFlagを基に祝日・振替休日の判断をし祝日はデータベースから、振替休日はデータベースにてオンオフの切り替えをしています。TextViewでも.setOnClickListenerが使えるので表示されている祝日名をタップするとその詳細をダイアログで表示しています。

## 実行結果
　2020年の2月23(日)がちょうど天皇誕生日なのでスマホの日にちをいじってその日にしてみました。
![holidayExp1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534506/e91cb950-9b4b-2fe0-c924-4bacc8443d3f.png)
左の赤文字の「天皇誕生日」をタップすると..
![holidayExp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534506/026ba0a8-3c15-2d31-e973-98519111f1d3.png)
と詳細を表示できるようにしています。(右側の青字の「天皇誕生日」は別の記念日にしないと...)
　そして次の日の月曜日が振替休日となっていますので、24日にしてみます。
![transExp1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534506/ae8c8a2f-e476-57ae-b8a7-fa6cacc5e937.png)
振替休日自体は別に詳細を表示する必要がないのでアンダーラインを付けていませんが一応タップすると
![transExp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534506/cf83f66d-1915-74cd-6a18-55020de56a57.png)
このように「今日は振替休日です」と表示するようにしています。

## 欠点
　振替休日の定義は「日曜に祝日があるならば次の平日が振替休日」でした。国民の祝日は数字が分かるので判定できますが振替休日は日曜で祝日であることで判断しました。ここで問題となるのが
1. 毎日ログインしないと振替休日のフラグが判断できない
2. 振替休日を表示した時、フラグを0にするので再起動すると振替休日が表示されなくなる
3. まれにある平日が祝日に挟まれた時にその日が祝日となるのに対応できていない
が挙げられます。この記事を書いている時もうんうん考えているのですが(笑)、1,2は共通してフラグが0になる、0にしてしまうから起きる現象です。当日しか調べられないので、かなーりの悩みの種どころか花まで咲いています(笑)。解決しそうでなかなか出てきません...あっ、3のパターンは勘弁してください...
(欠点部分かくだけで1時間もかかった...)

　祝日のところはうまく動いているはずです！まぁ冗長ではありますが(笑)。まぁとりあえずこのままで進めていって振替休日の件が解決できそうだったらまたご報告します。gekimuzu..

**追記** 第○月曜日の処理が間違っていたので修正版を投稿しています。
→https://qiita.com/ysk75/items/278c52bf4052991eb99d

### 参考文献
国民の祝日：https://ja.wikipedia.org/wiki/%E5%9B%BD%E6%B0%91%E3%81%AE%E7%A5%9D%E6%97%A5
日時に関する情報を取得する：https://www.javadrive.jp/start/calendar/index2.html