You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
star/astro/calendar.go

432 lines
10 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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