package astro import ( "b612.me/astro/calendar" "b612.me/staros" "fmt" "github.com/spf13/cobra" "golang.org/x/term" "os" "strconv" "strings" "time" ) var year int var nowDay string var isTimestamp bool var isLunar, isLive, isLeap bool func init() { CmdDateInfo.Flags().StringVarP(&nowDay, "now", "n", "", "指定现在的时间") CmdDateInfo.Flags().BoolVarP(&isTimestamp, "timestamp", "t", false, "是否为时间戳") CmdDateInfo.Flags().BoolVarP(&isLunar, "lunar", "l", false, "是否为农历") CmdDateInfo.Flags().BoolVarP(&isLive, "live", "v", false, "是否为实时") CmdDateInfo.Flags().BoolVarP(&isLeap, "leap", "L", false, "是否为农历闰月") CmdHoliday.Flags().IntVarP(&year, "year", "y", 0, "年份") CmdCal.AddCommand(CmdHoliday, CmdDateInfo) } var CmdCal = &cobra.Command{ Use: "cal", Args: cobra.MaximumNArgs(100), Short: "简洁日历与日期相关方法", Long: "简洁日历,支持多个月份,多个年份,支持年份月份混合输入,日期方法请查看子命令", Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { now := time.Now() fmt.Println(now.Format("2006年01月")) fmt.Println("------------------------") ShowCalendar(now.Year(), int(now.Month())) return } for _, v := range args { if len(v) < 4 { fmt.Println("输入错误,年份至少4位") return } year, err := strconv.Atoi(v[:4]) if err != nil { fmt.Println("输入错误:", err) return } if len(v) == 4 { for i := 1; i <= 12; i++ { fmt.Printf("%d年%d月\n", year, i) fmt.Println("------------------------") ShowCalendar(year, i) fmt.Println() fmt.Println() } continue } month, err := strconv.Atoi(v[4:]) if err != nil { fmt.Println("输入错误:", err) return } fmt.Printf("%d年%d月\n", year, month) fmt.Println("------------------------") ShowCalendar(year, month) fmt.Println() } }, } var CmdHoliday = &cobra.Command{ Use: "hol", Short: "中国法定节日放假安排", Long: "中国法定节日放假安排", Run: func(cmd *cobra.Command, args []string) { if year == 0 { year = time.Now().Year() } fmt.Printf("%d年放假安排\n", year) for _, v := range args { fmt.Println("------------------------") var d Holiday switch v { case "1", "元旦": d = YuanDan(year) case "2", "春节": d = ChunJie(year) case "3", "清明", "清明节": d = QingMing(year) case "4", "劳动", "劳动节": d = LaoDongJie(year) case "5", "端午", "端午节": d = DuanWu(year) case "6", "中秋", "中秋节": d = ZhongQiu(year) case "7", "国庆", "国庆节": d = GuoQing(year) } d.BasicInfo() d.Detail() } if len(args) == 0 { for _, v := range ChineseHoliday(year) { fmt.Println("------------------------") v.BasicInfo() v.Detail() fmt.Println(" ") } } }, } var CmdDateInfo = &cobra.Command{ Use: "info", Short: "指定时刻的详情", Long: "指定时刻的详情,格式:info [时间] [选项]", Run: func(cmd *cobra.Command, args []string) { var now = time.Now() var target = now var err error if nowDay != "" { now, err = parseDate(time.Time{}, nowDay, isTimestamp) if err != nil { fmt.Println(err) return } } if len(args) > 0 { target, err = parseDate(now, args[0], isTimestamp) if err != nil { fmt.Println(err) return } } for { if isLunar { LDateInfo(now, target, isLeap) } else { DateInfo(now, target) } if !isLive { break } time.Sleep(time.Nanosecond* time.Duration(1000000000-time.Now().Nanosecond()) + 1) if nowDay == "" { now = time.Now() now = now.Add(time.Duration(now.Nanosecond()*-1) * time.Nanosecond) } else { now = now.Add(time.Second) } ClearScreen() } }, } func ClearScreen() { fmt.Print("\033[H\033[2J") } func GenerateCalendar(year int, month int) []string { var days []string date := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.Local) firstWeekday := int(date.Weekday()) - 1 if firstWeekday < 0 { firstWeekday += 7 } count := 0 for i := 0; i < firstWeekday; i++ { days = append(days, " ") count++ } for i := 1; i <= 32; i++ { insertLunar := func(start int) { if start < 0 { start += 7 } for j := start; j >= 0; j-- { if i-j < 1 { days = append(days, "\u3000\u3000") continue } date = time.Date(year, time.Month(month), i-j, 0, 0, 0, 0, time.Local) _, d, _, str := calendar.RapidSolarToLunar(date) if d != 1 { str = str[strings.Index(str, "月")+3:] } else { str = str[:strings.Index(str, "月")+3] } days = append(days, str) } } date = time.Date(year, time.Month(month), i, 0, 0, 0, 0, time.Local) if date.Month() != time.Month(month) { if (count)%7 != 0 { i-- insertLunar(count%7 - 1) } break } days = append(days, fmt.Sprintf("%d", i)) count++ if count%7 == 0 { insertLunar(6) } } return days } func ShowCalendar(year int, month int) { days := GenerateCalendar(year, month) fd := int(os.Stdout.Fd()) width, _, err := term.GetSize(fd) if err != nil { fmt.Println("Error getting terminal size:", err) return } spark := 4 if width > 45 { spark = 5 } if width < 30 { fmt.Println("Terminal too small") return } for _, v := range []string{"\u3000一", "\u3000二", "\u3000三", "\u3000四", "\u3000五", "\u3000六", "\u3000日"} { fmt.Printf("%*s", spark-1, v) } fmt.Println() ct := 0 doBreak := false for i, v := range days { if len(days)-i < 7 && i%7 == len(days)-i && !doBreak { doBreak = true ct++ if ct%2 == 1 { spark -= 2 } else { spark += 2 } fmt.Println() fmt.Print(" ") } if i%7 == 0 && i != 0 && !doBreak { fmt.Println() ct++ if ct%2 == 1 { fmt.Print(" ") spark -= 2 } else { spark += 2 } } fmt.Printf("%*s ", spark, v) } } func LDateInfo(now, date time.Time, isLeap bool) { sdate := calendar.RapidLunarToSolar(date.Year(), int(date.Month()), date.Day(), isLeap) DateInfo(now, sdate) } func DateInfo(now, date time.Time) { if now.IsZero() { now = time.Now() } _, m, _, str := calendar.RapidSolarToLunar(date) gz := calendar.GanZhi(date.Year()) if m > 10 && int(date.Month()) < 3 { gz = calendar.GanZhi(date.Year() - 1) } fmt.Println("现在:", now.Format("2006年01月02日 15:04:05")) fmt.Println("-------------------------") xq := []string{"日", "一", "二", "三", "四", "五", "六"} fmt.Printf("公历:%s 星期%s\n", date.Format("2006年01月02日 15:04:05"), xq[date.Weekday()]) fmt.Println("农历:", gz+str) fmt.Printf("时间戳:%v\n", date.Unix()) fmt.Println("-------------------------") diff := date.Sub(now) fmt.Printf("距今: %.5f秒\n", diff.Seconds()) fmt.Printf("距今: %.5f分钟\n", diff.Minutes()) fmt.Printf("距今: %.5f小时\n", diff.Hours()) fmt.Printf("距今: %.5f天\n", diff.Hours()/24) fmt.Printf("距今: %.5f年\n", diff.Hours()/24/365.2425) fmt.Println("距今:", dayDiff(now, date)) } func dayDiff(date1, date2 time.Time) string { // 提取年、月、日 pr := "" if date1.After(date2) { pr = "-" date1, date2 = date2, date1 } years, months, days := date2.Date() yearDiff := years - date1.Year() monthDiff := int(months) - int(date1.Month()) dayDiff := days - date1.Day() // 处理负的月份和日期差 if dayDiff < 0 { monthDiff-- // 计算上个月的天数 prevMonth := date2.AddDate(0, -1, 0) dayDiff += time.Date(prevMonth.Year(), prevMonth.Month(), 0, 0, 0, 0, 0, time.UTC).Day() } if monthDiff < 0 { yearDiff-- monthDiff += 12 } // 提取小时、分钟和秒 hours := date2.Hour() - date1.Hour() minutes := date2.Minute() - date1.Minute() seconds := date2.Second() - date1.Second() // 处理负的小时、分钟和秒差 if seconds < 0 { minutes-- seconds += 60 } if minutes < 0 { hours-- minutes += 60 } if hours < 0 { days-- hours += 24 } return fmt.Sprintf("%s%d年%d月%d日%d时%d分%d秒", pr, yearDiff, monthDiff, dayDiff, hours, minutes, seconds) } func parseDate(base time.Time, date string, isTimestamp bool) (time.Time, error) { if isTimestamp { i, err := strconv.Atoi(date) if err != nil { return time.Time{}, err } return time.Unix(int64(i), 0), nil } if base.IsZero() { base = time.Now() } if strings.HasPrefix(date, "+") || strings.HasPrefix(date, "p") { val, err := staros.Calc(date[1:]) if err != nil { return time.Time{}, err } for val > 9.22e09 { val = val - 9.22e09 base = base.Add(9.22e09 * time.Second) } return base.Add(time.Second * time.Duration(int(val))), nil } if strings.HasPrefix(date, "-") || strings.HasPrefix(date, "—") || strings.HasPrefix(date, "~") || strings.HasPrefix(date, "!") || strings.HasPrefix(date, "m") { val, err := staros.Calc(date[1:]) if err != nil { return time.Time{}, err } for val > 9.22e09 { val = val - 9.22e09 base = base.Add(-9.22e09 * time.Second) } return base.Add(-1 * time.Second * time.Duration(int(val))), nil } if strings.Contains(date, "-") { d, err := time.ParseInLocation("2006-01-02 15:04:05", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("2006-01-02", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("2006-01-02T15:04:05-0700", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("2006-01-02T15:04:05Z", date, time.Local) if err == nil { return d, nil } return time.Time{}, err } if strings.Contains(date, "/") { d, err := time.ParseInLocation("2006/01/02 15:04:05", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("2006/01/02", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("02/01/2006 15:04:05", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("02/01/2006", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("01/02/2006 15:04:05", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("01/02/2006", date, time.Local) if err == nil { return d, nil } return time.Time{}, err } d, err := time.ParseInLocation("20060102150405", date, time.Local) if err == nil { return d, nil } d, err = time.ParseInLocation("20060102", date, time.Local) if err == nil { return d, nil } return time.Time{}, fmt.Errorf("无法解析日期") }