一、获取最近交易日
为了方便,我将代码写在一个类下,以供参考:
package test;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class Test {
public static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
// 待确认交易日:15点之前取当前日期,15点之后取下一交易日
String nextDay = sdf.format(new Date());
if (cal.get(Calendar.HOUR_OF_DAY) >= 15) {
if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) {
cal.add(Calendar.DAY_OF_MONTH, 3);
nextDay = sdf.format(cal.getTime());
} else {
nextDay = getNextDay(nextDay);
}
}
nextDay = "20150930";
// 时间必须要从小到大排序,否则会出问题
String[] a = {"20150930", "20151002", "20151003", "20151005", "20151006"};
List<String> holidayList = Arrays.asList(a);
System.out.println("有效的最近交易日nextDay:" + getDayAfterHoliday(nextDay, holidayList));
}
public static String getDayAfterHoliday(String nextDay, List<String> holidayList) {
if (null != holidayList) {
for (int i = 0; i < holidayList.size(); i++) {
if (holidayList.get(i).equals(nextDay)) {
nextDay = getDayAfterHoliday(getNextDay(nextDay), holidayList);
}
}
}
nextDay = getDayExceptWeekend(nextDay);
return nextDay;
}
/**
* 获取下一天
*
* @param day 当前日期yyyyMMdd
* @return
*/
public static String getNextDay(String day) {
Calendar cal = formatYYYYMMDD(day);
cal.add(Calendar.DAY_OF_MONTH, 1);
return sdf.format(cal.getTime());
}
/**
* 如果当前日期处于周末,则返回下周一
*
* @param day 当前日期yyyyMMdd
* @return
*/
public static String getDayExceptWeekend(String day) {
Calendar cal = formatYYYYMMDD(day);
if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
cal.add(Calendar.DAY_OF_MONTH, 2);
} else if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
cal.add(Calendar.DAY_OF_MONTH, 1);
}
return sdf.format(cal.getTime());
}
/**
* 将字符串类型日期转换为Calendar
*
* @param day 当前日期yyyyMMdd
* @return
*/
public static Calendar formatYYYYMMDD(String day) {
Calendar cal = Calendar.getInstance();
cal.set(Integer.parseInt(day.substring(0, 4)), (Integer.parseInt(day.substring(4, 6)) - 1), Integer.parseInt(day.substring(6)));
return cal;
}
}
下面稍微讲解一下实际项目中大致的思路。
1、需求:
获取最近交易日。根据当前时间,如果当天为交易日且股市还未收盘,则显示为当天,否则获取下一交易日。
2、已知:
法定交易时间为9:00 - 11:30和13:00-15:00
国内的股市交易日不包括国家法定假日和周六周日。
国家法定假日不会在年初就确定下来,所以法定假日的数据需要手动维护。
3、简单分析:
首先, 法定假日的数据要手动维护,那么可以在数据库(此例使用MYSQL)专门建一张表holiday来维护这些假日数据。考虑到周末可以通过程序来判断,所以就不准备存储在表中,这样也减少了holiday表的管理工作。
DROP TABLE IF EXISTS `holiday`;
CREATE TABLE `holiday` (
`id` int (11) NOT NULL AUTO_INCREMENT,
`yyyymmdd` varchar (8) NOT NULL COMMENT '年月日(格式20151001)' ,
`description` varchar (255) DEFAULT NULL ,
`status` varchar (1) NOT NULL DEFAULT '1' COMMENT '0有效 1被禁用' ,
`create_time` varchar (30) DEFAULT NULL ,
`modify_time` varchar (30) DEFAULT NULL ,
`create_by` varchar (100) DEFAULT NULL ,
`modify_by` varchar (100) DEFAULT NULL ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT= '法定节日' ;
这时这个假日数据可能需要一个后台管理系统的页面来维护,这里不做讨论,先手动插入几条数据。
INSERT INTO `holiday` VALUES ( '5' , '20151001' , '国庆节放假1' , '0' , '2015-09-17 10:40:24' , '2015-09-17 10:57:12' , 'admin' , 'admin' );
INSERT INTO `holiday` VALUES ( '6' , '20151002' , '国庆节放假2' , '0' , '2015-09-17 10:40:24' , '2015-09-17 10:59:04' , 'admin' , 'admin' );
INSERT INTO `holiday` VALUES ( '7' , '20151003' , '国庆节放假3' , '0' , '2015-09-17 10:40:24' , '2015-09-17 11:03:07' , 'admin' , 'admin' );
INSERT INTO `holiday` VALUES ( '8' , '20151004' , '国庆节放假' , '0' , '2015-09-17 10:41:29' , null , 'admin' , null );
INSERT INTO `holiday` VALUES ( '9' , '20151005' , '国庆节放假' , '0' , '2015-09-17 10:41:37' , null , 'admin' , null );
INSERT INTO `holiday` VALUES ( '11' , '20151006' , '国庆节放假' , '0' , '2015-09-17 10:42:11' , null , 'admin' , null );
INSERT INTO `holiday` VALUES ( '12' , '20151007' , '国庆节放假' , '0' , '2015-09-17 10:42:19' , null , 'admin' , null );
注:表里面只存假期,不需要保存周六周日的时间,因为程序里面进行了判断。
二、获取接下来的第N个交易日(N为整数,允许正负)
以上的解决思路解决了获取下一个交易日,同时还用到了递归,这对于程序来说是不好的习惯,因为会严重拖慢程序执行效率。
这时如果需求变动成获取接下来的第N个交易日,为了实现程序的复用,那么就要多次调用这个获取下一个交易日的方法,这就会使得程序效率成倍的降低。所以需要改变一下思路。
需求:
1.获取最近交易日。根据当前时间,如果当天为交易日且股市还未收盘,则显示为当天,否则获取下一交易日。
2.支持获取上一交易日和下一交易日
分析:
1.假设当前日期为D;
2.如果需要获取接下来的第5个交易日,那么如果把当前时间加上5天,也就是(D + 5);
3.这个(D + 5)不一定是我们的真实答案,这5天中间有周末的同时可能还有国家规定的一些节日假期;
4.这时我们需要找出这中间假期的天数M1,然后算出(D + 5 + M1);
5.但是从(D + 5)到(D + 5 + M1)中又有可能存在假期,所以需要再次找出假期天数M2……
最后,当(D + 5 + M1 + … + Mn)到(D + 5 + M1 + … + Mn + Mn1)之间的假期天数Mn1为零时,(D + 5 + M1 + … + Mn + Mn1)即为结果。
这里可以看到,取假期天数成了问题的关键。仔细考虑了下,准备给holiday表把周末的日期也加入,从而减少程序的复杂性。
先上代码(考虑到MVC编程繁复的结构,这里只展示下Service的代码,算是伪代码吧):
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dao.HolidayDao;
import com.utils.DateUtils;
import com.utils.AndCondition;
@Service
public class HolidayService {
@Autowired
private HolidayDao holidayDao;
/**
* 获得自startDate开始的第days个交易日(下一交易日即days为1,上一交易日为-1)
*
* @param startDate
* @param days
* @return
*/
public Date getTradingDate(Date startDate, int days) {
Date tradingDate = startDate;
if (0 == days) {
AndCondition andCondition = new AndCondition();
andCondition.put("yyyymmdd", "=", tradingDate);
if (0 < holidayDao.countHoliday(andCondition)) {
// 判断当天是否为交易日,不是则取下一交易日,未考虑取历史交易日
getTradingDate(tradingDate, 1);
}
} else {
// 上days交易日或下days交易日
days = Math.abs(days);
while (days > 0) {
tradingDate = DateUtils.add(startDate, days, Calendar.DATE);
days = getHolidaysNum(startDate, tradingDate);
startDate = tradingDate;
}
}
return tradingDate;
}
/**
* 获得startDate至endDate之间的假期数
*
* @param startDate
* @param endDate
* @return
*/
private int getHolidaysNum(Date startDate, Date endDate) {
AndCondition andCondition = new AndCondition();
if (DateUtils.compare(startDate, endDate) < 0) {
// startDate < endDate
andCondition.put("yyyymmdd", ">", startDate);
andCondition.put("yyyymmdd", "<=", endDate);
} else {
andCondition.put("yyyymmdd", ">=", endDate);
andCondition.put("yyyymmdd", "<", startDate);
}
return holidayDao.countHoliday(andCondition);
}
}
这段代码有一个小遗憾,就是如果获取最近的交易日不能取历史的交易日,而是只能取当日或下一交易日。
****************************************END****************************************
该博客介绍了如何使用Java获取最近及未来的交易日。针对获取最近交易日的需求,考虑到股市交易时间及法定节假日,通过维护数据库中的holiday表来排除非交易日。对于获取第N个交易日,采用递归方式可能导致效率低下,故需要优化算法,避免重复调用。文章提供了部分伪代码作为实现思路的示例。
1565





