『自己構建節假日API』
RickAndMorty.png
大家好,我叫謝偉,是一名程式員。
之前梳理了一些內建庫的學習,收到了一些評論,絕大多數評論都在直指一個問題:為什麼梳理這些無關痛癢的內建庫?
好吧,看上去確實都是些簡單的內建庫的梳理。主要原因是為了: ofollow,noindex">《後端工程師的攻略》 這個系列,從零起步,教程到這個地步,看上去是需要提高難度了。後續加以改善。另外一個原因,其實是想告訴初學者,內建庫的很多程式碼組織方式,程式碼的編寫方式指的學習、借鑑、參考。
這期是我之前準備的,趁著這期還是放送出來吧。
核心很簡單:懂 Go 的基本語法,會使用內建庫的 time, 基本能搞到這些。
大綱:
前段日子專案中需要使用的國家規定的節假日,所以需要獲取這些資料。其實獲取這些資料的方式也很多:
- 比如比較笨的方式:搜尋引擎,手動整理
- 使用一些免費開放的第三方節假日 API: 不穩定,雖然這些資料也不需要頻繁的使用
- 使用一些付費的第三方節假日 API:付費
於是本著簡潔的方式,編寫這麼一個節假日的庫。
要求:
- 簡單的功能
- 簡單的API
1. 資料獲取
資料來源需要可靠,所以需要尋找官方的通知來源。
一般的方式呢,就是網頁資料抓取,解析出得到的資料。
這是第一步,獲取資料;當然,很多網站都可以找到這些資訊,這裡僅僅舉例。
2. 定義結構體
關於節假日,我們最需要知道的是什麼資訊?
- 名稱
- 時間安排
基於此,可以這麼設計結構體:
type OneCollection struct { Startstring `json:"start"` Endstring `json:"end"` ChName string `json:"ch_name"` EnName string `json:"en_name"` }
包括:
- 中文名稱
- 英文名稱
- 開始時間
- 結束時間
關於節假日名稱呢,國家法定的節日是這麼幾個:元旦、春節、清明、端午、勞動、中秋、國慶
借鑑許多內建庫的處理方式:這種固定的資料的處理,可以使用列舉型別:
const ( NewYearDay = iota SpringFestivalDay TombSweepingDay LaborDay DragonBoatFestivalDay NationalDay MidAutumnFestivalDay ) var ChHolidays = [...]string{ "元旦", "春節", "清明節", "勞動節", "端午節", "中秋節", "國慶節", } var EnHolidays = [...]string{ "New Year\\'s Day", "Spring Festival", "Tomb-sweeping Day", "Labour Day", "Dragon Boat Festival", "Mid-autumn Festival", "National Day", }
中英文,獲取指定偏移量上的資料即可,這種處理方式在內建庫很常見:比如時間型別的時間基本單位月:一月、二月、三月等
3. 歷史資料
基於上文的分析,要構建這個簡單的庫,要組織歷史節假日,這邊選取 2010年到 2019 年的資料。
// 一年 type YearCollection struct { Data []OneCollection `json:"data"` } // n 年 type CollectionYearHistory struct { Data [][]OneCollection `json:"data"` } // 2010 年到 2019年曆史資料 func FetchCollectionYearHistory() CollectionYearHistory { return CollectionYearHistory{ Data: [][]OneCollection{ holiday2019, holiday2018, holiday2017, holiday2016, holiday2015, holiday2014, holiday2013, holiday2012, holiday2011, holiday2010, }, } }
4. 構建API
- 選擇好的命名方式
- 選擇好的資料返回格式
- FetchAll - FetchByChName(year int, name string) - FetchByEnName(year int, name string) - FetchByMonth(year int, month int) - FetchByYear(year int) - FetchMonthHolidayCount(year int, month int) - FetchYearHolidayCount(year int) - IsHoliday - IsWeekDay - IsWorkDay
之所以這樣設計, 是因為專案中經常會是這樣的操作:
- 獲取所有的歷史資料
- 獲取某年的歷史資料
- 獲取某月的歷史資料
- 統計某年某月的放假天數
- 統計某年的放假天數
- 判斷一個日期是否是節假日
- 判斷一個日期是否是週末
- 判斷一個日期是否是工作日
基於這些需求,構建了上文的API
以幾個API 為例,詳細的操作如何實現?
- FetchByYear(year int): 從歷史資料中獲取
// FetchByYear get holidays by year in china func FetchByYear(year int) []history.OneCollection { var index int nowYear, _, _ := time.Now().Date() if year > nowYear+1 { return nil } index = nowYear + 1 - year return history.FetchCollectionYearHistory().Data[index] }
- FetchByMonth: 從某年的歷史資料中獲取
func FetchByMonth(year int, month int) []history.OneCollection { if month < 1 || month > 12 { return nil } collections := FetchByYear(year) var data []history.OneCollection for _, collection := range collections { collectionTime, _ := time.Parse("2006/01/02", collection.End) if int(collectionTime.Month()) == month { data = append(data, collection) } } return data }
- IsHoliday: 歷史資料中是否能擊中目標
func IsHoliday(value string) bool { collectionTime, err := time.Parse("2006/01/02", value) if err != nil { return false } nowYear, _, _ := time.Now().Date() if collectionTime.Year() > nowYear+1 { return false } collections := FetchByYear(collectionTime.Year()) for _, collection := range collections { startDate, _ := getDate(collection.Start) endDate, _ := getDate(collection.End) if collectionTime.Unix() >= startDate.Unix() && collectionTime.Unix() <= endDate.Unix() { return true } } return false }
- IsWeekDay: 不是節假日、也不是工作日的
// IsWeekDay: judge date is week day or not func IsWeekDay(value string) bool { return !IsWorkDay(value) && !IsHoliday(value) }
- holidays 僅供參考
<後記>
基於上文的理念,其實可以寫很多小工具:
比如:
- 獲取古詩詞
- 給定一個日期,判斷星座
- 給定一個省份證,判斷是否有效,屬於哪個地區等
- 給定一個ip, 給出地理位置
- 給定一箇中文,給出英文或者拼音
- 給定文字,解釋其中文含義
...
<完>