Java 每年节假日获取
参考链接
说明
自动获取每年节假日,因为国家每一年的假期可能不一样,是人为设定的;所以需要每年生成一次或者在项目中写定时任务实现。因为我这里的需求稍微简单一点,维护的时候执行一次就可以了。我是参照上面链接的文章再根据自己的需求稍有修改和完善。问题之处欢迎大家指出。不说废话直接来代码。
代码
环境依赖,这里说明一下,请注意依赖版本号,因为我在用最新依赖时发现里面大不一样,我没有找到完整的使用案例和API,所以这里用了一个老版本(也是使用人数最多的版本了)。
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.23</version>
</dependency>
实体类
package ***.**.utils.holidays;
import java.io.Serializable;
import java.util.Date;
/**
* @ProjectName: ****
* @Package: ***.**.utils.holidays
* @ClassName: ChinaDate
* @Author: 小王
* @Date: 2022/1/18 13:55
* @Description: 定义一个中国日期类
*/
public class ChinaDate implements Serializable {
/**
* 公历时间
*/
private Date solarDate;
/**
* 农历日
*/
private String lunar;
/**
* 公历日
*/
private String solar;
/**
* 是否是 休
*/
private boolean isVacation = false;
/**
* 如果是 休情况下的假期名字
*/
private String vacationName = "工作日";
private String vacationType="0";//类型
private String practicalMonth;//实际月份
private String formatDate;//yyyyMMdd格式日期
/**
* 是否是 班
*/
private boolean isWorkFlag = false;
private boolean isSaturday = false;
private boolean isSunday = false;
public ChinaDate() {
}
public ChinaDate(Date solarDate, String lunar, String solar, boolean isVacation, String vacationName, String vacationType, String practicalMonth, String formatDate, boolean isWorkFlag, boolean isSaturday, boolean isSunday) {
this.solarDate = solarDate;
this.lunar = lunar;
this.solar = solar;
this.isVacation = isVacation;
this.vacationName = vacationName;
this.vacationType = vacationType;
this.practicalMonth = practicalMonth;
this.formatDate = formatDate;
this.isWorkFlag = isWorkFlag;
this.isSaturday = isSaturday;
this.isSunday = isSunday;
}
public String getVacationName() {
return vacationName;
}
public void setVacationName(String vacationName) {
this.vacationName = vacationName;
}
public String getVacationType() {
return vacationType;
}
public void setVacationType(String vacationType) {
this.vacationType = vacationType;
}
public String getPracticalMonth() {
return practicalMonth;
}
public void setPracticalMonth(String practicalMonth) {
this.practicalMonth = practicalMonth;
}
public String getFormatDate() {
return formatDate;
}
public void setFormatDate(String formatDate) {
this.formatDate = formatDate;
}
public Date getSolarDate() {
return solarDate;
}
public void setSolarDate(Date solarDate) {
this.solarDate = solarDate;
}
public String getLunar() {
return lunar;
}
public void setLunar(String lunar) {
this.lunar = lunar;
}
public String getSolar() {
return solar;
}
public void setSolar(String solar) {
this.solar = solar;
}
public boolean isVacation() {
return isVacation;
}
public void setVacation(boolean vacation) {
isVacation = vacation;
}
public boolean isWorkFlag() {
return isWorkFlag;
}
public void setWorkFlag(boolean workFlag) {
isWorkFlag = workFlag;
}
public boolean isSaturday() {
return isSaturday;
}
public void setSaturday(boolean saturday) {
isSaturday = saturday;
}
public boolean isSunday() {
return isSunday;
}
public void setSunday(boolean sunday) {
isSunday = sunday;
}
@Override
public String toString() {
return "ChinaDate{" +
"solarDate=" + solarDate +
", lunar='" + lunar + '\'' +
", solar='" + solar + '\'' +
", isVacation=" + isVacation +
", vacationName='" + vacationName + '\'' +
", vacationType='" + vacationType + '\'' +
", practicalMonth='" + practicalMonth + '\'' +
", formatDate='" + formatDate + '\'' +
", isWorkFlag=" + isWorkFlag +
", isSaturday=" + isSaturday +
", isSunday=" + isSunday +
'}';
}
}
基础工具类
定义解析页面数据的工具类,主要用于基础数据提取,后面所有方法均在此类上使用
package ***.**.utils.holidays;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.DomNodeList;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.yx.utils.common.DateUtils;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @ProjectName: ****
* @Package: ***.**.utils.holidays
* @ClassName: holidays
* @Author: 小王
* @Date: 2022/1/18 15:04
* @Description: 解析网页,获取年详情:
*/
public class holidays {
private static String url="http://hao.360.com/rili/";//要解析的网页链接
private static WebClient webClient = new WebClient();//网页请求对象
private static HtmlPage page=null;//页面内容对象
private static String datesId="M-dates";//DOM对象 ,日期元素id
private static String datesLi="li";//DOM对象 ,日期元素标签名称
private static String toolId="M-controls";//DOM对象 ,日期工具头元素id
private static String toolDiv="div";//DOM对象 ,日期工具头标签名称
private static String toolClass="control-bar mouth-bar";//DOM对象 ,被点击元素的class名称
private static String nextLabel="a";//DOM对象 ,下个月元素的标签名称
private static String attributeName="class";//DOM对象 ,公用属性名称
private static String nextAttributeValue="next";//DOM对象 ,下个月元素的属性值
private static String lastLabel="a";//DOM对象 ,上个月元素的标签名称
private static String lastAttributeValue="prev";//DOM对象 ,上个月元素的属性值
//静态代码块初始化链接和对象
static {
try {
//htmlunit 对css和javascript的支持不好,所以直接关闭
//webClient.getOptions().setCssEnabled(false);
//webClient.getOptions().setJavaScriptEnabled(false);
//获取网页对象
page = webClient.getPage(url);
//最大等待30秒
for(int k = 0; k < 30; k++){
//由于网络原因可能会导致页面解析不全所以设置等待
if(!page.getElementById(datesId).asText().equals("")) break;
Thread.sleep(1000);
}
String titleText = page.getTitleText();
System.err.println("get date from "+url+" success~,The page title is:"+titleText);
} catch (Exception e) {
System.err.println("get date from "+url+" error~");
e.printStackTrace();
}
}
/**
* @author 小王
* @Date 2022/1/19 10:30
* @param null:
* @return: null
* @Description: 获取当前页日期元素集合
*/
public static List<HtmlElement> getHtmlElements(){
DomElement elementById = page.getElementById(datesId);
DomNodeList<HtmlElement> htmlElements = page.getElementById(datesId).getElementsByTagName(datesLi);
if(htmlElements.size()<=0){
Iterator<HtmlElement> iterator = elementById.getHtmlElementDescendants().iterator();
while (iterator.hasNext()){
htmlElements.add(iterator.next());
// System.err.println("============iterator========"+iterator.next().asText());
}
}
return htmlElements;
}
/**
* @author 小王
* @Date 2022/1/19 10:53
* @param null:
* @return: null
* @Description: 获取下一页日期元素集合
*/
public static List<HtmlElement> getNextPage(){
try {
DomElement elementHead = page.getElementById(toolId);
List<HtmlElement> byXPath = elementHead.getElementsByTagName(toolDiv);
if(byXPath.size()>0){
for (HtmlElement element:byXPath){
if(Objects.equals(toolClass,element.getAttribute(attributeName))){
List<HtmlElement> elementsByAttribute = element.getElementsByAttribute(nextLabel, attributeName, nextAttributeValue);
HtmlPage click = elementsByAttribute.get(0).click();
page = click.getPage();
return getHtmlElements();
}
}
}
} catch (IOException e) {
System.err.println("get next page date from "+url+" error~");
e.printStackTrace();
}
return null;
}
/**
* @author 小王
* @Date 2022/1/19 15:01
* @param null:
* @return: null
* @Description: 获取上个月
*/
public static List<HtmlElement> getLastPage(){
try {
DomElement elementHead = page.getElementById(toolId);
List<HtmlElement> byXPath = elementHead.getElementsByTagName(toolDiv);
if(byXPath.size()>0){
for (HtmlElement element:byXPath){
if(Objects.equals(toolClass,element.getAttribute(attributeName))){
List<HtmlElement> elementsByAttribute = element.getElementsByAttribute(lastLabel, attributeName, lastAttributeValue);
HtmlPage click = elementsByAttribute.get(0).click();
page = click.getPage();
return getHtmlElements();
}
}
}
} catch (IOException e) {
System.err.println("get next page date from "+url+" error~");
e.printStackTrace();
}
return null;
}
}
封装工具类
这里因为目前项目需求用的不深,所以没有继续优化了。当跨年份太长了之后对网络要求较高,实际上有另一种高效率的实现但比较麻烦。目前我这里影响不是很大所以暂时没写,有需要的可以联系我。
执行效率低的代码:
package ***.**.utils.holidays;
import com.alibaba.fastjson.JSON;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.DomNodeList;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.yx.utils.common.DateUtils;
import com.yx.utils.common.StringUtil;
import org.springframework.util.CollectionUtils;
import javax.swing.text.html.HTMLDocument;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Year;
import java.util.*;
/**
* @ProjectName: ****
* @Package: ***.**.utils.holidays
* @ClassName: holidaysUtils
* @Author: 小王
* @Date: 2022/1/18 15:04
* @Description: 全年节假日工具类,支持最大到本年:
*/
public class holidaysUtils {
private static String lunarElementName="span";//DOM对象 ,农历日期标签名称
private static String attributeName="class";//DOM对象 ,公用属性名称
private static String lunarAttributeValue="lunar";//DOM对象 ,农历日期属性值
private static String solarElementName="div";//DOM对象 ,农历日期标签名称
private static String solarAttributeValue="solar";//DOM对象 ,公历日期属性值
private static String solarDateAttributeValue="date";//DOM对象 ,公历日期结果属性名称
private static String patternStr="yyyy/MM/dd";//日期解析字符串
private static String patternMonthStr="MM";//日期解析月份字符串
private static String staClass="weekend";//周六属性值
private static String sunPrefixClass="last";//周日前缀属性值
private static String sunClass="last weekend";//周日属性值
private static String workClass="work";//工作日属性值
private static String vacationClass="vacation";//节假日属性值
private static String weekendName="周六日";//周六日名称
private static String weekendType="1";//周六日类型值
private static String workName="工作日";//周六日名称
private static String workType="0";//周六日类型值
private static String vacationName="";//节假日名称
private static String vacationType="2";//节假日类型值
private static int pioneerYear=1901;//最小可执行年份
private static int latestYear=2100;//最大可执行年份
/**
* @author 小王
* @Date 2022/1/19 11:18
* @param null:
* @return: null
* @Description: 最近一次的可见的假期名
*/
public static String getVocationName(List<HtmlElement> htmlElements, String date) {
String rst = "";
boolean pastTimeFlag = false;
Date paramDate = DateUtils.parseDate(date, patternStr);
if (new Date().getTime() >= paramDate.getTime()) {
pastTimeFlag = true;
}
//first step //jugde if can get vocation name from html page
for (int i = 0; i < htmlElements.size(); i++) {
HtmlElement element = htmlElements.get(i);
if (element.getAttribute(attributeName).indexOf(vacationClass) != -1) {
boolean hitFlag = false;
String voationName = "";
for (; i < htmlElements.size(); i++) {
HtmlElement elementTmp = htmlElements.get(i);
String liDate = elementTmp.getAttribute(solarDateAttributeValue);
List<HtmlElement> lunar = elementTmp.getElementsByAttribute(lunarElementName, attributeName, lunarAttributeValue);
String lanarText = lunar.get(0).asText();
if (lanarText.equals("元旦")) {
voationName = "元旦";
} else if (lanarText.equals("除夕") || lanarText.equals("春节")) {
voationName = "春节";
} else if (lanarText.equals("清明")) {
voationName = "清明";
} else if (lanarText.equals("国际劳动节")) {
voationName = "国际劳动节";
} else if (lanarText.equals("端午节")) {
voationName = "端午节";
} else if (lanarText.equals("中秋节")) {
voationName = "中秋节";
} else if (lanarText.equals("国庆节")) {
voationName = "国庆节";
}
if (liDate.equals(date)) {
hitFlag = true;
}
if (elementTmp.getAttribute(attributeName).indexOf(vacationClass) == -1) {
break;
}
}
if (hitFlag == true && !voationName.equals("")) {
rst = voationName;
break;
}
} else {
continue;
}
}
if (rst.equals("")) {
System.out.println("warning: fail to get vocation name from html page.");
rst = holidaysUtils.vacationName;
} else if (pastTimeFlag == true) {
//更新《当前时间,且最近一次的可见的假期名
holidaysUtils.vacationName = rst;
}
return rst;
}
/**
* @author 小王
* @Date 2022/1/19 11:12
* @param null:
* @return: null
* @Description: 获取本月数据
*/
public static List<ChinaDate> getCurrentDateInfo(List<HtmlElement> htmlElements){
List<ChinaDate> dateList=new ArrayList<>();
if(!CollectionUtils.isEmpty(htmlElements)){
for(HtmlElement element : htmlElements){
ChinaDate chinaDate = new ChinaDate();
List<HtmlElement> lunar = element.getElementsByAttribute(lunarElementName, attributeName, lunarAttributeValue);
List<HtmlElement> solar = element.getElementsByAttribute(solarElementName, attributeName, solarAttributeValue);
chinaDate.setLunar(lunar.get(0).asText());
chinaDate.setSolar(solar.get(0).asText());
chinaDate.setSolarDate(DateUtils.parseDate(element.getAttribute(solarDateAttributeValue),patternStr));
chinaDate.setFormatDate(DateUtils.format(chinaDate.getSolarDate(),DateUtils.DATEPATTERN));
chinaDate.setPracticalMonth(DateUtils.format(chinaDate.getSolarDate(),patternMonthStr));
if(element.getAttribute(attributeName).indexOf(staClass)!=-1 &&
element.getAttribute(attributeName).indexOf(sunPrefixClass)==-1