1時間プログラミング 012 次の休日までの日数を表示する
概要
次の休日までの日数を表示する
休日の対象は土日と祝日
次の休日なので、土曜日に表示すると日曜日までの日数(1日)が表示される
https://e1-1hour-012-next-holiday.deno.dev/#.svg
所要時間: 3時間
使い方
code:terminal
$ deno run --allow-net --allow-read mod.ts
WebページにアクセスするとSVG画像が表示される
実装メモ
次の休日(土日と祝日)までの日数をSVG画像で表示する
TS(Deno)で実装する
HTTPサーバーでレスポンスを返す
code:ts
const ROOT_ROUTE = new URLPattern({ pathname: "/" });
serve((req) => {
const match = ROOT_ROUTE.exec(req.url);
if (match) {
// ...
return new Response(svg, {
headers: {
"content-type": "image/svg+xml",
"cache-control": "max-age=1800",
},
});
}
return new Response("Not found", {
status: 404,
});
});
次の土日の判定
日本のタイムゾーン(+09:00)で計算する必要がある
code:ts
// 日本のタイムゾーンに変更
const now = datetime();
const nowJp = now.toZonedTime("Asia/Tokyo");
現在日時に1〜7日を加算していき、土日か判定する
code:ts
const now = datetime();
const dt = now.add({ day: 1 }).toZonedTime("Asia/Tokyo");
// 以下は正しく動かない。余裕があればissueを立てる
const dt = now.toZonedTime("Asia/Tokyo").add({ day: 1 });
code:ts
type Holiday = {
daysLeft: number; // 休日まであとN日
};
function getNextHoliday(now: DateTime): Holiday {
let daysLeft = 0;
for (let dayOffset = 1; dayOffset <= 7; dayOffset++) {
const dt = now.add({ day: dayOffset }).toZonedTime("Asia/Tokyo");
// 土日の判定
const dayOfWeek = dt.weekDay();
if (dayOfWeek === 6 || dayOfWeek === 0) {
daysLeft = dayOffset;
break;
}
// 祝日の判定
// ...
}
return {
daysLeft,
};
}
次の祝日の判定
そのため、npmパッケージをesm.sh経由でインポートした isHoliday(date: Date)で判定する
引数のDateオブジェクトから年月日への変換処理を読む
code:js
function format(date) {
var year = date.getFullYear();
var month = ('0' + (date.getMonth() + 1)).slice(-2);
var day = ('0' + (date.getDate())).slice(-2);
return (year + '-' + month + '-' + day);
}
日本のタイムゾーンには変換されず、ローカルのタイムゾーンのまま計算されることに注意する
code:ts
function getNextHoliday(now: DateTime): Holiday {
let daysLeft = 0;
for (let dayOffset = 1; dayOffset <= 7; dayOffset++) {
const dt = now.add({ day: dayOffset }).toZonedTime("Asia/Tokyo");
// ...
// 祝日の判定
const jsDate = new Date(dt.year, dt.month - 1, dt.day);
if (holidayJp.isHoliday(jsDate)) {
daysLeft = dayOffset;
break;
}
}
// ...
}
SVG画像を返す
今回は、SVG画像を事前に用意して置換文字列を埋め込んだ
可能であれば、XMLをパースして指定したIDのテキストを変更したかったが、面倒だったので単純な置換で対応
code:ts
const SVG_TEMPLATE = await Deno.readTextFile("resources/holiday-template.svg");
function createHolidaySVG(holiday: Holiday): string {
return SVG_TEMPLATE.replaceAll("{day}", holiday.daysLeft.toString());
}
serve((req) => {
const match = ROOT_ROUTE.exec(req.url);
if (match) {
const nextHoliday = getNextHoliday(datetime());
const svg = createHolidaySVG(nextHoliday);
return new Response(svg, {
headers: {
"content-type": "image/svg+xml",
"cache-control": "max-age=1800",
},
});
}
// ...
});
通常はGithubリポジトリと連携するのだが、1時間プログラミングは1つのリポジトリ内にプロジェクトを作るため、手動でデプロイした code:terminal
$ deployctl deploy --project=e1-1hour-012-next-holiday mod.ts
感想
JSの日付周りはかなり扱いにくい
Deno Deployを初めて使用した。かなり手軽なので1時間プログラミングだと重宝しそう