1.数据源
数据来源于百度搜索置顶日历
2.源码:
http调用及数据处理均采用了hutool, 也可以用别的工具。
hutool 依赖如下:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.2</version>
</dependency>
package com.visy.utils;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* @author visy.wang
* @description: 日历数据查询工具
* @date 2023/1/29 15:55
*/
public class CalendarUtil {
private static final String URL_V1 = "https://opendata.baidu.com/api.php";
private static final String URL_V2 = "https://opendata.baidu.com/data/inner";
private static final String JSONP_CALLBACK_NAME = "jsonp_callback";
private static final Logger logger = LoggerFactory.getLogger(CalendarUtil.class);
/**
* 查询日历(V1)
* 注意:返回结果的oDate采用的时区是0时区
* @param year 年,如:2023
* @param month 月,如:2
* @param isJSONP 是否返回JSONP(默认返回JSON)
* @return 返回当前月,上一个月以及下一个月的日历数据
*/
public static JSONArray queryCalendarV1(int year, int month, boolean isJSONP){
//拼接参数
String url = URL_V1 + "?" + getQueryStrV1(year, month, isJSONP);
return queryCalendar(url, isJSONP, json -> {
//仅当状态=0时调用成功
if(!"0".equals(json.getStr("status"))){
logger.info("接口调用失败");
return null;
}
//提取黄历数据
return json.getByPath("data[0].almanac", JSONArray.class);
});
}
/**
* 查询日历(V2)
* 注意:返回结果的oDate采用的时区是0时区
* @param year 年,如:2023
* @param month 月,如:2
* @param isJSONP 是否返回JSONP(默认返回JSON)
* @return 返回当前月,上一个月以及下一个月的日历数据
*/
public static JSONArray queryCalendarV2(int year, int month, boolean isJSONP){
//拼接参数
String url = URL_V2 + "?" + getQueryStrV2(year, month, isJSONP);
return queryCalendar(url, isJSONP, json -> {
Integer resultCode = json.getInt("ResultCode");
//仅当结果码=0时调用成功
if(!Integer.valueOf(0).equals(resultCode)){
logger.info("接口调用失败");
return null;
}
//提取黄历数据
return json.getByPath("Result[0].DisplayData.resultData.tplData.data.almanac", JSONArray.class);
});
}
private static JSONArray queryCalendar(String url, boolean isJSONP, Function<JSONObject, JSONArray> parser){
//拼接参数
logger.info("完整请求地址: {}", url);
//发起调用
String response = HttpUtil.get(url);
logger.info("原始响应结果: {}", response);
//提取JSON
String jsonBody;
if(isJSONP){
//返回结果为JSONP时,去除多余字符,提取JSON部分
int start = response.indexOf("{"), end = response.lastIndexOf("}")+1;
jsonBody = response.substring(start, end);
}else{
jsonBody = response;
}
logger.info("JSON响应体: {}", jsonBody);
//解析返回结果
return parser.apply(JSONUtil.parseObj(jsonBody));
}
private static String getQueryStrV1(int year, int month, boolean isJSONP){
Map<String,Object> params = new HashMap<>();
params.put("tn", "wisetpl");
params.put("format", "json");
params.put("resource_id", "39043");
params.put("t", System.currentTimeMillis());
params.put("query", year+"年"+month+"月");
if(isJSONP){
//不加这个参数返回结果为JSON,加了返回的是JSONP
params.put("cb", JSONP_CALLBACK_NAME);
}
return toQueryStr(params);
}
private static String getQueryStrV2(int year, int month, boolean isJSONP){
Map<String,Object> params = new HashMap<>();
params.put("tn", "reserved_all_res_tn");
params.put("type", "json");
params.put("resource_id", "52109");
params.put("apiType", "yearMonthData");
params.put("query", year+"年"+month+"月");
if(isJSONP){
//不加这个参数返回结果为JSON,加了返回的是JSONP
params.put("cb", JSONP_CALLBACK_NAME);
}
return toQueryStr(params);
}
private static String toQueryStr(Map<String,Object> params){
List<String> kvList = new ArrayList<>();
params.forEach((k, v) -> {
kvList.add(k + "=" + v);
});
return URLUtil.encode(String.join("&", kvList), StandardCharsets.UTF_8);
}
}
3.测试
package com.visy.utils;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
/**
* @author visy.wang
* @date 2024/9/23 11:14
*/
public class CalendarUtilTest {
public static void main(String[] args) {
JSONArray list1 = CalendarUtil.queryCalendarV1(2023, 2, false);
JSONArray list2 = CalendarUtil.queryCalendarV1(2023, 2, true);
JSONArray list3 = CalendarUtil.queryCalendarV2(2023, 2, false);
JSONArray list4 = CalendarUtil.queryCalendarV2(2023, 2, true);
//只打印一行看看
System.out.println(JSONUtil.formatJsonStr(list1.get(0).toString()));
System.out.println("---------------------------------------------");
System.out.println(JSONUtil.formatJsonStr(list2.get(0).toString()));
System.out.println("---------------------------------------------");
System.out.println(JSONUtil.formatJsonStr(list3.get(0).toString()));
System.out.println("---------------------------------------------");
System.out.println(JSONUtil.formatJsonStr(list4.get(0).toString()));
}
}
4.输出
完整请求地址: https://opendata.baidu.com/api.php?t=1727062131671&query=2023%E5%B9%B42%E6%9C%88&format=json&resource_id=39043&tn=wisetpl
原始响应结果: {"status":"0","t":"1727062131671","set_cache_time":"","data":[]}
完整请求地址: https://opendata.baidu.com/api.php?t=1727062132396&query=2023%E5%B9%B42%E6%9C%88&format=json&resource_id=39043&tn=wisetpl&cb=jsonp_callback
原始响应结果: /**/jsonp_callback({"status":"0","t":"1727062132396","set_cache_time":"","data":[]})
完整请求地址: https://opendata.baidu.com/data/inner?query=2023%E5%B9%B42%E6%9C%88&resource_id=52109&tn=reserved_all_res_tn&type=json&apiType=yearMonthData
原始响应结果: {"DispExt":null,"QueryDispInfo":null,"ResultCode":0,"ResultNum":1,"QueryID":"3430800766","Result":[]}
完整请求地址: https://opendata.baidu.com/data/inner?query=2023%E5%B9%B42%E6%9C%88&resource_id=52109&tn=reserved_all_res_tn&type=json&apiType=yearMonthData&cb=jsonp_callback
原始响应结果: jsonp_callback({"DispExt":null,"QueryDispInfo":null,"ResultCode":0,"ResultNum":1,"QueryID":"3430807017","Result":[]})
{
"animal": "虎",
"avoid": "栽种.安门.治病.作灶",
"cnDay": "日",
"day": "1",
"festivalList": "元旦",
"gzDate": "己未",
"gzMonth": "壬子",
"gzYear": "壬寅",
"isBigMonth": "1",
"jiri": "0",
"lDate": "初十",
"lMonth": "腊",
"lunarDate": "10",
"lunarMonth": "12",
"lunarYear": "2022",
"month": "1",
"oDate": "2022-12-31T16:00:00.000Z",
"status": "1",
"suit": "动土.祈福.安床.架马.开厕.祭祀.入殓.成人礼.成服.除服.伐木.结网.开池.求子",
"term": "元旦",
"timestamp": "1672502400",
"type": "h",
"year": "2023",
"yjJumpUrl": "https://mobile.51wnl-cq.com/huangli_tab_h5/?posId=BDSS&STIME=2023-01-01",
"yj_from": "51wnl"
}
---------------------------------------------
{
"animal": "虎",
"avoid": "栽种.安门.治病.作灶",
"cnDay": "日",
"day": "1",
"festivalList": "元旦",
"gzDate": "己未",
"gzMonth": "壬子",
"gzYear": "壬寅",
"isBigMonth": "1",
"jiri": "0",
"lDate": "初十",
"lMonth": "腊",
"lunarDate": "10",
"lunarMonth": "12",
"lunarYear": "2022",
"month": "1",
"oDate": "2022-12-31T16:00:00.000Z",
"status": "1",
"suit": "动土.祈福.安床.架马.开厕.祭祀.入殓.成人礼.成服.除服.伐木.结网.开池.求子",
"term": "元旦",
"timestamp": "1672502400",
"type": "h",
"year": "2023",
"yjJumpUrl": "https://mobile.51wnl-cq.com/huangli_tab_h5/?posId=BDSS&STIME=2023-01-01",
"yj_from": "51wnl"
}
---------------------------------------------
{
"animal": "虎",
"avoid": "栽种.安门.治病.作灶",
"cnDay": "日",
"day": "1",
"festivalInfoList":
[
{
"baikeId": "137017",
"baikeName": "元旦",
"baikeUrl": "https://baike.baidu.com/item/元旦/137017",
"name": "元旦"
}
],
"festivalList": "元旦",
"gzDate": "己未",
"gzMonth": "壬子",
"gzYear": "壬寅",
"isBigMonth": "1",
"jiri": "0",
"lDate": "初十",
"lMonth": "腊",
"lunarDate": "10",
"lunarMonth": "12",
"lunarYear": "2022",
"month": "1",
"oDate": "2022-12-31T16:00:00Z",
"status": "1",
"suit": "动土.祈福.安床.架马.开厕.祭祀.入殓.成人礼.成服.除服.伐木.结网.开池.求子",
"term": "元旦",
"timestamp": "1672502400",
"type": "h",
"year": "2023",
"yjJumpUrl": "https://mobile.51wnl-cq.com/huangli_tab_h5/?posId=BDSS&STIME=2023-01-01",
"yj_from": "51wnl"
}
---------------------------------------------
{
"animal": "虎",
"avoid": "栽种.安门.治病.作灶",
"cnDay": "日",
"day": "1",
"festivalInfoList":
[
{
"baikeId": "137017",
"baikeName": "元旦",
"baikeUrl": "https://baike.baidu.com/item/元旦/137017",
"name": "元旦"
}
],
"festivalList": "元旦",
"gzDate": "己未",
"gzMonth": "壬子",
"gzYear": "壬寅",
"isBigMonth": "1",
"jiri": "0",
"lDate": "初十",
"lMonth": "腊",
"lunarDate": "10",
"lunarMonth": "12",
"lunarYear": "2022",
"month": "1",
"oDate": "2022-12-31T16:00:00Z",
"status": "1",
"suit": "动土.祈福.安床.架马.开厕.祭祀.入殓.成人礼.成服.除服.伐木.结网.开池.求子",
"term": "元旦",
"timestamp": "1672502400",
"type": "h",
"year": "2023",
"yjJumpUrl": "https://mobile.51wnl-cq.com/huangli_tab_h5/?posId=BDSS&STIME=2023-01-01",
"yj_from": "51wnl"
}
5.字段说明:
字段名 | 描述 | 示例值 |
---|---|---|
oDate | 时间(0时区) | 2022-12-31T16:00:00.000Z |
year | 年(阳历) | 2023 |
month | 月(阳历) | 1 |
day | 日(阳历) | 1 |
lunarYear | 年(农历) | 2022 |
lunarMonth | 月(农历) | 12 |
lunarDate | 日(农历) | 10 |
lMonth | 月(农历描述) | 腊 |
lDate | 日(农历描述) | 初十 |
gzYear | 年-干支 | 壬寅 |
gzMonth | 月-干支 | 壬子 |
gzDate | 日-干支 | 己未 |
animal | 生肖年 | 虎 |
cnDay | 星期描述(一二三四五六日) | 日 |
isBigMonth | 是否是农历大月(1-是) | 1 |
term | 节假日描述(含节气) | 元旦 |
status | 节假日状态(1-休假,2-补班) | 1 |
type | 节假日类型 | h |
yj_from | 数据来源 | 51wnl |
suit | 宜 | 动土.祈福.安床.架马.开厕.祭祀.入殓.成人礼.成服.除服.伐木.结网.开池.求子 |
avoid | 忌 | 栽种.安门.治病.作灶 |
6.版本说明
工具提供了两个版本,请求地址不一样,参数略微不同
两个版本都是百度搜索置顶日历,V1是早期的地址, V2是新地址
V1请求地址:https://opendata.baidu.com/api.php
V2请求地址:https://opendata.baidu.com/data/inner
7.其他方式查询法定节假日
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
public static void main(String[] args) throws ParseException {
List<JSONObject> holidayList = getHolidayList(2025);
System.out.println("节假日天数:"+holidayList.size());
holidayList.forEach(System.out::println);
}
public static List<JSONObject> getHolidayList(int year){
String url = String.format("https://timor.tech/api/holiday/year/%s", year);
String respBody = HttpUtil.get(url);
JSONObject result = JSON.parseObject(respBody);
List<JSONObject> holidayList = new ArrayList<>();
if(Integer.valueOf(0).equals(result.getInteger("code"))){
JSONObject holiday = result.getJSONObject("holiday");
for (Map.Entry<String, Object> entry : holiday.entrySet()) {
JSONObject day = (JSONObject)entry.getValue();
if(day.getBoolean("holiday")){
//法定节假日
JSONObject dayJson = new JSONObject();
dayJson.put("date", Integer.valueOf(day.getString("date").replace("-", "")));
dayJson.put("holiday", day.getString("name"));
holidayList.add(dayJson);
}else{
//法定节假日补班
}
}
holidayList.sort(Comparator.comparing(d -> d.getInteger("date")));
}
return holidayList;
}
节假日天数:28
{"date":20250101,"holiday":"元旦"}
{"date":20250128,"holiday":"除夕"}
{"date":20250129,"holiday":"初一"}
{"date":20250130,"holiday":"初二"}
{"date":20250131,"holiday":"初三"}
{"date":20250201,"holiday":"初四"}
{"date":20250202,"holiday":"初五"}
{"date":20250203,"holiday":"初六"}
{"date":20250204,"holiday":"初七"}
{"date":20250404,"holiday":"清明节"}
{"date":20250405,"holiday":"清明节"}
{"date":20250406,"holiday":"清明节"}
{"date":20250501,"holiday":"劳动节"}
{"date":20250502,"holiday":"劳动节"}
{"date":20250503,"holiday":"劳动节"}
{"date":20250504,"holiday":"劳动节"}
{"date":20250505,"holiday":"劳动节"}
{"date":20250531,"holiday":"端午节"}
{"date":20250601,"holiday":"端午节"}
{"date":20250602,"holiday":"端午节"}
{"date":20251001,"holiday":"国庆节"}
{"date":20251002,"holiday":"国庆节"}
{"date":20251003,"holiday":"国庆节"}
{"date":20251004,"holiday":"国庆节"}
{"date":20251005,"holiday":"国庆节"}
{"date":20251006,"holiday":"中秋节"}
{"date":20251007,"holiday":"国庆节"}
{"date":20251008,"holiday":"国庆节"}