Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support julian day and modified julian day #218

Merged
merged 13 commits into from
Jan 26, 2024
3 changes: 2 additions & 1 deletion README.cn.md
Original file line number Diff line number Diff line change
@@ -1360,7 +1360,8 @@ fmt.Printf("%d", person.Field8) // 1596604455999999999

目前支持的日历有

* [中国农历](./calendar/lunar/README.cn.md "中国农历"): 由 [gouguoyin](https://github.com/gouguoyin "gouguoyin") 贡献
* [中国农历](./calendar/lunar/README.cn.md "中国农历")
* [儒略日/简化儒略日](./calendar/julian/README.cn.md "儒略日/简化儒略日")

##### 国际化

3 changes: 2 additions & 1 deletion README.jp.md
Original file line number Diff line number Diff line change
@@ -1360,7 +1360,8 @@ fmt.Printf("%d", person.Field8) // 1596604455999999999

現在サポートされているカレンダーは

* [中国の旧暦](./calendar/lunar/README.jp.md "中国の旧暦"): [gouguoyin](https://github.com/gouguoyin "gouguoyin") による貢献
* [中国の旧暦](./calendar/lunar/README.jp.md "中国の旧暦")
* [儒略の日/簡略化儒略の日](./calendar/julian/README.jp.md "儒略日/简化儒略日")

##### 国際化

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1355,8 +1355,8 @@ fmt.Printf("%d", person.Field8) // 1596604455999999999

The following calendars are supported

* [Chinese Lunar](./calendar/lunar/README.md "Chinese Lunar"):contributed
by [gouguoyin](https://github.com/gouguoyin "gouguoyin")
* [Chinese Lunar](./calendar/lunar/README.md "Chinese Lunar")
* [Julian Day/Modified Julian Day](./calendar/julian/README.md "JD/MJD")

##### I18n

20 changes: 19 additions & 1 deletion calendar.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package carbon

import (
"github.com/golang-module/carbon/v2/calendar/julian"
"github.com/golang-module/carbon/v2/calendar/lunar"
)

// Lunar converts gregorian calendar to lunar calendar.
// 将 公历 转为 农历
// 将 公历 转化为 农历
func (c Carbon) Lunar() (l lunar.Lunar) {
if c.Error != nil {
l.Error = c.Error
@@ -21,3 +22,20 @@ func CreateFromLunar(year, month, day, hour, minute, second int, isLeapMonth boo
c.time = lunar.NewLunar(year, month, day, hour, minute, second, isLeapMonth).ToGregorian().Time
return c
}

// Julian converts gregorian calendar to Julian calendar
// 将 公历 转化为 儒略历
func (c Carbon) Julian() (j julian.Julian) {
if c.Error != nil {
return j
}
return julian.NewGregorian(c.ToStdTime()).ToJulian()
}

// CreateFromJulian creates a Carbon instance from Julian Day.or Modified Julian Day
// 从 儒略日/简化儒略日 创建 Carbon 实例
func CreateFromJulian(f float64) Carbon {
c := NewCarbon()
c.time = julian.NewJulian(f).ToGregorian().Time
return c
}
35 changes: 35 additions & 0 deletions calendar/gregorian.go
Original file line number Diff line number Diff line change
@@ -128,6 +128,8 @@ func NewGregorian(t time.Time) (g Gregorian) {
return g
}

// Date gets gregorian year, month, and day like 2020, 8, 5.
// 获取公历年、月、日
func (g Gregorian) Date() (year, month, day int) {
if g.IsZero() {
return 0, 0, 0
@@ -138,66 +140,99 @@ func (g Gregorian) Date() (year, month, day int) {
return
}

// Clock gets gregorian hour, minute, and second like 13, 14, 15.
// 获取公历时、分、秒
func (g Gregorian) Clock() (hour, minute, second int) {
if g.IsZero() {
return 0, 0, 0
}
return g.Time.Clock()
}

// Year gets gregorian year like 2020.
// 获取公历年
func (g Gregorian) Year() int {
if g.IsZero() {
return 0
}
return g.Time.Year()
}

// Month gets gregorian month like 8.
// 获取公历月
func (g Gregorian) Month() int {
if g.IsZero() {
return 0
}
return int(g.Time.Month())
}

// Day gets gregorian day like 5.
// 获取公历日
func (g Gregorian) Day() int {
if g.IsZero() {
return 0
}
return g.Time.Day()
}

// Hour gets gregorian hour like 13.
// 获取公历小时
func (g Gregorian) Hour() int {
if g.IsZero() {
return 0
}
return g.Time.Hour()
}

// Minute gets gregorian minute like 14.
// 获取公历分钟数
func (g Gregorian) Minute() int {
if g.IsZero() {
return 0
}
return g.Time.Minute()
}

// Second gets gregorian second like 15.
// 获取公历秒数
func (g Gregorian) Second() int {
if g.IsZero() {
return 0
}
return g.Time.Second()
}

// Location gets gregorian timezone information.
// 获取时区信息
func (g Gregorian) Location() *time.Location {
return g.Time.Location()
}

// String implements the interface Stringer for Gregorian struct.
// 实现 Stringer 接口
func (g Gregorian) String() string {
if g.IsZero() {
return ""
}
return g.Time.Format(DateTimeLayout)
}

// IsZero reports whether is zero time.
// 是否是零值时间
func (g Gregorian) IsZero() bool {
return g.Time.IsZero()
}

// IsLeapYear reports whether is a leap year.
// 是否是闰年
func (g Gregorian) IsLeapYear() bool {
if g.IsZero() {
return false
}
year := g.Year()
if year%400 == 0 || (year%4 == 0 && year%100 != 0) {
return true
}
return false
}
159 changes: 91 additions & 68 deletions calendar/gregorian_test.go
Original file line number Diff line number Diff line change
@@ -1,256 +1,230 @@
package calendar

import (
"strconv"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestSolar_Year(t *testing.T) {
func TestGregorian_Year(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
name string
args args
want int
}{
{
name: "0",
args: args{NewGregorian(time.Time{})},
want: 0,
},
{
name: "2020",
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: 2020,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Year(), "NewGregorian(%v)", tt.args.g)
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Year(), "args(%v)", tt.args.g)
})
}
}

func TestSolar_Month(t *testing.T) {
func TestGregorian_Month(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
name string
args args
want int
}{
{
name: "0",
args: args{NewGregorian(time.Time{})},
want: 0,
},
{
name: "2020",
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: 8,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Month(), "NewGregorian(%v)", tt.args.g)
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Month(), "args(%v)", tt.args.g)
})
}
}

func TestSolar_Day(t *testing.T) {
func TestGregorian_Day(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
name string
args args
want int
}{
{
name: "0",
args: args{NewGregorian(time.Time{})},
want: 0,
},
{
name: "2020",
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: 5,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Day(), "NewGregorian(%v)", tt.args.g)
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Day(), "args(%v)", tt.args.g)
})
}
}

func TestSolar_Hour(t *testing.T) {
func TestGregorian_Hour(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
name string
args args
want int
}{
{
name: "0",
args: args{NewGregorian(time.Time{})},
want: 0,
},
{
name: "2020",
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: 13,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Hour(), "NewGregorian(%v)", tt.args.g)
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Hour(), "args(%v)", tt.args.g)
})
}
}

func TestSolar_Minute(t *testing.T) {
func TestGregorian_Minute(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
name string
args args
want int
}{
{
name: "0",
args: args{NewGregorian(time.Time{})},
want: 0,
},
{
name: "2020",
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: 14,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Minute(), "NewGregorian(%v)", tt.args.g)
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Minute(), "args(%v)", tt.args.g)
})
}
}

func TestSolar_Second(t *testing.T) {
func TestGregorian_Second(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
name string
args args
want int
}{
{
name: "0",
args: args{NewGregorian(time.Time{})},
want: 0,
},
{
name: "2020",
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: 15,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Second(), "NewGregorian(%v)", tt.args.g)
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Second(), "args(%v)", tt.args.g)
})
}
}

func TestSolar_String(t *testing.T) {
func TestGregorian_String(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
name string
args args
want string
}{
{
name: "0",
args: args{NewGregorian(time.Time{})},
want: "",
},
{
name: "2020",
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: "2020-08-05 13:14:15",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.String(), "NewGregorian(%v)", tt.args.g)
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.String(), "args(%v)", tt.args.g)
})
}
}

func TestSolar_Location(t *testing.T) {
func TestGregorian_Location(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
name string
args args
want string
}{
{
name: "UTC",
args: args{NewGregorian(time.Time{})},
want: "UTC",
},
{
name: "Local",
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: "Local",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Location().String(), "NewGregorian(%v)", tt.args.g)
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.Location().String(), "args(%v)", tt.args.g)
})
}
}

func TestSolar_Date(t *testing.T) {
func TestGregorian_Date(t *testing.T) {
type args struct {
Time time.Time
}
tests := []struct {
name string
args args
wantYear int
wantMonth int
wantDay int
}{
{
name: "zeroTime",
args: args{time.Time{}},
wantYear: 0,
wantMonth: 0,
wantDay: 0,
},
{
name: "normalDate",
args: args{time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local)},
wantYear: 2020,
wantMonth: 8,
wantDay: 5,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
g := NewGregorian(tt.args.Time)
gotYear, gotMonth, gotDay := g.Date()
assert.Equalf(t, tt.wantYear, gotYear, "Date()")
@@ -260,34 +234,31 @@ func TestSolar_Date(t *testing.T) {
}
}

func TestSolar_Clock(t *testing.T) {
func TestGregorian_Clock(t *testing.T) {
type args struct {
Time time.Time
}
tests := []struct {
name string
args args
wantHour int
wantMinute int
wantSecond int
}{
{
name: "zeroTime",
args: args{time.Time{}},
wantHour: 0,
wantMinute: 0,
wantSecond: 0,
},
{
name: "normalTime",
args: args{time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local)},
wantHour: 13,
wantMinute: 14,
wantSecond: 15,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
g := NewGregorian(tt.args.Time)
gotHour, goMinute, gotSecond := g.Clock()
assert.Equalf(t, tt.wantHour, gotHour, "Clock()")
@@ -296,3 +267,55 @@ func TestSolar_Clock(t *testing.T) {
})
}
}

func TestGregorian_IsZero(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
args args
want bool
}{
{
args: args{NewGregorian(time.Time{})},
want: true,
},
{
args: args{NewGregorian(time.Date(2020, 8, 5, 13, 14, 15, 0, time.Local))},
want: false,
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.IsZero(), "args(%v)", tt.args.g)
})
}
}

func TestGregorian_IsLeapYear(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
args args
want bool
}{
{
args: args{NewGregorian(time.Time{})},
want: false,
},
{
args: args{NewGregorian(time.Date(2020, 8, 5, 0, 0, 0, 0, time.Local))},
want: true,
},
{
args: args{NewGregorian(time.Date(2021, 8, 5, 0, 0, 0, 0, time.Local))},
want: false,
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.g.IsLeapYear(), "args(%v)", tt.args.g)
})
}
}
76 changes: 76 additions & 0 deletions calendar/julian/README.cn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Carbon

[![Carbon Release](https://img.shields.io/github/release/golang-module/carbon.svg)](https://github.com/golang-module/carbon/releases)
[![Go Test](https://github.com/golang-module/carbon/actions/workflows/test.yml/badge.svg)](https://github.com/golang-module/carbon/actions)
[![Go Report Card](https://goreportcard.com/badge/github.com/golang-module/carbon/v2)](https://goreportcard.com/report/github.com/golang-module/carbon/v2)
[![Go Coverage](https://codecov.io/gh/golang-module/carbon/branch/master/graph/badge.svg)](https://codecov.io/gh/golang-module/carbon)
[![Goproxy.cn](https://goproxy.cn/stats/github.com/golang-module/carbon/badges/download-count.svg)](https://goproxy.cn)
[![Carbon Doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/golang-module/carbon/v2)
[![License](https://img.shields.io/github/license/golang-module/carbon)](https://github.com/golang-module/carbon/blob/master/LICENSE)

简体中文 | [English](README.md) | [日本語](README.jp.md)

一个轻量级、语义化、对开发者友好的 golang 时间处理库,支持链式调用,已被 [awesome-go-cn](https://github.com/yinggaozhen/awesome-go-cn#日期和时间 "awesome-go-cn") 收录

[github.com/golang-module/carbon](https://github.com/golang-module/carbon "github.com/golang-module/carbon")

[gitee.com/golang-module/carbon](https://gitee.com/golang-module/carbon "gitee.com/golang-module/carbon")

#### 儒略日/简化儒略日

##### `公历` 转换成 `儒略日`
```go
// 默认保留 6 位小数精度
carbon.Parse("2024-01-24 12:00:00").Julian().JD() // 2460334
carbon.Parse("2024-01-24 13:14:15").Julian().JD() // 2460334.051563

// 保留 4 位小数精度
carbon.Parse("2024-01-24 12:00:00").Julian().JD(4) // 2460334
carbon.Parse("2024-01-24 13:14:15").Julian().JD(4) // 2460334.0516
```

##### `公历` 转换成 `简化儒略日`
```go
// 默认保留 6 位小数精度
carbon.Parse("2024-01-24 12:00:00").Julian().MJD() // 60333.5
carbon.Parse("2024-01-24 13:14:15").Julian().MJD() // 60333.551563

// 保留 4 位小数精度
carbon.Parse("2024-01-24 12:00:00").Julian().MJD(4) // 60333.5
carbon.Parse("2024-01-24 13:14:15").Julian().MJD(4) // 60333.5516
```

##### `儒略日` 转换成 `简化儒略日`
```go
// 默认保留 6 位小数精度
carbon.CreateFromJulian(2460334).Julian().MJD() // 60333.5
carbon.CreateFromJulian(2460334.051563).Julian().MJD() // 60332.551563

// 保留 4 位小数精度
carbon.CreateFromJulian(2460334).Julian().MJD(4) // 60333.5
carbon.CreateFromJulian(2460334.051563).Julian().MJD(4) // 60332.5516
```

##### `简化儒略日` 转换成 `儒略日`
```go
// 默认保留 6 位小数精度
carbon.CreateFromJulian(60333.5).Julian().JD()() // 2460334
carbon.CreateFromJulian(60333.551563).Julian().JD()() // 2460333.051563

// 保留 4 位小数精度
carbon.CreateFromJulian(60333.5).Julian().JD(4) // 2460334
carbon.CreateFromJulian(60333.551563).Julian().JD(4) // 2460333.0516
```

##### `儒略日`/`简化儒略日` 转换成 `公历`
```go
// 将 儒略日 转换成 公历
carbon.CreateFromJulian(2460334).ToDateTimeString() // 2024-01-24 12:00:00
carbon.CreateFromJulian(2460334.051563).ToDateTimeString() // 2024-01-24 13:14:15

// 将 简化儒略日 转换成 公历
carbon.CreateFromJulian(60333.5).ToDateTimeString() // 2024-01-24 12:00:00
carbon.CreateFromJulian(60333.551563).ToDateTimeString() // 2024-01-24 13:14:15
```


74 changes: 74 additions & 0 deletions calendar/julian/README.jp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Carbon

[![Carbon Release](https://img.shields.io/github/release/golang-module/carbon.svg)](https://github.com/golang-module/carbon/releases)
[![Go Test](https://github.com/golang-module/carbon/actions/workflows/test.yml/badge.svg)](https://github.com/golang-module/carbon/actions)
[![Go Report Card](https://goreportcard.com/badge/github.com/golang-module/carbon/v2)](https://goreportcard.com/report/github.com/golang-module/carbon/v2)
[![Go Coverage](https://codecov.io/gh/golang-module/carbon/branch/master/graph/badge.svg)](https://codecov.io/gh/golang-module/carbon)
[![Goproxy.cn](https://goproxy.cn/stats/github.com/golang-module/carbon/badges/download-count.svg)](https://goproxy.cn)
[![Carbon Doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/golang-module/carbon/v2)
[![License](https://img.shields.io/github/license/golang-module/carbon)](https://github.com/golang-module/carbon/blob/master/LICENSE)

日本語 | [English](README.md) | [简体中文](README.cn.md)

一个轻量级、语义化、对开发者友好的 golang 时间处理库,支持链式调用,已被 [awesome-go-cn](https://github.com/yinggaozhen/awesome-go-cn#日期和时间 "awesome-go-cn") 收录

[github.com/golang-module/carbon](https://github.com/golang-module/carbon "github.com/golang-module/carbon")

[gitee.com/golang-module/carbon](https://gitee.com/golang-module/carbon "gitee.com/golang-module/carbon")

#### 儒略の日/簡略儒略の日

##### `西暦``儒略日` に変換する
```go
// デフォルトの保持 6 ビット小数点精度
carbon.Parse("2024-01-24 12:00:00").Julian().JD() // 2460334
carbon.Parse("2024-01-24 13:14:15").Julian().JD() // 2460334.051563

// 4 ビット小数点精度の保持
carbon.Parse("2024-01-24 12:00:00").Julian().JD(4) // 2460334
carbon.Parse("2024-01-24 13:14:15").Julian().JD(4) // 2460334.0516
```

##### `西暦``簡略儒略日` に変換する
```go
// デフォルトの保持 6 ビット小数点精度
carbon.Parse("2024-01-24 12:00:00").Julian().MJD() // 60333.5
carbon.Parse("2024-01-24 13:14:15").Julian().MJD() // 60333.551563

// 4 ビット小数点精度の保持
carbon.Parse("2024-01-24 12:00:00").Julian().MJD(4) // 60333.5
carbon.Parse("2024-01-24 13:14:15").Julian().MJD(4) // 60333.5516
```

##### `儒略日``簡略儒略日` に変換する
```go
// デフォルトの保持 6 ビット小数点精度
carbon.CreateFromJulian(2460334).Julian().MJD() // 60333.5
carbon.CreateFromJulian(2460334.051563).Julian().MJD() // 60332.551563

// 4 ビット小数点精度の保持
carbon.CreateFromJulian(2460334).Julian().MJD(4) // 60333.5
carbon.CreateFromJulian(2460334.051563).Julian().MJD(4) // 60332.5516
```

##### `簡略儒略日``儒略日` に変換する
```go
// デフォルトの保持 6 ビット小数点精度
carbon.CreateFromJulian(60333.5).Julian().JD()() // 2460334
carbon.CreateFromJulian(60333.551563).Julian().JD()() // 2460333.051563

// 4 ビット小数点精度の保持
carbon.CreateFromJulian(60333.5).Julian().JD(4) // 2460334
carbon.CreateFromJulian(60333.551563).Julian().JD(4) // 2460333.0516
```

##### `儒略日`/`簡略儒略日``公历` に変換する
```go
// 儒略日 を 公历 に変換する
carbon.CreateFromJulian(2460334).ToDateTimeString() // 2024-01-24 12:00:00
carbon.CreateFromJulian(2460334.051563).ToDateTimeString() // 2024-01-24 13:14:15

// 簡略儒略日` を 公历 に変換する
carbon.CreateFromJulian(60333.5).ToDateTimeString() // 2024-01-24 12:00:00
carbon.CreateFromJulian(60333.551563).ToDateTimeString() // 2024-01-24 13:14:15
```
77 changes: 77 additions & 0 deletions calendar/julian/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Carbon

[![Carbon Release](https://img.shields.io/github/release/golang-module/carbon.svg)](https://github.com/golang-module/carbon/releases)
[![Go Test](https://github.com/golang-module/carbon/actions/workflows/test.yml/badge.svg)](https://github.com/golang-module/carbon/actions)
[![Go Report Card](https://goreportcard.com/badge/github.com/golang-module/carbon/v2)](https://goreportcard.com/report/github.com/golang-module/carbon/v2)
[![Go Coverage](https://codecov.io/gh/golang-module/carbon/branch/master/graph/badge.svg)](https://codecov.io/gh/golang-module/carbon)
[![Goproxy.cn](https://goproxy.cn/stats/github.com/golang-module/carbon/badges/download-count.svg)](https://goproxy.cn)
[![Carbon Doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/golang-module/carbon/v2)
[![License](https://img.shields.io/github/license/golang-module/carbon)](https://github.com/golang-module/carbon/blob/master/LICENSE)

English | [简体中文](README.cn.md) | [日本語](README.jp.md)

#### Introduction

A simple, semantic and developer-friendly golang package for time, has been included
by [awesome-go](https://github.com/avelino/awesome-go#date-and-time "awesome-go")

[github.com/golang-module/carbon](https://github.com/golang-module/carbon "github.com/golang-module/carbon")

[gitee.com/golang-module/carbon](https://gitee.com/golang-module/carbon "gitee.com/golang-module/carbon")

#### Julian Day/Modified Julian Day

##### Convert `Gregorian` calendar to `Julian Day`
```go
// By default, 6 decimal places are retained for precision
carbon.Parse("2024-01-24 12:00:00").Julian().JD() // 2460334
carbon.Parse("2024-01-24 13:14:15").Julian().JD() // 2460334.051563

// 4 decimal places are retained for precision
carbon.Parse("2024-01-24 12:00:00").Julian().JD(4) // 2460334
carbon.Parse("2024-01-24 13:14:15").Julian().JD(4) // 2460334.0516
```

##### Convert `Gregorian` calendar to `Modified Julian Day`
```go
// By default, 6 decimal places are retained for precision
carbon.Parse("2024-01-24 12:00:00").Julian().MJD() // 60333.5
carbon.Parse("2024-01-24 13:14:15").Julian().MJD() // 60333.551563

// 4 decimal places are retained for precision
carbon.Parse("2024-01-24 12:00:00").Julian().MJD(4) // 60333.5
carbon.Parse("2024-01-24 13:14:15").Julian().MJD(4) // 60333.5516
```

##### Convert `Julian Day` to `Modified Julian Day`
```go
// By default, 6 decimal places are retained for precision
carbon.CreateFromJulian(2460334).Julian().MJD() // 60333.5
carbon.CreateFromJulian(2460334.051563).Julian().MJD() // 60332.551563

// 4 decimal places are retained for precision
carbon.CreateFromJulian(2460334).Julian().MJD(4) // 60333.5
carbon.CreateFromJulian(2460334.051563).Julian().MJD(4) // 60332.5516
```

##### Convert `Modified Julian Day` to `Julian Day`
```go
// By default, 6 decimal places are retained for precision
carbon.CreateFromJulian(60333.5).Julian().JD()() // 2460334
carbon.CreateFromJulian(60333.551563).Julian().JD()() // 2460333.051563

// 4 decimal places are retained for precision
carbon.CreateFromJulian(60333.5).Julian().JD(4) // 2460334
carbon.CreateFromJulian(60333.551563).Julian().JD(4) // 2460333.0516
```

##### Convert `Julian Day`/`Modified Julian Day` to `Gregorian` calendar
```go
// Convert Julian Day to Gregorian calendar`
carbon.CreateFromJulian(2460334).ToDateTimeString() // 2024-01-24 12:00:00
carbon.CreateFromJulian(2460334.051563).ToDateTimeString() // 2024-01-24 13:14:15

// Convert Modified Julian Day to Gregorian calendar`
carbon.CreateFromJulian(60333.5).ToDateTimeString() // 2024-01-24 12:00:00
carbon.CreateFromJulian(60333.551563).ToDateTimeString() // 2024-01-24 13:14:15
```
140 changes: 140 additions & 0 deletions calendar/julian/julian.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package julian

import (
"math"
"strconv"
"time"

"github.com/golang-module/carbon/v2/calendar"
)

var (
// julian day or modified julian day decimal precision
// 儒略日或简化儒略日小数精度
decimalPrecision = 6
)

// Gregorian defines a Gregorian struct.
// 定义 Gregorian 结构体
type Gregorian struct {
calendar.Gregorian
}

// NewGregorian returns a new Gregorian instance.
// 初始化 Gregorian 结构体
func NewGregorian(t time.Time) (g Gregorian) {
g.Time = t
return g
}

// Julian defines a Julian struct.
// 定义 Julian 结构体
type Julian struct {
jd, mjd float64
}

// NewJulian returns a new Julian instance.
// 初始化 Julian 结构体
func NewJulian(f float64) (j Julian) {
// get length of the integer part of f
n := len(strconv.Itoa(int(math.Ceil(f))))
switch n {
// modified julian day
case 5:
j.mjd = f
j.jd = f + 2400000.5
// julian day
case 7:
j.jd = f
j.mjd = f - 2400000.5
default:
j.jd = 0
j.mjd = 0
}
return
}

// ToJulian Convert Gregorian calendar to Julian calendar.
// 将 公历 转化为 儒略历
func (g Gregorian) ToJulian() (j Julian) {
if g.IsZero() {
return j
}
y := g.Year()
m := g.Month()
d := float64(g.Day()) + ((float64(g.Second())/60+float64(g.Minute()))/60+float64(g.Hour()))/24
n := 0
if y*372+m*31+int(d) >= 588829 {
n = y / 100
n = 2 - n + n/4
}
if m <= 2 {
m += 12
y--
}
jd := float64(int(365.25*(float64(y)+4716))) + float64(int(30.6001*(float64(m)+1))) + d + float64(n) - 1524.5
return NewJulian(jd)
}

// ToGregorian Convert Julian calendar to Gregorian calendar.
// 将 儒略历 转化为 公历
func (j Julian) ToGregorian() (g Gregorian) {
if j.jd == 0 || j.mjd == 0 {
return g
}
d := int(j.jd + 0.5)
f := j.jd + 0.5 - float64(d)
if d >= 2299161 {
c := int((float64(d) - 1867216.25) / 36524.25)
d += 1 + c - c/4
}
d += 1524
year := int((float64(d) - 122.1) / 365.25)
d -= int(365.25 * float64(year))
month := int(float64(d) / 30.601)
d -= int(30.601 * float64(month))
day := d
if month > 13 {
month -= 13
year -= 4715
} else {
month -= 1
year -= 4716
}
f *= 24
hour := int(f)

f -= float64(hour)
f *= 60
minute := int(f)

f -= float64(minute)
f *= 60
second := int(math.Round(f))
return NewGregorian(time.Date(year, time.Month(month), day, hour, minute, second, 0, time.Local))
}

// JD gets julian day like 2460332.5
// 获取儒略日
func (j Julian) JD(precision ...int) float64 {
if len(precision) > 0 {
decimalPrecision = precision[0]
}
return parseFloat64(j.jd, decimalPrecision)
}

// MJD gets modified julian day like 60332
// 获取简化儒略日
func (j Julian) MJD(precision ...int) float64 {
if len(precision) > 0 {
decimalPrecision = precision[0]
}
return parseFloat64(j.mjd, decimalPrecision)
}

// parseFloat64 round to n decimal places
// 四舍五入保留 n 位小数点
func parseFloat64(f float64, n int) float64 {
p := math.Pow10(n)
return math.Round(f*p) / p
}
145 changes: 145 additions & 0 deletions calendar/julian/julian_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package julian

import (
"strconv"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestGregorian_ToJulian(t *testing.T) {
type args struct {
g Gregorian
}
type want struct {
j Julian
}
tests := []struct {
args args
want want
}{
{
args: args{NewGregorian(time.Time{})},
want: want{NewJulian(0)},
},
{
args: args{NewGregorian(time.Date(2024, 1, 23, 0, 0, 0, 0, time.Local))},
want: want{NewJulian(2460332.5)},
},
{
args: args{NewGregorian(time.Date(2024, 1, 23, 0, 0, 0, 0, time.Local))},
want: want{NewJulian(60332)},
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want.j, (tt.args.g).ToJulian(), "args{%v}", tt.args.g)
})
}
}

func TestJulian_ToGregorian(t *testing.T) {
type args struct {
j Julian
}
tests := []struct {
args args
want string
}{
{
args: args{NewJulian(0)},
want: "",
},
{
args: args{NewJulian(2460332.5)},
want: "2024-01-23 00:00:00",
},
{
args: args{NewJulian(60332)},
want: "2024-01-23 00:00:00",
},
{
args: args{NewJulian(2460333)},
want: "2024-01-23 12:00:00",
},
{
args: args{NewJulian(60332.5)},
want: "2024-01-23 12:00:00",
},

{
args: args{NewJulian(2460333.051563)},
want: "2024-01-23 13:14:15",
},
{
args: args{NewJulian(60332.551563)},
want: "2024-01-23 13:14:15",
},
{
args: args{NewJulian(60232.5)},
want: "2023-10-15 12:00:00",
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, (tt.args.j).ToGregorian().String(), "args{%v}", tt.args.j)
})
}
}

func TestGregorian_JD(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
args args
want float64
}{
{
args: args{NewGregorian(time.Date(2024, 1, 23, 0, 0, 0, 0, time.Local))},
want: 2460332.5,
},
{
args: args{NewGregorian(time.Date(2024, 1, 23, 12, 0, 0, 0, time.Local))},
want: 2460333,
},
{
args: args{NewGregorian(time.Date(2024, 1, 23, 13, 14, 15, 0, time.Local))},
want: 2460333.051563,
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, (tt.args.g).ToJulian().JD(6), "args{%v}", tt.args.g)
})
}
}

func TestGregorian_MJD(t *testing.T) {
type args struct {
g Gregorian
}
tests := []struct {
args args
want float64
}{
{
args: args{NewGregorian(time.Date(2024, 1, 23, 0, 0, 0, 0, time.Local))},
want: 60332,
},
{
args: args{NewGregorian(time.Date(2024, 1, 23, 12, 0, 0, 0, time.Local))},
want: 60332.5,
},
{
args: args{NewGregorian(time.Date(2024, 1, 23, 13, 14, 15, 0, time.Local))},
want: 60332.551563,
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, (tt.args.g).ToJulian().MJD(6), "args{%v}", tt.args.g)
})
}
}
28 changes: 0 additions & 28 deletions calendar/lunar/README.cn.md
Original file line number Diff line number Diff line change
@@ -87,34 +87,6 @@ carbon.Parse("2020-08-05 13:14:15").Lunar().IsRoosterYear() // false
carbon.Parse("2020-08-05 13:14:15").Lunar().IsDogYear() // false
// 是否是猪年
carbon.Parse("2020-08-05 13:14:15").Lunar().IsPigYear() // false

// 获取农历时辰
carbon.Parse("2020-02-05 21:00:00").Lunar().DoubleHour() // 亥时

// 是否是子时
carbon.Parse("2020-03-21 00:00:00").Lunar().IsFirstDoubleHour() // true
// 是否是丑时
carbon.Parse("2020-03-21 01:00:00").Lunar().IsSecondDoubleHour() // true
// 是否是寅时
carbon.Parse("2020-03-21 03:00:00").Lunar().IsThirdDoubleHour() // true
// 是否是卯时
carbon.Parse("2020-03-21 05:00:00").Lunar().IsFourthDoubleHour() // true
// 是否是辰时
carbon.Parse("2020-03-21 07:00:00").Lunar().IsFifthDoubleHour() // true
// 是否是巳时
carbon.Parse("2020-03-21 09:00:00").Lunar().IsSixthDoubleHour() // true
// 是否是午时
carbon.Parse("2020-03-21 11:00:00").Lunar().IsSeventhDoubleHour() // true
// 是否是未时
carbon.Parse("2020-03-21 13:00:00").Lunar().IsEighthDoubleHour() // true
// 是否是申时
carbon.Parse("2020-03-21 15:00:00").Lunar().IsNinthDoubleHour() // true
// 是否是酉时
carbon.Parse("2020-03-21 17:00:00").Lunar().IsTenthDoubleHour() // true
// 是否是戌时
carbon.Parse("2020-03-21 19:00:00").Lunar().IsEleventhDoubleHour() // true
// 是否是亥时
carbon.Parse("2020-03-21 21:00:00").Lunar().IsTwelfthDoubleHour() // true
```

##### `农历` 转化成 `公历`
28 changes: 0 additions & 28 deletions calendar/lunar/README.jp.md
Original file line number Diff line number Diff line change
@@ -87,34 +87,6 @@ carbon.Parse("2020-08-05 13:14:15").Lunar().IsRoosterYear() // false
carbon.Parse("2020-08-05 13:14:15").Lunar().IsDogYear() // false
// 豚年かどうか
carbon.Parse("2020-08-05 13:14:15").Lunar().IsPigYear() // false

// 旧正月を取得する
carbon.Parse("2020-02-05 21:00:00").Lunar().DoubleHour() // 亥时

// であるかどうかね時
carbon.Parse("2020-03-21 00:00:00").Lunar().IsFirstDoubleHour() // true
// であるかどううし時
carbon.Parse("2020-03-21 01:00:00").Lunar().IsSecondDoubleHour() // true
// であるかどうとら時
carbon.Parse("2020-03-21 03:00:00").Lunar().IsThirdDoubleHour() // true
// であるかどうう時
carbon.Parse("2020-03-21 05:00:00").Lunar().IsFourthDoubleHour() // true
// であるかどうたつ時
carbon.Parse("2020-03-21 07:00:00").Lunar().IsFifthDoubleHour() // true
// であるかどうみ時
carbon.Parse("2020-03-21 09:00:00").Lunar().IsSixthDoubleHour() // true
// であるかどううま時
carbon.Parse("2020-03-21 11:00:00").Lunar().IsSeventhDoubleHour() // true
// であるかどうひつじ時
carbon.Parse("2020-03-21 13:00:00").Lunar().IsEighthDoubleHour() // true
// であるかどうさる時
carbon.Parse("2020-03-21 15:00:00").Lunar().IsNinthDoubleHour() // true
// であるかどうとり時
carbon.Parse("2020-03-21 17:00:00").Lunar().IsTenthDoubleHour() // true
// であるかどういぬ時
carbon.Parse("2020-03-21 19:00:00").Lunar().IsEleventhDoubleHour() // true
// であるかどうい時
carbon.Parse("2020-03-21 21:00:00").Lunar().IsTwelfthDoubleHour() // true
```

##### `旧暦``西暦`に変換する
32 changes: 2 additions & 30 deletions calendar/lunar/README.md
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ by [awesome-go](https://github.com/avelino/awesome-go#date-and-time "awesome-go"

> Currently only `200` years from `1900` to `2100` are supported
##### Convert Gregorian into Lunar
##### Convert `Gregorian` calendar to `Lunar` calendar

```go
// Get Lunar year of animal
@@ -90,37 +90,9 @@ carbon.Parse("2020-08-05 13:14:15").Lunar().IsRoosterYear() // false
carbon.Parse("2020-08-05 13:14:15").Lunar().IsDogYear() // false
// Whether is a lunar year of the dig
carbon.Parse("2020-08-05 13:14:15").Lunar().IsPigYear() // false

// Get lunar double-hour
carbon.Parse("2020-02-05 21:00:00").Lunar().DoubleHour() // 亥时

// Whether is FirstDoubleHour
carbon.Parse("2020-03-21 00:00:00").Lunar().IsFirstDoubleHour() // true
// Whether is SecondDoubleHour
carbon.Parse("2020-03-21 01:00:00").Lunar().IsSecondDoubleHour() // true
// Whether is ThirdDoubleHour
carbon.Parse("2020-03-21 03:00:00").Lunar().IsThirdDoubleHour() // true
// Whether is FourthDoubleHour
carbon.Parse("2020-03-21 05:00:00").Lunar().IsFourthDoubleHour() // true
// Whether is FifthDoubleHour
carbon.Parse("2020-03-21 07:00:00").Lunar().IsFifthDoubleHour() // true
// Whether is SixthDoubleHour
carbon.Parse("2020-03-21 09:00:00").Lunar().IsSixthDoubleHour() // true
// Whether is SeventhDoubleHour
carbon.Parse("2020-03-21 11:00:00").Lunar().IsSeventhDoubleHour() // true
// Whether is EighthDoubleHour
carbon.Parse("2020-03-21 13:00:00").Lunar().IsEighthDoubleHour() // true
// Whether is NinthDoubleHour
carbon.Parse("2020-03-21 15:00:00").Lunar().IsNinthDoubleHour() // true
// Whether is TenthDoubleHour
carbon.Parse("2020-03-21 17:00:00").Lunar().IsTenthDoubleHour() // true
// Whether is EleventhDoubleHour
carbon.Parse("2020-03-21 19:00:00").Lunar().IsEleventhDoubleHour() // true
// Whether is TwelfthDoubleHour
carbon.Parse("2020-03-21 21:00:00").Lunar().IsTwelfthDoubleHour() // true
```

##### Convert Lunar into Gregorian
##### Convert `Lunar` calendar to `Gregorian` calendar

```go
// Convert the Lunar Calendar December 11, 2023 to the gregorian calendar
292 changes: 42 additions & 250 deletions calendar/lunar/lunar.go
Original file line number Diff line number Diff line change
@@ -10,11 +10,25 @@ import (

var (
minYear, maxYear = 1900, 2100
lunarNumbers = []string{"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"}
lunarMonths = []string{"正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "腊"}
lunarTimes = []string{"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"}
lunarAnimals = []string{"猴", "鸡", "狗", "猪", "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊"}
lunarFestivals = []string{"春节", "元宵节", "端午节", "七夕节", "中元节", "中秋节", "重阳节", "寒衣节", "下元节", "腊八节", "小年"}

numbers = []string{"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"}
months = []string{"正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "腊"}
animals = []string{"猴", "鸡", "狗", "猪", "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊"}

festivals = map[string]string{
"1-1": "春节",
"1-15": "元宵节",
"2-2": "龙抬头",
"3-3": "上巳节",
"5-5": "端午节",
"7-7": "七夕节",
"7-15": "中元节",
"8-15": "中秋节",
"9-9": "重阳节",
"10-1": "寒衣节",
"10-15": "下元节",
"12-8": "腊八节",
}

lunarTerms = []int{
0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
@@ -63,7 +77,7 @@ type Lunar struct {
// 初始化 Gregorian 结构体
func NewGregorian(t time.Time) (g Gregorian) {
g.Time = t
return g
return
}

// NewLunar returns a new Lunar instance.
@@ -72,18 +86,16 @@ func NewLunar(year, month, day, hour, minute, second int, isLeapMonth bool) (l L
l.year, l.month, l.day = year, month, day
l.hour, l.minute, l.second = hour, minute, second
l.isLeapMonth = isLeapMonth
return l
return
}

// ToLunar Convert Solar calendar into Gregorian calendar
// ToLunar Convert Gregorian calendar to Lunar calendar.
// 将 公历 转化为 农历
func (g Gregorian) ToLunar() (l Lunar) {
// leapMonths:闰月总数,daysOfYear:年天数,daysOfMonth:月天数,leapMonth:闰月月份
daysInYear, daysInMonth, leapMonth := 365, 30, 0
year := g.Year()
if year < minYear || year > maxYear {
if g.Year() < minYear || g.Year() > maxYear {
l.Error = invalidYearError()
return l
return
}
// 与 1900-01-31 相差多少天
offset := g.diffInDays(time.Date(minYear, 1, 31, 0, 0, 0, 0, g.Location()))
@@ -125,15 +137,15 @@ func (g Gregorian) ToLunar() (l Lunar) {
}
l.day = offset + 1
l.hour, l.minute, l.second = g.Clock()
return l
return
}

// ToGregorian Convert Lunar calendar into Gregorian calendar
// ToGregorian Convert Lunar calendar to Gregorian calendar.
// 将 农历 转化为 公历
func (l Lunar) ToGregorian() (g Gregorian) {
if l.year < minYear || l.year > maxYear {
g.Error = invalidYearError()
return g
return
}

days := l.getDaysInMonth()
@@ -143,10 +155,10 @@ func (l Lunar) ToGregorian() (g Gregorian) {
if l.isLeapMonth {
offset += days
}

ts := (offset+l.day)*86400 + -2206512000 + l.hour*3600 + l.minute*60 + l.second
g.Time = time.Unix(int64(ts), 0)
return g
// https://github.com/golang-module/carbon/issues/219
ts := int64(offset+l.day)*86400 - int64(2206512000) + int64(l.hour)*3600 + int64(l.minute)*60 + int64(l.second)
g.Time = time.Unix(ts, 0)
return
}

func (g Gregorian) diffInDays(t time.Time) int {
@@ -213,41 +225,16 @@ func (l Lunar) Animal() string {
if l.Error != nil {
return ""
}
return lunarAnimals[l.year%calendar.MonthsPerYear]
return animals[l.year%calendar.MonthsPerYear]
}

// Festival gets lunar festival name like "春节".
// 获取农历节日
func (l Lunar) Festival() (festival string) {
func (l Lunar) Festival() string {
if l.Error != nil {
return
}
month, day := l.month, l.day
switch {
case month == 1 && day == 1:
festival = lunarFestivals[0]
case month == 1 && day == 15:
festival = lunarFestivals[1]
case month == 5 && day == 5:
festival = lunarFestivals[2]
case month == 7 && day == 7:
festival = lunarFestivals[3]
case month == 7 && day == 15:
festival = lunarFestivals[4]
case month == 8 && day == 15:
festival = lunarFestivals[5]
case month == 9 && day == 9:
festival = lunarFestivals[6]
case month == 10 && day == 1:
festival = lunarFestivals[7]
case month == 10 && day == 15:
festival = lunarFestivals[8]
case month == 12 && day == 8:
festival = lunarFestivals[9]
case month == 12 && day == 23:
festival = lunarFestivals[10]
return ""
}
return
return festivals[fmt.Sprintf("%d-%d", l.month, l.day)]
}

// Year gets lunar year like 2020.
@@ -268,7 +255,7 @@ func (l Lunar) Month() int {
return l.month
}

// LeapMonth gets lunar leap month like 8.
// LeapMonth gets lunar leap month like 2.
// 获取农历闰月月份
func (l Lunar) LeapMonth() int {
if l.Error != nil {
@@ -293,23 +280,23 @@ func (l Lunar) ToYearString() string {
return ""
}
year := fmt.Sprintf("%d", l.year)
for i, replace := range lunarNumbers {
year = strings.Replace(year, fmt.Sprintf("%d", i), replace, -1)
for index, number := range numbers {
year = strings.Replace(year, fmt.Sprintf("%d", index), number, -1)
}
return year
}

// ToMonthString outputs a string in lunar month format like "正月".
// 获取农历月字符串
func (l Lunar) ToMonthString() string {
func (l Lunar) ToMonthString() (month string) {
if l.Error != nil {
return ""
}
month := lunarMonths[l.month-1] + "月"
month = months[l.month-1] + "月"
if l.IsLeapMonth() {
return "闰" + month
}
return month
return
}

// ToDayString outputs a string in lunar day format like "廿一".
@@ -318,7 +305,7 @@ func (l Lunar) ToDayString() (day string) {
if l.Error != nil {
return
}
num := lunarNumbers[l.day%10]
num := numbers[l.day%10]
switch {
case l.day == 30:
day = "三十"
@@ -515,198 +502,3 @@ func (l Lunar) IsPigYear() bool {
}
return false
}

// DoubleHour gets double-hour name like "子时".
// 获取当前时辰
func (l Lunar) DoubleHour() (dh string) {
if l.Error != nil {
return ""
}
hour, minute := l.hour, l.minute
switch {
case hour >= 23, hour == 0 && minute <= 59:
dh = lunarTimes[0] // FirstDoubleHour
case hour >= 1 && hour < 3:
dh = lunarTimes[1] // SecondDoubleHour
case hour >= 3 && hour < 5:
dh = lunarTimes[2] // ThirdDoubleHour
case hour >= 5 && hour < 7:
dh = lunarTimes[3] // FourthDoubleHour
case hour >= 7 && hour < 9:
dh = lunarTimes[4] // FifthDoubleHour
case hour >= 9 && hour < 11:
dh = lunarTimes[5] // SixthDoubleHour
case hour >= 11 && hour < 13:
dh = lunarTimes[6] // SeventhDoubleHour
case hour >= 13 && hour < 15:
dh = lunarTimes[7] // EighthDoubleHour
case hour >= 15 && hour < 17:
dh = lunarTimes[8] // NinthDoubleHour
case hour >= 17 && hour < 19:
dh = lunarTimes[9] // TenthDoubleHour
case hour >= 19 && hour < 21:
dh = lunarTimes[10] // EleventhDoubleHour
case hour >= 21 && hour < 23:
dh = lunarTimes[11] // TwelfthDoubleHour
}
return dh + "时"
}

// IsFirstDoubleHour reports whether is FirstDoubleHour.
// 是否是子时
func (l Lunar) IsFirstDoubleHour() bool {
if l.Error != nil {
return false
}
hour, minute := l.hour, l.minute
if hour >= 23 {
return true
}
if hour == 0 && minute <= 59 {
return true
}
return false
}

// IsSecondDoubleHour reports whether is SecondDoubleHour.
// 是否是丑时
func (l Lunar) IsSecondDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 1 && hour < 3 {
return true
}
return false
}

// IsThirdDoubleHour reports whether is ThirdDoubleHour.
// 是否是寅时
func (l Lunar) IsThirdDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 3 && hour < 5 {
return true
}
return false
}

// IsFourthDoubleHour reports whether is FourthDoubleHour.
// 是否是卯时
func (l Lunar) IsFourthDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 5 && hour < 7 {
return true
}
return false
}

// IsFifthDoubleHour reports whether is FifthDoubleHour.
// 是否是辰时
func (l Lunar) IsFifthDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 7 && hour < 9 {
return true
}
return false
}

// IsSixthDoubleHour reports whether is SixthDoubleHour.
// 是否是巳时
func (l Lunar) IsSixthDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 9 && hour < 11 {
return true
}
return false
}

// IsSeventhDoubleHour reports whether is SeventhDoubleHour.
// 是否是午时
func (l Lunar) IsSeventhDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 11 && hour < 13 {
return true
}
return false
}

// IsEighthDoubleHour reports whether is EighthDoubleHour.
// 是否是未时
func (l Lunar) IsEighthDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 13 && hour < 15 {
return true
}
return false
}

// IsNinthDoubleHour reports whether is NinthDoubleHour.
// 是否是申时
func (l Lunar) IsNinthDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 15 && hour < 17 {
return true
}
return false
}

// IsTenthDoubleHour reports whether is TenthDoubleHour.
// 是否是酉时
func (l Lunar) IsTenthDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 17 && hour < 19 {
return true
}
return false
}

// IsEleventhDoubleHour reports whether is EleventhDoubleHour.
// 是否是戌时
func (l Lunar) IsEleventhDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 19 && hour < 21 {
return true
}
return false
}

// IsTwelfthDoubleHour reports whether is TwelfthDoubleHour.
// 是否是亥时
func (l Lunar) IsTwelfthDoubleHour() bool {
if l.Error != nil {
return false
}
hour := l.hour
if hour >= 21 && hour < 23 {
return true
}
return false
}
416 changes: 0 additions & 416 deletions calendar/lunar/lunar_test.go

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions calendar_bench_test.go
Original file line number Diff line number Diff line change
@@ -14,3 +14,16 @@ func BenchmarkCarbon_CreateFromLunar(b *testing.B) {
CreateFromLunar(2023, 12, 8, 0, 0, 0, false)
}
}

func BenchmarkCarbon_Julian(b *testing.B) {
now := Now()
for n := 0; n < b.N; n++ {
now.Julian()
}
}

func BenchmarkCarbon_CreateFromJulian(b *testing.B) {
for n := 0; n < b.N; n++ {
CreateFromJulian(60332)
}
}
138 changes: 127 additions & 11 deletions calendar_unit_test.go
Original file line number Diff line number Diff line change
@@ -2,29 +2,145 @@ package carbon

import (
"fmt"
"strconv"
"testing"

"github.com/stretchr/testify/assert"
)

func TestCarbon_Lunar(t *testing.T) {
l := Parse("2024-01-18").Lunar()
func TestCreateFromLunar1(t *testing.T) {
l := CreateFromLunar(2023, 12, 11, 0, 0, 0, false)
assert.Nil(t, l.Error)
assert.Equal(t, "2023-12-08 00:00:00", l.String())
assert.Equal(t, "2024-01-21 00:00:00", l.String())
}

func TestCarbon_Lunar(t *testing.T) {
type args struct {
c Carbon
}
tests := []struct {
args args
want string
}{
{
args: args{Parse("xxx")},
want: "",
},
{
args: args{Parse("2024-01-18 00:00:00")},
want: "2023-12-08 00:00:00",
},
{
args: args{Parse("2024-01-21 00:00:00")},
want: "2023-12-11 00:00:00",
},
{
args: args{Parse("2024-01-24 12:00:00")},
want: "2023-12-14 12:00:00",
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, tt.args.c.Lunar().String(), "args{%v}", tt.args.c)
})
}
}

func TestCreateFromLunar(t *testing.T) {
l := CreateFromLunar(2023, 12, 11, 0, 0, 0, false)
assert.Nil(t, l.Error)
assert.Equal(t, "2024-01-21 00:00:00", l.String())
type args struct {
year, month, day, hour, minute, second int
isLeapMonth bool
}
tests := []struct {
args args
want string
}{
{
args: args{year: 2023, month: 12, day: 11, isLeapMonth: false},
want: "2024-01-21 00:00:00",
},
{
args: args{year: 2023, month: 12, day: 8, isLeapMonth: false},
want: "2024-01-18 00:00:00",
},
{
args: args{year: 2023, month: 12, day: 14, hour: 12, isLeapMonth: false},
want: "2024-01-24 12:00:00",
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, CreateFromLunar(tt.args.year, tt.args.month, tt.args.day, tt.args.hour, tt.args.minute, tt.args.second, tt.args.isLeapMonth).ToDateTimeString(), "args{%v}", tt.args)
})
}
}

func TestError_Lunar(t *testing.T) {
l := Parse("xxx").Lunar()
assert.NotNil(t, l.Error)
func TestCarbon_Julian(t *testing.T) {
type args struct {
c Carbon
}
tests := []struct {
args args
wantJD float64
wantMJD float64
}{
{
args: args{Parse("xxx")},
wantJD: 0,
wantMJD: 0,
},
{
args: args{Parse("2024-01-24 12:00:00")},
wantJD: 2460334,
wantMJD: 60333.5,
},
{
args: args{CreateFromJulian(2460334)},
wantJD: 2460334,
wantMJD: 60333.5,
},
{
args: args{CreateFromJulian(60333.5)},
wantJD: 2460334,
wantMJD: 60333.5,
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.wantJD, tt.args.c.Julian().JD(), "Julian().JD()")
assert.Equalf(t, tt.wantMJD, tt.args.c.Julian().MJD(), "Julian().MJD()")
})
}
}

func TestCreateFromJulian(t *testing.T) {
type args struct {
j float64
}
tests := []struct {
args args
want string
}{
{
args: args{j: 2460334},
want: "2024-01-24 12:00:00",
},
{
args: args{j: 60333.5},
want: "2024-01-24 12:00:00",
},
}
for index, tt := range tests {
t.Run(strconv.Itoa(index), func(t *testing.T) {
assert.Equalf(t, tt.want, CreateFromJulian(tt.args.j).ToDateTimeString(), "args{%v}", tt.args.j)
})
}
}

func TestName(t *testing.T) {
r := CreateFromLunar(2023, 12, 11, 0, 0, 0, false).ToDateTimeString()
fmt.Println(r)
r1 := Parse("2024-01-24 13:14:15").Julian().MJD()
fmt.Println(r1)

r2 := CreateFromJulian(2460334.051563).ToDateTimeString()
fmt.Println(r2)
}
2 changes: 1 addition & 1 deletion carbon.go
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ import (

// Version current version
// 当前版本号
const Version = "2.3.6"
const Version = "2.3.7"

// timezone constants
// 时区常量