package utils import ( "bufio" "crypto/md5" "crypto/sha1" "encoding/base64" "encoding/hex" "encoding/json" "errors" "fmt" "github.com/shopspring/decimal" "image" "image/png" "io" "math" "math/rand" "net" "net/http" "os" "os/exec" "path" "regexp" "runtime" "strconv" "strings" "time" "unicode" ) // 随机数种子 var rnd = rand.New(rand.NewSource(time.Now().UnixNano())) func GetRandString(size int) string { allLetterDigit := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "!", "@", "#", "$", "%", "^", "&", "*"} randomSb := "" digitSize := len(allLetterDigit) for i := 0; i < size; i++ { randomSb += allLetterDigit[rnd.Intn(digitSize)] } return randomSb } func GetRandStringNoSpecialChar(size int) string { allLetterDigit := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"} randomSb := "" digitSize := len(allLetterDigit) for i := 0; i < size; i++ { randomSb += allLetterDigit[rnd.Intn(digitSize)] } return randomSb } func StringsToJSON(str string) string { rs := []rune(str) jsons := "" for _, r := range rs { rint := int(r) if rint < 128 { jsons += string(r) } else { jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json } } return jsons } // 序列化 func ToString(v interface{}) string { data, _ := json.Marshal(v) return string(data) } // md5加密 func MD5(data string) string { m := md5.Sum([]byte(data)) return hex.EncodeToString(m[:]) } // 获取数字随机字符 func GetRandDigit(n int) string { return fmt.Sprintf("%0"+strconv.Itoa(n)+"d", rnd.Intn(int(math.Pow10(n)))) } // 获取随机数 func GetRandNumber(n int) int { return rnd.Intn(n) } func GetRandInt(min, max int) int { if min >= max || min == 0 || max == 0 { return max } return rand.Intn(max-min) + min } func GetToday(format string) string { today := time.Now().Format(format) return today } // 获取今天剩余秒数 func GetTodayLastSecond() time.Duration { today := GetToday(FormatDate) + " 23:59:59" end, _ := time.ParseInLocation(FormatDateTime, today, time.Local) return time.Duration(end.Unix()-time.Now().Local().Unix()) * time.Second } // 处理出生日期函数 func GetBrithDate(idcard string) string { l := len(idcard) var s string if l == 15 { s = "19" + idcard[6:8] + "-" + idcard[8:10] + "-" + idcard[10:12] return s } if l == 18 { s = idcard[6:10] + "-" + idcard[10:12] + "-" + idcard[12:14] return s } return GetToday(FormatDate) } // 处理性别 func WhichSexByIdcard(idcard string) string { var sexs = [2]string{"女", "男"} length := len(idcard) if length == 18 { sex, _ := strconv.Atoi(string(idcard[16])) return sexs[sex%2] } else if length == 15 { sex, _ := strconv.Atoi(string(idcard[14])) return sexs[sex%2] } return "男" } // 截取小数点后几位 func SubFloatToString(f float64, m int) string { n := strconv.FormatFloat(f, 'f', -1, 64) if n == "" { return "" } if m >= len(n) { return n } newn := strings.Split(n, ".") if m == 0 { return newn[0] } if len(newn) < 2 || m >= len(newn[1]) { return n } return newn[0] + "." + newn[1][:m] } // 截取小数点后几位 func SubFloatToFloat(f float64, m int) float64 { newn := SubFloatToString(f, m) newf, _ := strconv.ParseFloat(newn, 64) return newf } // 截取小数点后几位 func SubFloatToFloatStr(f float64, m int) string { newn := SubFloatToString(f, m) return newn } // 获取相差时间-年 func GetYearDiffer(start_time, end_time string) int { t1, _ := time.ParseInLocation("2006-01-02", start_time, time.Local) t2, _ := time.ParseInLocation("2006-01-02", end_time, time.Local) age := t2.Year() - t1.Year() if t2.Month() < t1.Month() || (t2.Month() == t1.Month() && t2.Day() < t1.Day()) { age-- } return age } // 获取相差时间-秒 func GetSecondDifferByTime(start_time, end_time time.Time) int64 { diff := end_time.Unix() - start_time.Unix() return diff } func FixFloat(f float64, m int) float64 { newn := SubFloatToString(f+0.00000001, m) newf, _ := strconv.ParseFloat(newn, 64) return newf } // 将字符串数组转化为逗号分割的字符串形式 ["str1","str2","str3"] >>> "str1,str2,str3" func StrListToString(strList []string) (str string) { if len(strList) > 0 { for k, v := range strList { if k == 0 { str = v } else { str = str + "," + v } } return } return "" } // Token func GetToken() string { randStr := GetRandString(64) token := MD5(randStr + Md5Key) tokenLen := 64 - len(token) return strings.ToUpper(token + GetRandString(tokenLen)) } // 数据没有记录 func ErrNoRow() string { return " no row found" } // 判断文件是否存在 func FileIsExist(filePath string) bool { _, err := os.Stat(filePath) return err == nil || os.IsExist(err) } // 获取图片扩展名 func GetImgExt(file string) (ext string, err error) { var headerByte []byte headerByte = make([]byte, 8) fd, err := os.Open(file) if err != nil { return "", err } defer fd.Close() _, err = fd.Read(headerByte) if err != nil { return "", err } xStr := fmt.Sprintf("%x", headerByte) switch { case xStr == "89504e470d0a1a0a": ext = ".png" case xStr == "0000010001002020": ext = ".ico" case xStr == "0000020001002020": ext = ".cur" case xStr[:12] == "474946383961" || xStr[:12] == "474946383761": ext = ".gif" case xStr[:10] == "0000020000" || xStr[:10] == "0000100000": ext = ".tga" case xStr[:8] == "464f524d": ext = ".iff" case xStr[:8] == "52494646": ext = ".ani" case xStr[:4] == "4d4d" || xStr[:4] == "4949": ext = ".tiff" case xStr[:4] == "424d": ext = ".bmp" case xStr[:4] == "ffd8": ext = ".jpg" case xStr[:2] == "0a": ext = ".pcx" default: ext = "" } return ext, nil } // 保存图片 func SaveImage(path string, img image.Image) (err error) { //需要保持的文件 imgfile, err := os.Create(path) defer imgfile.Close() // 以PNG格式保存文件 err = png.Encode(imgfile, img) return err } // 下载图片 func DownloadImage(imgUrl string) (filePath string, err error) { imgPath := "./static/imgs/" fileName := path.Base(imgUrl) res, err := http.Get(imgUrl) if err != nil { fmt.Println("A error occurred!") return } defer res.Body.Close() // 获得get请求响应的reader对象 reader := bufio.NewReaderSize(res.Body, 32*1024) filePath = imgPath + fileName file, err := os.Create(filePath) if err != nil { return } // 获得文件的writer对象 writer := bufio.NewWriter(file) written, _ := io.Copy(writer, reader) fmt.Printf("Total length: %d \n", written) return } // 保存base64数据为文件 func SaveBase64ToFile(content, path string) error { data, err := base64.StdEncoding.DecodeString(content) if err != nil { return err } f, err := os.Create(path) defer f.Close() if err != nil { return err } f.Write(data) return nil } func SaveBase64ToFileBySeek(content, path string) (err error) { data, err := base64.StdEncoding.DecodeString(content) exist, err := PathExists(path) if err != nil { return } if !exist { f, err := os.Create(path) if err != nil { return err } n, _ := f.Seek(0, 2) // 从末尾的偏移量开始写入内容 _, err = f.WriteAt([]byte(data), n) defer f.Close() } else { f, err := os.OpenFile(path, os.O_WRONLY, 0644) if err != nil { return err } n, _ := f.Seek(0, 2) // 从末尾的偏移量开始写入内容 _, err = f.WriteAt([]byte(data), n) defer f.Close() } return nil } func PathExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err } func StartIndex(page, pagesize int) int { if page > 1 { return (page - 1) * pagesize } return 0 } func PageCount(count, pagesize int) int { if count%pagesize > 0 { return count/pagesize + 1 } else { return count / pagesize } } func TrimHtml(src string) string { //将HTML标签全转换成小写 re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") src = re.ReplaceAllStringFunc(src, strings.ToLower) re, _ = regexp.Compile("\\") src = re.ReplaceAllString(src, "[图片]") re, _ = regexp.Compile("class[\\S\\s]+?>") src = re.ReplaceAllString(src, "") re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") src = re.ReplaceAllString(src, "") return strings.TrimSpace(src) } //1556164246 -> 2019-04-25 03:50:46 +0000 //timestamp func TimeToTimestamp() { fmt.Println(time.Unix(1556164246, 0).Format("2006-01-02 15:04:05")) } func ToUnicode(text string) string { textQuoted := strconv.QuoteToASCII(text) textUnquoted := textQuoted[1 : len(textQuoted)-1] return textUnquoted } func VersionToInt(version string) int { version = strings.Replace(version, ".", "", -1) n, _ := strconv.Atoi(version) return n } func IsCheckInList(list []int, s int) bool { for _, v := range list { if v == s { return true } } return false } func round(num float64) int { return int(num + math.Copysign(0.5, num)) } func toFixed(num float64, precision int) float64 { output := math.Pow(10, float64(precision)) return float64(round(num*output)) / output } // GetWilsonScore returns Wilson Score func GetWilsonScore(p, n float64) float64 { if p == 0 && n == 0 { return 0 } return toFixed(((p+1.9208)/(p+n)-1.96*math.Sqrt(p*n/(p+n)+0.9604)/(p+n))/(1+3.8416/(p+n)), 2) } // 将中文数字转化成数字,比如 第三百四十五章,返回第345章 不支持一亿及以上 func ChangeWordsToNum(str string) (numStr string) { words := ([]rune)(str) num := 0 n := 0 for i := 0; i < len(words); i++ { word := string(words[i : i+1]) switch word { case "万": if n == 0 { n = 1 } n = n * 10000 num = num*10000 + n n = 0 case "千": if n == 0 { n = 1 } n = n * 1000 num += n n = 0 case "百": if n == 0 { n = 1 } n = n * 100 num += n n = 0 case "十": if n == 0 { n = 1 } n = n * 10 num += n n = 0 case "一": n += 1 case "二": n += 2 case "三": n += 3 case "四": n += 4 case "五": n += 5 case "六": n += 6 case "七": n += 7 case "八": n += 8 case "九": n += 9 case "零": default: if n > 0 { num += n n = 0 } if num == 0 { numStr += word } else { numStr += strconv.Itoa(num) + word num = 0 } } } if n > 0 { num += n n = 0 } if num != 0 { numStr += strconv.Itoa(num) } return } func Sha1(data string) string { sha1 := sha1.New() sha1.Write([]byte(data)) return hex.EncodeToString(sha1.Sum([]byte(""))) } func GetVideoPlaySeconds(videoPath string) (playSeconds float64, err error) { cmd := `ffmpeg -i ` + videoPath + ` 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//` out, err := exec.Command("bash", "-c", cmd).Output() if err != nil { return } outTimes := string(out) fmt.Println("outTimes:", outTimes) if outTimes != "" { timeArr := strings.Split(outTimes, ":") h := timeArr[0] m := timeArr[1] s := timeArr[2] hInt, err := strconv.Atoi(h) if err != nil { return playSeconds, err } mInt, err := strconv.Atoi(m) if err != nil { return playSeconds, err } s = strings.Trim(s, " ") s = strings.Trim(s, "\n") sInt, err := strconv.ParseFloat(s, 64) if err != nil { return playSeconds, err } playSeconds = float64(hInt)*3600 + float64(mInt)*60 + float64(sInt) } return } func GetMaxTradeCode(tradeCode string) (maxTradeCode string, err error) { tradeCode = strings.Replace(tradeCode, "W", "", -1) tradeCode = strings.Trim(tradeCode, " ") tradeCodeInt, err := strconv.Atoi(tradeCode) if err != nil { return } tradeCodeInt = tradeCodeInt + 1 maxTradeCode = fmt.Sprintf("W%06d", tradeCodeInt) return } // excel日期字段格式化 yyyy-mm-dd func ConvertToFormatDay(excelDaysString string) string { // 2006-01-02 距离 1900-01-01的天数 baseDiffDay := 38719 //在网上工具计算的天数需要加2天,什么原因没弄清楚 curDiffDay := excelDaysString b, _ := strconv.Atoi(curDiffDay) // 获取excel的日期距离2006-01-02的天数 realDiffDay := b - baseDiffDay //fmt.Println("realDiffDay:",realDiffDay) // 距离2006-01-02 秒数 realDiffSecond := realDiffDay * 24 * 3600 //fmt.Println("realDiffSecond:",realDiffSecond) // 2006-01-02 15:04:05距离1970-01-01 08:00:00的秒数 网上工具可查出 baseOriginSecond := 1136185445 resultTime := time.Unix(int64(baseOriginSecond+realDiffSecond), 0).Format("2006-01-02") return resultTime } func CheckPwd(pwd string) bool { compile := `([0-9a-z]+){6,12}|(a-z0-9]+){6,12}` reg := regexp.MustCompile(compile) flag := reg.MatchString(pwd) return flag } func GetMonthStartAndEnd(myYear string, myMonth string) (startDate, endDate string) { // 数字月份必须前置补零 if len(myMonth) == 1 { myMonth = "0" + myMonth } yInt, _ := strconv.Atoi(myYear) timeLayout := "2006-01-02 15:04:05" loc, _ := time.LoadLocation("Local") theTime, _ := time.ParseInLocation(timeLayout, myYear+"-"+myMonth+"-01 00:00:00", loc) newMonth := theTime.Month() t1 := time.Date(yInt, newMonth, 1, 0, 0, 0, 0, time.Local).Format("2006-01-02") t2 := time.Date(yInt, newMonth+1, 0, 0, 0, 0, 0, time.Local).Format("2006-01-02") return t1, t2 } // 移除字符串中的空格 func TrimStr(str string) (str2 string) { if str == "" { return str } return strings.Replace(str, " ", "", -1) } // 字符串转换为time func StrTimeToTime(strTime string) time.Time { timeLayout := "2006-01-02 15:04:05" //转化所需模板 loc, _ := time.LoadLocation("Local") //重要:获取时区 resultTime, _ := time.ParseInLocation(timeLayout, strTime, loc) return resultTime } // 字符串类型时间转周几 func StrDateTimeToWeek(strTime string) string { var WeekDayMap = map[string]string{ "Monday": "周一", "Tuesday": "周二", "Wednesday": "周三", "Thursday": "周四", "Friday": "周五", "Saturday": "周六", "Sunday": "周日", } var ctime = StrTimeToTime(strTime).Format("2006-01-02") startday, _ := time.ParseInLocation("2006-01-02", ctime, time.Local) staweek_int := startday.Weekday().String() return WeekDayMap[staweek_int] } // 时间格式转年月日字符串 func TimeToStrYmd(time2 time.Time) string { var Ymd string year := time2.Year() month := time2.Format("1") day1 := time.Now().Day() Ymd = strconv.Itoa(year) + "年" + month + "月" + strconv.Itoa(day1) + "日" return Ymd } // 时间格式去掉时分秒 func TimeRemoveHms(strTime string) string { var Ymd string var resultTime = StrTimeToTime(strTime) year := resultTime.Year() month := resultTime.Format("01") day1 := resultTime.Day() Ymd = strconv.Itoa(year) + "." + month + "." + strconv.Itoa(day1) return Ymd } // 时间格式去掉时分秒 func TimeRemoveHms2(strTime string) string { var Ymd string var resultTime = StrTimeToTime(strTime) year := resultTime.Year() month := resultTime.Format("01") day1 := resultTime.Day() Ymd = strconv.Itoa(year) + "-" + month + "-" + strconv.Itoa(day1) return Ymd } // 文章上一次编辑时间 func ArticleLastTime(strTime string) string { var newTime string stamp, _ := time.ParseInLocation("2006-01-02 15:04:05", strTime, time.Local) diffTime := time.Now().Unix() - stamp.Unix() if diffTime <= 60 { newTime = "当前" } else if diffTime < 60*60 { newTime = strconv.FormatInt(diffTime/60, 10) + "分钟前" } else if diffTime < 24*60*60 { newTime = strconv.FormatInt(diffTime/(60*60), 10) + "小时前" } else if diffTime < 30*24*60*60 { newTime = strconv.FormatInt(diffTime/(24*60*60), 10) + "天前" } else if diffTime < 12*30*24*60*60 { newTime = strconv.FormatInt(diffTime/(30*24*60*60), 10) + "月前" } else { newTime = "1年前" } return newTime } // 人民币小写转大写 func ConvertNumToCny(num float64) (str string, err error) { strNum := strconv.FormatFloat(num*100, 'f', 0, 64) sliceUnit := []string{"仟", "佰", "拾", "亿", "仟", "佰", "拾", "万", "仟", "佰", "拾", "元", "角", "分"} // log.Println(sliceUnit[:len(sliceUnit)-2]) s := sliceUnit[len(sliceUnit)-len(strNum):] upperDigitUnit := map[string]string{"0": "零", "1": "壹", "2": "贰", "3": "叁", "4": "肆", "5": "伍", "6": "陆", "7": "柒", "8": "捌", "9": "玖"} for k, v := range strNum[:] { str = str + upperDigitUnit[string(v)] + s[k] } reg, err := regexp.Compile(`零角零分$`) str = reg.ReplaceAllString(str, "整") reg, err = regexp.Compile(`零角`) str = reg.ReplaceAllString(str, "零") reg, err = regexp.Compile(`零分$`) str = reg.ReplaceAllString(str, "整") reg, err = regexp.Compile(`零[仟佰拾]`) str = reg.ReplaceAllString(str, "零") reg, err = regexp.Compile(`零{2,}`) str = reg.ReplaceAllString(str, "零") reg, err = regexp.Compile(`零亿`) str = reg.ReplaceAllString(str, "亿") reg, err = regexp.Compile(`零万`) str = reg.ReplaceAllString(str, "万") reg, err = regexp.Compile(`零*元`) str = reg.ReplaceAllString(str, "元") reg, err = regexp.Compile(`亿零{0, 3}万`) str = reg.ReplaceAllString(str, "^元") reg, err = regexp.Compile(`零元`) str = reg.ReplaceAllString(str, "零") return } // GetNowWeekMonday 获取本周周一的时间 func GetNowWeekMonday() time.Time { offset := int(time.Monday - time.Now().Weekday()) if offset == 1 { //正好是周日,但是按照中国人的理解,周日是一周最后一天,而不是一周开始的第一天 offset = -6 } mondayTime := time.Now().AddDate(0, 0, offset) mondayTime = time.Date(mondayTime.Year(), mondayTime.Month(), mondayTime.Day(), 0, 0, 0, 0, mondayTime.Location()) return mondayTime } // GetNowWeekLastDay 获取本周最后一天的时间 func GetNowWeekLastDay() time.Time { offset := int(time.Monday - time.Now().Weekday()) if offset == 1 { //正好是周日,但是按照中国人的理解,周日是一周最后一天,而不是一周开始的第一天 offset = -6 } firstDayTime := time.Now().AddDate(0, 0, offset) firstDayTime = time.Date(firstDayTime.Year(), firstDayTime.Month(), firstDayTime.Day(), 0, 0, 0, 0, firstDayTime.Location()).AddDate(0, 0, 6) lastDayTime := time.Date(firstDayTime.Year(), firstDayTime.Month(), firstDayTime.Day(), 23, 59, 59, 0, firstDayTime.Location()) return lastDayTime } // GetNowMonthFirstDay 获取本月第一天的时间 func GetNowMonthFirstDay() time.Time { nowMonthFirstDay := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location()) return nowMonthFirstDay } // GetNowMonthLastDay 获取本月最后一天的时间 func GetNowMonthLastDay() time.Time { nowMonthLastDay := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 1, -1) nowMonthLastDay = time.Date(nowMonthLastDay.Year(), nowMonthLastDay.Month(), nowMonthLastDay.Day(), 23, 59, 59, 0, nowMonthLastDay.Location()) return nowMonthLastDay } // GetNowQuarterFirstDay 获取本季度第一天的时间 func GetNowQuarterFirstDay() time.Time { month := int(time.Now().Month()) var nowQuarterFirstDay time.Time if month >= 1 && month <= 3 { //1月1号 nowQuarterFirstDay = time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location()) } else if month >= 4 && month <= 6 { //4月1号 nowQuarterFirstDay = time.Date(time.Now().Year(), 4, 1, 0, 0, 0, 0, time.Now().Location()) } else if month >= 7 && month <= 9 { nowQuarterFirstDay = time.Date(time.Now().Year(), 7, 1, 0, 0, 0, 0, time.Now().Location()) } else { nowQuarterFirstDay = time.Date(time.Now().Year(), 10, 1, 0, 0, 0, 0, time.Now().Location()) } return nowQuarterFirstDay } // GetNowQuarterLastDay 获取本季度最后一天的时间 func GetNowQuarterLastDay() time.Time { month := int(time.Now().Month()) var nowQuarterLastDay time.Time if month >= 1 && month <= 3 { //03-31 23:59:59 nowQuarterLastDay = time.Date(time.Now().Year(), 3, 31, 23, 59, 59, 0, time.Now().Location()) } else if month >= 4 && month <= 6 { //06-30 23:59:59 nowQuarterLastDay = time.Date(time.Now().Year(), 6, 30, 23, 59, 59, 0, time.Now().Location()) } else if month >= 7 && month <= 9 { //09-30 23:59:59 nowQuarterLastDay = time.Date(time.Now().Year(), 9, 30, 23, 59, 59, 0, time.Now().Location()) } else { //12-31 23:59:59 nowQuarterLastDay = time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location()) } return nowQuarterLastDay } // GetNowHalfYearFirstDay 获取当前半年的第一天的时间 func GetNowHalfYearFirstDay() time.Time { month := int(time.Now().Month()) var nowHalfYearLastDay time.Time if month >= 1 && month <= 6 { //03-31 23:59:59 nowHalfYearLastDay = time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location()) } else { //12-31 23:59:59 nowHalfYearLastDay = time.Date(time.Now().Year(), 7, 1, 0, 0, 0, 0, time.Now().Location()) } return nowHalfYearLastDay } // GetNowHalfYearLastDay 获取当前半年的最后一天的时间 func GetNowHalfYearLastDay() time.Time { month := int(time.Now().Month()) var nowHalfYearLastDay time.Time if month >= 1 && month <= 6 { //03-31 23:59:59 nowHalfYearLastDay = time.Date(time.Now().Year(), 6, 30, 23, 59, 59, 0, time.Now().Location()) } else { //12-31 23:59:59 nowHalfYearLastDay = time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location()) } return nowHalfYearLastDay } // GetNowYearFirstDay 获取当前年的最后一天的时间 func GetNowYearFirstDay() time.Time { //12-31 23:59:59 nowYearFirstDay := time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location()) return nowYearFirstDay } // GetNowYearLastDay 获取当前年的最后一天的时间 func GetNowYearLastDay() time.Time { //12-31 23:59:59 nowYearLastDay := time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location()) return nowYearLastDay } // CalculationDate 计算两个日期之间相差n年m月y天 // FormatPrice 格式化展示金额数字(财务金额展示,小数点前,每三位用,隔开) 1,234,567,898.55 func FormatPrice(price float64) (str string) { str = decimal.NewFromFloat(price).String() length := len(str) if length < 4 { return str } arr := strings.Split(str, ".") //用小数点符号分割字符串,为数组接收 length1 := len(arr[0]) if length1 < 4 { return str } count := (length1 - 1) / 3 for i := 0; i < count; i++ { arr[0] = arr[0][:length1-(i+1)*3] + "," + arr[0][length1-(i+1)*3:] } return strings.Join(arr, ".") //将一系列字符串连接为一个字符串,之间用sep来分隔。 } // getMonthDay 获取某年某月有多少天 func getMonthDay(year, month int) (days int) { if month != 2 { if month == 4 || month == 6 || month == 9 || month == 11 { days = 30 } else { days = 31 } } else { if ((year%4) == 0 && (year%100) != 0) || (year%400) == 0 { days = 29 } else { days = 28 } } return } func SaveToFile(content, path string) error { f, err := os.Create(path) defer f.Close() if err != nil { return err } f.Write([]byte(content)) return nil } // HideString 给字段加***(从字符串中间替换,少于需要替换的长度,那么就补全*的长度) // src 待*字符串 // hideLen 需要加*的长度 func HideString(src string, hideLen int) string { if src == "" { return src } str := []rune(src) if hideLen == 0 { hideLen = 4 } hideStr := "" for i := 0; i < hideLen; i++ { hideStr += "*" } strLen := len(str) // 字符长度是1 if strLen == 1 { return string(str[:1]) + hideStr } //字符长度大于1,但是小于等于需要隐藏的字符长度,那么就隐藏中间,保留前后各一位字符 if strLen <= hideLen+2 { return string(str[:1]) + hideStr + string(str[strLen-1:]) } subLen := strLen - hideLen //剩余需要展示的字符长度 decimal.NewFromFloat(2) frontLenDecimal := decimal.NewFromInt(int64(subLen)).Div(decimal.NewFromInt(2)) //前面需要展示的字符的长度 frontLen := frontLenDecimal.Floor().IntPart() return string(str[:frontLen]) + hideStr + string(str[frontLen+int64(hideLen):]) } // 用户参会时间转换 func GetAttendanceDetailSeconds(secondNum int) string { var timeStr string if secondNum <= 60 { if secondNum < 10 { timeStr = "0" + strconv.Itoa(secondNum) + "''" } else { timeStr = strconv.Itoa(secondNum) + "''" } } else { var remainderStr string remainderNum := secondNum % 60 minuteNum := secondNum / 60 if remainderNum < 10 { remainderStr = "0" + strconv.Itoa(remainderNum) + "''" } else { remainderStr = strconv.Itoa(remainderNum) + "''" } if minuteNum < 10 { timeStr = "0" + strconv.Itoa(minuteNum) + "'" + remainderStr } else { timeStr = strconv.Itoa(minuteNum) + "'" + remainderStr } } return timeStr } // SubStr 截取字符串(中文) func SubStr(str string, subLen int) string { strRune := []rune(str) bodyRuneLen := len(strRune) if bodyRuneLen > subLen { bodyRuneLen = subLen } str = string(strRune[:bodyRuneLen]) return str } func GetLocalIP() (ip string, err error) { addrs, err := net.InterfaceAddrs() if err != nil { return } for _, addr := range addrs { ipAddr, ok := addr.(*net.IPNet) if !ok { continue } if ipAddr.IP.IsLoopback() { continue } if !ipAddr.IP.IsGlobalUnicast() { continue } return ipAddr.IP.String(), nil } return } func PrintLog(params ...string) { _, file, line, ok := runtime.Caller(1) fmt.Println(file, line, ok, params) } // InArrayByStr php中的in_array(判断String类型的切片中是否存在该string值) func InArrayByStr(idStrList []string, searchId string) (has bool) { for _, id := range idStrList { if id == searchId { has = true return } } return } // InArrayByInt php中的in_array(判断Int类型的切片中是否存在该Int值) func InArrayByInt(idStrList []int, searchId int) (has bool) { for _, id := range idStrList { if id == searchId { has = true return } } return } // GetOrmInReplace 获取orm的in查询替换?的方法 func GetOrmInReplace(num int) string { template := make([]string, num) for i := 0; i < num; i++ { template[i] = "?" } return strings.Join(template, ",") } // GetTimeSubDay 计算两个时间的自然日期差 func GetTimeSubDay(t1, t2 time.Time) int { var day int swap := false if t1.Unix() > t2.Unix() { t1, t2 = t2, t1 swap = true } t1_ := t1.Add(time.Duration(t2.Sub(t1).Milliseconds()%86400000) * time.Millisecond) day = int(t2.Sub(t1).Hours() / 24) // 计算在t1+两个时间的余数之后天数是否有变化 if t1_.Day() != t1.Day() { day += 1 } if swap { day = -day } return day } // GetFrequencyEndDay 根据当前时间和频度,获取该频度下最后一天的日期 func GetFrequencyEndDay(currDate time.Time, frequency string) (endDate time.Time) { switch frequency { case "周度": // 如果当前就是最后一天,那么就直接返回本日期就好了 if currDate.Weekday() == 0 { endDate = currDate } else { endDate = currDate.AddDate(0, 0, 7-int(currDate.Weekday())) } case "旬度": nextDay := currDate.AddDate(0, 0, 1) if nextDay.Day() == 1 || currDate.Day() == 10 || currDate.Day() == 20 { //如果是每月10、20、最后一天,那么就直接返回本日期就好了 endDate = currDate } else { if currDate.Day() < 10 { // 每月10号 endDate = time.Date(currDate.Year(), currDate.Month(), 10, 0, 0, 0, 0, time.Local) } else if currDate.Day() < 20 { // 每月10号 endDate = time.Date(currDate.Year(), currDate.Month(), 20, 0, 0, 0, 0, time.Local) } else { // 下旬,多种可能,最大天数可能存在8天,9天,10天,11天, tmpNextMonth := currDate.AddDate(0, 0, 13) endDate = time.Date(tmpNextMonth.Year(), tmpNextMonth.Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1) } } case "月度": nextDay := currDate.AddDate(0, 0, 1) if nextDay.Day() == 1 { //如果是每月的最后一天,那么就直接返回本日期就好了 endDate = currDate } else { endDate = time.Date(nextDay.Year(), nextDay.Month()+1, 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1) } case "季度": nextDay := currDate.AddDate(0, 0, 1) if (nextDay.Month() == 1 || nextDay.Month() == 4 || nextDay.Month() == 7 || nextDay.Month() == 10) && nextDay.Day() == 1 { //如果是每季的最后一天,那么就直接返回本日期就好了 endDate = currDate } else { if currDate.Month() < 4 { // 1季度 endDate = time.Date(currDate.Year(), 3, 31, 0, 0, 0, 0, time.Local) } else if currDate.Month() < 7 { // 2季度 endDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, time.Local) } else if currDate.Month() < 10 { // 3季度 endDate = time.Date(currDate.Year(), 9, 30, 0, 0, 0, 0, time.Local) } else { // 4季度 endDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, time.Local) } } case "年度": endDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, time.Local) default: endDate = currDate return } return } // CheckFrequency 获取两个频度之间是否相对低高频 // 大于0,代表左侧是高频(例:左侧:日度,右侧:周度) // 等于0,代表同频 // 小于0,代表右侧是高频(例:左侧:周度,右侧:日度) func CheckFrequency(leftFrequency, rightFrequency string) int { frequencyMap := map[string]int{ "年度": 0, "半年度": 1, "季度": 2, "月度": 3, "旬度": 4, "周度": 5, "日度": 6, } return frequencyMap[leftFrequency] - frequencyMap[rightFrequency] } // 将下划线命名转为驼峰命名 func SnakeToCamel(s string) string { var result string upper := true for _, c := range s { if c == '_' { upper = true continue } if upper { result += string(unicode.ToUpper(c)) upper = false } else { result += string(c) } } return result } // 将驼峰命名转为下划线命名 func CamelToSnake(s string) string { var result string for i, c := range s { if unicode.IsUpper(c) { if i > 0 { result += "_" } result += string(unicode.ToLower(c)) } else { result += string(c) } } return result } func TimeTransferString(format string, t time.Time) string { str := t.Format(format) if t.IsZero() { return "" } return str } // GetEdbRefreshStartDate // @Description: 获取开始刷新时间 // @author: Roc // @datetime 2024-02-05 11:23:55 // @param startDate string // @return string func GetEdbRefreshStartDate(startDate string) string { // 没有传入日期,或者日期异常的话,那么就是从1990-01-01开始 if startDate == `` || strings.Contains(startDate, "0000-") { return "1990-01-01" } return startDate } // GetEdbRefreshEndDate // @Description: 获取结束刷新时间 // @author: Roc // @datetime 2024-02-05 11:23:55 // @param startDate string // @return string func GetEdbRefreshEndDate(endDate string) string { // 没有传入日期,或者日期异常的话,那么就是从1990-01-01开始 if endDate == `` || strings.Contains(endDate, "0000-") { return time.Now().Format(FormatDate) } return endDate } func DealExcelDate(date string) (newDate time.Time, err error) { /*newDate, err = dateparse.ParseAny(date) if err != nil { return } newDate = time.Date(newDate.Year(), newDate.Month(), newDate.Day(), 0, 0, 0, 0, time.Local)*/ if strings.Contains(date, "/") { newDate, err = time.ParseInLocation("2006/01/02", date, time.Local) return } else { newDate, err = time.ParseInLocation(FormatDate, date, time.Local) return } return } // FloatAlmostEqual 判断两个浮点数是否相等 func FloatAlmostEqual(a, b float64) bool { epsilon := 1e-9 // 容差值 return math.Abs(a-b) <= epsilon } // VerifyFrequency // @Description: 校验频度是否合规 // @author: Roc // @datetime 2024-04-26 13:30:22 // @param frequency string 待校验的频度 // @return bool func VerifyFrequency(frequency string) bool { return InArrayByStr([]string{"年度", "半年度", "季度", "月度", "旬度", "周度", "日度"}, frequency) } // DateConvMysqlConvMongo // @Description: 将mysql中的日期比较符转换成mongo中的日期比较符 // @author: Roc // @datetime 2024-05-08 11:03:26 // @param dateCon string func DateConvMysqlConvMongo(dateCon string) string { cond := "" switch dateCon { case "=": cond = "$eq" case "<": cond = "$lt" case "<=": cond = "$lte" case ">": cond = "$gt" case ">=": cond = "$gte" } return cond } // GenerateEdbCodeMap 当前已经生成的指标编码map(暂时不做定时数据清理了,因为数据不大,我们至少每个月会重启一次,所以暂时不做定时数据清理) var GenerateEdbCodeMap = map[string]bool{} // GenerateEdbCode // @Description: 生成指标编码 // @author: Roc // @datetime 2024-06-05 09:49:53 // @param num int // @param pre string 前缀 // @return edbCode string // @return err error func GenerateEdbCode(num int, pre string) (edbCode string, err error) { if num >= 10 { err = errors.New("指标编码生成失败,请重新生成") return } // 4位随机数 randStr := GetRandDigit(4) // 年月日时分秒+4位随机数 edbCode = `C` + pre + time.Now().Format(FormatShortDateTimeUnSpace) + randStr if _, ok := GenerateEdbCodeMap[edbCode]; ok { num++ edbCode, err = GenerateEdbCode(num, pre) } GenerateEdbCodeMap[edbCode] = true return } // InsertStr2StrIdx 可分隔的字符串中插入指定字符串, 例如: CO1 Comdty插入V => CO1 V Comdty func InsertStr2StrIdx(str, sep string, idx int, value string) string { str = strings.TrimSpace(str) // 默认以空格作为分隔符 if sep == "" { sep = " " } slice := strings.Split(str, sep) if len(slice) < 2 { return str } // 如果idx不在切片的有效范围内,直接返回原字符串 if idx < 0 || idx > len(slice) { return str } slice = append(slice[:idx], append([]string{value}, slice[idx:]...)...) return strings.Join(slice, sep) } // FormatFloatPlaces 格式化浮点数位数 func FormatFloatPlaces(val float64, places int32) (newVal float64, err error) { if places <= 0 { places = 4 } strNewVal := decimal.NewFromFloat(val).Round(places).String() di, e := decimal.NewFromString(strNewVal) if e != nil { err = fmt.Errorf("NewFromString err: %v", e) return } newVal, _ = di.Float64() return } // handleSystemAppointDateT // @Description: 处理系统日期相关的指定频率(所在周/旬/月/季/半年/年的最后/最早一天) // @author: Roc // @datetime2023-10-27 09:31:35 // @param Frequency string // @param Day string // @return date string // @return err error // @return errMsg string func HandleSystemAppointDateT(currDate time.Time, appointDay, frequency string) (date string, err error, errMsg string) { //currDate := time.Now() switch frequency { case "本周": day := int(currDate.Weekday()) if day == 0 { // 周日 day = 7 } num := 0 switch appointDay { case "周一": num = 1 case "周二": num = 2 case "周三": num = 3 case "周四": num = 4 case "周五": num = 5 case "周六": num = 6 case "周日": num = 7 } day = num - day date = currDate.AddDate(0, 0, day).Format(FormatDate) case "本旬": day := currDate.Day() var tmpDate time.Time switch appointDay { case "第一天": if day <= 10 { tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location()) } else if day <= 20 { tmpDate = time.Date(currDate.Year(), currDate.Month(), 11, 0, 0, 0, 0, currDate.Location()) } else { tmpDate = time.Date(currDate.Year(), currDate.Month(), 21, 0, 0, 0, 0, currDate.Location()) } case "最后一天": if day <= 10 { tmpDate = time.Date(currDate.Year(), currDate.Month(), 10, 0, 0, 0, 0, currDate.Location()) } else if day <= 20 { tmpDate = time.Date(currDate.Year(), currDate.Month(), 20, 0, 0, 0, 0, currDate.Location()) } else { tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1) } } date = tmpDate.Format(FormatDate) case "本月": var tmpDate time.Time switch appointDay { case "第一天": tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location()) case "最后一天": tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1) } date = tmpDate.Format(FormatDate) case "本季": month := currDate.Month() var tmpDate time.Time switch appointDay { case "第一天": if month <= 3 { tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location()) } else if month <= 6 { tmpDate = time.Date(currDate.Year(), 4, 1, 0, 0, 0, 0, currDate.Location()) } else if month <= 9 { tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location()) } else { tmpDate = time.Date(currDate.Year(), 10, 1, 0, 0, 0, 0, currDate.Location()) } case "最后一天": if month <= 3 { tmpDate = time.Date(currDate.Year(), 3, 31, 0, 0, 0, 0, currDate.Location()) } else if month <= 6 { tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location()) } else if month <= 9 { tmpDate = time.Date(currDate.Year(), 9, 30, 0, 0, 0, 0, currDate.Location()) } else { tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location()) } } date = tmpDate.Format(FormatDate) case "本半年": month := currDate.Month() var tmpDate time.Time switch appointDay { case "第一天": if month <= 6 { tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location()) } else { tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location()) } case "最后一天": if month <= 6 { tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location()) } else { tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location()) } } date = tmpDate.Format(FormatDate) case "本年": var tmpDate time.Time switch appointDay { case "第一天": tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location()) case "最后一天": tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location()) } date = tmpDate.Format(FormatDate) default: errMsg = "错误的日期频度:" + frequency err = errors.New(errMsg) return } return } func CompareFloatByOpStrings(op string, a, b float64) bool { switch op { case "=": return a == b case ">": return a > b case ">=": return a >= b case "<=": return a <= b case "<": return a < b } return false } // IsDivideZero // @Description: 判断是否分母为0的bug // @author: Roc // @datetime 2024-08-23 11:21:25 // @param err error // @return bool func IsDivideZero(err error) bool { if err == nil { return false } //if strings.Contains(err.Error(), "divide by zero") { // return true //} if strings.Contains(err.Error(), "division by zero") { return true } return false } // GetTradingDays 获取开始时间至结束时间之间的交易日期(日度) func GetTradingDays(startDate, endDate time.Time) []time.Time { var tradingDays []time.Time for curr := startDate; !curr.After(endDate); curr = curr.AddDate(0, 0, 1) { if curr.Weekday() >= time.Monday && curr.Weekday() <= time.Friday { tradingDays = append(tradingDays, curr) } } return tradingDays } // CalculateTradingDays 计算天数 跳过周末 func CalculateTradingDays(baseDate time.Time, tradingDays int, resultMap map[string]float64, moveType int) int { oldDate := baseDate // Move to the next day var moveDays int if moveType != 2 { moveDays = tradingDays } else { moveDays = -tradingDays } daysMoved := 0 // 实际移动的工作日数 for daysMoved < tradingDays { // 根据 moveType,决定是前进一天还是后退一天 if moveDays > 0 { baseDate = baseDate.AddDate(0, 0, 1) // 向后移动一天 } else { baseDate = baseDate.AddDate(0, 0, -1) // 向前移动一天 } // 如果当前日期不是周六或周日,则计入交易日 weekday := baseDate.Weekday() if weekday != time.Saturday && weekday != time.Sunday { daysMoved++ } } // 计算实际天数差(包含跳过周末后的移动天数) subDays := baseDate.Sub(oldDate) days := int(math.Abs(subDays.Hours() / 24)) return days } // getLastDayOfMonth 获取某个月的最后一天 func getLastDayOfMonth(t time.Time) time.Time { // 移动到下个月的第一天,然后回退一天得到当前月的最后一天 return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()).AddDate(0, 1, -1) } // 获取某年某月的天数 func daysInMonth(year int, month time.Month) int { if month == time.February { // 闰年处理 if (year%4 == 0 && year%100 != 0) || (year%400 == 0) { return 29 } return 28 } if month == time.April || month == time.June || month == time.September || month == time.November { return 30 } return 31 } // CalculateEndOfMonth 使用天数计算未来月末的天数差 /*func CalculateEndOfMonth(baseDate time.Time, months, moveType int) int { // 假设每个月28天,然后算到目标月的下个月 var daysToAdd int // 计算目标月的下个月月初 if moveType == 2 { daysToAdd = -(28 * months) } else { daysToAdd = 28 * (months + 2) } nextMonth := baseDate.AddDate(0, 0, daysToAdd) // 获取目标月月初的第一天 firstDayOfNextMonth := time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, nextMonth.Location()) // 获取目标月的最后一天(即月初减去1天) lastDayOfTargetMonth := firstDayOfNextMonth.AddDate(0, 0, -1) // 计算天数差 daysDifference := int(math.Abs(lastDayOfTargetMonth.Sub(baseDate).Hours() / 24)) return daysDifference }*/ // CalculateEndOfMonth 计算从 baseDate 开始经过 months 后目标月的最后一天距离 baseDate 的天数差 func CalculateEndOfMonth(baseDate time.Time, months, moveType int) int { // 初始化目标日期为当前日期 targetDate := baseDate // 如果 moveType == 2,表示倒退月份;否则为前进月份 if moveType == 2 { months = -months } // 手动通过天数加减月份 for i := 0; i < int(math.Abs(float64(months))); i++ { // 首先将日期调整到当前月份的第一天 targetDate = time.Date(targetDate.Year(), targetDate.Month(), 1, 0, 0, 0, 0, targetDate.Location()) // 根据 moveType 来前进或倒退到下一个月的第一天 if months > 0 { // 前进到下一个月的第一天 targetDate = targetDate.AddDate(0, 1, 0) } else { // 倒退到上一个月的第一天 targetDate = targetDate.AddDate(0, -1, 0) } // 如果是倒退,调整为目标月的最后一天 if months < 0 { daysInCurrentMonth := daysInMonth(targetDate.Year(), targetDate.Month()) targetDate = time.Date(targetDate.Year(), targetDate.Month(), daysInCurrentMonth, 0, 0, 0, 0, targetDate.Location()) } } // 获取目标月的下个月月初第一天 firstDayOfNextMonth := time.Date(targetDate.Year(), targetDate.Month()+1, 1, 0, 0, 0, 0, targetDate.Location()) // 获取目标月的最后一天(即下个月月初减去一天) lastDayOfTargetMonth := firstDayOfNextMonth.AddDate(0, 0, -1) // 计算天数差 daysDifference := int(math.Abs(lastDayOfTargetMonth.Sub(baseDate).Hours() / 24)) return daysDifference } // CalculateDekadTime 计算旬度时间 func CalculateDekadTime(baseDate time.Time, tradingDays, moveType int) int { // 记录原始日期 oldDate := baseDate // 计算移动的旬数,1 旬为 10 天 var moveDekads int if moveType != 2 { moveDekads = tradingDays } else { moveDekads = -tradingDays } // 移动的天数为旬数 * 10,初步移动日期 baseDate = baseDate.AddDate(0, 0, moveDekads*10) // 调整日期到最近的旬:10号、20号或月末 baseDate = adjustToNearestDekad(baseDate) // 计算时间差 subDays := baseDate.Sub(oldDate) days := int(math.Abs(subDays.Hours() / 24)) fmt.Printf("最终日期: %s, 总天数差: %d 天\n", baseDate.Format("2006-01-02"), days) return days } // adjustToNearestDekad 调整日期到最近的旬 func adjustToNearestDekad(date time.Time) time.Time { day := date.Day() lastDayOfMonth := getLastDayOfMonth(date).Day() // 这里有些无可奈何了,暂时这么写吧。。。需要跟据润 平年根据每个月进行单独处理 if day < 5 { dateOneMonthAgo := date.AddDate(0, -1, 0) lastDayOfMonth2 := getLastDayOfMonth(dateOneMonthAgo).Day() return time.Date(date.Year(), date.Month()-1, lastDayOfMonth2, 0, 0, 0, 0, date.Location()) } else if day > 5 && day <= 15 { return time.Date(date.Year(), date.Month(), 10, 0, 0, 0, 0, date.Location()) } else if day > 11 && day <= 25 { return time.Date(date.Year(), date.Month(), 20, 0, 0, 0, 0, date.Location()) } else { return time.Date(date.Year(), date.Month(), lastDayOfMonth, 0, 0, 0, 0, date.Location()) } } /*// getLastDayOfMonth 返回指定日期所在月份的最后一天 func getLastDayOfMonth(date time.Time) time.Time { // 获取下个月的第一天 nextMonth := date.AddDate(0, 1, -date.Day()+1) // 下个月第一天减去一天即为当前月的最后一天 lastDay := nextMonth.AddDate(0, 0, -1) return lastDay }*/ // CalculateEndOfQuarter 计算从当前到未来的季度末的天数差 func CalculateEndOfQuarter(baseDate time.Time, quarters, moveType int) int { // Move forward `quarters` quarters (3 months per quarter) return CalculateEndOfMonth(baseDate, quarters*3, moveType) } // CalculateEndOfYear 计算从当前到未来的年末的天数差 func CalculateEndOfYear(baseDate time.Time, years, moveType int) int { // Move forward `years` years return CalculateEndOfMonth(baseDate, years*12, moveType) } // CalculateEndOfHalfYear 计算从当前到未来的半年的天数差 func CalculateEndOfHalfYear(baseDate time.Time, years, moveType int) int { // Move forward `half years` years return CalculateEndOfMonth(baseDate, years*6, moveType) } // GetCurrentTime 获取当前时间 格式为 2024-08-07 15:29:58 func GetCurrentTime() string { return time.Now().Format("2006-01-02 15:04:05") } // ReverseTimeSlice 反转时间类型切片 func ReverseTimeSlice(s []time.Time) []time.Time { for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s }