|
|
|
|
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("无法解析日期")
|
|
|
|
|
}
|