package com.yami.trading.service;
|
|
import java.time.*;
|
import java.time.temporal.ChronoUnit;
|
import java.util.*;
|
|
/**
|
* 美股交易日统计工具(适配java.util.Date)
|
*/
|
public class UsStockTradingDayCalculator {
|
// 美股纽约时区
|
private static final ZoneId NEW_YORK_ZONE = ZoneId.of("America/New_York");
|
// UTC时区(Date转ZonedDateTime的基准)
|
private static final ZoneId UTC_ZONE = ZoneId.of("UTC");
|
|
// ==================== 基础版:仅排除周末 ====================
|
/**
|
* 计算两个Date之间的美股交易日天数(仅排除周六/周日)
|
* @param startDate 开始日期(java.util.Date)
|
* @param endDate 结束日期(java.util.Date)
|
* @return 美股交易日天数
|
*/
|
public static int countUsStockTradingDays(Date startDate, Date endDate) {
|
// 1. 参数校验
|
if (startDate == null || endDate == null) {
|
throw new IllegalArgumentException("startDate error");
|
}
|
if (startDate.after(endDate)) {
|
throw new IllegalArgumentException("endDate error");
|
}
|
|
// 2. 将Date转换为纽约时区的LocalDate(核心:按纽约时间算日期)
|
LocalDate startLocalDate = convertDateToNyLocalDate(startDate);
|
LocalDate endLocalDate = convertDateToNyLocalDate(endDate);
|
|
// 3. 遍历日期并统计交易日(非周六/周日)
|
int tradingDays = 0;
|
LocalDate currentDate = startLocalDate;
|
while (!currentDate.isAfter(endLocalDate)) {
|
DayOfWeek dayOfWeek = currentDate.getDayOfWeek();
|
// 非周六、非周日即为交易日
|
if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) {
|
tradingDays++;
|
}
|
currentDate = currentDate.plusDays(1);
|
}
|
return tradingDays;
|
}
|
|
// ==================== 进阶版:排除周末+美股法定节假日 ====================
|
/**
|
* 计算两个Date之间的美股交易日天数(排除周六/周日+美股法定节假日)
|
* @param startDate 开始日期
|
* @param endDate 结束日期
|
* @return 美股交易日天数
|
*/
|
public static int countUsStockTradingDaysWithHolidays(Date startDate, Date endDate) {
|
// 基础参数校验
|
if (startDate == null || endDate == null) {
|
throw new IllegalArgumentException("开始日期和结束日期不能为空");
|
}
|
if (startDate.after(endDate)) {
|
throw new IllegalArgumentException("开始日期不能晚于结束日期");
|
}
|
|
// 转换为纽约时区的LocalDate
|
LocalDate startLocalDate = convertDateToNyLocalDate(startDate);
|
LocalDate endLocalDate = convertDateToNyLocalDate(endDate);
|
|
// 预加载周期内涉及年份的美股节假日
|
Set<LocalDate> holidays = new HashSet<>();
|
for (int year = startLocalDate.getYear(); year <= endLocalDate.getYear(); year++) {
|
holidays.addAll(getUsStockHolidays(year));
|
}
|
|
// 遍历统计(非周末+非节假日)
|
int tradingDays = 0;
|
LocalDate currentDate = startLocalDate;
|
while (!currentDate.isAfter(endLocalDate)) {
|
DayOfWeek dayOfWeek = currentDate.getDayOfWeek();
|
if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY
|
&& !holidays.contains(currentDate)) {
|
tradingDays++;
|
}
|
currentDate = currentDate.plusDays(1);
|
}
|
return tradingDays;
|
}
|
|
// ==================== 工具方法:Date转纽约时区LocalDate ====================
|
/**
|
* 将java.util.Date转换为纽约时区的LocalDate
|
* @param date 待转换的Date
|
* @return 纽约时区的LocalDate
|
*/
|
private static LocalDate convertDateToNyLocalDate(Date date) {
|
// Date -> Instant -> ZonedDateTime(UTC) -> ZonedDateTime(纽约) -> LocalDate
|
Instant instant = date.toInstant();
|
ZonedDateTime utcZoned = instant.atZone(UTC_ZONE);
|
ZonedDateTime nyZoned = utcZoned.withZoneSameInstant(NEW_YORK_ZONE);
|
return nyZoned.toLocalDate();
|
}
|
|
// ==================== 工具方法:获取指定年份的美股法定节假日 ====================
|
/**
|
* 获取指定年份的美股法定节假日(含调休规则)
|
* @param year 年份
|
* @return 节假日LocalDate集合
|
*/
|
private static Set<LocalDate> getUsStockHolidays(int year) {
|
Set<LocalDate> holidays = new HashSet<>();
|
|
// 1. 新年(1月1日,周末则调休)
|
LocalDate newYear = LocalDate.of(year, 1, 1);
|
if (newYear.getDayOfWeek() == DayOfWeek.SATURDAY) {
|
newYear = newYear.minusDays(1);
|
} else if (newYear.getDayOfWeek() == DayOfWeek.SUNDAY) {
|
newYear = newYear.plusDays(1);
|
}
|
holidays.add(newYear);
|
|
// 2. 马丁·路德·金日(1月第三个周一)
|
LocalDate mlkDay = LocalDate.of(year, 1, 1);
|
while (mlkDay.getDayOfWeek() != DayOfWeek.MONDAY) {
|
mlkDay = mlkDay.plusDays(1);
|
}
|
mlkDay = mlkDay.plusWeeks(2);
|
holidays.add(mlkDay);
|
|
// 3. 总统日(2月第三个周一)
|
LocalDate presidentsDay = LocalDate.of(year, 2, 1);
|
while (presidentsDay.getDayOfWeek() != DayOfWeek.MONDAY) {
|
presidentsDay = presidentsDay.plusDays(1);
|
}
|
presidentsDay = presidentsDay.plusWeeks(2);
|
holidays.add(presidentsDay);
|
|
// 4. 阵亡将士纪念日(5月最后一个周一)
|
LocalDate memorialDay = LocalDate.of(year, 5, 31);
|
while (memorialDay.getDayOfWeek() != DayOfWeek.MONDAY) {
|
memorialDay = memorialDay.minusDays(1);
|
}
|
holidays.add(memorialDay);
|
|
// 5. 独立日(7月4日,周末则调休)
|
LocalDate independenceDay = LocalDate.of(year, 7, 4);
|
if (independenceDay.getDayOfWeek() == DayOfWeek.SATURDAY) {
|
independenceDay = independenceDay.minusDays(1);
|
} else if (independenceDay.getDayOfWeek() == DayOfWeek.SUNDAY) {
|
independenceDay = independenceDay.plusDays(1);
|
}
|
holidays.add(independenceDay);
|
|
// 6. 劳工节(9月第一个周一)
|
LocalDate laborDay = LocalDate.of(year, 9, 1);
|
while (laborDay.getDayOfWeek() != DayOfWeek.MONDAY) {
|
laborDay = laborDay.plusDays(1);
|
}
|
holidays.add(laborDay);
|
|
// 7. 感恩节(11月第四个周四)
|
LocalDate thanksgiving = LocalDate.of(year, 11, 1);
|
while (thanksgiving.getDayOfWeek() != DayOfWeek.THURSDAY) {
|
thanksgiving = thanksgiving.plusDays(1);
|
}
|
thanksgiving = thanksgiving.plusWeeks(3);
|
holidays.add(thanksgiving);
|
|
// 8. 圣诞节(12月25日,周末则调休)
|
LocalDate christmas = LocalDate.of(year, 12, 25);
|
if (christmas.getDayOfWeek() == DayOfWeek.SATURDAY) {
|
christmas = christmas.minusDays(1);
|
} else if (christmas.getDayOfWeek() == DayOfWeek.SUNDAY) {
|
christmas = christmas.plusDays(1);
|
}
|
holidays.add(christmas);
|
|
return holidays;
|
}
|
|
// ==================== 测试示例 ====================
|
public static void main(String[] args) {
|
// 测试:2025-12-22(周一) ~ 2025-12-28(周日)
|
Calendar cal1 = Calendar.getInstance();
|
cal1.set(2025, Calendar.DECEMBER, 22);
|
Date start = cal1.getTime();
|
|
Calendar cal2 = Calendar.getInstance();
|
cal2.set(2025, Calendar.DECEMBER, 28);
|
Date end = cal2.getTime();
|
|
// 基础版:仅排除周末,共5天(22-26日)
|
int basicDays = countUsStockTradingDays(start, end);
|
// 进阶版:若2025-12-25是圣诞节(周四),则排除,共4天
|
int advancedDays = countUsStockTradingDaysWithHolidays(start, end);
|
|
System.out.println("基础版(仅排周末)交易日数:" + basicDays); // 输出:5
|
System.out.println("进阶版(排周末+节假日)交易日数:" + advancedDays); // 输出:4
|
}
|
}
|