-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
90fa264
commit e7c063f
Showing
84 changed files
with
14,047 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/riley-martine/sundial/internal/core" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// Set by goreleaser: | ||
// https://goreleaser.com/cookbooks/using-main.version/?h=version | ||
var version = "dev" | ||
|
||
var ( | ||
debug bool | ||
cityName string | ||
countryCode string | ||
fipsCode string | ||
givenTime string | ||
) | ||
|
||
var rootCmd = &cobra.Command{ | ||
Use: "sundial --city CITY", | ||
Short: "Print the percent through the day or night.", | ||
Version: version, | ||
Long: `Sundial is a program to print the percent through the day or night. | ||
https://github.com/riley-martine/sundial`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
city, err := core.FindCity(cityName, countryCode, fipsCode) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
if strings.HasPrefix(err.Error(), "could not narrow") { | ||
fmt.Fprintln(os.Stderr, "You may need to be more specific about which city you're in. Try specifying a country code (second field) and a fips code (third field).") | ||
fmt.Fprintln(os.Stderr, " e.g. sundial -city Washington -country US -fipscode IL") | ||
} | ||
os.Exit(1) | ||
} | ||
|
||
// This probably doesn't need ParseInLocation | ||
// Adding that data back to the cities CSV balloons the size | ||
t := time.Now() | ||
if givenTime != "" { | ||
t, err = time.Parse(time.UnixDate, givenTime) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
out, err := core.GetPeriodPercent(city, t, debug) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
os.Exit(1) | ||
} | ||
fmt.Println(out) | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.Flags().BoolVar(&debug, "debug", false, "Print debug logging. Default: false") | ||
|
||
rootCmd.Flags().StringVar(&cityName, "city", "", "Name of city you're in. Required.") | ||
rootCmd.MarkFlagRequired("city") | ||
rootCmd.Flags().StringVar(&countryCode, "country", "", "Two-letter country code, e.g. 'US'. Not required if only one city with name.") | ||
rootCmd.Flags().StringVar(&fipsCode, "fipscode", "", `Fipscode of region you're in. In the US, this is the two-letter state abbreviation. | ||
Otherwise, search http://download.geonames.org/export/dump/admin1CodesASCII.txt | ||
for '$countryCode.' and select the value after the period for the region you're in. | ||
Not required if only one city in country with name.`) | ||
|
||
rootCmd.Flags().StringVar(&givenTime, "time", "", "Time to convert, in time.UnixDate format. Defaults to now.") | ||
} | ||
|
||
func Execute() { | ||
if err := rootCmd.Execute(); err != nil { | ||
// fmt.Fprintln(os.Stderr, err) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,13 @@ | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= | ||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||
github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5 h1:ouekCqYkMw4QXFCaLyYqjBe99/MUW4Qf3DJhCRh1G18= | ||
github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5/go.mod h1:3oZ7G+fb8Z8KF+KPHxeDO3GWpEjgvk/f+d/yaxmDRT4= | ||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= | ||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= | ||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package core | ||
|
||
import ( | ||
"embed" | ||
"encoding/csv" | ||
"fmt" | ||
"github.com/kelvins/sunrisesunset" | ||
"io" | ||
"strconv" | ||
"time" | ||
) | ||
|
||
/* | ||
What I want to display: percent through the apparent solar day we are | ||
*/ | ||
|
||
//go:embed cities.csv | ||
var fs embed.FS | ||
|
||
type CityInfo struct { | ||
Name string | ||
CountryCode string | ||
FipsCode string | ||
Latitude float64 | ||
Longitude float64 | ||
} | ||
|
||
func (c *CityInfo) String() string { | ||
return fmt.Sprintf("{%s, %s, %s, %0.2f, %0.2f}", c.Name, c.CountryCode, c.FipsCode, c.Latitude, c.Longitude) | ||
} | ||
|
||
func (c *CityInfo) GetSunriseSunset(at time.Time) (sunrise time.Time, sunset time.Time, err error) { | ||
_, secondsOffset := at.Zone() | ||
hoursOffset := float64(secondsOffset) / 60 / 60 | ||
p := sunrisesunset.Parameters{ | ||
Latitude: c.Latitude, | ||
Longitude: c.Longitude, | ||
UtcOffset: hoursOffset, | ||
Date: at, | ||
} | ||
|
||
return p.GetSunriseSunset() | ||
} | ||
|
||
func GetPeriodPercent(c *CityInfo, at time.Time, debug bool) (string, error) { | ||
if debug { | ||
fmt.Printf("City: %#v\n", c) | ||
} | ||
|
||
sunrise, sunset, err := c.GetSunriseSunset(at) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if debug { | ||
fmt.Println("Sunrise:", sunrise.Format("15:04:05")) // Sunrise: 06:11:44 | ||
fmt.Println("Sunset:", sunset.Format("15:04:05")) // Sunset: 18:14:27 | ||
fmt.Println("Length of apparent solar time in mean solar time:", sunset.Sub(sunrise)) | ||
fmt.Println("Solar noon:", sunrise.Add(sunset.Sub(sunrise)/2).Format("15:04:05")) | ||
} | ||
|
||
dayDuration := sunset.Sub(sunrise) | ||
if at.After(sunrise) && at.Before(sunset) { | ||
hasPassed := at.Sub(sunrise) | ||
if debug { | ||
fmt.Println("Time passed since sunrise:", hasPassed) | ||
} | ||
fractionPassed := hasPassed.Seconds() / dayDuration.Seconds() | ||
return fmt.Sprintf("%.0f%% ☉", fractionPassed*100), nil | ||
} | ||
|
||
// https://glossary.ametsoc.org/wiki/Mean_solar_day | ||
meanDayDuration, err := time.ParseDuration("86400s") | ||
if err != nil { | ||
return "", err | ||
} | ||
nightDuration := meanDayDuration - dayDuration | ||
|
||
if at.Before(sunrise) { | ||
nightEnd := sunrise | ||
nightStart := nightEnd.Add(-nightDuration) | ||
hasPassedNight := at.Sub(nightStart) | ||
fractionPassedNight := hasPassedNight.Seconds() / nightDuration.Seconds() | ||
if debug { | ||
fmt.Println("Time passed since sunset:", hasPassedNight) | ||
} | ||
return fmt.Sprintf("%.0f%% ☾", fractionPassedNight*100), nil | ||
} | ||
|
||
if at.After(sunset) { | ||
nightStart := sunset | ||
hasPassedNight := at.Sub(nightStart) | ||
fractionPassedNight := hasPassedNight.Seconds() / nightDuration.Seconds() | ||
if debug { | ||
fmt.Println("Time passed since sunset:", hasPassedNight) | ||
} | ||
return fmt.Sprintf("%.0f%% ☾", fractionPassedNight*100), nil | ||
} | ||
|
||
panic("This should never happen") | ||
} | ||
|
||
func FindCity(name, countryCode, fipsCode string) (*CityInfo, error) { | ||
// TODO cache successful runs and don't bother with opening this | ||
file, err := fs.Open("cities.csv") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
r := csv.NewReader(file) | ||
r.Comma = '\t' | ||
r.ReuseRecord = true | ||
|
||
cities := []*CityInfo{} | ||
for { | ||
record, err := r.Read() | ||
if err == io.EOF { | ||
break | ||
} else if err != nil { | ||
return nil, err | ||
} | ||
|
||
if record[0] != name { | ||
continue | ||
} | ||
|
||
if countryCode != "" && record[1] != countryCode { | ||
continue | ||
} | ||
|
||
if fipsCode != "" && record[2] != fipsCode { | ||
continue | ||
} | ||
lat, err := strconv.ParseFloat(record[3], 64) | ||
if err != nil { | ||
return nil, err | ||
} | ||
long, err := strconv.ParseFloat(record[4], 64) | ||
if err != nil { | ||
return nil, err | ||
} | ||
cities = append(cities, &CityInfo{Name: record[0], CountryCode: record[1], FipsCode: record[2], Latitude: lat, Longitude: long}) | ||
} | ||
if len(cities) == 0 { | ||
return nil, fmt.Errorf("unable to find city '%s' in country '%s' with fips code '%s'", name, countryCode, fipsCode) | ||
} | ||
if len(cities) > 1 { | ||
return nil, fmt.Errorf("could not narrow down between cities: %+v", cities) | ||
} | ||
return cities[0], nil | ||
} |
Oops, something went wrong.