import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.time.Instant; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /* * 内閣府の「国民の祝日」CSVを取得して祝日情報を管理するクラス */ public class HolidayInfo{ /******************************************************************************* * 定数 ******************************************************************************/ private final static String FETCH_URI = "https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"; private final static String FILENAME = "env"+File.separator+"holidays.csv"; private final static String encoding = "MS932"; /******************************************************************************* * 静的メンバー ******************************************************************************/ private static HashMap<String, HolidayInfo> holidays = null; /******************************************************************************* * 部品以外のインスタンスメンバー ******************************************************************************/ private int year; private int month; private int day; private String holidayName; /******************************************************************************* * コンストラクタ ******************************************************************************/ public HolidayInfo(){ year = 0; month = 0; day = 0; holidayName = null; } /******************************************************************************* * 公開メソッド ******************************************************************************/ // 祝日の西暦年を返す public int getYear(){ return year; } // 祝日の月を返す public int getMonth(){ return month; } // 祝日の日を返す public int getDay(){ return day; } // 祝日の名称を返す public String getHolidayName(){ return holidayName; } /******************************************************************************* * 静的公開メソッド ******************************************************************************/ /* * CSVファイルを読み込む */ public static boolean Load(String uri, int interval){ if (IsCSVFileLifetimeExpired(interval)){ FetchCSVFile(uri); } return LoadFromCSVFile(FILENAME); } /* * CSVファイルを更新する */ public static boolean Refresh(String uri, int interval){ if (!IsCSVFileLifetimeExpired(interval)) return true; FetchCSVFile(uri); return LoadFromCSVFile(FILENAME); } /* * CSVファイルを内閣府のサーバーからダウンロードする */ public static boolean FetchCSVFile(String uri){ if (uri == null || uri.isEmpty()) uri = FETCH_URI; BufferedWriter bw = null; BufferedReader br = null; try { // 内閣府のホームページに接続する URL url = new URL(uri); HttpURLConnection ucon = (HttpURLConnection)url.openConnection(); ucon.setConnectTimeout(5000); ucon.setReadTimeout(5000); ucon.setRequestMethod("GET"); ucon.connect(); // レスポンスを一時ファイルに書き出す String tmpfile = FILENAME + ".tmp"; bw = new BufferedWriter(new FileWriter(tmpfile)); br = new BufferedReader(new InputStreamReader(ucon.getInputStream(), encoding)); String str; while((str = br.readLine()) != null){ bw.write(str); bw.write("\n"); } br.close(); br = null; bw.close(); bw = null; // CSVファイルにリネームする File ofile = new File(FILENAME); if ( ofile.exists() && ! ofile.delete() ) { System.err.println("[HolidayInfo]CSVファイルを削除できない:" + ofile.getAbsolutePath()); } File nfile = new File(tmpfile); if ( ! nfile.renameTo(ofile) ) { System.err.println("[HolidayInfo]CSVファイルをリネームできない:" + nfile.getAbsolutePath() + "=>" + ofile.getAbsolutePath()); return false; } return true; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ if (br != null) try { br.close(); } catch (Exception e) {} if (bw != null) try { bw.close(); } catch (Exception e) {} } return false; } /* * CSVファイルを読み込む */ public static boolean LoadFromCSVFile(String path){ if (path == null) return false; HashMap<String, HolidayInfo> map = new HashMap<String, HolidayInfo>(); BufferedReader br = null; // CSVファイルが存在するかチェックする File file = new File(path); try { if (!file.exists()){ System.err.println("[HolidayInfo]ファイルがない: " + file.getAbsolutePath()); return false; } br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); // 1行ずつ読み込む String str; while ((str = br.readLine()) != null) { // CSVの形式は YYYY/MM/DD,名称となっている Matcher ma = Pattern.compile("^(\\d{4})/(\\d{1,2})/(\\d{1,2}),(.*)$").matcher(str); if (!ma.find()) continue; // 祝日情報を生成する HolidayInfo hi = new HolidayInfo(); hi.year = Integer.parseInt(ma.group(1)); hi.month = Integer.parseInt(ma.group(2)); hi.day = Integer.parseInt(ma.group(3)); hi.holidayName = ma.group(4); // キーに紐づけてハッシュマップに追加する String s = FormatKey( hi.year, hi.month, hi.day); map.put(s, hi); } } catch (Exception e) { e.printStackTrace(); System.err.println("[HolidayInfo]ファイルの読み込みに失敗: "+file.getAbsolutePath()); return false; } finally { if (br != null) try { br.close(); } catch (Exception e) {} } holidays = map; System.out.println("[HolidayInfo]祝日数: "+map.size()); return true; } /* * 指定した日が祝日かどうかを返す */ public static boolean IsHoliday(int year, int month, int day){ return IsHoliday(FormatKey(year, month, day)); } /* * 指定した日が祝日の場合、その名前を返す */ public static String GetHolidayName(int year, int month, int day){ return GetHolidayName(FormatKey(year, month, day)); } /* * 指定した日の祝日情報を返す */ public static HolidayInfo GetHolidayInfo(int year, int month, int day){ return GetHolidayInfo(FormatKey(year, month, day)); } /* * YYYY/MM/DD形式で指定した日が祝日かどうかを返す */ public static boolean IsHoliday(String date){ HolidayInfo hi = GetHolidayInfo(date); return (hi != null); } /* * YYYY/MM/DD形式で指定した日が祝日の場合、その名前を返す */ public static String GetHolidayName(String date){ HolidayInfo hi = GetHolidayInfo(date); return (hi != null) ? hi.holidayName : null; } /* * YYYY/MM/DD形式で指定した日の祝日情報を返す */ public static HolidayInfo GetHolidayInfo(String date){ if (date == null || holidays == null) return null; if (date.length() > 10) date = date.substring(0, 10); return holidays.get(date); } /******************************************************************************* * 静的内部関数 ******************************************************************************/ /* * ハッシュマップのキーを生成する */ private static String FormatKey(int year, int month, int day){ return String.format("%04d/%02d/%02d", year, month, day); } /* * CSVファイルの寿命が尽きているかどうかを返す */ private static boolean IsCSVFileLifetimeExpired(int interval){ // インターバルが0以下の場合は寿命はない if (interval <= 0) return false; // CSVファイルが存在しない場合は尽きているとみなす File file = new File(FILENAME); if (!file.exists()) return true; try { BasicFileAttributes attrs = Files.readAttributes(file.toPath(), BasicFileAttributes.class); FileTime time = attrs.creationTime(); Instant now = Instant.now(); // ファイル生成後の経過ミリ秒数を計算する long elapsed = now.toEpochMilli() - time.toMillis(); System.out.println("[HolidayInfo]経過時間(日):" + (elapsed/1000/24/3600)); // インターバルを超えていない場合は尽きていない if ( elapsed/1000 < interval*24*3600) return false; } catch (IOException e) { } return true; } }