432 lines
10 KiB
Go
432 lines
10 KiB
Go
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("无法解析日期")
|
||
}
|