Why not login to Qiita and try out its useful features?

We'll deliver articles that match you.

You can read useful information later.

0
0

【豆記事】日付の配列から休・祝日を判定して真偽値の配列を返す

Last updated at Posted at 2024-10-02

1. 概要

休みの日か平日かどうかを判定するような機能がプロジェクトを作っていく中で必要になったので、忘備録も兼ねて作ったものを紹介しようと思います。

ただ、今回の仕組みは外部APIありきなので、このAPIの仕様が変わったりサービスが終了したらうまく機能しなくなります。業務用で堅牢さが求められるような場面においてはそぐわないかもしれないことをご承知おきください。

2. 祝日の取得

祝日の判定は少々面倒だと思います。年によって若干異なる可能性がありますし、日曜日にかぶったら月曜日が振替休日になってしまうので、この処理をするコードも必要です。
ということで、この部分の処理だけでも結構大変なのでこれは外部APIに頼ることにしました。今回使っていくのはholidays-jpというサイトが提供してくださっているAPIです。

早速コードの中身を見ていきましょう。

function isObj(value:unknown):value is Object{
  return value != null && typeof value === "object";
}

export async function getHolidaysOf(year: string):Promise<string[]>{
  const uri = `https://holidays-jp.github.io/api/v1/${year}/date.json`

  try{
    const res = await fetch(uri);
    const holidays = await res.json();
    if(!isObj(holidays)) throw new Error("fetch error");

    return Object.keys(holidays);

  }catch(err){
    console.log(err);
    throw err
  }  
}

https://holidays-jp.github.io/api/v1/${year}/date.jsonというuriを指定してjsonを取得します。dateとdatetimeの二つの表示形式があるのですが、この二つはキーの違いになります。値はどちらも祝日の名称を示しており、dateはハイフン形式のキーになっています。
今回は何日が祝日なのかだけに興味があるので、キーだけを返すようにしています。
外部APIを使えばこれだけで祝日の配列を取得できるのでとても便利ですね。

これだけのコードですが関数にした理由は一応あります。
祝日の一覧の取得コードはgetHolidaysOfという関数でまとめ、具体的な処理は次に紹介するカスタムフックからは切り出してあります。このため、このAPIサービスの提供が終了したとしても引数と返り値に注意しつつこの関数内のコードを変更すれば、他のコードに予期せぬ影響を及ぼすことなく改修することができます。

3. 休・祝日を判定する

次に、休日と祝日をまとめて判定するコードを紹介します。

import { getHolidaysOf } from "@/libs/holidays";
import { useEffect, useState } from "react";

type Holidays = {
  [key: string]: string[];
}

export function useHolidayFlags(dates:string[]){
  const [holidayFlags, setHolidayFlags] = useState<Array<boolean>>(Array(dates.length).fill(false));
  const [holidays, setHolidays] = useState<Holidays>({});
  const [err, setErr] = useState("");

  const yearSet = new Set(dates.map(date => date.trim().split("/")[0]));

  useEffect(() => {
    yearSet.forEach(year => getHolidaysOf(year)
      .then(holidays => setHolidays(prev => ({...prev, [year]: holidays})) )
      .catch(err => err instanceof Error ? setErr(err.message) : setErr("something went wrong!")));
    },[]);

  useEffect(() => {
    const hyphenDates = dates.map(date => date.replaceAll("/","-"));
    const updatedFlags = hyphenDates.map(hyphenDate => {
      const date = new Date(hyphenDate);
      const day = date.getDay();
      const isWeekend = day === 0 || day == 6;
  
      const year = date.getFullYear().toString();
      const isHoliday = holidays[year]?.includes(hyphenDate);

      return isWeekend || isHoliday;
      
    });
    setHolidayFlags(updatedFlags);

  }, [holidays]);

  return{
    holidayFlags,
    err
  }
}

datesの文字列配列においては、年が異なることもあるかもしれません。全ての年度の祝日を集めるために、以下のコードで重複のない年の集合を作っています。
const yearSet = new Set(dates.map(date => date.trim().split("/")[0]))
trimは左右の空文字を取り除くためのメソッドです。空文字が入っていると、リストの0番目の値が空文字になってしまいエラーになってしまいます。


次に、yearSetをforEachで回すことによって祝日を順番に取得しています。
年をキーにして、その年の祝日の配列を値とするオブジェクトを作っています。
変数をキーとして埋め込むためにはブランケットを使わないといけないのが意外に忘れがちな大事なところです。

yearSet.forEach(year => getHolidaysOf(year)
      .then(holidays => setHolidays(prev => ({...prev, [year]: holidays})) )
      .catch(err => err instanceof Error ? setErr(err.message) : setErr("something went wrong!")));

そして最後に、其々の日付が休・祝日かどうかを判定するコードを紹介して終わります。

const hyphenDates = dates.map(date => date.replaceAll("/","-"));
const updatedFlags = hyphenDates.map(hyphenDate => {
    const date = new Date(hyphenDate);
    const day = date.getDay();
    const isWeekend = day === 0 || day == 6;
  
    const year = date.getFullYear().toString();
    const isHoliday = holidays[year]?.includes(hyphenDate);

      return isWeekend || isHoliday;
      
    });
setHolidayFlags(updatedFlags);

曜日は0始まりで0が日曜なので、dayが0と6のときに休日ということになります。
また、holidays[year]?.includes(hyphenDate)の部分はholidaysオブジェクトにその日付の年でアクセスし、その中の祝日のリストに含まれているかを判定しています。

これにより、休・祝日をtrueとして、平日をfalseとする配列を返すことができました。

4. おわりに

休日かどうかを判定するものが組み込みであれば便利なのになぁと思います。せめてdate.isWeekends()とかdate.isWeekdays()とかあればちょっとは楽になるんですけどね。

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

@TechForGood's pickup articles

TechForGood

@TechForGood(Kawamura Yuki)

プログラミングを趣味と実益を兼ねて勉強中。 学習言語:JavaScript(TypeScript,React.js,Next.js), Python(Pandas,FastAPI),NoSQL(MongoDB),SQL(MySQL),Go(echo) ちょっと知ってる:Java,Dart, Firebase, GAS 勉強したい:C++

Comments

juner
@juner

並列で投げるのはAPI的に良くないのでは……?(直列に投げるべきでは?

0
akebi_mh
@akebi_mh

https://qiita.com/akebi_mh/items/1e2f8ab623e90b9a12ad
以前こんな祝日取得クラスを作ったことがありますが、これを使って例えばこんな感じの配列を作ることもできます。

sample.html
<meta charset='utf-8'>
<script src='holiday_class.js'></script>
<script>
const holiday = new Holiday;

const from = new Date('2025-05-01T00:00:00');
const to   = new Date('2025-05-10T00:00:00');

const result = Array(1 + (to - from) / 864e5).fill(+from).map((t, i) => {
  const d = new Date(t + i * 864e5);
  holiday.setYear(d.getFullYear());
  const nationalHolidayName = holiday.getHolidayOfYear(d.getFullYear())[1 + d.getMonth()][d.getDate()];
  const isHoliday = !!(nationalHolidayName ?? [0, 6].includes(d.getDay()));
  return {
    date: d.toLocaleString('ja', {dateStyle: 'short'}),
    isHoliday, 
    nationalHolidayName,
  };
});

console.log(result);
</script>
[
​ { date: "2025/05/01", isHoliday: false, nationalHolidayName: undefined }
​ { date: "2025/05/02", isHoliday: false, nationalHolidayName: undefined }
​ { date: "2025/05/03", isHoliday: true, nationalHolidayName: "憲法記念日" }
​ { date: "2025/05/04", isHoliday: true, nationalHolidayName: "みどりの日" }
​ { date: "2025/05/05", isHoliday: true, nationalHolidayName: "こどもの日" }
​ { date: "2025/05/06", isHoliday: true, nationalHolidayName: "振替休日" }
​ { date: "2025/05/07", isHoliday: false, nationalHolidayName: undefined }
​ { date: "2025/05/08", isHoliday: false, nationalHolidayName: undefined }
​ { date: "2025/05/09", isHoliday: false, nationalHolidayName: undefined }
​ { date: "2025/05/10", isHoliday: true, nationalHolidayName: undefined }
]
0

Let's comment your feelings that are more than good

Qiita Conference 2024 Autumn will be held!: 11/14(Thu) - 11/15(Fri)

Qiita Conference is the largest tech conference in Qiita!

Keynote Speaker

Takahiro Anno, Masaki Fujimoto, Yukihiro Matsumoto(Matz)

View event details

Being held Article posting campaign

0
0

Login to continue?

Login or Sign up with social account

Login or Sign up with your email address