【Java による祝日判定ロジック実装の試み】

「Oracle RDBMS における和暦の利用」という資料を作成しようと考え
Web上で関連情報を調査している時に偶然AddinBoxというサイトを発見
しました。そのサイトでは、「祝日判定ロジック」というものが紹介
されており、有志の皆さんの手によって各言語へ移植されたコードが
公開されていました。
しかし、そのコードの中には、C言語やJavascriptへ移植されたコードは
あるものの Javaへ移植されたものがありませんでした。この状況をじゃびる
としては放置することができず、早速Javaへの移植を開始しました。
その成果物として得られたものが下記のコードです。
このコードはAddinBoxの方でも同じものが公開されています。
いつかはこのコードをPL/SQLへ移植してみたいものです。

ソースファイルはコチラ→KtHoliday.java 13.8 KB (14,154 バイト)



001 /*
002   この Java(J2SE1.4) 用祝日判定コードは、下記ウェブサイトで
003   公開されているのVBA用のコードを元に、主にJavascript版と
004   C言語版を参考にして
005   阿蛭 栄一( http://www.age.ne.jp/x/abiru/index.html )が
006   編集移植しました。
007 */
008 
009 /*_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
010 _/
011 _/ CopyRight(C) K.Tsunoda(AddinBox) 2001 All Rights Reserved.
012 _/ ( http://www.h3.dion.ne.jp/~sakatsu/index.htm )
013 _/
014 _/  この祝日マクロは『kt関数アドイン』で使用しているものです。
015 _/  このロジックは、レスポンスを第一義として、可能な限り少ない
016 _/ 【条件判定の実行】で結果を出せるように設計してあります。
017 _/  この関数では、2003年施行の改正祝日法までをサポートして
018 _/ います(9月の国民の休日を含む)。
019 _/
020 _/ (*1)このマクロを引用するに当たっては、必ずこのコメントも
021 _/   一緒に引用する事とします。
022 _/ (*2)他サイト上で本マクロを直接引用する事は、ご遠慮願います。
023 _/   【 http://www.h3.dion.ne.jp/~sakatsu/holiday_logic.htm024 _/   へのリンクによる紹介で対応して下さい。
025 _/ (*3)[ktHolidayName]という関数名そのものは、各自の環境に
026 _/   おける命名規則に沿って変更しても構いません。
027 _/ 
028 _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/*/
029 
030 
031 import java.util.Calendar;
032 import java.util.GregorianCalendar;
033 import java.text.SimpleDateFormat;
034 import java.text.ParseException;
035 
036 public class KtHoliday{
037 
038     private KtHoliday(){
039     }
040 
041     private static final  Calendar cstImplementTheLawOfHoliday
042         = new GregorianCalendar( 1948, Calendar.JULY, 20 );       // 祝日法施行
043     private static final  Calendar cstAkihitoKekkon
044         = new GregorianCalendar( 1959, Calendar.APRIL, 10 );  // 明仁親王の結婚の儀
045     private static final  Calendar cstShowaTaiso
046         = new GregorianCalendar( 1989, Calendar.FEBRUARY, 24 );// 昭和天皇大喪の礼
047     private static final  Calendar cstNorihitoKekkon
048         = new GregorianCalendar( 1993, Calendar.JUNE, 9 );// 徳仁親王の結婚の儀
049     private static final  Calendar cstSokuireiseiden
050         = new GregorianCalendar( 1990, Calendar.NOVEMBER, 12 );// 即位礼正殿の儀
051     private static final  Calendar cstImplementHoliday
052         = new GregorianCalendar( 1973, Calendar.APRIL, 12 );// 振替休日施行
053 
054     // [prmDate]には "yyyy/m/d"形式の日付文字列を渡す
055     public static String getHolidayName( String prmDate ) throws ParseException
056     {
057         String HolidayName_ret = "";
058         SimpleDateFormat formatter = new SimpleDateFormat ( "yyyy/MM/dd" );
059         Calendar MyDate = Calendar.getInstance();
060         MyDate.setTime( formatter.parse( prmDate ) );
061         String HolidayName = prvHolidayChk( MyDate );
062         if ( HolidayName == "" ) {
063             if ( MyDate.get( Calendar.DAY_OF_WEEK ) == Calendar.MONDAY ) {
064                 // 月曜以外は振替休日判定不要
065                 if ( MyDate.after( cstImplementHoliday ) || MyDate.equals( cstImplementHoliday ) ) {
066                     Calendar YesterDay = (Calendar )MyDate.clone();
067                     YesterDay.add( Calendar.DATE, -1 );
068                     HolidayName = prvHolidayChk( YesterDay );
069                     HolidayName_ret = "";
070                     if ( HolidayName != "" ) {
071                         HolidayName_ret = "振替休日";
072                     } else {
073                         HolidayName_ret = "";
074                     }
075                 } else {
076                     HolidayName_ret = "";
077                 }
078             } else {
079                 HolidayName_ret = "";
080             }
081         } else {
082             HolidayName_ret = HolidayName;
083         }
084         return HolidayName_ret;
085     }
086 
087     //===============================================================
088 
089     private static String prvHolidayChk( Calendar MyDate )
090     {
091         int NumberOfWeek;
092         String Result;
093         int MyYear = MyDate.get( Calendar.YEAR );
094         int MyMonth = MyDate.get( Calendar.MONTH ) + 1;    // MyMonth:1〜12
095         int MyDay = MyDate.get( Calendar.DATE );
096 
097         if ( MyDate.before( cstImplementTheLawOfHoliday ) ) {
098         return ""; // 祝日法施行(1948/7/20 )以前
099         } else;
100 
101         Result = "";
102         switch ( MyMonth ) {
103     // 1月 //
104         case 1:
105             if ( MyDay == 1 ) {
106                 Result = "元日";
107             } else {
108                 if ( MyYear >= 2000 ) {
109                     NumberOfWeek = ( (MyDay - 1 ) / 7 ) + 1;
110                     if ( ( NumberOfWeek == 2 ) && ( MyDate.get( Calendar.DAY_OF_WEEK ) == Calendar.MONDAY ) ) {
111                         Result = "成人の日";
112                     } else;
113                 } else {
114                     if ( MyDay == 15 ) {
115                         Result = "成人の日";
116                     } else;
117                 }
118             }
119             break;
120     // 2月 //
121         case 2:
122             if ( MyDay == 11 ) {
123                 if ( MyYear >= 1967 ) {
124                     Result = "建国記念の日";
125                 } else;
126             } else {
127                 if ( MyDate.equals( cstShowaTaiso ) ) {
128                     Result = "昭和天皇の大喪の礼";
129                 } else;
130             }
131             break;
132     // 3月 //
133         case 3:
134             if ( MyDay == prvDayOfSpringEquinox( MyYear ) ) {  // 1948〜2150以外は[99]
135                 Result = "春分の日";                       // が返るので、必ず≠になる
136             } else;
137             break;
138     // 4月 //
139         case 4:
140             if ( MyDay == 29 ) {
141                 if ( MyYear >= 1989 ) {
142                     Result = "みどりの日";
143                 } else {
144                 Result = "天皇誕生日";
145                 }
146             } else {
147                 if ( MyDate.equals( cstAkihitoKekkon ) ) {
148                     Result = "皇太子明仁親王の結婚の儀";// ( =1959/4/10 )
149                 } else;
150             }
151             break;
152     // 5月 //
153         case 5:
154             if ( MyDay == 3 ) {
155                 Result = "憲法記念日";
156             } else {
157                 if ( MyDay == 4 ) {
158                     if ( MyDate.get( Calendar.DAY_OF_WEEK ) > Calendar.MONDAY ) {
159                     // 5/4が日曜日は『只の日曜』、月曜日は『憲法記念日の振替休日』
160                         if ( MyYear >= 1986 ) {
161                             Result = "国民の休日";
162                         } else;
163                     } else;
164                 } else {
165                     if ( MyDay == 5 ) {
166                         Result = "子供の日";
167                     } else;
168                 }
169             }
170             break;
171     // 6月 //
172         case 6:
173             if ( MyDate.equals( cstNorihitoKekkon ) ) {
174                 Result = "皇太子徳仁親王の結婚の儀";
175             } else;
176             break;
177     // 7月 //
178         case 7:
179             if ( MyYear >= 2003 ) {
180                 NumberOfWeek = ( (MyDay - 1 ) / 7 ) + 1;
181                 if ( ( NumberOfWeek == 3 ) && ( MyDate.get( Calendar.DAY_OF_WEEK ) == Calendar.MONDAY ) ) {
182                     Result = "海の日";
183                 } else;
184             } else {
185                 if ( MyYear >= 1996 ) {
186                     if ( MyDay == 20 ) {
187                         Result = "海の日";
188                     } else;
189                 } else;
190             }
191             break;
192     // 9月 //
193         case 9:
194             //第3月曜日( 15〜21 )と秋分日(22〜24 )が重なる事はない
195             int MyAutumnEquinox = prvDayOfAutumnEquinox( MyYear );
196             if ( MyDay == MyAutumnEquinox ) {    // 1948〜2150以外は[99]
197                 Result = "秋分の日";           // が返るので、必ず≠になる
198             } else {
199                 if ( MyYear >= 2003 ) {
200                     NumberOfWeek = ( (MyDay - 1 ) / 7 ) + 1;
201                     if ( (NumberOfWeek == 3 ) && ( MyDate.get( Calendar.DAY_OF_WEEK ) == Calendar.MONDAY ) ) {
202                         Result = "敬老の日";
203                     } else {
204                         if ( MyDate.get( Calendar.DAY_OF_WEEK ) == Calendar.TUESDAY ) {
205                             if ( MyDay == ( MyAutumnEquinox - 1 ) ) {
206                                 Result = "国民の休日";
207                             } else;
208                         } else;
209                     }
210                 } else {
211                     if ( MyYear >= 1966 ) {
212                         if ( MyDay == 15 ) {
213                             Result = "敬老の日";
214                         } else;
215                     } else;
216                 }
217             }
218             break;
219     // 10月 //
220         case 10:
221             if ( MyYear >= 2000 ) {
222                 NumberOfWeek = ( ( MyDay - 1 ) / 7 ) + 1;
223                 if ( (NumberOfWeek == 2 ) && ( MyDate.get( Calendar.DAY_OF_WEEK ) == Calendar.MONDAY ) ) {
224                     Result = "体育の日";
225                 } else;
226             } else {
227                 if ( MyYear >= 1966 ) {
228                     if ( MyDay == 10 ) {
229                         Result = "体育の日";
230                     } else;
231                 } else;
232             }
233             break;
234     // 11月 //
235         case 11:
236             if ( MyDay == 3 ) {
237                 Result = "文化の日";
238             } else {
239                 if ( MyDay == 23 ) {
240                     Result = "勤労感謝の日";
241                 } else {
242                     if ( MyDate.equals( cstSokuireiseiden ) ) {
243                         Result = "即位礼正殿の儀";
244                     } else;
245                 }
246             }
247             break;
248     // 12月 //
249         case 12:
250             if ( MyDay == 23 ) {
251                 if ( MyYear >= 1989 ) {
252                     Result = "天皇誕生日";
253                 } else;
254             } else;
255             break;
256         }
257 
258         return Result;
259     }
260 
261     //===================================================================
262     // 春分/秋分日の略算式は
263     // 『海上保安庁水路部 暦計算研究会編 新こよみ便利帳』
264     // で紹介されている式です。
265     private static int prvDayOfSpringEquinox( int MyYear )
266     {
267         int SpringEquinox_ret;
268         if ( MyYear <= 1947 ) {
269             SpringEquinox_ret = 99;    //祝日法施行前
270         } else {
271             if ( MyYear <= 1979 ) {
272                 SpringEquinox_ret = (int)( 20.8357 + 
273                 ( 0.242194 * ( MyYear - 1980 ) ) - (int)( (MyYear - 1983 ) / 4 ) );
274             } else {
275                 if ( MyYear <= 2099 ) {
276                     SpringEquinox_ret = (int)( 20.8431 + 
277                     ( 0.242194 * ( MyYear - 1980 ) ) - (int)( (MyYear - 1980 ) / 4 ) );
278                 } else {
279                     if ( MyYear <= 2150 ) {
280                         SpringEquinox_ret = (int)( 21.851 + 
281                         ( 0.242194 * ( MyYear - 1980 ) ) - (int)( (MyYear - 1980 ) / 4 ) );
282                     } else {
283                         SpringEquinox_ret = 99;    //2151年以降は略算式が無いので不明
284                     }
285                 }
286             }
287         }
288         return SpringEquinox_ret;
289     }
290 
291     //=====================================================================
292     private static int prvDayOfAutumnEquinox( int MyYear )
293     {
294         int AutumnEquinox_ret;
295         if ( MyYear <= 1947 ) {
296             AutumnEquinox_ret = 99; //祝日法施行前
297         } else {
298             if ( MyYear <= 1979 ) {
299                 AutumnEquinox_ret = (int)( 23.2588 + 
300                 ( 0.242194 * ( MyYear - 1980 ) ) - (int)( (MyYear - 1983 ) / 4 ) );
301             } else {
302                 if ( MyYear <= 2099 ) {
303                     AutumnEquinox_ret = (int)( 23.2488 + 
304                     ( 0.242194 * ( MyYear - 1980 ) ) - (int)( (MyYear - 1980 ) / 4 ) );
305                 } else {
306                     if ( MyYear <= 2150 ) {
307                         AutumnEquinox_ret = (int)( 24.2488 + 
308                         ( 0.242194 * ( MyYear - 1980 ) ) - (int)( (MyYear - 1980 ) / 4 ) );
309                     } else {
310                         AutumnEquinox_ret = 99;    //2151年以降は略算式が無いので不明
311                     }
312                 }
313             }
314         }
315         return AutumnEquinox_ret;
316     }
317 
318 
319     /*_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
320     _/ 
321     _/ ここから先は、阿蛭栄一が独自に追加したコーディングです。
322     _/ Thu, 18 Dec 2003 02:25:27 +0900
323     _/ abiru@home.104.net
324     _/ 
325     _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
326 
327     //=====================================================================
328 
329     /*このメソッドは、引数にjava.util.Calendarを取るgetHolidayNameメソッドです。
330         単にCalendarオブジェクトから"yyyy/MM/dd"形式の文字列を組み立てて
331         getHolidayName( String prmDate )の方のメソッドを呼び出しているだけですが、
332         他のアプリケーションからこのクラスを使う時のことを考慮するとString
333         ではなく、Calendarを引数に取るメソッドがあった方が便利ですよね?*/
334     public static String getHolidayName( Calendar prmDate ) throws ParseException
335     {
336         SimpleDateFormat f = new SimpleDateFormat ( "yyyy/MM/dd" );
337         return getHolidayName( f.format( prmDate.getTime() ) );
338     }
339 
340     //=====================================================================
341 
342     /*このメソッドは動作確認やデバッグ用に用意したメソッドです。
343         通常は利用しませんので不要な場合には削除して頂いて結構です。
344         使い方は以下の通りです。
345         java KtHoliday 1948/01/01 2050/12/30
346     */
347     public static void main( String args[] ) throws Exception
348     {
349         SimpleDateFormat f = new SimpleDateFormat ( "yyyy/MM/dd" );
350         Calendar current = new GregorianCalendar();
351         current.setTime( f.parse( args[0] ) );
352         Calendar end = new GregorianCalendar();
353         end.setTime( f.parse( args[1] ) );
354 
355         String result = "";
356         while( current.before( end ) || current.equals( end ) ){
357             result = getHolidayName( current );
358             if( !result.equals( "" ) ){
359                 System.out.print( f.format( current.getTime() ) );
360                 System.out.println( ",\"" + result + "\"" );
361             }
362             current.add( Calendar.DATE , 1 );
363         }
364 
365     }
366 }