-
Notifications
You must be signed in to change notification settings - Fork 0
/
JapanDate.cls
381 lines (321 loc) · 12.2 KB
/
JapanDate.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
public class JapanDate {
// 参考: https://qiita.com/chiyoyo/items/539dc2840a1b70a8e2c3
// Dateクラスを継承できないので、Dateクラスのメソッドは全てコピー
// Non-virtual and non-abstract type cannot be extended: Date
private Date dt;
// Dateクラスは、staticメソッド経由でしかインスタンス化できないが、
// JapanDateはDateクラスを引数にインスタンス化を許容
public JapanDate(Date dt) {
this.dt = dt;
}
private class Holiday {
String type;
Integer month;
Integer dayOrWeek;
Integer startYear;
Integer endYear;
String name;
Holiday(String type, Integer month, Integer dayOrWeek, Integer startYear, Integer endYear, String name) {
this.type = type;
this.month = month;
this.dayOrWeek = dayOrWeek;
this.startYear = startYear;
this.endYear = endYear;
this.name = name;
}
}
/** 祝日一覧 */
// 種別:
// fixed=日付固定
// happy=指定の週の月曜日
// spring=春分の日専用
// autumn=秋分の日専用
private static final List<Holiday> HOLIDAYS = new List<Holiday>{
// 種別, 月, 日or週, 開始年, 終了年, 祝日名
new Holiday('fixed', 1, 1, 1949, 9999, '元日'),
new Holiday('fixed', 1, 15, 1949, 1999, '成人の日'),
new Holiday('happy', 1, 2, 2000, 9999, '成人の日'),
new Holiday('fixed', 2, 11, 1967, 9999, '建国記念の日'),
new Holiday('fixed', 2, 23, 2020, 9999, '天皇誕生日'),
new Holiday('spring', 3, 0, 1949, 9999, '春分の日'),
new Holiday('fixed', 4, 29, 1949, 1989, '天皇誕生日'),
new Holiday('fixed', 4, 29, 1990, 2006, 'みどりの日'),
new Holiday('fixed', 4, 29, 2007, 9999, '昭和の日'),
new Holiday('fixed', 5, 3, 1949, 9999, '憲法記念日'),
new Holiday('fixed', 5, 4, 1988, 2006, '国民の休日'),
new Holiday('fixed', 5, 4, 2007, 9999, 'みどりの日'),
new Holiday('fixed', 5, 5, 1949, 9999, 'こどもの日'),
new Holiday('happy', 7, 3, 2021, 9999, '海の日'),
new Holiday('fixed', 7, 23, 2020, 2020, '海の日'),
new Holiday('happy', 7, 3, 2003, 2019, '海の日'),
new Holiday('fixed', 7, 20, 1996, 2002, '海の日'),
new Holiday('fixed', 8, 11, 2021, 9999, '山の日'),
new Holiday('fixed', 8, 10, 2020, 2020, '山の日'),
new Holiday('fixed', 8, 11, 2016, 2019, '山の日'),
new Holiday('autumn', 9, 0, 1948, 9999, '秋分の日'),
new Holiday('fixed', 9, 15, 1966, 2002, '敬老の日'),
new Holiday('happy', 9, 3, 2003, 9999, '敬老の日'),
new Holiday('fixed', 10, 10, 1966, 1999, '体育の日'),
new Holiday('happy', 10, 2, 2000, 2019, '体育の日'),
new Holiday('fixed', 7, 24, 2020, 2020, 'スポーツの日'),
new Holiday('happy', 10, 2, 2021, 9999, 'スポーツの日'),
new Holiday('fixed', 11, 3, 1948, 9999, '文化の日'),
new Holiday('fixed', 11, 23, 1948, 9999, '勤労感謝の日'),
new Holiday('fixed', 12, 23, 1989, 2018, '天皇誕生日'),
//以下、1年だけの祝日
new Holiday('fixed', 4, 10, 1959, 1959, '皇太子明仁親王の結婚の儀'),
new Holiday('fixed', 2, 24, 1989, 1989, '昭和天皇の大喪の礼'),
new Holiday('fixed', 11, 12, 1990, 1990, '即位礼正殿の儀'),
new Holiday('fixed', 6, 9, 1993, 1993, '皇太子徳仁親王の結婚の儀'),
new Holiday('fixed', 5, 1, 2019, 2019, '天皇の即位の日'),
new Holiday('fixed', 10, 22, 2019, 2019, '即位礼正殿の儀')
};
/* CONSTANTS */
private static final JapanDate SUBSTITUTE_HOLIDAY_ISSUED = JapanDate.newInstance(1973, 4, 12);
private static final JapanDate SUNDAY = JapanDate.newInstance(1, 1, 2);
private static final JapanDate NATIONAL_HOLIDAY_ISSUED = JapanDate.newInstance(2003,1,1);
private static final Integer SUBSTITUTE_HOLIDAY_LAW_REVISED_YEAR = 2006;
// 平日、振替休日、祝日の名前。平日の場合はnullを返す。
public String holiday() {
// 祝日
String holiday = this.getUsualHolidayName();
if (holiday != null) return holiday;
// 振替休日
holiday = this.getSubstituteHolidayName();
if (holiday != null) return holiday;
// 国民の休日
return this.getNationalHolidayName();
}
/**
* 祝日を取得
*/
public Boolean isHoliday() {
// 祝日
String holiday = this.getUsualHolidayName();
if (holiday != null) return true;
// 振替休日
holiday = this.getSubstituteHolidayName();
if (holiday != null) return true;
// 国民の休日チェック
return this.getNationalHolidayName() != null;
}
/**
* 設定された休日のみチェック
* 国民の休日と振替休日はチェックしない
*/
public String getUsualHolidayName() {
String holiday = null;
// 全ての祝日を判定
for(Holiday h : HOLIDAYS) {
switch on h.type {
when 'fixed' {
holiday = this.fixedHoliday(h);
}
when 'happy' {
holiday = this.happyHoliday(h);
}
when 'autumn' {
holiday = this.autumnHoliday(h);
}
when 'spring' {
holiday = this.springHoliday(h);
}
when else {
System.debug('unknown type');
}
}
if(holiday != null) return holiday;
}
return holiday;
}
/**
* 振替休日チェック
*/
public String getSubstituteHolidayName()
{
// 施行日チェック
if (this.getDate() < SUBSTITUTE_HOLIDAY_ISSUED.getDate()) return null;
// 当日が祝日の場合はfalse
if (this.getUsualHolidayName() != null) return null;
//改正法なら最大7日間遡る
Integer num = (this.year() <= SUBSTITUTE_HOLIDAY_LAW_REVISED_YEAR) ? 1 : 7;
JapanDate comp = this.addDays(-1);
String result = null;
for (Integer i = 0 ; i < num ; i++) {
String holidayName = comp.getUsualHolidayName();
if (holidayName != null) {
// 祝日かつ日曜ならば振替休日
if (comp.dayOfWeek() == 0) {
result = holidayName;
break;
}
comp = comp.addDays(-1);
} else {
break;
}
}
return result == null ? null : '振替休日(' + result + ')';
}
/**
* 国民の休日チェック
* 1日前と1日後が祝日の場合、間の平日も休日になる。
*/
public String getNationalHolidayName()
{
// 施行日チェック
if (this.getDate() < NATIONAL_HOLIDAY_ISSUED.getDate()) return null;
JapanDate before = this.addDays(-1);
if (before.getUsualHolidayName() == null) return null;
JapanDate after = this.addDays(1);
if (after.getUsualHolidayName() == null) return null;
return '国民の休日';
}
/**
* 固定祝日かどうか
*/
private String fixedHoliday(Holiday h)
{
if (!this.isWithinYear(h.startYear, h.endYear)) return null;
if (this.month() != h.month) return null;
if (this.day() != h.dayOrWeek) return null;
return h.name;
}
/**
* ハッピーマンデー
*/
private String happyHoliday(Holiday h) {
if (!this.isWithinYear(h.startYear, h.endYear)) return null;
if (this.month() != h.month) return null;
// 第*月曜日の日付を求める
Integer w = 1; // 月曜日固定
JapanDate d1 = this.toStartOfMonth();
Integer w1 = d1.dayOfWeek();
Integer day = w - w1 < 0 ? 7 + w - w1 : w - w1;
day++;
day = day + 7 * (h.dayOrWeek - 1);
if (this.day() != day) return null;
return h.name;
}
/**
* 春分の日
*/
private String springHoliday(Holiday h) {
if (!this.isWithinYear(h.startYear, h.endYear)) return null;
if (this.month() != h.month) return null;
Integer day = (Integer) Math.floor(20.8431 + 0.242194 * (this.year() - 1980) - Math.floor((this.year() - 1980) / 4));
if (this.day() != day) return null;
return h.name;
}
/**
* 秋分の日
*/
private String autumnHoliday(Holiday h)
{
if (!this.isWithinYear(h.startYear, h.endYear)) return null;
if (this.month() != h.month) return null;
Integer day = (Integer) Math.floor(23.2488 + 0.242194 * (this.year() - 1980) - Math.floor((this.year() - 1980) / 4));
if (this.day() != day) return null;
return h.name;
}
/**
* 年が祝日適用範囲内であるか
*/
private Boolean isWithinYear(Integer startYear, Integer endYear) {
if (this.year() < startYear || endYear < this.year()) {
return false;
}
return true;
}
/**
* Dateクラスのインスタンスメソッド
*/
public Date getDate() {
return this.dt;
}
public Integer dayOfWeek() {
return Math.mod(SUNDAY.daysBetween(this), 7);
}
public JapanDate addDays(Integer additionalDays) {
Date dt = this.dt.addDays(additionalDays);
return new JapanDate(dt);
}
public JapanDate addMonths(Integer additionalMonths) {
Date dt = this.dt.addMonths(additionalMonths);
return new JapanDate(dt);
}
public JapanDate addYears(Integer additionalYears) {
Date dt = this.dt.addYears(additionalYears);
return new JapanDate(dt);
}
public Integer day() {
return this.dt.day();
}
public Integer dayOfYear() {
return this.dt.dayOfYear();
}
public Integer daysBetween(Date secondDate) {
return this.dt.daysBetween(secondDate);
}
public Integer daysBetween(JapanDate secondDate) {
return this.dt.daysBetween(secondDate.getDate());
}
public String format() {
return this.dt.format();
}
public Boolean isSameDay(Date dateToCompare) {
return this.dt.isSameDay(dateToCompare);
}
public Boolean isSameDay(JapanDate dateToCompare) {
return this.dt.isSameDay(dateToCompare.getDate());
}
public Integer month() {
return this.dt.month();
}
public Integer monthsBetween(Date secondDate) {
return this.dt.monthsBetween(secondDate);
}
public Integer monthsBetween(JapanDate secondDate) {
return this.dt.monthsBetween(secondDate.getDate());
}
public JapanDate toStartOfMonth() {
Date dt = this.dt.toStartOfMonth();
return new JapanDate(dt);
}
public JapanDate toStartOfWeek() {
Date dt = this.dt.toStartOfWeek();
return new JapanDate(dt);
}
public Integer year() {
return this.dt.year();
}
/**
* DateクラスのStaticメソッド
*/
public static Integer daysInMonth(Integer year, Integer month) {
return Date.daysInMonth(year, month);
}
// holidaysInMonth()
public static Boolean isLeapYear(Integer year) {
return Date.isLeapYear(year);
}
public static JapanDate newInstance(Integer year, Integer month, Integer day) {
Date dt = Date.newInstance(year, month, day);
return new JapanDate(dt);
}
public static JapanDate parse(String stringDate) {
Date dt = Date.parse(stringDate);
return new JapanDate(dt);
}
public static JapanDate today() {
Date dt = Date.today();
return new JapanDate(dt);
}
public static JapanDate valueOf(String stringDate) {
Date dt = Date.valueOf(stringDate);
return new JapanDate(dt);
}
public static JapanDate valueOf(Object fieldValue) {
Date dt = Date.valueOf(fieldValue);
return new JapanDate(dt);
}
}