前言
最近有需要研究阴历和阳历互相转换的问题。因此找到两个库carbon和solarlunar
但是感觉计算出来的总是不太放心,而且也会占用计算资源。我的想法是通过接口获取现成的阴历和阳历数据,存到本地数据库,这样查询的时候一步到位。
方案
我通过百度搜索
万年历
,抓取网页请求得到百度的一个接口正好可以获取万年历的信息,还是挺全面的。
因此我写代码实现了将百度万年历的数据获取,然后存入数据库的代码。
下面是 calendar.go
的代码,主要使用 gorm
创建表,以及写入拉取的数据。
package calendar
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
type (
PerpetualCalendar struct {
//Status string json:"status"
//T string json:"t"
//SetCacheTime string json:"set_cache_time"
Data []PerpetualCalendarData json:"data"
}
PerpetualCalendarData struct {
//ExtendedLocation string json:"ExtendedLocation"
//OriginQuery string json:"OriginQuery"
//SiteID int json:"SiteId"
//StdStg int json:"StdStg"
//StdStl int json:"StdStl"
//SelectTime int json:"_select_time"
//UpdateTime string json:"_update_time"
//Version int json:"_version"
//Appinfo string json:"appinfo"
//CambrianAppid string json:"cambrian_appid"
//DispType int json:"disp_type"
//Fetchkey string json:"fetchkey"
//Key string json:"key"
//Loc string json:"loc"
//Resourceid string json:"resourceid"
//RoleID int json:"role_id"
//Showlamp string json:"showlamp"
//Tplt string json:"tplt"
//URL string json:"url"
Almanac []PerpetualCalendarAlmanac json:"almanac"
}
PerpetualCalendarAlmanac struct {
Id int gorm:"primarykey"
// 自增主键
Animal string json:"animal" gorm:"column:animal;not null;size:4"
// 生肖
Suit string json:"suit" gorm:"column:suit;not null"
// 宜
Avoid string json:"avoid" gorm:"column:avoid;not null"
// 忌
CnDay string json:"cnDay" gorm:"column:cnDay;not null;size:4"
// 星期
Day int json:"day,string" gorm:"column:day;not null;uniqueIndex:Solar"
// 阳历日
Month int json:"month,string" gorm:"column:month;not null;uniqueIndex:Solar"
// 阳历月
Year int json:"year,string" gorm:"column:year;not null;uniqueIndex:Solar"
// 阳历年
GzDate string json:"gzDate" gorm:"column:gzDate;not null;size:8"
// 干支日
GzMonth string json:"gzMonth" gorm:"column:gzMonth;not null;size:8"
// 干支月
GzYear string json:"gzYear" gorm:"column:gzYear;not null;size:8"
// 干支年
IsBigMonth string json:"isBigMonth" gorm:"-"
// json取数据,忽略gorm
IsBigMonthBool bool gorm:"column:isBigMonth;not null;default:0"
// 是否为阴历大月
LDate string json:"lDate" gorm:"column:lDate;not null;size:4"
// 阴历日,汉字
LMonth string json:"lMonth" gorm:"column:lMonth;not null;size:4"
// 阴历月,汉字,带'闰'字表示闰月
LunarDate int json:"lunarDate,string" gorm:"column:lunarDate;not null;index:Lunar"
// 阴历日,数字
LunarMonth int json:"lunarMonth,string" gorm:"column:lunarMonth;not null;index:Lunar"
// 阴历月,数字
LunarYear int json:"lunarYear,string" gorm:"column:lunarYear;not null;index:Lunar"
// 阴历年,数字
ODate time.Time json:"oDate" gorm:"column:oDate;not null"
// ODate.Local(),阳历当天0点
Term string json:"term,omitempty" gorm:"column:term;not null"
// 如'除夕','万圣节','三伏'等
Desc string json:"desc,omitempty" gorm:"column:desc;not null"
// 如'腊八节','下元节'等
Type string json:"type,omitempty" gorm:"column:type;not null;size:2"
// 各种和节日有关的类型
Value string json:"value,omitempty" gorm:"column:value;not null"
// 如'国际残疾人日'等
Status int json:"status,string,omitempty" gorm:"column:status;not null;default:0"
// 0 工作日,1 休假,2 上班,3 周末
}
)
func (PerpetualCalendarAlmanac) TableName() string {
return "perpetualCalendarAlmanac"
}
// GetPerpetualCalendar 返回[前一个月,本月,后一个月]的数据
func GetPerpetualCalendar(year, mouth int) ([]PerpetualCalendarAlmanac, error) {
u := url.Values{}
u.Add("tn", "wisetpl")
u.Add("format", "json")
u.Add("resource_id", "39043") // 这个用浏览器请求后得到的
u.Add("query", fmt.Sprintf("%d年%d月", year, mouth))
u.Add("t", strconv.FormatInt(time.Now().UnixMilli(), 10))
urls := "https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?" + u.Encode()
resp, err := http.Get(urls) // 百度这个接口可能现在请求速度,所以可能报错
if err != nil {
return nil, err
}
defer resp.Body.Close()
var ret PerpetualCalendar // 需要将gbk转为utf8
err = json.NewDecoder(transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())).Decode(&ret)
if err != nil {
return nil, err
}
if len(ret.Data) != 1 { // 该数组目前只会有一个
return nil, errors.New("get Data error")
}
for i, v := range ret.Data[0].Almanac {
// 赋值大月
ret.Data[0].Almanac[i].IsBigMonthBool = v.IsBigMonth == "1"
if v.Status == 0 && (v.CnDay == "六" || v.CnDay == "日") {
ret.Data[0].Almanac[i].Status = 3 // 不是特殊类型,且为周末则赋值
}
}
return ret.Data[0].Almanac, nil
}
func SaveCalendar(dsnSrc string) error {
ts := time.Now()
defer func() {
fmt.Println(time.Since(ts))
}()
iDsn := strings.Index(dsnSrc, ":")
if iDsn < 0 {
return errors.New("dsn error")
}
var gormOpen gorm.Dialector
switch strings.ToLower(dsnSrc[:iDsn]) {
case "mysql":
gormOpen = mysql.Open(dsnSrc[iDsn+1:])
case "sqlite":
gormOpen = sqlite.Open(dsnSrc[iDsn+1:])
default:
return errors.New("just support mysql or sqlite")
}
db, err := gorm.Open(gormOpen, &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
return err
}
res := db.Exec("DROP table if exists " + PerpetualCalendarAlmanac{}.TableName())
if res.Error != nil {
return res.Error
}
// 每次重建数据表
err = db.AutoMigrate(PerpetualCalendarAlmanac{})
if err != nil {
return err
}
// 起止时间按照百度万年历得到
start := time.Date(1900, time.February, 1, 0, 0, 0, 0, time.Local)
end := time.Date(2050, time.December, 1, 0, 0, 0, 0, time.Local)
wg := sync.WaitGroup{}
// 由于每次查询包含前一个月,当月,下个月,因此每次都增加3个月进行查询
for ; start.Before(end); start = start.AddDate(0, 3, 0) {
wg.Add(1)
y, m, _ := start.Date()
go func(y, m int) {
defer wg.Done()
for { // 使用协程并发请求,提高速度,出现错误时重试
data, err := GetPerpetualCalendar(y, m)
if err != nil {
fmt.Println("GetPerpetualCalendar", y, m, err)
continue // 报错重试,直到成功
}
res := db.Create(&data)
if res.Error != nil {
fmt.Println("Create", y, m, res.Error)
continue // 报错重试,直到成功
}
break
}
}(y, int(m))
}
wg.Wait()
return nil
}
下面是 main.go
,根据传入的参数,选择是保存在 mysql
还是 sqlite
中。
package main
import (
calendar "interesting/perpetual_calendar"
)
func main() {
dsn := "mysql:user:pass@tcp(127.0.0.1:3306)/janbar?charset=utf8mb4&parseTime=True&loc=Local"
//dsn := "sqlite:test.db"
err := calendar.SaveCalendar(dsn)
if err != nil {
panic(err)
}
}
结果展示
如下图所示,是存入的数据,已经为阳历年月日创建唯一联合索引,阴历年月日因为存在闰月会重复,因此只创建了联合索引。
导出MySQL数据 下载
查询语句可以按照下面的来做。大部分属性已经按照我的理解写到注释里面了,可以结合百度的万年历看看,展示在哪个位置吧。
查询阳历 2021-09-16 号的数据,结果有对应的阴历情况
SELECT *FROM perpetualCalendarAlmanac WHERE year=2021 AND month=9 AND day=16
查询阴历 2021-09-03 号的数据,结果有对应阳历的情况
SELECT *FROM perpetualCalendarAlmanac WHERE lunarYear=2021 AND lunarMonth=9 AND lunarDate=3
总结
这个接口调用,和存数据没啥难度。主要是让我加深对gorm等库的使用。
当然最主要是我想实现按照阴历定时的cron规则,结合cron库来搞。Original: https://www.cnblogs.com/janbar/p/15293288.html
Author: janbar
Title: 将百度万年历存入自己的数据库
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516596/
转载文章受原作者版权保护。转载请注明原作者出处!