【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.htm 】
024 _/ へのリンクによる紹介で対応して下さい。
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 }