diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index dbde3f3..c189f79 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -15,52 +15,44 @@ on: - cron: '0 20 * * 0' jobs: - analyze: - name: Analyze + CodeQL-Build: + # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['go'] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + permissions: + # required for all workflows + security-events: write - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # only required for workflows in private repositories + actions: read + contents: read - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + # Override language selection by uncommenting this and choosing your languages + with: + languages: go + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below). + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following + # three lines and modify them (or add more) to build your code if your + # project uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fa0bb03..b4bb793 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,21 +4,43 @@ on: branches: - master pull_request: + +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read jobs: golangci: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: ^1.18 + - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: latest + + # Optional: working directory, useful for monorepos + # working-directory: somedir + # Optional: golangci-lint command line arguments. args: --enable gosec + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the all caching functionality will be complete disabled, + # takes precedence over all other caching options. + # skip-cache: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true - name: testing run: go test -shuffle on ./... diff --git a/README.md b/README.md index 0cca242..18d69a5 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,50 @@ Year 2021 is 辛丑 Day 2021-07-28 is 丁丑 ``` +### ユリウス日 + +```go +//go:build run +// +build run + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/goark/koyomi" + "github.com/goark/koyomi/jdn" +) + +func main() { + flag.Parse() + args := flag.Args() + if len(args) < 1 { + fmt.Fprintln(os.Stderr, os.ErrInvalid) + return + } + for _, s := range args { + t, err := koyomi.DateFrom(s) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + j := jdn.GetJDN(t.Time) + fmt.Printf("Julian Day Number of %v is %v\n", t.Format("2006-01-02"), j) + } +} +``` + +これを実行すると以下のような結果になります。 + +``` +$ go run sample/sample4.go 2022-01-01 +$ go run sample/sample4.go 2022-01-01 +Julian Day Number of 2022-01-01 is 2459580 +``` + ## Modules Requirement Graph [![dependency.png](./dependency.png)](./dependency.png) @@ -231,6 +275,7 @@ Day 2021-07-28 is 丁丑 - [国立天文台 天文情報センター 暦計算室](https://eco.mtk.nao.ac.jp/koyomi/) - [今月のこよみ powered by Google Calendar - 国立天文台暦計算室](https://eco.mtk.nao.ac.jp/koyomi/cande/calendar.html) + - [ユリウス日について - 国立天文台暦計算室](https://eco.mtk.nao.ac.jp/koyomi/topics/html/topics2023_1.html) - [日本の暦情報を取得するパッケージを作ってみた — リリース情報 | text.Baldanders.info](https://text.baldanders.info/release/2020/05/koyomi/) [Go 言語]: https://golang.org/ "The Go Programming Language" diff --git a/jdn/jdn.go b/jdn/jdn.go new file mode 100644 index 0000000..7a52722 --- /dev/null +++ b/jdn/jdn.go @@ -0,0 +1,99 @@ +package jdn + +import ( + "math/big" + "time" +) + +// GetJD returns Julian Day from time.Time. +func GetJD(dt time.Time) *big.Rat { + dt = dt.In(time.UTC) + y := intRat(int64(dt.Year())) + m := int64(dt.Month()) + d := int64(dt.Day()) + k := floorRat(quoInt(intRat(14-m), 12)) + jdn := floorRat(mulRat(addInt(subRat(y, k), 4800), fracInt(1461, 4))) + jdn = addRat(jdn, floorRat(mulRat(addInt(mulInt(k, 12), m-2), fracInt(367, 12)))) + jdn = subRat(jdn, floorRat(mulRat(floorRat(quoInt(addInt(subRat(y, k), 4900), 100)), fracInt(3, 4)))) + jdn = addInt(jdn, d-32075) + jdn = addRat(jdn, subRat(quoRat(addRat(intRat(int64(dt.Second()+dt.Minute()*60+dt.Hour()*3600)), fracInt(int64(dt.Nanosecond()), 999999999)), floatRat((24*time.Hour).Seconds())), floatRat(0.5))) + return jdn +} + +// GetJDN returns Julian Day Number from time.Time. +func GetJDN(dt time.Time) int64 { + return floorRat(GetJD(dt)).Num().Int64() +} + +// GetMJD returns Modified Julian Day from time.Time. +func GetMJD(dt time.Time) *big.Rat { + return subRat(GetJD(dt), floatRat(2400000.5)) +} + +// GetMJDN returns Modified Julian Day Number from time.Time. +func GetMJDN(dt time.Time) int64 { + return floorRat(GetMJD(dt)).Num().Int64() +} + +func intRat(x int64) *big.Rat { + return fracInt(x, 1) +} + +func floatRat(x float64) *big.Rat { + return (&big.Rat{}).SetFloat64(x) +} + +func fracInt(x, y int64) *big.Rat { + return big.NewRat(x, y) +} + +func addInt(x *big.Rat, y int64) *big.Rat { + return addRat(x, intRat(y)) +} + +// func subInt(x *big.Rat, y int64) *big.Rat { +// return subRat(x, intRat(y)) +// } + +func mulInt(x *big.Rat, y int64) *big.Rat { + return mulRat(x, intRat(y)) +} + +func quoInt(x *big.Rat, y int64) *big.Rat { + return quoRat(x, intRat(y)) +} + +func addRat(x, y *big.Rat) *big.Rat { + return (&big.Rat{}).Add(x, y) +} + +func subRat(x, y *big.Rat) *big.Rat { + return (&big.Rat{}).Sub(x, y) +} + +func mulRat(x, y *big.Rat) *big.Rat { + return (&big.Rat{}).Mul(x, y) +} + +func quoRat(x, y *big.Rat) *big.Rat { + return (&big.Rat{}).Quo(x, y) +} + +func floorRat(n *big.Rat) *big.Rat { + return (&big.Rat{}).SetInt((&big.Int{}).Div(n.Num(), n.Denom())) +} + +/* Copyright 2022 Spiegel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/jdn/jdn_test.go b/jdn/jdn_test.go new file mode 100644 index 0000000..5ca380c --- /dev/null +++ b/jdn/jdn_test.go @@ -0,0 +1,77 @@ +package jdn + +import ( + "math/big" + "testing" + "time" +) + +func TestGetJDN(t *testing.T) { + jst := time.FixedZone("JST", int((9 * time.Hour).Seconds())) // Japan standard Time + testCases := []struct { + inp time.Time + outp1 *big.Rat + outp2 int64 + outp3 *big.Rat + outp4 int64 + }{ + {inp: time.Date(2015, time.January, 1, 0, 0, 0, 0, time.UTC), outp1: floatRat(2457023.5), outp2: 2457023, outp3: floatRat(57023.0), outp4: 57023}, + {inp: time.Date(2022, time.January, 1, 0, 0, 0, 0, jst), outp1: floatRat(2459580.125), outp2: 2459580, outp3: floatRat(59579.625), outp4: 59579}, + } + for _, tc := range testCases { + jd := GetJD(tc.inp) + if jd.Cmp(tc.outp1) != 0 { + t.Errorf("GetJD(%v) is %v, want %v.", tc.inp, jd.FloatString(5), tc.outp1.FloatString(5)) + } + jdn := GetJDN(tc.inp) + if jdn != tc.outp2 { + t.Errorf("GetJDN(%v) is %v, want %v.", tc.inp, jdn, tc.outp2) + } + mjd := GetMJD(tc.inp) + if mjd.Cmp(tc.outp3) != 0 { + t.Errorf("GetMJD(%v) is %v, want %v.", tc.inp, mjd.FloatString(5), tc.outp3.FloatString(5)) + } + mjdn := GetMJDN(tc.inp) + if mjdn != tc.outp4 { + t.Errorf("GetMJDN(%v) is %v, want %v.", tc.inp, mjdn, tc.outp4) + } + } +} + +func TestFloorRat(t *testing.T) { + testCases := []struct { + inp float64 + outp float64 + }{ + {inp: 1.1, outp: 1}, + {inp: 1.0, outp: 1}, + {inp: 0.9, outp: 0}, + {inp: 0.1, outp: 0}, + {inp: 0.0, outp: 0}, + {inp: -0.1, outp: -1}, + {inp: -0.9, outp: -1}, + {inp: -1.0, outp: -1}, + {inp: -1.1, outp: -2}, + } + for _, tc := range testCases { + f := floorRat(floatRat(tc.inp)) + if ff, _ := f.Float64(); ff != tc.outp { + t.Errorf("floorRat(%v) is %v, want %v.", tc.inp, f, tc.outp) + } + } +} + +/* Copyright 2022 Spiegel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/sample/sample4.go b/sample/sample4.go new file mode 100644 index 0000000..115015e --- /dev/null +++ b/sample/sample4.go @@ -0,0 +1,46 @@ +//go:build run +// +build run + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/goark/koyomi" + "github.com/goark/koyomi/jdn" +) + +func main() { + flag.Parse() + args := flag.Args() + if len(args) < 1 { + fmt.Fprintln(os.Stderr, os.ErrInvalid) + return + } + for _, s := range args { + t, err := koyomi.DateFrom(s) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + j := jdn.GetJDN(t.Time) + fmt.Printf("Julian Day Number of %v is %v\n", t.Format("2006-01-02"), j) + } +} + +/* Copyright 2022 Spiegel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/zodiac/zodiac.go b/zodiac/zodiac.go index 0226d7d..dc2b1be 100644 --- a/zodiac/zodiac.go +++ b/zodiac/zodiac.go @@ -1,9 +1,8 @@ package zodiac import ( - "time" - "github.com/goark/koyomi" + "github.com/goark/koyomi/jdn" ) type Kan10 uint @@ -53,13 +52,12 @@ func (s Shi12) String() string { } var ( - baseDay = time.Date(2001, time.January, 1, 0, 0, 0, 0, koyomi.JST) // 2001-01-01 is 甲子 - baseYear = 1984 // Year 1984 is 甲子 + baseYear = 1984 // Year 1984 is 甲子 ) -// ZodiacDayNumber function returns japanese zodiac day number from 2001-01-01. +// ZodiacDayNumber function returns japanese zodiac day number. func ZodiacDayNumber(t koyomi.DateJp) (Kan10, Shi12) { - n := int64(t.Sub(baseDay).Hours()) / 24 + n := jdn.GetJDN(t.Time) + 50 k := n % int64(KanMax) if k < 0 { k += int64(KanMax) diff --git a/zodiac/zodiac_test.go b/zodiac/zodiac_test.go index 0bf5c23..3fa5123 100644 --- a/zodiac/zodiac_test.go +++ b/zodiac/zodiac_test.go @@ -81,17 +81,17 @@ func TestZodiac(t *testing.T) { for _, tc := range testCases { kanYear, shiYear := zodiac.ZodiacYearNumber(tc.t.Year()) if kanYear != tc.kanYear { - t.Errorf("result of ZodiacYearNumber(\"%v\") is \"%v\", want %v", tc.t, kanYear, tc.kanYear) + t.Errorf("result of ZodiacYearNumber(\"%v\") is \"%v\" (kan), want %v", tc.t, kanYear, tc.kanYear) } if shiYear != tc.shiYear { - t.Errorf("result of ZodiacYearNumber(\"%v\") is \"%v\", want %v", tc.t, shiYear, tc.shiYear) + t.Errorf("result of ZodiacYearNumber(\"%v\") is \"%v\" (shi), want %v", tc.t, shiYear, tc.shiYear) } kanDay, shiDay := zodiac.ZodiacDayNumber(tc.t) if kanDay != tc.kanDay { - t.Errorf("result of ZodiacDayNumber(\"%v\") is \"%v\", want %v", tc.t, kanDay, tc.kanDay) + t.Errorf("result of ZodiacDayNumber(\"%v\") is \"%v\" (kan), want %v", tc.t, kanDay, tc.kanDay) } if shiYear != tc.shiYear { - t.Errorf("result of ZodiacDayNumber(\"%v\") is \"%v\", want %v", tc.t, shiDay, tc.shiDay) + t.Errorf("result of ZodiacDayNumber(\"%v\") is \"%v\" (shi), want %v", tc.t, shiDay, tc.shiDay) } } }