From 435005f64f6de4686237d412ba9272b5dba968da Mon Sep 17 00:00:00 2001 From: prostraction Date: Tue, 4 Nov 2025 20:26:45 +0500 Subject: [PATCH 01/18] jtime methods, closes #11 --- doc/postman_collection | 375 ++++++++++++++++++++++++++++++++++++-- internal/server/models.go | 6 + internal/server/routes.go | 73 ++++++++ internal/server/server.go | 9 +- pkg/julian-time/time.go | 26 --- 5 files changed, 442 insertions(+), 47 deletions(-) diff --git a/doc/postman_collection b/doc/postman_collection index 9badfd6..0ebaa71 100644 --- a/doc/postman_collection +++ b/doc/postman_collection @@ -52,32 +52,32 @@ { "key": "year", "value": "2025", - "description": "Format: YYYY Allowed range: [1, 9999]" + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" }, { "key": "month", "value": "01", - "description": "Format: M or MM. Allowed range: [1, 12]" + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" }, { "key": "day", "value": "01", - "description": "Format: D or DD. Allowed range: [1, 31]" + "description": "Format: D or DD. Allowed range: [1, 31]. Current, if not specified" }, { "key": "hour", "value": "01", - "description": "Format: h or hh. Allowed range: [0, 23]" + "description": "Format: h or hh. Allowed range: [0, 23]. Current, if not specified" }, { "key": "minute", "value": "01", - "description": "Format: m or mm. Allowed range: [0, 59]" + "description": "Format: m or mm. Allowed range: [0, 59]. Current, if not specified" }, { "key": "second", "value": "01", - "description": "Format: s or ss. Allowed range: [0, 59]" + "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" } ] }, @@ -127,32 +127,32 @@ { "key": "year", "value": "2025", - "description": "Format: YYYY Allowed range: [1, 9999]" + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" }, { "key": "month", "value": "01", - "description": "Format: M or MM. Allowed range: [1, 12]" + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" }, { "key": "day", "value": "01", - "description": "Format: D or DD. Allowed range: [1, 31]" + "description": "Format: D or DD. Allowed range: [1, 31]. Current, if not specified" }, { "key": "hour", "value": "01", - "description": "Format: h or hh. Allowed range: [0, 23]" + "description": "Format: h or hh. Allowed range: [0, 23]. Current, if not specified" }, { "key": "minute", "value": "01", - "description": "Format: m or mm. Allowed range: [0, 59]" + "description": "Format: m or mm. Allowed range: [0, 59]. Current, if not specified" }, { "key": "second", "value": "01", - "description": "Format: s or ss. Allowed range: [0, 59]" + "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" } ] } @@ -360,7 +360,7 @@ { "key": "timestamp", "value": "1758045697", - "description": "Timestamp for calculations" + "description": "Timestamp for calculations. Current, if not specified" } ] }, @@ -410,7 +410,7 @@ { "key": "timestamp", "value": "1758045697", - "description": "Timestamp for calculations" + "description": "Timestamp for calculations. Current, if not specified" } ] } @@ -430,7 +430,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" }, @@ -480,7 +479,7 @@ { "key": "year", "value": "2025", - "description": "Year for request: YYYY" + "description": "Year for request: YYYY. Current, if not specified" } ] }, @@ -510,7 +509,7 @@ { "key": "year", "value": "2025", - "description": "Year for request: YYYY" + "description": "Year for request: YYYY. Current, if not specified" } ] } @@ -530,7 +529,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" }, @@ -636,7 +634,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" }, @@ -659,6 +656,346 @@ ] } ] + }, + { + "name": "Julian Time", + "item": [ + { + "name": "Get Julian Time by specified/current date", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/toJulianTimeByDate?utc=5&precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01&", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "toJulianTimeByDate" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "year", + "value": "2025", + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" + }, + { + "key": "month", + "value": "01", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "day", + "value": "01", + "description": "Format: D or DD. Allowed range: [1, 31]. Current, if not specified" + }, + { + "key": "hour", + "value": "01", + "description": "Format: h or hh. Allowed range: [0, 23]. Current, if not specified" + }, + { + "key": "minute", + "value": "01", + "description": "Format: m or mm. Allowed range: [0, 59] Current, if not specified" + }, + { + "key": "second", + "value": "01", + "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "", + "value": null, + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" + } + ] + } + }, + "response": [ + { + "name": "toJulianTimeByDate", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/toJulianTimeByDate?precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "toJulianTimeByDate" + ], + "query": [ + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "year", + "value": "2025", + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" + }, + { + "key": "month", + "value": "01", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "day", + "value": "01", + "description": "Format: D or DD. Allowed range: [1, 31]. Current, if not specified" + }, + { + "key": "hour", + "value": "01", + "description": "Format: h or hh. Allowed range: [0, 23]. Current, if not specified" + }, + { + "key": "minute", + "value": "01", + "description": "Format: m or mm. Allowed range: [0, 59] Current, if not specified" + }, + { + "key": "second", + "value": "01", + "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Tue, 04 Nov 2025 15:16:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "104" + } + ], + "cookie": [], + "body": "{\n \"CivilDate\": \"2025-01-01 01:01:01 +0000 UTC\",\n \"CivilDateTimestamp\": 1735693261,\n \"JulianDate\": 2460676.54237\n}" + } + ] + }, + { + "name": "Get Julian Time by timestamp", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/toJulianTimeByTimestamp?precision=5", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "toJulianTimeByTimestamp" + ], + "query": [ + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + } + ] + } + }, + "response": [ + { + "name": "toJulianTimeByTimestamp", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/toJulianTimeByTimestamp?precision=10×tamp=1735693261", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "toJulianTimeByTimestamp" + ], + "query": [ + { + "key": "precision", + "value": "10", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "timestamp", + "value": "1735693261", + "description": "Timestamp for calculations. Current, if not specified" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Tue, 04 Nov 2025 15:23:01 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "109" + } + ], + "cookie": [], + "body": "{\n \"CivilDate\": \"2025-01-01 01:01:01 +0000 UTC\",\n \"CivilDateTimestamp\": 1735693261,\n \"JulianDate\": 2460676.5423726854\n}" + } + ] + }, + { + "name": "Get Civil Time by Julian Time", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/fromJulianTime?precision=5&jtime=2460676.5423726854", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "fromJulianTime" + ], + "query": [ + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "jtime", + "value": "2460676.5423726854", + "description": "Julian Time to convert (float64)" + } + ] + } + }, + "response": [ + { + "name": "fromJulianTime", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/fromJulianTime?precision=5&jtime=2460676.5423726854", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "fromJulianTime" + ], + "query": [ + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "jtime", + "value": "2460676.5423726854", + "description": "Julian Time to convert (float64)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Tue, 04 Nov 2025 15:17:41 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "104" + } + ], + "cookie": [], + "body": "{\n \"CivilDate\": \"2025-01-01 01:01:01 +0000 UTC\",\n \"CivilDateTimestamp\": 1735693261,\n \"JulianDate\": 2460676.54237\n}" + } + ] + } + ] } ] } \ No newline at end of file diff --git a/internal/server/models.go b/internal/server/models.go index 7899d5a..6ae5195 100644 --- a/internal/server/models.go +++ b/internal/server/models.go @@ -53,3 +53,9 @@ type Coordinates struct { Longitude float64 IsValid bool } + +type JulianTimeResp struct { + CivilDate string + CivilDateTimestamp int64 + JulianDate float64 +} diff --git a/internal/server/routes.go b/internal/server/routes.go index fde32c8..135d82d 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -5,6 +5,7 @@ import ( phase "moon/pkg/phase" pos "moon/pkg/position" zodiac "moon/pkg/zodiac" + "strconv" "time" @@ -190,3 +191,75 @@ func (s *Server) moonTableV1(c *fiber.Ctx, tGiven time.Time) error { resp.Table = s.moonCache.GenerateMoonTable(tGiven) return c.JSON(resp.Table) } + +/* Julian Time methods */ +func (s *Server) toJulianTimeByDateV1(c *fiber.Ctx) error { + tNow := time.Now() + tNow = tNow.In(time.UTC) + + precision := strToInt(c.Query("precision", "2"), 2, 0, 20) + + year := strToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) + month := strToInt(c.Query("month", strconv.Itoa(int(tNow.Month()))), int(tNow.Month()), 1, 12) + day := strToInt(c.Query("day", strconv.Itoa(int(tNow.Day()))), int(tNow.Day()), 1, 31) + + err := isValidDate(year, month, day) + if err != nil { + c.Status(400) + return c.SendString("Validation error: " + err.Error()) + } + + hour := strToInt(c.Query("hour", strconv.Itoa(int(tNow.Hour()))), int(tNow.Hour()), 0, 23) + minute := strToInt(c.Query("minute", strconv.Itoa(int(tNow.Minute()))), int(tNow.Minute()), 0, 59) + second := strToInt(c.Query("second", strconv.Itoa(int(tNow.Second()))), int(tNow.Second()), 0, 59) + + tGiven := time.Date(year, jt.GetMonth(month), day, hour, minute, second, 0, time.UTC) + return s.toJulianTimeV1(c, tGiven, precision) +} + +func (s *Server) toJulianTimeByTimestampV1(c *fiber.Ctx) error { + tStr := c.Query("timestamp", strconv.FormatInt(time.Now().Unix(), 10)) + t, err := strconv.ParseInt(tStr, 10, 64) + if err != nil { + t = time.Now().Unix() + } + + precision := strToInt(c.Query("precision", "2"), 2, 0, 20) + + tm := time.Unix(t, 0) + tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, time.Local) + tGiven = tGiven.In(time.UTC) + return s.toJulianTimeV1(c, tGiven, precision) +} + +func (s *Server) toJulianTimeV1(c *fiber.Ctx, tGiven time.Time, precision int) error { + resp := JulianTimeResp{} + resp.CivilDate = tGiven.String() + resp.CivilDateTimestamp = tGiven.Unix() + resp.JulianDate = toFixed(jt.ToJulianDate(tGiven), precision) + return c.JSON(resp) +} + +func (s *Server) fromJulianTimeV1(c *fiber.Ctx) error { + utc := c.Query("utc", "UTC:+0") + loc, _ := jt.SetTimezoneLocFromString(utc) + + jtStr := c.Query("jtime", "none") + jtime, err := strconv.ParseFloat(jtStr, 64) + if err != nil { + c.Status(400) + c.JSON("missing required parameter: 'jtime' (float)") + return nil + } + + t := jt.FromJulianDate(jtime, loc) + + precision := strToInt(c.Query("precision", "2"), 2, 0, 20) + + resp := JulianTimeResp{} + resp.CivilDate = t.String() + resp.CivilDateTimestamp = t.Unix() + resp.JulianDate = toFixed(jtime, precision) + + return c.JSON(resp) +} diff --git a/internal/server/server.go b/internal/server/server.go index 0081c89..ade60cc 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -81,8 +81,13 @@ func (s *Server) NewRouter() *fiber.App { //app.Get("/v1/nextMoonRise") // jtime methods: - //app.Get("/v1/toJulianTime") - //app.Get("/v1/fromJulianTime") + app.Get("/v1/toJulianTimeByDate", s.toJulianTimeByDateV1) + app.Get("/v1/toJulianTimeByTimestamp", s.toJulianTimeByTimestampV1) + app.Get("/v1/fromJulianTime", s.fromJulianTimeV1) + + app.Get("/api/v1/toJulianTimeByDate", s.toJulianTimeByDateV1) + app.Get("/api/v1/toJulianTimeByTimestamp", s.toJulianTimeByTimestampV1) + app.Get("/api/v1/fromJulianTime", s.fromJulianTimeV1) // some kind of faq //app.Get("/v1") diff --git a/pkg/julian-time/time.go b/pkg/julian-time/time.go index 5361b70..1c49519 100644 --- a/pkg/julian-time/time.go +++ b/pkg/julian-time/time.go @@ -45,32 +45,6 @@ func ToJulianDate(t time.Time) float64 { return jd } -// old -/*func ToJulianDate(t time.Time) float64 { - m := 1 - for i := range months { - if t.Month() == months[i] { - m = i + 1 - } - } - if m < 3 { - t = t.AddDate(-1, 0, 0) - m += 12 - } - - A := float64(t.Year()) / 100 - B := A / 4 - C := 2 - A + B - E := 365.25 * float64(t.Year()+4716) - F := 30.6001 * (float64(m) + 1) - JD := C + float64(float64(t.Day())) + E + F - 1524.5 - - val := float64(t.Hour())/Fhour + float64(t.Minute())/Fminute + float64(t.Second())/Fseconds - JD += val - - return JD - 0.5 -}*/ - func FromJulianDate(j float64, loc *time.Location) time.Time { datey, datem, dated := Jyear(j) timeh, timem, times := Jhms(j) From 6f736ef20903411f14831f6ac92c873bc397884e Mon Sep 17 00:00:00 2001 From: prostraction Date: Tue, 4 Nov 2025 20:42:15 +0500 Subject: [PATCH 02/18] closes #37 --- internal/server/routes.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/server/routes.go b/internal/server/routes.go index 135d82d..e9b9e8c 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -24,7 +24,8 @@ func (s *Server) moonPhaseCurrentV1(c *fiber.Ctx) error { /*if err != nil { log.Println(err) }*/ - tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, loc) + tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, time.Local) + tGiven = tGiven.In(loc) precision := strToInt(c.Query("precision", "2"), 2, 0, 20) latStr := c.Query("latitude", "no-value") @@ -47,7 +48,8 @@ func (s *Server) moonPhaseTimestampV1(c *fiber.Ctx) error { t = time.Now().Unix() } tm := time.Unix(t, 0) - tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, loc) + tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, time.Local) + tGiven = tGiven.In(loc) precision := strToInt(c.Query("precision", "2"), 2, 0, 20) @@ -87,7 +89,8 @@ func (s *Server) moonPhaseDatetV1(c *fiber.Ctx) error { lonStr := c.Query("longitude", "no-value") locationCords := parseCoords(latStr, lonStr) - tGiven := time.Date(year, jt.GetMonth(month), day, hour, minute, second, 0, loc) + tGiven := time.Date(year, jt.GetMonth(month), day, hour, minute, second, 0, time.Local) + tGiven = tGiven.In(loc) return s.moonPhaseV1(c, tGiven, precision, locationCords) } From a7251288ddb3fe4bfcd63da24c5374ef4e5a235a Mon Sep 17 00:00:00 2001 From: prostraction Date: Sun, 16 Nov 2025 16:29:39 +0500 Subject: [PATCH 03/18] feat: nextMoonPhase() v1 #5 --- internal/server/helpers.go | 6 +- internal/server/models.go | 16 +- internal/server/route-jtime.go | 82 ++ internal/server/route-moon-table.go | 40 + internal/server/route-next.go | 19 + internal/server/{routes.go => route-phase.go} | 148 +--- internal/server/server.go | 13 +- pkg/moon/days.go | 38 +- pkg/moon/models.go | 22 +- pkg/moon/moon.go | 136 +++- pkg/moon/moon_test.go | 759 ++++++++++++++++++ 11 files changed, 1123 insertions(+), 156 deletions(-) create mode 100644 internal/server/route-jtime.go create mode 100644 internal/server/route-moon-table.go create mode 100644 internal/server/route-next.go rename internal/server/{routes.go => route-phase.go} (51%) create mode 100644 pkg/moon/moon_test.go diff --git a/internal/server/helpers.go b/internal/server/helpers.go index 8f8f504..d6d3be5 100644 --- a/internal/server/helpers.go +++ b/internal/server/helpers.go @@ -10,12 +10,12 @@ func round(num float64) int { return int(num + math.Copysign(0.5, num)) } -func toFixed(num float64, precision int) float64 { +func ToFixed(num float64, precision int) float64 { output := math.Pow(10, float64(precision)) return float64(round(num*output)) / output } -func strToInt(val string, fallback int, min int, max int) int { +func StrToInt(val string, fallback int, min int, max int) int { v, err := strconv.Atoi(val) if err != nil { v = fallback @@ -48,7 +48,7 @@ func parseCoords(latStr, lonStr string) Coordinates { return locationCords } -func isValidDate(year, month, day int) error { +func IsValidDate(year, month, day int) error { if year < 0 || year > 9999 { return errors.New("'year' should be in range [0,9999]") } diff --git a/internal/server/models.go b/internal/server/models.go index 6ae5195..e467266 100644 --- a/internal/server/models.go +++ b/internal/server/models.go @@ -8,10 +8,6 @@ import ( "time" ) -type MoonTable struct { - Table []*moon.MoonTableElement -} - type MoonStat struct { MoonDays float64 Illumination float64 @@ -59,3 +55,15 @@ type JulianTimeResp struct { CivilDateTimestamp int64 JulianDate float64 } + +type NextMoonPhaseElement struct { + Date string + Countdown int64 +} + +type NextMoonPhase struct { + New *NextMoonPhaseElement + First *NextMoonPhaseElement + Full *NextMoonPhaseElement + Last *NextMoonPhaseElement +} diff --git a/internal/server/route-jtime.go b/internal/server/route-jtime.go new file mode 100644 index 0000000..19aad48 --- /dev/null +++ b/internal/server/route-jtime.go @@ -0,0 +1,82 @@ +package server + +import ( + "strconv" + "time" + + "github.com/gofiber/fiber/v2" + + jt "moon/pkg/julian-time" +) + +/* Julian Time methods */ +func (s *Server) toJulianTimeByDateV1(c *fiber.Ctx) error { + tNow := time.Now() + tNow = tNow.In(time.UTC) + + precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) + + year := StrToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) + month := StrToInt(c.Query("month", strconv.Itoa(int(tNow.Month()))), int(tNow.Month()), 1, 12) + day := StrToInt(c.Query("day", strconv.Itoa(int(tNow.Day()))), int(tNow.Day()), 1, 31) + + err := IsValidDate(year, month, day) + if err != nil { + c.Status(400) + return c.SendString("Validation error: " + err.Error()) + } + + hour := StrToInt(c.Query("hour", strconv.Itoa(int(tNow.Hour()))), int(tNow.Hour()), 0, 23) + minute := StrToInt(c.Query("minute", strconv.Itoa(int(tNow.Minute()))), int(tNow.Minute()), 0, 59) + second := StrToInt(c.Query("second", strconv.Itoa(int(tNow.Second()))), int(tNow.Second()), 0, 59) + + tGiven := time.Date(year, jt.GetMonth(month), day, hour, minute, second, 0, time.UTC) + return s.toJulianTimeV1(c, tGiven, precision) +} + +func (s *Server) toJulianTimeByTimestampV1(c *fiber.Ctx) error { + tStr := c.Query("timestamp", strconv.FormatInt(time.Now().Unix(), 10)) + t, err := strconv.ParseInt(tStr, 10, 64) + if err != nil { + t = time.Now().Unix() + } + + precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) + + tm := time.Unix(t, 0) + tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, time.Local) + tGiven = tGiven.In(time.UTC) + return s.toJulianTimeV1(c, tGiven, precision) +} + +func (s *Server) toJulianTimeV1(c *fiber.Ctx, tGiven time.Time, precision int) error { + resp := JulianTimeResp{} + resp.CivilDate = tGiven.String() + resp.CivilDateTimestamp = tGiven.Unix() + resp.JulianDate = ToFixed(jt.ToJulianDate(tGiven), precision) + return c.JSON(resp) +} + +func (s *Server) fromJulianTimeV1(c *fiber.Ctx) error { + utc := c.Query("utc", "UTC:+0") + loc, _ := jt.SetTimezoneLocFromString(utc) + + jtStr := c.Query("jtime", "none") + jtime, err := strconv.ParseFloat(jtStr, 64) + if err != nil { + c.Status(400) + c.JSON("missing required parameter: 'jtime' (float)") + return nil + } + + t := jt.FromJulianDate(jtime, loc) + + precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) + + resp := JulianTimeResp{} + resp.CivilDate = t.String() + resp.CivilDateTimestamp = t.Unix() + resp.JulianDate = ToFixed(jtime, precision) + + return c.JSON(resp) +} diff --git a/internal/server/route-moon-table.go b/internal/server/route-moon-table.go new file mode 100644 index 0000000..17127d1 --- /dev/null +++ b/internal/server/route-moon-table.go @@ -0,0 +1,40 @@ +package server + +import ( + jt "moon/pkg/julian-time" + "strconv" + "time" + + "github.com/gofiber/fiber/v2" +) + +/* MOON TABLE */ +func (s *Server) moonTableCurrentV1(c *fiber.Ctx) error { + utc := c.Query("utc", "UTC:+0") + loc, _ := jt.SetTimezoneLocFromString(utc) + /*if err != nil { + log.Println(err) + }*/ + + tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, loc) + return s.moonTableV1(c, tGiven) +} + +func (s *Server) moonTableYearV1(c *fiber.Ctx) error { + utc := c.Query("utc", "UTC:+0") + loc, _ := jt.SetTimezoneLocFromString(utc) + /*if err != nil { + log.Println(err) + }*/ + + tNow := time.Now() + year := StrToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) + + tGiven := time.Date(year, time.January, 1, 0, 0, 0, 0, loc) + return s.moonTableV1(c, tGiven) +} + +func (s *Server) moonTableV1(c *fiber.Ctx, tGiven time.Time) error { + resp := s.moonCache.GenerateMoonTable(tGiven) + return c.JSON(resp.Elems) +} diff --git a/internal/server/route-next.go b/internal/server/route-next.go new file mode 100644 index 0000000..865f266 --- /dev/null +++ b/internal/server/route-next.go @@ -0,0 +1,19 @@ +package server + +import ( + jt "moon/pkg/julian-time" + "time" + + "github.com/gofiber/fiber/v2" +) + +func (s *Server) moonNextMoonPhaseV1(c *fiber.Ctx) error { + utc := c.Query("utc", "UTC:+0") + loc, _ := jt.SetTimezoneLocFromString(utc) + tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, time.Local) + tGiven = tGiven.In(loc) + + np := s.moonCache.FindNearestPhase(tGiven) + + return c.JSON(np) +} diff --git a/internal/server/routes.go b/internal/server/route-phase.go similarity index 51% rename from internal/server/routes.go rename to internal/server/route-phase.go index e9b9e8c..bd162e4 100644 --- a/internal/server/routes.go +++ b/internal/server/route-phase.go @@ -2,10 +2,9 @@ package server import ( jt "moon/pkg/julian-time" - phase "moon/pkg/phase" + "moon/pkg/phase" pos "moon/pkg/position" - zodiac "moon/pkg/zodiac" - + "moon/pkg/zodiac" "strconv" "time" @@ -13,10 +12,6 @@ import ( "github.com/gofiber/fiber/v2/log" ) -func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.1.2") -} - /* MOON PHASE */ func (s *Server) moonPhaseCurrentV1(c *fiber.Ctx) error { utc := c.Query("utc", "UTC:+0") @@ -26,7 +21,7 @@ func (s *Server) moonPhaseCurrentV1(c *fiber.Ctx) error { }*/ tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, time.Local) tGiven = tGiven.In(loc) - precision := strToInt(c.Query("precision", "2"), 2, 0, 20) + precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) latStr := c.Query("latitude", "no-value") lonStr := c.Query("longitude", "no-value") @@ -51,7 +46,7 @@ func (s *Server) moonPhaseTimestampV1(c *fiber.Ctx) error { tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, time.Local) tGiven = tGiven.In(loc) - precision := strToInt(c.Query("precision", "2"), 2, 0, 20) + precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) latStr := c.Query("latitude", "no-value") lonStr := c.Query("longitude", "no-value") @@ -69,21 +64,21 @@ func (s *Server) moonPhaseDatetV1(c *fiber.Ctx) error { tNow := time.Now() - year := strToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) - month := strToInt(c.Query("month", strconv.Itoa(int(tNow.Month()))), int(tNow.Month()), 1, 12) - day := strToInt(c.Query("day", strconv.Itoa(int(tNow.Day()))), int(tNow.Day()), 1, 31) + year := StrToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) + month := StrToInt(c.Query("month", strconv.Itoa(int(tNow.Month()))), int(tNow.Month()), 1, 12) + day := StrToInt(c.Query("day", strconv.Itoa(int(tNow.Day()))), int(tNow.Day()), 1, 31) - err := isValidDate(year, month, day) + err := IsValidDate(year, month, day) if err != nil { c.Status(400) return c.SendString("Validation error: " + err.Error()) } - hour := strToInt(c.Query("hour", strconv.Itoa(int(tNow.Hour()))), int(tNow.Hour()), 0, 23) - minute := strToInt(c.Query("minute", strconv.Itoa(int(tNow.Minute()))), int(tNow.Minute()), 0, 59) - second := strToInt(c.Query("second", strconv.Itoa(int(tNow.Second()))), int(tNow.Second()), 0, 59) + hour := StrToInt(c.Query("hour", strconv.Itoa(int(tNow.Hour()))), int(tNow.Hour()), 0, 23) + minute := StrToInt(c.Query("minute", strconv.Itoa(int(tNow.Minute()))), int(tNow.Minute()), 0, 59) + second := StrToInt(c.Query("second", strconv.Itoa(int(tNow.Second()))), int(tNow.Second()), 0, 59) - precision := strToInt(c.Query("precision", "2"), 2, 0, 20) + precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) latStr := c.Query("latitude", "no-value") lonStr := c.Query("longitude", "no-value") @@ -117,17 +112,17 @@ func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, loca resp.info.MoonDaysCurrent = currentDuration.Minutes() / jt.Fminute resp.info.MoonDaysEnd = endDuration.Minutes() / jt.Fminute - resp.BeginDay.MoonDays = toFixed(resp.info.MoonDaysBegin, precision) - resp.CurrentState.MoonDays = toFixed(resp.info.MoonDaysCurrent, precision) - resp.EndDay.MoonDays = toFixed(resp.info.MoonDaysEnd, precision) + resp.BeginDay.MoonDays = ToFixed(resp.info.MoonDaysBegin, precision) + resp.CurrentState.MoonDays = ToFixed(resp.info.MoonDaysCurrent, precision) + resp.EndDay.MoonDays = ToFixed(resp.info.MoonDaysEnd, precision) resp.info.IlluminationCurrent, resp.info.IlluminationBeginDay, resp.info.IlluminationEndDay, resp.CurrentState.Phase, resp.BeginDay.Phase, resp.EndDay.Phase = phase.CurrentMoonPhase(tGiven, lang) - resp.BeginDay.Illumination = toFixed(resp.info.IlluminationBeginDay*100, precision) - resp.CurrentState.Illumination = toFixed(resp.info.IlluminationCurrent*100, precision) - resp.EndDay.Illumination = toFixed(resp.info.IlluminationEndDay*100, precision) + resp.BeginDay.Illumination = ToFixed(resp.info.IlluminationBeginDay*100, precision) + resp.CurrentState.Illumination = ToFixed(resp.info.IlluminationCurrent*100, precision) + resp.EndDay.Illumination = ToFixed(resp.info.IlluminationEndDay*100, precision) - resp.ZodiacDetailed, resp.BeginDay.Zodiac, resp.CurrentState.Zodiac, resp.EndDay.Zodiac = zodiac.CurrentZodiacs(tGiven, loc, lang, s.moonCache.CreateMoonTable(tGiven)) + resp.ZodiacDetailed, resp.BeginDay.Zodiac, resp.CurrentState.Zodiac, resp.EndDay.Zodiac = zodiac.CurrentZodiacs(tGiven, loc, lang, s.moonCache.CreateMoonTable(tGiven).Elems) if locationCords.IsValid { resp.MoonRiseAndSet, err = pos.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision, locationCords.Longitude, locationCords.Latitude) @@ -161,108 +156,3 @@ func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, loca return c.JSON(resp) } - -/* MOON TABLE */ - -func (s *Server) moonTableCurrentV1(c *fiber.Ctx) error { - utc := c.Query("utc", "UTC:+0") - loc, _ := jt.SetTimezoneLocFromString(utc) - /*if err != nil { - log.Println(err) - }*/ - - tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, loc) - return s.moonTableV1(c, tGiven) -} - -func (s *Server) moonTableYearV1(c *fiber.Ctx) error { - utc := c.Query("utc", "UTC:+0") - loc, _ := jt.SetTimezoneLocFromString(utc) - /*if err != nil { - log.Println(err) - }*/ - - tNow := time.Now() - year := strToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) - - tGiven := time.Date(year, time.January, 1, 0, 0, 0, 0, loc) - return s.moonTableV1(c, tGiven) -} - -func (s *Server) moonTableV1(c *fiber.Ctx, tGiven time.Time) error { - resp := MoonTable{} - resp.Table = s.moonCache.GenerateMoonTable(tGiven) - return c.JSON(resp.Table) -} - -/* Julian Time methods */ -func (s *Server) toJulianTimeByDateV1(c *fiber.Ctx) error { - tNow := time.Now() - tNow = tNow.In(time.UTC) - - precision := strToInt(c.Query("precision", "2"), 2, 0, 20) - - year := strToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) - month := strToInt(c.Query("month", strconv.Itoa(int(tNow.Month()))), int(tNow.Month()), 1, 12) - day := strToInt(c.Query("day", strconv.Itoa(int(tNow.Day()))), int(tNow.Day()), 1, 31) - - err := isValidDate(year, month, day) - if err != nil { - c.Status(400) - return c.SendString("Validation error: " + err.Error()) - } - - hour := strToInt(c.Query("hour", strconv.Itoa(int(tNow.Hour()))), int(tNow.Hour()), 0, 23) - minute := strToInt(c.Query("minute", strconv.Itoa(int(tNow.Minute()))), int(tNow.Minute()), 0, 59) - second := strToInt(c.Query("second", strconv.Itoa(int(tNow.Second()))), int(tNow.Second()), 0, 59) - - tGiven := time.Date(year, jt.GetMonth(month), day, hour, minute, second, 0, time.UTC) - return s.toJulianTimeV1(c, tGiven, precision) -} - -func (s *Server) toJulianTimeByTimestampV1(c *fiber.Ctx) error { - tStr := c.Query("timestamp", strconv.FormatInt(time.Now().Unix(), 10)) - t, err := strconv.ParseInt(tStr, 10, 64) - if err != nil { - t = time.Now().Unix() - } - - precision := strToInt(c.Query("precision", "2"), 2, 0, 20) - - tm := time.Unix(t, 0) - tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, time.Local) - tGiven = tGiven.In(time.UTC) - return s.toJulianTimeV1(c, tGiven, precision) -} - -func (s *Server) toJulianTimeV1(c *fiber.Ctx, tGiven time.Time, precision int) error { - resp := JulianTimeResp{} - resp.CivilDate = tGiven.String() - resp.CivilDateTimestamp = tGiven.Unix() - resp.JulianDate = toFixed(jt.ToJulianDate(tGiven), precision) - return c.JSON(resp) -} - -func (s *Server) fromJulianTimeV1(c *fiber.Ctx) error { - utc := c.Query("utc", "UTC:+0") - loc, _ := jt.SetTimezoneLocFromString(utc) - - jtStr := c.Query("jtime", "none") - jtime, err := strconv.ParseFloat(jtStr, 64) - if err != nil { - c.Status(400) - c.JSON("missing required parameter: 'jtime' (float)") - return nil - } - - t := jt.FromJulianDate(jtime, loc) - - precision := strToInt(c.Query("precision", "2"), 2, 0, 20) - - resp := JulianTimeResp{} - resp.CivilDate = t.String() - resp.CivilDateTimestamp = t.Unix() - resp.JulianDate = toFixed(jtime, precision) - - return c.JSON(resp) -} diff --git a/internal/server/server.go b/internal/server/server.go index ade60cc..0351db6 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -59,7 +59,7 @@ func (s *Server) NewRouter() *fiber.App { //app.Get("/v1/moonRiseSetCalendar") //app.Get("/v1/moonPhaseCalendar") //app.Get("/v1/moonZodiacCalendar") - //app.Get("/v1/moonMonthCalendar") -- all combined? think more + //app.Get("/v1/moonMonthCalendar") -- all combined? think more.... // eclipses //app.Get("/v1/moonEclipseYear") @@ -68,11 +68,8 @@ func (s *Server) NewRouter() *fiber.App { //app.Get("/v1/sunEclipseCalendar") // methods when? - //app.Get("/v1/nextMoonPhase") - //app.Get("/v1/nextMoonPhaseFull") - //app.Get("/v1/nextMoonPhaseNew") - //app.Get("/v1/nextMoonPhaseFirst") - //app.Get("/v1/nextMoonPhaseThird") + app.Get("/v1/nextMoonPhase", s.moonNextMoonPhaseV1) + app.Get("/api/v1/nextMoonPhase", s.moonNextMoonPhaseV1) //app.Get("/v1/nextMoonEclipse") //app.Get("/v1/nextSunEclipse") @@ -99,3 +96,7 @@ func (s *Server) NewRouter() *fiber.App { s.moonCache = new(moon.Cache) return app } + +func (s *Server) versionV1(c *fiber.Ctx) error { + return c.JSON("1.2.0rc1") +} diff --git a/pkg/moon/days.go b/pkg/moon/days.go index 00bfd72..bb810b1 100644 --- a/pkg/moon/days.go +++ b/pkg/moon/days.go @@ -9,12 +9,12 @@ import ( phase "moon/pkg/phase" ) -func (c *Cache) CreateMoonTable(timeGiven time.Time) []*MoonTableElement { +func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { t := time.Date(timeGiven.Year(), 0, 0, 0, 0, 0, 0, timeGiven.Location()) - if c.tables != nil && c.tables[t.String()] != nil { - return c.tables[t.String()] + if c.moonTable != nil && c.moonTable[t.String()] != nil { + return c.moonTable[t.String()] } - moonTable := []*MoonTableElement{} + moonTable := new(MoonTable) var l int var k1, mtime float64 @@ -76,18 +76,18 @@ func (c *Cache) CreateMoonTable(timeGiven time.Time) []*MoonTableElement { elem.FullMoon = jt.FromJulianDate(mp, timeGiven.Location()) if elem.t1 != elem.t2 { - moonTable = append(moonTable, elem) + moonTable.Elems = append(moonTable.Elems, elem) } if elem.LastQuarter.Year() > timeGiven.Year() { break } } - if c.tables == nil { - c.tables = make(map[string][]*MoonTableElement) + if c.moonTable == nil { + c.moonTable = make(map[string]*MoonTable) } - if c.tables[t.String()] == nil { - c.tables[t.String()] = moonTable + if c.moonTable[t.String()] == nil { + c.moonTable[t.String()] = moonTable } return moonTable } @@ -117,6 +117,26 @@ func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, moonTable return time.Time{} } +func (c *Cache) FindNearestPhase(tGiven time.Time) NearestPhase { + var np NearestPhase + table := c.CreateMoonTable(tGiven) + + if val, err := c.SearchPhase(tGiven, table, NewMoon); err == nil { + np.NewMoon = val + } + if val, err := c.SearchPhase(tGiven, table, FirstQuarter); err == nil { + np.FirstQuarter = val + } + if val, err := c.SearchPhase(tGiven, table, FullMoon); err == nil { + np.FullMoon = val + } + if val, err := c.SearchPhase(tGiven, table, LastQuarter); err == nil { + np.LastQuarter = val + } + + return np +} + func GetMoonDays(tGiven time.Time, table []*MoonTableElement) time.Duration { var moonDays time.Duration for i := range table { diff --git a/pkg/moon/models.go b/pkg/moon/models.go index 5b5c562..a406d07 100644 --- a/pkg/moon/models.go +++ b/pkg/moon/models.go @@ -2,6 +2,19 @@ package moon import "time" +type EnumPhase int + +const ( + NewMoon EnumPhase = iota + FirstQuarter + FullMoon + LastQuarter +) + +type MoonTable struct { + Elems []*MoonTableElement +} + type MoonTableElement struct { NewMoon time.Time FirstQuarter time.Time @@ -12,7 +25,7 @@ type MoonTableElement struct { } type Cache struct { - tables map[string][]*MoonTableElement + moonTable map[string]*MoonTable } type MoonDay struct { @@ -26,3 +39,10 @@ type MoonDaysDetailed struct { Count int Day []MoonDay } + +type NearestPhase struct { + NewMoon time.Time + FirstQuarter time.Time + FullMoon time.Time + LastQuarter time.Time +} diff --git a/pkg/moon/moon.go b/pkg/moon/moon.go index def4dd4..8d355c5 100644 --- a/pkg/moon/moon.go +++ b/pkg/moon/moon.go @@ -1,6 +1,8 @@ package moon import ( + "errors" + "log" pos "moon/pkg/position" "time" ) @@ -14,9 +16,9 @@ func (c *Cache) CurrentMoonDays(tGiven time.Time, loc *time.Location) (time.Dura dayEndTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day()+1, 0, 0, 0, 0, loc) moonTable := c.CreateMoonTable(tGiven) - beginMoonDays := GetMoonDays(dayBeginTime, moonTable) - currentMoonDays := GetMoonDays(tGiven, moonTable) - endMoonDays := GetMoonDays(dayEndTime, moonTable) + beginMoonDays := GetMoonDays(dayBeginTime, moonTable.Elems) + currentMoonDays := GetMoonDays(tGiven, moonTable.Elems) + endMoonDays := GetMoonDays(dayEndTime, moonTable.Elems) return beginMoonDays, currentMoonDays, endMoonDays } @@ -70,6 +72,132 @@ func (c *Cache) MoonDetailed(tGiven time.Time, loc *time.Location, lang string, return moonDaysDetailed } -func (c *Cache) GenerateMoonTable(tGiven time.Time) []*MoonTableElement { +func (c *Cache) GenerateMoonTable(tGiven time.Time) *MoonTable { return c.CreateMoonTable(tGiven) } + +func (c *Cache) SearchPhase(tGiven time.Time, moonTable *MoonTable, phase EnumPhase) (t time.Time, err error) { + if moonTable == nil { + err = errors.New("passed empty moonTable to SearchNewMoon") + log.Println(err.Error()) + return + } + err = errors.New("not found") + for i := range moonTable.Elems { + elem := moonTable.Elems[i] + if elem.t1 != elem.t2 { + elemSearch1 := elem.NewMoon + if tGiven.Before(elemSearch1) { + elemSearch1 = time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day()-1, 0, 0, 0, 0, tGiven.Location()) + } + elemSearch2 := elem.LastQuarter + + // range current phase: + if tGiven.After(elemSearch1) && tGiven.Before(elemSearch2) { + // found in current phase + switch phase { + case NewMoon: + if tGiven.Before(elem.NewMoon) { + return elem.NewMoon, nil + } + case FirstQuarter: + if tGiven.Before(elem.FirstQuarter) { + return elem.FirstQuarter, nil + } + case FullMoon: + if tGiven.Before(elem.FullMoon) { + return elem.FullMoon, nil + } + case LastQuarter: + if tGiven.Before(elem.LastQuarter) { + return elem.LastQuarter, nil + } + } + // found in next phase + if i < len(moonTable.Elems)-1 { + // use values if in table + switch phase { + case NewMoon: + return moonTable.Elems[i+1].NewMoon, nil + case FirstQuarter: + return moonTable.Elems[i+1].FirstQuarter, nil + case FullMoon: + return moonTable.Elems[i+1].FullMoon, nil + case LastQuarter: + return moonTable.Elems[i+1].LastQuarter, nil + } + } else { + // create table for next table + newT := time.Date(tGiven.Year()+1, 0, 0, 0, 0, 0, 0, tGiven.Location()) + newMoonTable := c.CreateMoonTable(newT) + + if newMoonTable != nil && newMoonTable.Elems != nil && len(newMoonTable.Elems) > 0 { + switch phase { + case NewMoon: + if tGiven.Before(newMoonTable.Elems[0].NewMoon) { + return newMoonTable.Elems[0].NewMoon, nil + } + case FirstQuarter: + if tGiven.Before(newMoonTable.Elems[0].FirstQuarter) { + return newMoonTable.Elems[0].FirstQuarter, nil + } + case FullMoon: + if tGiven.Before(newMoonTable.Elems[0].FullMoon) { + return newMoonTable.Elems[0].FullMoon, nil + } + case LastQuarter: + if tGiven.Before(newMoonTable.Elems[0].LastQuarter) { + return newMoonTable.Elems[0].LastQuarter, nil + } + } + } + } + } + // range next phase + if i < len(moonTable.Elems)-1 { + // try to find in current table + elem2 := moonTable.Elems[i+1] + elemSearch1 = elem.LastQuarter + + switch phase { + case NewMoon: + elemSearch2 = elem2.NewMoon + case FirstQuarter: + elemSearch2 = elem2.FirstQuarter + case FullMoon: + elemSearch2 = elem2.FullMoon + case LastQuarter: + elemSearch2 = elem2.LastQuarter + } + + if tGiven.After(elemSearch1) && tGiven.Before(elemSearch2) { + return elemSearch2, nil + } + + } else { + // try to find in next table + newT := time.Date(tGiven.Year()+1, 0, 0, 0, 0, 0, 0, tGiven.Location()) + newMoonTable := c.CreateMoonTable(newT) + if newMoonTable != nil && newMoonTable.Elems != nil && len(newMoonTable.Elems) > 0 { + elemSearch1 = elem.LastQuarter + + switch phase { + case NewMoon: + elemSearch2 = newMoonTable.Elems[0].NewMoon + case FirstQuarter: + elemSearch2 = newMoonTable.Elems[0].FirstQuarter + case FullMoon: + elemSearch2 = newMoonTable.Elems[0].FullMoon + case LastQuarter: + elemSearch2 = newMoonTable.Elems[0].LastQuarter + } + + if tGiven.After(elemSearch1) && tGiven.Before(elemSearch2) { + return elemSearch2, nil + } + } + } + } + } + return +} diff --git a/pkg/moon/moon_test.go b/pkg/moon/moon_test.go new file mode 100644 index 0000000..743374f --- /dev/null +++ b/pkg/moon/moon_test.go @@ -0,0 +1,759 @@ +package moon + +import ( + "errors" + "testing" + "time" +) + +func TestSearchPhase_NextPhase(t *testing.T) { + location := time.UTC + + tests := []struct { + name string + tGiven time.Time + moonTable *MoonTable + phase EnumPhase + wantTime time.Time + wantError error + }{ + { + name: "nil moon table returns error", + tGiven: time.Now(), + moonTable: nil, + phase: NewMoon, + wantError: errors.New("passed empty moonTable to SearchNewMoon"), + }, + { + name: "empty moon table returns not found", + tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{}, + }, + phase: NewMoon, + wantError: errors.New("not found"), + }, + { + name: "next new moon after current time - found in current element", + tGiven: time.Date(2023, 1, 5, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + }, + }, + phase: FirstQuarter, + wantTime: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + }, + { + name: "next new moon when current phase already passed - get from next element", + tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 3.0, + t2: 4.0, + }, + }, + }, + phase: NewMoon, + wantTime: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + }, + { + name: "next full moon between phases - found in next element", + tGiven: time.Date(2023, 1, 25, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 3.0, + t2: 4.0, + }, + }, + }, + phase: FullMoon, + wantTime: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + }, + { + name: "next phase when time is exactly on phase - should return next occurrence", + tGiven: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 3.0, + t2: 4.0, + }, + }, + }, + phase: FirstQuarter, + wantTime: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + }, + { + name: "time before all phases in table - return first phase", + tGiven: time.Date(2022, 12, 31, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + }, + }, + phase: NewMoon, + wantTime: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + }, + { + name: "element with t1 == t2 should be skipped", + tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 1.0, + }, + }, + }, + phase: NewMoon, + wantError: errors.New("not found"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cache := &Cache{} + + gotTime, err := cache.SearchPhase(tt.tGiven, tt.moonTable, tt.phase) + if tt.wantError != nil { + if err == nil { + t.Errorf("Test: %v, SearchPhase() error = %v, wantErr %v", tt.name, err, tt.wantError) + } else if err.Error() != tt.wantError.Error() { + t.Errorf("Test: %v, SearchPhase() error = %v, wantErr %v", tt.name, err, tt.wantError) + } + return + } + + if err != nil { + t.Errorf("Test: %v, SearchPhase() unexpected error = %v", tt.name, err) + return + } + + if !gotTime.Equal(tt.wantTime) { + t.Errorf("Test: %v, SearchPhase() gotTime = %v, want %v", tt.name, gotTime, tt.wantTime) + } + }) + } +} + +func TestSearchPhase_AllPhaseTypes(t *testing.T) { + location := time.UTC + + moonTable := &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 3.0, + t2: 4.0, + }, + }, + } + + tests := []struct { + name string + tGiven time.Time + phase EnumPhase + wantTime time.Time + }{ + { + name: "next new moon", + tGiven: time.Date(2023, 1, 5, 0, 0, 0, 0, location), + phase: NewMoon, + wantTime: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + }, + { + name: "next first quarter", + tGiven: time.Date(2023, 1, 5, 0, 0, 0, 0, location), + phase: FirstQuarter, + wantTime: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + }, + { + name: "next full moon", + tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), + phase: FullMoon, + wantTime: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + }, + { + name: "next last quarter", + tGiven: time.Date(2023, 1, 20, 0, 0, 0, 0, location), + phase: LastQuarter, + wantTime: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + }, + } + + cache := &Cache{} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := cache.SearchPhase(tt.tGiven, moonTable, tt.phase) + + if err != nil { + t.Errorf("SearchPhase() for %s failed with error: %v", tt.name, err) + return + } + + if !result.Equal(tt.wantTime) { + t.Errorf("SearchPhase() for %s gotTime = %v, want %v", tt.name, result, tt.wantTime) + } + }) + } +} + +func TestSearchPhase_ComplexScenarios(t *testing.T) { + location := time.UTC + + tests := []struct { + name string + tGiven time.Time + moonTable *MoonTable + phase EnumPhase + wantTime time.Time + }{ + { + name: "multiple elements - find in third element", + tGiven: time.Date(2023, 3, 1, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 3.0, + t2: 4.0, + }, + { + NewMoon: time.Date(2023, 3, 3, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 3, 10, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 3, 17, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 3, 24, 0, 0, 0, 0, location), + t1: 5.0, + t2: 6.0, + }, + }, + }, + phase: FirstQuarter, + wantTime: time.Date(2023, 3, 10, 0, 0, 0, 0, location), + }, + { + name: "phase in gap between elements", + tGiven: time.Date(2023, 1, 25, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 3.0, + t2: 4.0, + }, + }, + }, + phase: NewMoon, + wantTime: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + }, + } + + cache := &Cache{} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := cache.SearchPhase(tt.tGiven, tt.moonTable, tt.phase) + + if err != nil { + t.Errorf("SearchPhase() for %s failed with error: %v", tt.name, err) + return + } + + if !result.Equal(tt.wantTime) { + t.Errorf("SearchPhase() for %s gotTime = %v, want %v", tt.name, result, tt.wantTime) + } + }) + } +} + +func TestSearchPhase_EdgeCases(t *testing.T) { + location := time.UTC + + tests := []struct { + name string + tGiven time.Time + moonTable *MoonTable + phase EnumPhase + wantTime time.Time + wantError error + }{ + { + name: "time exactly at last quarter - should return next phase from next element", + tGiven: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 3.0, + t2: 4.0, + }, + }, + }, + phase: LastQuarter, + wantTime: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + }, + { + name: "time between last quarter and next new moon - should return next new moon", + tGiven: time.Date(2023, 1, 25, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 3.0, + t2: 4.0, + }, + }, + }, + phase: NewMoon, + wantTime: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + }, + { + name: "all elements skipped due to t1 == t2", + tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 1.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 2.0, + t2: 2.0, + }, + }, + }, + phase: NewMoon, + wantError: errors.New("not found"), + }, + { + name: "mixed valid and invalid elements", + tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 1.0, + }, + { + NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), + t1: 2.0, + t2: 3.0, + }, + }, + }, + phase: NewMoon, + wantTime: time.Date(2023, 2, 1, 0, 0, 0, 0, location), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cache := &Cache{} + + gotTime, err := cache.SearchPhase(tt.tGiven, tt.moonTable, tt.phase) + + if tt.wantError != nil { + if err == nil { + t.Errorf("Test: %v, SearchPhase() error = %v, wantErr %v", tt.name, err, tt.wantError) + } else if err.Error() != tt.wantError.Error() { + t.Errorf("Test: %v, SearchPhase() error = %v, wantErr %v", tt.name, err, tt.wantError) + } + return + } + + if err != nil { + t.Errorf("Test: %v, SearchPhase() unexpected error = %v", tt.name, err) + return + } + + if !gotTime.Equal(tt.wantTime) { + t.Errorf("Test: %v, SearchPhase() gotTime = %v, want %v", tt.name, gotTime, tt.wantTime) + } + }) + } +} + +func TestSearchPhase_TimeLocation(t *testing.T) { + utcLocation := time.UTC + moscowLocation, _ := time.LoadLocation("Europe/Moscow") + newYorkLocation, _ := time.LoadLocation("America/New_York") + + tests := []struct { + name string + tGiven time.Time + moonTable *MoonTable + phase EnumPhase + wantTime time.Time + }{ + { + name: "Moscow timezone", + tGiven: time.Date(2023, 1, 5, 0, 0, 0, 0, moscowLocation), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, moscowLocation), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, moscowLocation), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, moscowLocation), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, moscowLocation), + t1: 1.0, + t2: 2.0, + }, + }, + }, + phase: FirstQuarter, + wantTime: time.Date(2023, 1, 8, 0, 0, 0, 0, moscowLocation), + }, + { + name: "New York timezone", + tGiven: time.Date(2023, 1, 5, 0, 0, 0, 0, newYorkLocation), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, newYorkLocation), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, newYorkLocation), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, newYorkLocation), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, newYorkLocation), + t1: 1.0, + t2: 2.0, + }, + }, + }, + phase: FirstQuarter, + wantTime: time.Date(2023, 1, 8, 0, 0, 0, 0, newYorkLocation), + }, + { + name: "mixed timezones - should handle correctly", + tGiven: time.Date(2023, 1, 5, 0, 0, 0, 0, moscowLocation), + moonTable: &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, utcLocation), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, utcLocation), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, utcLocation), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, utcLocation), + t1: 1.0, + t2: 2.0, + }, + }, + }, + phase: FirstQuarter, + wantTime: time.Date(2023, 1, 8, 0, 0, 0, 0, utcLocation), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cache := &Cache{} + + result, err := cache.SearchPhase(tt.tGiven, tt.moonTable, tt.phase) + + if err != nil { + t.Errorf("SearchPhase() for %s failed with error: %v", tt.name, err) + return + } + + if !result.Equal(tt.wantTime) { + t.Errorf("SearchPhase() for %s gotTime = %v, want %v", tt.name, result, tt.wantTime) + } + + if result.Location() != tt.wantTime.Location() { + t.Errorf("SearchPhase() for %s location = %v, want %v", + tt.name, result.Location(), tt.wantTime.Location()) + } + }) + } +} + +func TestSearchPhase_PhaseOrder(t *testing.T) { + location := time.UTC + + moonTable := &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + }, + } + + tests := []struct { + name string + tGiven time.Time + phase EnumPhase + wantTime time.Time + desc string + }{ + { + name: "between new moon and first quarter - find first quarter", + tGiven: time.Date(2023, 1, 3, 0, 0, 0, 0, location), + phase: FirstQuarter, + wantTime: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + }, + { + name: "between first quarter and full moon - find full moon", + tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), + phase: FullMoon, + wantTime: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + }, + { + name: "between full moon and last quarter - find last quarter", + tGiven: time.Date(2023, 1, 18, 0, 0, 0, 0, location), + phase: LastQuarter, + wantTime: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + }, + } + + cache := &Cache{} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := cache.SearchPhase(tt.tGiven, moonTable, tt.phase) + + if err != nil { + t.Errorf("SearchPhase() for %s failed with error: %v", tt.name, err) + return + } + + if !result.Equal(tt.wantTime) { + t.Errorf("SearchPhase() for %s gotTime = %v, want %v. %s", + tt.name, result, tt.wantTime, tt.desc) + } + }) + } +} + +func TestSearchPhase_SingleElement(t *testing.T) { + location := time.UTC + + singleElementTable := &MoonTable{ + Elems: []*MoonTableElement{ + { + NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), + FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), + t1: 1.0, + t2: 2.0, + }, + }, + } + + tests := []struct { + name string + tGiven time.Time + phase EnumPhase + wantTime time.Time + wantError error + }{ + { + name: "before all phases - return first phase", + tGiven: time.Date(2022, 12, 31, 0, 0, 0, 0, location), + phase: NewMoon, + wantTime: time.Date(2023, 1, 1, 0, 0, 0, 0, location), + }, + { + name: "after all phases - should try to create next table", + tGiven: time.Date(2023, 12, 31, 0, 0, 0, 0, location), + phase: NewMoon, + wantError: errors.New("not found"), + }, + { + name: "exactly in middle - find next phase", + tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), + phase: FullMoon, + wantTime: time.Date(2023, 1, 15, 0, 0, 0, 0, location), + }, + } + + cache := &Cache{} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := cache.SearchPhase(tt.tGiven, singleElementTable, tt.phase) + + if tt.wantError != nil { + if err == nil { + t.Errorf("Test: %v, SearchPhase() error = %v, wantErr %v", tt.name, err, tt.wantError) + } else if err.Error() != tt.wantError.Error() { + t.Errorf("Test: %v, SearchPhase() error = %v, wantErr %v", tt.name, err, tt.wantError) + } + return + } + + if err != nil { + t.Errorf("Test: %v, SearchPhase() unexpected error = %v", tt.name, err) + return + } + + if !result.Equal(tt.wantTime) { + t.Errorf("Test: %v, SearchPhase() gotTime = %v, want %v", tt.name, result, tt.wantTime) + } + }) + } +} + +func TestSearchPhase_Performance(t *testing.T) { + location := time.UTC + + var elems []*MoonTableElement + for i := 0; i < 100; i++ { + baseTime := time.Date(2023, time.Month(i+1), 1, 0, 0, 0, 0, location) + elem := &MoonTableElement{ + NewMoon: baseTime, + FirstQuarter: baseTime.AddDate(0, 0, 7), + FullMoon: baseTime.AddDate(0, 0, 14), + LastQuarter: baseTime.AddDate(0, 0, 21), + t1: float64(i * 2), + t2: float64(i*2 + 1), + } + elems = append(elems, elem) + } + + largeMoonTable := &MoonTable{Elems: elems} + cache := &Cache{} + + tGiven := time.Date(2023, 6, 15, 0, 0, 0, 0, location) + result, err := cache.SearchPhase(tGiven, largeMoonTable, FullMoon) + + if err != nil { + t.Errorf("SearchPhase() with large table failed: %v", err) + return + } + + expected := time.Date(2023, 7, 15, 0, 0, 0, 0, location) + if !result.Equal(expected) { + t.Errorf("SearchPhase() with large table gotTime = %v, want %v", result, expected) + } +} From 8210ce3326aeaa4573b36105001bfdfc717a3b7f Mon Sep 17 00:00:00 2001 From: prostraction Date: Sun, 16 Nov 2025 20:40:35 +0500 Subject: [PATCH 04/18] moonPositionMonthly - closes #30, improve nextMoonPhase - #5 --- internal/server/models.go | 23 ++---- internal/server/route-next.go | 23 ++++++ internal/server/route-phase.go | 4 +- internal/server/route-position.go | 45 +++++++++++ internal/server/server.go | 10 ++- pkg/moon/models.go | 7 ++ pkg/position/position.go | 126 +++++++++++++++++++++++++++--- 7 files changed, 205 insertions(+), 33 deletions(-) create mode 100644 internal/server/route-position.go diff --git a/internal/server/models.go b/internal/server/models.go index e467266..b650ec7 100644 --- a/internal/server/models.go +++ b/internal/server/models.go @@ -8,6 +8,11 @@ import ( "time" ) +type ErrorPrintable struct { + Status int + Message string +} + type MoonStat struct { MoonDays float64 Illumination float64 @@ -32,9 +37,9 @@ type MoonDay struct { } type MoonPhaseResponse struct { - BeginDay *MoonStat - CurrentState *MoonStat - EndDay *MoonStat + BeginDay *MoonStat `json:"BeginDay,omitempty"` + CurrentState *MoonStat `json:"CurrentState,omitempty"` + EndDay *MoonStat `json:"EndDay,omitempty"` MoonDaysDetailed *moon.MoonDaysDetailed `json:"MoonDaysDetailed,omitempty"` ZodiacDetailed *zodiac.Zodiacs @@ -55,15 +60,3 @@ type JulianTimeResp struct { CivilDateTimestamp int64 JulianDate float64 } - -type NextMoonPhaseElement struct { - Date string - Countdown int64 -} - -type NextMoonPhase struct { - New *NextMoonPhaseElement - First *NextMoonPhaseElement - Full *NextMoonPhaseElement - Last *NextMoonPhaseElement -} diff --git a/internal/server/route-next.go b/internal/server/route-next.go index 865f266..077bccb 100644 --- a/internal/server/route-next.go +++ b/internal/server/route-next.go @@ -2,6 +2,8 @@ package server import ( jt "moon/pkg/julian-time" + "moon/pkg/moon" + "strings" "time" "github.com/gofiber/fiber/v2" @@ -15,5 +17,26 @@ func (s *Server) moonNextMoonPhaseV1(c *fiber.Ctx) error { np := s.moonCache.FindNearestPhase(tGiven) + format := c.Query("format", "ISO") + if strings.ToLower(format) == "timestamp" { + npt := moon.NearestPhaseTimestamp{ + NewMoon: np.NewMoon.Unix(), + FirstQuarter: np.FirstQuarter.Unix(), + FullMoon: np.FullMoon.Unix(), + LastQuarter: np.LastQuarter.Unix(), + } + return c.JSON(npt) + } + + if strings.ToLower(format) == "duration" { + npd := moon.NearestPhaseTimestamp{ + NewMoon: np.NewMoon.Unix() - tGiven.Unix(), + FirstQuarter: np.FirstQuarter.Unix() - tGiven.Unix(), + FullMoon: np.FullMoon.Unix() - tGiven.Unix(), + LastQuarter: np.LastQuarter.Unix() - tGiven.Unix(), + } + return c.JSON(npd) + } + return c.JSON(np) } diff --git a/internal/server/route-phase.go b/internal/server/route-phase.go index bd162e4..1ea531b 100644 --- a/internal/server/route-phase.go +++ b/internal/server/route-phase.go @@ -125,7 +125,7 @@ func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, loca resp.ZodiacDetailed, resp.BeginDay.Zodiac, resp.CurrentState.Zodiac, resp.EndDay.Zodiac = zodiac.CurrentZodiacs(tGiven, loc, lang, s.moonCache.CreateMoonTable(tGiven).Elems) if locationCords.IsValid { - resp.MoonRiseAndSet, err = pos.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision, locationCords.Longitude, locationCords.Latitude) + resp.MoonRiseAndSet, err = s.positionCache.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision, locationCords.Longitude, locationCords.Latitude) if err != nil { log.Error(err) } @@ -147,7 +147,7 @@ func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, loca log.Error(err) } } else { - resp.MoonRiseAndSet, err = pos.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision) + resp.MoonRiseAndSet, err = s.positionCache.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision) } if err != nil && err.Error() != "no location prodived" { diff --git a/internal/server/route-position.go b/internal/server/route-position.go new file mode 100644 index 0000000..fd50e40 --- /dev/null +++ b/internal/server/route-position.go @@ -0,0 +1,45 @@ +package server + +import ( + jt "moon/pkg/julian-time" + "strconv" + "strings" + "time" + + "github.com/gofiber/fiber/v2" +) + +func (s *Server) moonPositionMonthly(c *fiber.Ctx) error { + utc := c.Query("utc", "UTC:+0") + loc, _ := jt.SetTimezoneLocFromString(utc) + + tNow := time.Now() + year := StrToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) + month := StrToInt(c.Query("month", strconv.Itoa(int(tNow.Month()))), int(tNow.Month()), 1, 12) + + precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) + + latStr := c.Query("latitude", "no-value") + lonStr := c.Query("longitude", "no-value") + locationCords := parseCoords(latStr, lonStr) + + if !locationCords.IsValid { + c.Status(400) + return c.JSON("latitude and longitude is required for this method.") + } + + if resp, err := s.positionCache.GetRisesMonthly(year, month, loc, precision, locationCords.Longitude, locationCords.Latitude); err == nil { + return c.JSON(resp) + } else { + e := ErrorPrintable{} + if strings.Contains(err.Error(), "400 Bad Request") { + e.Status = 400 + c.Status(400) + } else { + e.Status = 500 + c.Status(500) + } + e.Message = err.Error() + return c.JSON(e) + } +} diff --git a/internal/server/server.go b/internal/server/server.go index 0351db6..5069ca9 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,13 +2,15 @@ package server import ( "moon/pkg/moon" + "moon/pkg/position" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" ) type Server struct { - moonCache *moon.Cache + moonCache *moon.Cache + positionCache *position.Cache } func (s *Server) NewRouter() *fiber.App { @@ -54,8 +56,9 @@ func (s *Server) NewRouter() *fiber.App { app.Get("/api/v1/moonPhaseTimestamp", s.moonPhaseTimestampV1) app.Get("/api/v1/moonPhaseDate", s.moonPhaseDatetV1) - // maybe rename "moon*" -> "*" // per month + app.Get("/v1/moonPositionMonthly", s.moonPositionMonthly) + app.Get("/api/v1/moonPositionMonthly", s.moonPositionMonthly) //app.Get("/v1/moonRiseSetCalendar") //app.Get("/v1/moonPhaseCalendar") //app.Get("/v1/moonZodiacCalendar") @@ -94,9 +97,10 @@ func (s *Server) NewRouter() *fiber.App { app.Get("/api/v1/version", s.versionV1) s.moonCache = new(moon.Cache) + s.positionCache = new(position.Cache) return app } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc1") + return c.JSON("1.2.0rc2") } diff --git a/pkg/moon/models.go b/pkg/moon/models.go index a406d07..16784a8 100644 --- a/pkg/moon/models.go +++ b/pkg/moon/models.go @@ -46,3 +46,10 @@ type NearestPhase struct { FullMoon time.Time LastQuarter time.Time } + +type NearestPhaseTimestamp struct { + NewMoon int64 + FirstQuarter int64 + FullMoon int64 + LastQuarter int64 +} diff --git a/pkg/position/position.go b/pkg/position/position.go index 6f630f5..8381ab7 100644 --- a/pkg/position/position.go +++ b/pkg/position/position.go @@ -8,9 +8,16 @@ import ( jt "moon/pkg/julian-time" "net/http" "net/url" + "strconv" + "strings" "time" ) +type Cache struct { + CacheDaily map[string]*DayData + CacheMonthly map[string]*[]DayData +} + // DayResponse type DayResponse struct { Status string `json:"Status"` @@ -22,6 +29,7 @@ type DayResponse struct { // MonthResponse type MonthResponse struct { Status string `json:"Status"` + Message string `json:"Message,omitempty"` Parameters Parameters `json:"Parameters"` Data []DayData `json:"Data"` Range string `json:"Range"` @@ -50,20 +58,46 @@ type MoonPosition struct { } type DayData struct { - Moonrise *MoonPosition `json:"Moonrise,omitempty"` - Moonset *MoonPosition `json:"Moonset,omitempty"` - Meridian *MoonPosition `json:"Meridian,omitempty"` + Day *string `json:"Date,omitempty"` IsMoonRise bool `json:"IsMoonRise"` IsMoonSet bool `json:"IsMoonSet"` IsMeridian bool `json:"IsMeridian"` + Moonrise *MoonPosition `json:"Moonrise,omitempty"` + Moonset *MoonPosition `json:"Moonset,omitempty"` + Meridian *MoonPosition `json:"Meridian,omitempty"` } -func GetRisesMonthly(year, month int, loc *time.Location, precision int, location ...float64) (*MonthResponse, error) { +func (c *Cache) GetRisesMonthly(year, month int, loc *time.Location, precision int, location ...float64) (*[]DayData, error) { lat, lon, err := parseLocation(location) if err != nil { return nil, err } + if c.CacheMonthly == nil { + c.CacheMonthly = make(map[string]*[]DayData) + } + + var strKey strings.Builder + strKey.WriteString(strconv.Itoa(year)) + strKey.WriteString("-") + strKey.WriteString(strconv.Itoa(month)) + strKey.WriteString("-") + if loc != nil { + strKey.WriteString(loc.String()) + } else { + strKey.WriteString("nil") + } + strKey.WriteString("-") + strKey.WriteString(strconv.Itoa(precision)) + strKey.WriteString("-") + strKey.WriteString(strconv.FormatFloat(lat, 'e', precision, 64)) + strKey.WriteString("-") + strKey.WriteString(strconv.FormatFloat(lon, 'e', precision, 64)) + + if c.CacheMonthly != nil && c.CacheMonthly[strKey.String()] != nil { + return c.CacheMonthly[strKey.String()], nil + } + h := 0 if loc != nil { jth, _, err := jt.GetTimeFromLocation(loc) @@ -80,29 +114,50 @@ func GetRisesMonthly(year, month int, loc *time.Location, precision int, locatio params.Add("month", fmt.Sprintf("%d", month)) params.Add("precision", fmt.Sprintf("%d", precision)) - url := baseURL + "?" + params.Encode() + url := baseURL + "monthly?" + params.Encode() client := &http.Client{Timeout: 69 * time.Second} resp, err := client.Get(url) if err != nil { - return nil, fmt.Errorf("failed to make request: %w", err) + return nil, fmt.Errorf("[%s] Failed to make request: %w", resp.Status, err) } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("server returned status: %s", resp.Status) - } - body, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("failed to read response: %w", err) + return nil, fmt.Errorf("[%s] Failed to read response: %w", resp.Status, err) } var monthResponse MonthResponse if err := json.Unmarshal(body, &monthResponse); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) + return nil, fmt.Errorf("[%s] Failed to unmarshal response: %w", resp.Status, err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("[%s] %s", resp.Status, monthResponse.Message) + } + + for i := range monthResponse.Data { + if monthResponse.Data[i].Meridian != nil { + timestampToGoTime(monthResponse.Data[i].Meridian, loc) + monthResponse.Data[i].Meridian.TimeISO = time.Unix(monthResponse.Data[i].Meridian.Timestamp, 0) + } + if monthResponse.Data[i].Moonrise != nil { + timestampToGoTime(monthResponse.Data[i].Moonrise, loc) + monthResponse.Data[i].Moonrise.TimeISO = time.Unix(monthResponse.Data[i].Moonrise.Timestamp, 0) + } + if monthResponse.Data[i].Moonset != nil { + timestampToGoTime(monthResponse.Data[i].Moonset, loc) + monthResponse.Data[i].Moonset.TimeISO = time.Unix(monthResponse.Data[i].Moonset.Timestamp, 0) + } + t := time.Date(year, jt.GetMonth(month), 1+i, 0, 0, 0, 0, time.UTC).Format("2006-01-02") + monthResponse.Data[i].Day = &t } - return &monthResponse, nil + if c.CacheMonthly != nil && c.CacheMonthly[strKey.String()] == nil { + c.CacheMonthly[strKey.String()] = &monthResponse.Data + } + + return &monthResponse.Data, nil } func GetRisesDay(year, month, day int, loc *time.Location, precision int, location ...float64) (*DayData, error) { @@ -167,6 +222,51 @@ func GetRisesDay(year, month, day int, loc *time.Location, precision int, locati return dayResponse.Data, nil } +func (c *Cache) GetRisesDay(year, month, day int, loc *time.Location, precision int, location ...float64) (*DayData, error) { + lat, lon, err := parseLocation(location) + if err != nil { + return nil, err + } + + if c.CacheDaily == nil { + c.CacheDaily = make(map[string]*DayData) + } + + var strKey strings.Builder + strKey.WriteString(strconv.Itoa(year)) + strKey.WriteString("-") + strKey.WriteString(strconv.Itoa(month)) + strKey.WriteString("-") + strKey.WriteString(strconv.Itoa(day)) + strKey.WriteString("-") + if loc != nil { + strKey.WriteString(loc.String()) + } else { + strKey.WriteString("nil") + } + strKey.WriteString("-") + strKey.WriteString(strconv.Itoa(precision)) + strKey.WriteString("-") + strKey.WriteString(strconv.FormatFloat(lat, 'e', precision, 64)) + strKey.WriteString("-") + strKey.WriteString(strconv.FormatFloat(lon, 'e', precision, 64)) + + if c.CacheDaily != nil && c.CacheDaily[strKey.String()] != nil { + return c.CacheDaily[strKey.String()], nil + } + + dayResponse, err := GetRisesDay(year, month, day, loc, precision, location...) + if err != nil { + return nil, err + } + + if c.CacheDaily != nil && c.CacheDaily[strKey.String()] == nil { + c.CacheDaily[strKey.String()] = dayResponse + } + + return dayResponse, nil +} + func GetMoonPosition(tGiven time.Time, loc *time.Location, precision int, location ...float64) (*MoonPosition, error) { lat, lon, err := parseLocation(location) if err != nil { From b465a90d1eaf8b9bac8ab5b1028c8d3c6ed8df76 Mon Sep 17 00:00:00 2001 From: prostraction Date: Sun, 16 Nov 2025 21:02:34 +0500 Subject: [PATCH 05/18] feat: docs --- README.md | 295 +++++++++++++++++++++++--- doc/postman_collection | 466 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 707 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 3ea2594..cfc62e7 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,11 @@ python addon/skyfield/server.py ## Methods -### GET /v1/moonPhaseDate +### GET /api/v1/moonPhaseDate The method returns the Moon parameters for the specified day and time. If the day or time is not specified, the current value for the unspecified fields is taken. If longitude and latitude are specified, the response will contain additional structures. -### Params +#### Params | Parameter | Type | Description | Example Value | | :--- | :--- | :--- | :--- | @@ -41,7 +41,7 @@ The method returns the Moon parameters for the specified day and time. If the da ---------------------------------------------------------------- -### /v1/moonPhaseDate Response: +#### Response: The method returns 6 objects: - ```BeginDay```, ```CurrentState```, ```EndDay``` objects of the ```MoonStat``` structure to display the position of the moon at the beginning of the day, the specified time and the end of the day, respectively; @@ -65,7 +65,7 @@ The method returns 6 objects: ---------------------------------------------------------------- -#### MoonStat (used as ```BeginDay```, ```CurrentState```, ```EndDay```) +##### MoonStat (used as ```BeginDay```, ```CurrentState```, ```EndDay```) ```MoonStat``` objects are used to display at a given time. In case of a method response, MoonStat will contain the values: @@ -118,7 +118,7 @@ MoonPosition structure (Exists only if latitude and longitude are specified): ---------------------------------------------------------------- -#### MoonDaysDetailed +##### MoonDaysDetailed ```MoonDaysDetailed``` is a structure that contains an array for each lunar day that falls on a given Earth day. Exists only if latitude and longitude are specified. @@ -138,7 +138,7 @@ MoonPosition structure (Exists only if latitude and longitude are specified): ---------------------------------------------------------------- -#### ZodiacDetailed +##### ZodiacDetailed ```ZodiacDetailed``` is a structure for determining which zodiac sign the moon is in on a given time interval, when it began and ended. It contains an array for each lunar day that falls on a given Earth day. @@ -159,7 +159,7 @@ MoonPosition structure (Exists only if latitude and longitude are specified): ---------------------------------------------------------------- -#### MoonRiseAndSet +##### MoonRiseAndSet ```MoonRiseAndSet``` is a structure for determining the moonrise, moonset and meridian on a given day. Exists only if latitude and longitude are specified. @@ -168,12 +168,13 @@ MoonPosition structure (Exists only if latitude and longitude are specified): | Response Variable | Type | Description | Example Value | | :--- | :--- | :--- | :--- | -|`MoonRiseAndSet.Moonrise` | `Object of struct MoonPosition [optional]` | Moonrise position data. Exists only if IsMoonRise = true | - | -|`MoonRiseAndSet.Moonset` | `Object of struct MoonPosition [optional]` | Moonset position data. Exists only if IsMoonSet = true | - | -|`MoonRiseAndSet.Meridian` | `Object of struct MoonPosition [optional]` | Meridian position data, Exists only if IsMeridian = true | - | +|`MoonRiseAndSet.Date` | `String [optional]` | Date for day of calculations, missing as default | `2025-01-15` | |`MoonRiseAndSet.IsMoonRise` | `Boolean [required]` | True if moonrise occurs at given day | `true` | |`MoonRiseAndSet.IsMoonSet` | `Boolean [required]` | True if moonset occurs at given day | `true` | |`MoonRiseAndSet.IsMeridian` | `Boolean [required]` | True if meridian transit occurs at given day | `true` | +|`MoonRiseAndSet.Moonrise` | `Object of struct MoonPosition [optional]` | Moonrise position data. Exists only if IsMoonRise = true | - | +|`MoonRiseAndSet.Moonset` | `Object of struct MoonPosition [optional]` | Moonset position data. Exists only if IsMoonSet = true | - | +|`MoonRiseAndSet.Meridian` | `Object of struct MoonPosition [optional]` | Meridian position data, Exists only if IsMeridian = true | - | MoonPosition structure: @@ -195,10 +196,10 @@ MoonPosition structure: ---------------------------------------------------------------- -#### Full response example: +#### Response example Response of: -```GET /v1/moonPhaseDate?lang=ru&utc=5&latitude=51.1655&longitude=71.4272&year=2025&month=09&day=15&precision=5&hour=12&minute=0&second=0``` +```GET /api/v1/moonPhaseDate?lang=ru&utc=5&latitude=51.1655&longitude=71.4272&year=2025&month=09&day=15&precision=5&hour=12&minute=0&second=0```
JSON @@ -304,6 +305,9 @@ Response of: ] }, "MoonRiseAndSet": { + "IsMoonRise": true, + "IsMoonSet": true, + "IsMeridian": true, "Moonrise": { "Timestamp": 1757957865, "TimeISO": "2025-09-15T22:37:45+05:00", @@ -327,10 +331,7 @@ Response of: "AltitudeDegrees": 67.1, "Direction": "S", "DistanceKm": 374133.37617 - }, - "IsMoonRise": true, - "IsMoonSet": true, - "IsMeridian": true + } } } ``` @@ -339,13 +340,13 @@ Response of: ---------------------------------------------------------------- -### GET /v1/moonPhaseCurrent +### GET /api/v1/moonPhaseCurrent The method returns the Moon parameters for the current day and time. If the day or time is not specified, the current value for the unspecified fields is taken. If longitude and latitude are specified, the response will contain additional structures. This is a synonym for the moonPhaseDate method without day and time Params. -### Params +#### Params | Parameter | Type | Description | Example Value | | :--- | :--- | :--- | :--- | @@ -355,19 +356,19 @@ This is a synonym for the moonPhaseDate method without day and time Params. |`latitude` | `float [optional, default=none]` | Latitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `51.1655` |`longitude` | `float [optional, default=none]` | Longitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `71.4272` -### Response +#### Response -Response as [GET /v1/moonPhaseDate](https://github.com/prostraction/moon/#v1moonphasedate-response) +Response as [GET /api/v1/moonPhaseDate](https://github.com/prostraction/moon/#v1moonphasedate-response) ---------------------------------------------------------------- -### GET /v1/moonPhaseTimestamp +### GET /api/v1/moonPhaseTimestamp The method returns the Moon parameters for the given timestamp. If it is not specified, the current value for the timestamp is taken. If longitude and latitude are specified, the response will contain additional structures. This is a synonym for the moonPhaseDate method but with timestamp instead of date. -### Params +#### Params | Parameter | Type | Description | Example Value | | :--- | :--- | :--- | :--- | @@ -378,24 +379,179 @@ This is a synonym for the moonPhaseDate method but with timestamp instead of dat |`longitude` | `float [optional, default=none]` | Longitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `71.4272` |`timestamp` | `int [optional, default=]` | Timestamp for calculations | `1758045697` -### Response +#### Response + +Response as [GET /api/v1/moonPhaseDate](https://github.com/prostraction/moon/#v1moonphasedate-response) + +---------------------------------------------------------------- + +### GET /api/v1/moonPositionMonthly + +The method returns Moon position for specified month. + +#### Params + + | Parameter | Type | Description | Example Value | +| :--- | :--- | :--- | :--- | +|`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` +|`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` +|`latitude` | `float [required]` | Latitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `51.1655` +|`longitude` | `float [required]` | Longitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `71.4272` +|`year` | `int [optional, default=]` | Format: YYYY Allowed range: [1, 9999] | `2025` +|`month` | `int [optional, default=]` | Format: M or MM. Allowed range: [1, 12] | `01` or `1` + +#### Response + +The method returns array of object MoonRiseAndSet for each day of selected month. + +##### MoonRiseAndSet + +```MoonRiseAndSet``` is a structure for determining the moonrise, moonset and meridian on a given day. Exists only if latitude and longitude are specified. + +
+ Table + +| Response Variable | Type | Description | Example Value | +| :--- | :--- | :--- | :--- | +|`MoonRiseAndSet.Date` | `String [optional]` | Date for day of calculations, missing as default | `2025-01-15` | +|`MoonRiseAndSet.IsMoonRise` | `Boolean [required]` | True if moonrise occurs at given day | `true` | +|`MoonRiseAndSet.IsMoonSet` | `Boolean [required]` | True if moonset occurs at given day | `true` | +|`MoonRiseAndSet.IsMeridian` | `Boolean [required]` | True if meridian transit occurs at given day | `true` | +|`MoonRiseAndSet.Moonrise` | `Object of struct MoonPosition [optional]` | Moonrise position data. Exists only if IsMoonRise = true | - | +|`MoonRiseAndSet.Moonset` | `Object of struct MoonPosition [optional]` | Moonset position data. Exists only if IsMoonSet = true | - | +|`MoonRiseAndSet.Meridian` | `Object of struct MoonPosition [optional]` | Meridian position data, Exists only if IsMeridian = true | - | + +MoonPosition structure: + +
+ Table + +| Response Variable | Type | Description | Example Value | +| :--- | :--- | :--- | :--- | +|`MoonPosition.Timestamp` | `Integer [required]` | Moonrise Unix timestamp | `1758048970` | +|`MoonPosition.TimeISO` | `String [required]` | Moonrise ISO time | `"2025-09-16T23:56:10+05:00"` | +|`MoonPosition.AzimuthDegrees` | `Float [required]` | Moonrise azimuth | `47.3` | +|`MoonPosition.AltitudeDegrees` | `Float [required]` | Moonrise altitude | `-0.6` | +|`MoonPosition.Direction` | `String [required]` | Moonrise direction | `"ENE"` | +|`MoonPosition.DistanceKm` | `Float [required]` | Earth-Moon distance in km | `376559.9` | + +
+ +
+ +#### Response example + +```json -Response as [GET /v1/moonPhaseDate](https://github.com/prostraction/moon/#v1moonphasedate-response) +[ + { + "Date": "2023-03-01", + "IsMoonRise": true, + "IsMoonSet": true, + "IsMeridian": true, + "Moonrise": { + "Timestamp": 1677650857, + "TimeISO": "2023-03-01T11:07:37+05:00", + "AzimuthDegrees": 42.8762, + "AltitudeDegrees": -0.56667, + "Direction": "NE", + "DistanceKm": 402636.75966 + }, + "Moonset": { + "Timestamp": 1677625807, + "TimeISO": "2023-03-01T04:10:07+05:00", + "AzimuthDegrees": 316.74613, + "AltitudeDegrees": -0.56667, + "Direction": "NW", + "DistanceKm": 401798.89266 + }, + "Meridian": { + "Timestamp": 1677683130, + "TimeISO": "2023-03-01T20:05:30+05:00", + "AzimuthDegrees": 180, + "AltitudeDegrees": 66.2, + "Direction": "S", + "DistanceKm": 403572.05151 + } + }, + { + "Date": "2023-03-02", + "IsMoonRise": true, + "IsMoonSet": true, + "IsMeridian": true, + "Moonrise": { + "Timestamp": 1677740467, + "TimeISO": "2023-03-02T12:01:07+05:00", + "AzimuthDegrees": 43.38104, + "AltitudeDegrees": -0.56667, + "Direction": "NE", + "DistanceKm": 404836.13007 + }, + "Moonset": { + "Timestamp": 1677715380, + "TimeISO": "2023-03-02T05:03:00+05:00", + "AzimuthDegrees": 317.04922, + "AltitudeDegrees": -0.56667, + "Direction": "NW", + "DistanceKm": 404345.126 + }, + "Meridian": { + "Timestamp": 1677772596, + "TimeISO": "2023-03-02T20:56:36+05:00", + "AzimuthDegrees": 180, + "AltitudeDegrees": 65.4, + "Direction": "S", + "DistanceKm": 405326.66664 + } + }, + ... + { + "Date": "2023-03-31", + "IsMoonRise": true, + "IsMoonSet": true, + "IsMeridian": true, + "Moonrise": { + "Timestamp": 1680245913, + "TimeISO": "2023-03-31T11:58:33+05:00", + "AzimuthDegrees": 49.55137, + "AltitudeDegrees": -0.56667, + "Direction": "NE", + "DistanceKm": 404935.96867 + }, + "Moonset": { + "Timestamp": 1680218226, + "TimeISO": "2023-03-31T04:17:06+05:00", + "AzimuthDegrees": 312.1322, + "AltitudeDegrees": -0.56667, + "Direction": "NW", + "DistanceKm": 404804.48186 + }, + "Meridian": { + "Timestamp": 1680276504, + "TimeISO": "2023-03-31T20:28:24+05:00", + "AzimuthDegrees": 180, + "AltitudeDegrees": 61.6, + "Direction": "S", + "DistanceKm": 404936.05398 + } + } +] +``` ---------------------------------------------------------------- -### GET /v1/moonTableYear +### GET /api/v1/moonTableYear The method returns the moon phases for the given year. The response contains an array for each month, each element of which contains the time of the new moon, first quarter, full moon, last quarter. -### Params +#### Params | Parameter | Type | Description | Example Value | | :--- | :--- | :--- | :--- | |`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` |`year` | `int [optional, default=]` | Format: YYYY Allowed range: [1, 9999] | `2025` -### /v1/moonTableYear Response +#### Response ```json [ @@ -426,16 +582,93 @@ The method returns the moon phases for the given year. The response contains an ---------------------------------------------------------------- -### GET /v1/moonTableCurrent +### GET /api/v1/moonTableCurrent The method returns the moon phases for the current year. The response contains an array for each month, each element of which contains the time of the new moon, first quarter, full moon, last quarter. -### Params +#### Params | Parameter | Type | Description | Example Value | | :--- | :--- | :--- | :--- | |`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` -### Response: +#### Response: + +Response: as [GET /api/v1/moonTableYear](https://github.com/prostraction/moon#v1moontableyear-response) + +---------------------------------------------------------------- + +### GET /api/v1/toJulianTimeByDate + +The method converts human date to julian time (UTC +0 timezone). + +#### Params + + | Parameter | Type | Description | Example Value | +| :--- | :--- | :--- | :--- | +|`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` +|`year` | `int [optional, default=]` | Format: YYYY Allowed range: [1, 9999] | `2025` +|`month` | `int [optional, default=]` | Format: M or MM. Allowed range: [1, 12] | `01` or `1` +|`day` | `int [optional, default=]` | Format: D or DD. Allowed range: [1, 31] | `01` or `1` +|`hour` | `int [optional, default=]` | Format: h or hh. Allowed range: [0, 23] | `01` or `1` +|`minute` | `int [optional, default=]` | Format: m or mm. Allowed range: [0, 59] | `01` or `1` +|`second` | `int [optional, default=]` | Format: s or ss. Allowed range: [0, 59] | `01` or `1` + +#### Response: + +```json +{ + "CivilDate": "2025-01-01 01:01:01 +0000 UTC", + "CivilDateTimestamp": 1735693261, + "JulianDate": 2460676.54237 +} +``` + +---------------------------------------------------------------- + +### GET /api/v1/toJulianTimeByTimestamp + +The method converts human date to julian time (UTC +0 timezone). + +#### Params + + | Parameter | Type | Description | Example Value | +| :--- | :--- | :--- | :--- | +|`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` +|`timestamp` | `int [optional, default=]` | Timestamp for calculations. Current, if not specified | `1735693261` + + +#### Response: + +```json +{ + "CivilDate": "2025-01-01 01:01:01 +0000 UTC", + "CivilDateTimestamp": 1735693261, + "JulianDate": 2460676.54237 +} +``` + +---------------------------------------------------------------- + +### GET /api/v1/fromJulianTime + +The method converts julian time to human time (UTC +0 timezone). + +#### Params + + | Parameter | Type | Description | Example Value | +| :--- | :--- | :--- | :--- | +|`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` +|`jtime` | `float64 [required]` | Julian Time to convert (float64) | `2460676.5423726854` + + +#### Response: + +```json +{ + "CivilDate": "2025-01-01 01:01:01 +0000 UTC", + "CivilDateTimestamp": 1735693261, + "JulianDate": 2460676.54237 +} +``` -Response: as GET [/v1/moonTableYear](https://github.com/prostraction/moon#v1moontableyear-response) diff --git a/doc/postman_collection b/doc/postman_collection index 0ebaa71..97636f6 100644 --- a/doc/postman_collection +++ b/doc/postman_collection @@ -7,7 +7,7 @@ }, "item": [ { - "name": "Moon Phase — Detailed information for day", + "name": "Moon Phase — Detailed information for day (phase, rise, zodiac)", "item": [ { "name": "Get moon plase by specified date", @@ -189,7 +189,7 @@ } ], "cookie": [], - "body": "{\n \"BeginDay\": {\n \"MoonDays\": 0.85568,\n \"Illumination\": 0.35744,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735671600,\n \"TimeISO\": \"2025-01-01T00:00:00+05:00\",\n \"AzimuthDegrees\": 326.86255,\n \"AltitudeDegrees\": -62.68617,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 382464.56722\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 0.89806,\n \"Illumination\": 0.4137,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735675261,\n \"TimeISO\": \"2025-01-01T01:01:01+05:00\",\n \"AzimuthDegrees\": 356.08557,\n \"AltitudeDegrees\": -65.49632,\n \"Direction\": \"N\",\n \"DistanceKm\": 382316.55173\n }\n },\n \"EndDay\": {\n \"MoonDays\": 1.85568,\n \"Illumination\": 2.79491,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735758000,\n \"TimeISO\": \"2025-01-02T00:00:00+05:00\",\n \"AzimuthDegrees\": 309.21002,\n \"AltitudeDegrees\": -54.25535,\n \"Direction\": \"NW\",\n \"DistanceKm\": 379194.7976\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2024-12-31T09:18:42+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-01T09:55:21+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-01-01T09:55:21+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-02T10:21:16+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\",\n \"Begin\": \"2024-12-31T03:27:49+05:00\",\n \"End\": \"2025-01-02T15:27:49+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"Moonrise\": {\n \"Timestamp\": 1735707321,\n \"TimeISO\": \"2025-01-01T09:55:21+05:00\",\n \"AzimuthDegrees\": 133.52155,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 381054.53085\n },\n \"Moonset\": {\n \"Timestamp\": 1735734376,\n \"TimeISO\": \"2025-01-01T17:26:16+05:00\",\n \"AzimuthDegrees\": 228.55471,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 380040.1844\n },\n \"Meridian\": {\n \"Timestamp\": 1735720605,\n \"TimeISO\": \"2025-01-01T13:36:45+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 380550.46947\n },\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true\n }\n}" + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 0.85568,\n \"Illumination\": 0.35744,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735671600,\n \"TimeISO\": \"2025-01-01T00:00:00+05:00\",\n \"AzimuthDegrees\": 326.86255,\n \"AltitudeDegrees\": -62.68617,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 382464.56722\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 0.89806,\n \"Illumination\": 0.4137,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735675261,\n \"TimeISO\": \"2025-01-01T01:01:01+05:00\",\n \"AzimuthDegrees\": 356.08557,\n \"AltitudeDegrees\": -65.49632,\n \"Direction\": \"N\",\n \"DistanceKm\": 382316.55173\n }\n },\n \"EndDay\": {\n \"MoonDays\": 1.85568,\n \"Illumination\": 2.79491,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735758000,\n \"TimeISO\": \"2025-01-02T00:00:00+05:00\",\n \"AzimuthDegrees\": 309.21002,\n \"AltitudeDegrees\": -54.25535,\n \"Direction\": \"NW\",\n \"DistanceKm\": 379194.7976\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2024-12-31T09:18:42+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-01T09:55:21+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-01-01T09:55:21+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-02T10:21:16+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\",\n \"Begin\": \"2024-12-31T03:27:49+05:00\",\n \"End\": \"2025-01-02T15:27:49+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1735707321,\n \"TimeISO\": \"2025-01-01T09:55:21+05:00\",\n \"AzimuthDegrees\": 133.52155,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 381054.53085\n },\n \"Moonset\": {\n \"Timestamp\": 1735734376,\n \"TimeISO\": \"2025-01-01T17:26:16+05:00\",\n \"AzimuthDegrees\": 228.55471,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 380040.1844\n },\n \"Meridian\": {\n \"Timestamp\": 1735720605,\n \"TimeISO\": \"2025-01-01T13:36:45+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 380550.46947\n }\n }\n}" } ] }, @@ -313,7 +313,7 @@ } ], "cookie": [], - "body": "{\n \"BeginDay\": {\n \"MoonDays\": 13.27363,\n \"Illumination\": 93.79012,\n \"Phase\": {\n \"Name\": \"Waxing Gibbous\",\n \"NameLocalized\": \"Растущая Луна\",\n \"Emoji\": \"🌔\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762196400,\n \"TimeISO\": \"2025-11-04T00:00:00+05:00\",\n \"AzimuthDegrees\": 215.21834,\n \"AltitudeDegrees\": 42.12633,\n \"Direction\": \"SW\",\n \"DistanceKm\": 361308.10318\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 13.75159,\n \"Illumination\": 96.27365,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762237695,\n \"TimeISO\": \"2025-11-04T11:28:15+05:00\",\n \"AzimuthDegrees\": 12.34775,\n \"AltitudeDegrees\": -26.87274,\n \"Direction\": \"NNE\",\n \"DistanceKm\": 359579.90558\n }\n },\n \"EndDay\": {\n \"MoonDays\": 14.27363,\n \"Illumination\": 98.3017,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762282800,\n \"TimeISO\": \"2025-11-05T00:00:00+05:00\",\n \"AzimuthDegrees\": 200.1139,\n \"AltitudeDegrees\": 52.28516,\n \"Direction\": \"SSW\",\n \"DistanceKm\": 358145.25242\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-11-03T15:31:43+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-04T15:47:07+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-11-04T15:47:07+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-05T16:06:53+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\",\n \"Begin\": \"2025-11-03T05:25:58+05:00\",\n \"End\": \"2025-11-05T17:25:58+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"Moonrise\": {\n \"Timestamp\": 1762253227,\n \"TimeISO\": \"2025-11-04T15:47:07+05:00\",\n \"AzimuthDegrees\": 69.12186,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 359030.2047\n },\n \"Moonset\": {\n \"Timestamp\": 1762215573,\n \"TimeISO\": \"2025-11-04T05:19:33+05:00\",\n \"AzimuthDegrees\": 286.14788,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 360458.99957\n },\n \"Meridian\": {\n \"Timestamp\": 1762279703,\n \"TimeISO\": \"2025-11-04T23:08:23+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 53.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 358227.74281\n },\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true\n }\n}" + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 13.27363,\n \"Illumination\": 93.79012,\n \"Phase\": {\n \"Name\": \"Waxing Gibbous\",\n \"NameLocalized\": \"Растущая Луна\",\n \"Emoji\": \"🌔\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762196400,\n \"TimeISO\": \"2025-11-04T00:00:00+05:00\",\n \"AzimuthDegrees\": 215.21834,\n \"AltitudeDegrees\": 42.12633,\n \"Direction\": \"SW\",\n \"DistanceKm\": 361308.10318\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 13.75159,\n \"Illumination\": 96.27365,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762237695,\n \"TimeISO\": \"2025-11-04T11:28:15+05:00\",\n \"AzimuthDegrees\": 12.34775,\n \"AltitudeDegrees\": -26.87274,\n \"Direction\": \"NNE\",\n \"DistanceKm\": 359579.90558\n }\n },\n \"EndDay\": {\n \"MoonDays\": 14.27363,\n \"Illumination\": 98.3017,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762282800,\n \"TimeISO\": \"2025-11-05T00:00:00+05:00\",\n \"AzimuthDegrees\": 200.1139,\n \"AltitudeDegrees\": 52.28516,\n \"Direction\": \"SSW\",\n \"DistanceKm\": 358145.25242\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-11-03T15:31:43+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-04T15:47:07+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-11-04T15:47:07+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-05T16:06:53+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\",\n \"Begin\": \"2025-11-03T05:25:58+05:00\",\n \"End\": \"2025-11-05T17:25:58+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1762253227,\n \"TimeISO\": \"2025-11-04T15:47:07+05:00\",\n \"AzimuthDegrees\": 69.12186,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 359030.2047\n },\n \"Moonset\": {\n \"Timestamp\": 1762215573,\n \"TimeISO\": \"2025-11-04T05:19:33+05:00\",\n \"AzimuthDegrees\": 286.14788,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 360458.99957\n },\n \"Meridian\": {\n \"Timestamp\": 1762279703,\n \"TimeISO\": \"2025-11-04T23:08:23+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 53.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 358227.74281\n }\n }\n}" } ] }, @@ -447,14 +447,220 @@ } ], "cookie": [], - "body": "{\n \"BeginDay\": {\n \"MoonDays\": 23.53674,\n \"Illumination\": 38.2726,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1757962800,\n \"TimeISO\": \"2025-09-16T00:00:00+05:00\",\n \"AzimuthDegrees\": 57.08873,\n \"AltitudeDegrees\": 8.79646,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376559.88437\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 24.28786,\n \"Illumination\": 30.38659,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1758027697,\n \"TimeISO\": \"2025-09-16T18:01:37+05:00\",\n \"AzimuthDegrees\": 332.40261,\n \"AltitudeDegrees\": -8.94892,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 379152.9693\n }\n },\n \"EndDay\": {\n \"MoonDays\": 24.53674,\n \"Illumination\": 27.90729,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1758049200,\n \"TimeISO\": \"2025-09-17T00:00:00+05:00\",\n \"AzimuthDegrees\": 48.0345,\n \"AltitudeDegrees\": -0.14735,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380020.57994\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-09-15T22:37:45+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-09-16T23:56:10+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-09-16T23:56:10+05:00\",\n \"IsBeginExists\": true,\n \"IsEndExists\": false\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\",\n \"Begin\": \"2025-09-14T23:07:06+05:00\",\n \"End\": \"2025-09-17T11:07:06+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"Moonrise\": {\n \"Timestamp\": 1758048970,\n \"TimeISO\": \"2025-09-16T23:56:10+05:00\",\n \"AzimuthDegrees\": 47.31764,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380011.3104\n },\n \"Moonset\": {\n \"Timestamp\": 1758022192,\n \"TimeISO\": \"2025-09-16T16:29:52+05:00\",\n \"AzimuthDegrees\": 314.4689,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 378931.34559\n },\n \"Meridian\": {\n \"Timestamp\": 1757990436,\n \"TimeISO\": \"2025-09-16T07:40:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 377658.15629\n },\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true\n }\n}" + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 23.53674,\n \"Illumination\": 38.2726,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1757962800,\n \"TimeISO\": \"2025-09-16T00:00:00+05:00\",\n \"AzimuthDegrees\": 57.08873,\n \"AltitudeDegrees\": 8.79646,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376559.88437\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 24.28786,\n \"Illumination\": 30.38659,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1758027697,\n \"TimeISO\": \"2025-09-16T18:01:37+05:00\",\n \"AzimuthDegrees\": 332.40261,\n \"AltitudeDegrees\": -8.94892,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 379152.9693\n }\n },\n \"EndDay\": {\n \"MoonDays\": 24.53674,\n \"Illumination\": 27.90729,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1758049200,\n \"TimeISO\": \"2025-09-17T00:00:00+05:00\",\n \"AzimuthDegrees\": 48.0345,\n \"AltitudeDegrees\": -0.14735,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380020.57994\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-09-15T22:37:45+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-09-16T23:56:10+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-09-16T23:56:10+05:00\",\n \"IsBeginExists\": true,\n \"IsEndExists\": false\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\",\n \"Begin\": \"2025-09-14T23:07:06+05:00\",\n \"End\": \"2025-09-17T11:07:06+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1758048970,\n \"TimeISO\": \"2025-09-16T23:56:10+05:00\",\n \"AzimuthDegrees\": 47.31764,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380011.3104\n },\n \"Moonset\": {\n \"Timestamp\": 1758022192,\n \"TimeISO\": \"2025-09-16T16:29:52+05:00\",\n \"AzimuthDegrees\": 314.4689,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 378931.34559\n },\n \"Meridian\": {\n \"Timestamp\": 1757990436,\n \"TimeISO\": \"2025-09-16T07:40:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 377658.15629\n }\n }\n}" } ] } ] }, { - "name": "Moon Phase — Core information for year", + "name": "Moon — Detailed information for month", + "item": [ + { + "name": "Get moon rises, moon sets and meridians monthly", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPositionMonthly" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "lang", + "value": "ru", + "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + } + ] + } + }, + "response": [ + { + "name": "moonPositionMonthly — ok", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPositionMonthly" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "year", + "value": "2023", + "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" + }, + { + "key": "month", + "value": "3", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 15:25:52 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "17334" + } + ], + "cookie": [ + { + "expires": "Invalid Date", + "domain": "", + "path": "" + } + ], + "body": "[\n {\n \"Date\": \"2023-03-01\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1677650857,\n \"TimeISO\": \"2023-03-01T11:07:37+05:00\",\n \"AzimuthDegrees\": 42.8762,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402636.75966\n },\n \"Moonset\": {\n \"Timestamp\": 1677625807,\n \"TimeISO\": \"2023-03-01T04:10:07+05:00\",\n \"AzimuthDegrees\": 316.74613,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 401798.89266\n },\n \"Meridian\": {\n \"Timestamp\": 1677683130,\n \"TimeISO\": \"2023-03-01T20:05:30+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 403572.05151\n }\n },\n {\n \"Date\": \"2023-03-02\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1677740467,\n \"TimeISO\": \"2023-03-02T12:01:07+05:00\",\n \"AzimuthDegrees\": 43.38104,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404836.13007\n },\n \"Moonset\": {\n \"Timestamp\": 1677715380,\n \"TimeISO\": \"2023-03-02T05:03:00+05:00\",\n \"AzimuthDegrees\": 317.04922,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404345.126\n },\n \"Meridian\": {\n \"Timestamp\": 1677772596,\n \"TimeISO\": \"2023-03-02T20:56:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405326.66664\n }\n },\n {\n \"Date\": \"2023-03-03\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1677830659,\n \"TimeISO\": \"2023-03-03T13:04:19+05:00\",\n \"AzimuthDegrees\": 46.62247,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405833.52922\n },\n \"Moonset\": {\n \"Timestamp\": 1677804219,\n \"TimeISO\": \"2023-03-03T05:43:39+05:00\",\n \"AzimuthDegrees\": 314.58446,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405661.87336\n },\n \"Meridian\": {\n \"Timestamp\": 1677861967,\n \"TimeISO\": \"2023-03-03T21:46:07+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 63.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405914.02206\n }\n },\n {\n \"Date\": \"2023-03-04\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1677921218,\n \"TimeISO\": \"2023-03-04T14:13:38+05:00\",\n \"AzimuthDegrees\": 52.09931,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405724.54322\n },\n \"Moonset\": {\n \"Timestamp\": 1677892432,\n \"TimeISO\": \"2023-03-04T06:13:52+05:00\",\n \"AzimuthDegrees\": 309.84464,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405870.41007\n },\n \"Meridian\": {\n \"Timestamp\": 1677951207,\n \"TimeISO\": \"2023-03-04T22:33:27+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 60.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 405470.71269\n }\n },\n {\n \"Date\": \"2023-03-05\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678011933,\n \"TimeISO\": \"2023-03-05T15:25:33+05:00\",\n \"AzimuthDegrees\": 59.20503,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 404665.50003\n },\n \"Moonset\": {\n \"Timestamp\": 1677980183,\n \"TimeISO\": \"2023-03-05T06:36:23+05:00\",\n \"AzimuthDegrees\": 303.43187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 405132.86805\n },\n \"Meridian\": {\n \"Timestamp\": 1678040308,\n \"TimeISO\": \"2023-03-05T23:18:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 55.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 404168.13916\n }\n },\n {\n \"Date\": \"2023-03-06\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": false,\n \"Moonrise\": {\n \"Timestamp\": 1678102672,\n \"TimeISO\": \"2023-03-06T16:37:52+05:00\",\n \"AzimuthDegrees\": 67.43208,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 402838.67123\n },\n \"Moonset\": {\n \"Timestamp\": 1678067624,\n \"TimeISO\": \"2023-03-06T06:53:44+05:00\",\n \"AzimuthDegrees\": 295.85243,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 403623.69367\n }\n },\n {\n \"Date\": \"2023-03-07\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678193387,\n \"TimeISO\": \"2023-03-07T17:49:47+05:00\",\n \"AzimuthDegrees\": 76.41149,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400418.46475\n },\n \"Moonset\": {\n \"Timestamp\": 1678154869,\n \"TimeISO\": \"2023-03-07T07:07:49+05:00\",\n \"AzimuthDegrees\": 287.47458,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 401508.60505\n },\n \"Meridian\": {\n \"Timestamp\": 1678129288,\n \"TimeISO\": \"2023-03-07T00:01:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 50.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 402183.01801\n }\n },\n {\n \"Date\": \"2023-03-08\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678284096,\n \"TimeISO\": \"2023-03-08T19:01:36+05:00\",\n \"AzimuthDegrees\": 85.87566,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"E\",\n \"DistanceKm\": 397547.14825\n },\n \"Moonset\": {\n \"Timestamp\": 1678242000,\n \"TimeISO\": \"2023-03-08T07:20:00+05:00\",\n \"AzimuthDegrees\": 278.56217,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 398928.36539\n },\n \"Meridian\": {\n \"Timestamp\": 1678218186,\n \"TimeISO\": \"2023-03-08T00:43:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 45.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 399673.27308\n }\n },\n {\n \"Date\": \"2023-03-09\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678374851,\n \"TimeISO\": \"2023-03-09T20:14:11+05:00\",\n \"AzimuthDegrees\": 95.60853,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 394323.19311\n },\n \"Moonset\": {\n \"Timestamp\": 1678329083,\n \"TimeISO\": \"2023-03-09T07:31:23+05:00\",\n \"AzimuthDegrees\": 269.31953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 395988.3718\n },\n \"Meridian\": {\n \"Timestamp\": 1678307052,\n \"TimeISO\": \"2023-03-09T01:24:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 396761.88573\n }\n },\n {\n \"Date\": \"2023-03-10\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678465722,\n \"TimeISO\": \"2023-03-10T21:28:42+05:00\",\n \"AzimuthDegrees\": 105.39738,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 390803.12388\n },\n \"Moonset\": {\n \"Timestamp\": 1678416177,\n \"TimeISO\": \"2023-03-10T07:42:57+05:00\",\n \"AzimuthDegrees\": 259.93073,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 392756.20131\n },\n \"Meridian\": {\n \"Timestamp\": 1678395944,\n \"TimeISO\": \"2023-03-10T02:05:44+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 33.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 393530.65092\n }\n },\n {\n \"Date\": \"2023-03-11\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678556769,\n \"TimeISO\": \"2023-03-11T22:46:09+05:00\",\n \"AzimuthDegrees\": 114.97706,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 387016.85411\n },\n \"Moonset\": {\n \"Timestamp\": 1678503344,\n \"TimeISO\": \"2023-03-11T07:55:44+05:00\",\n \"AzimuthDegrees\": 250.59757,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 389268.48137\n },\n \"Meridian\": {\n \"Timestamp\": 1678484929,\n \"TimeISO\": \"2023-03-11T02:48:49+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 27.5,\n \"Direction\": \"S\",\n \"DistanceKm\": 390025.04685\n }\n },\n {\n \"Date\": \"2023-03-12\",\n \"IsMoonRise\": false,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonset\": {\n \"Timestamp\": 1678590669,\n \"TimeISO\": \"2023-03-12T08:11:09+05:00\",\n \"AzimuthDegrees\": 241.58881,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 385546.82937\n },\n \"Meridian\": {\n \"Timestamp\": 1678574075,\n \"TimeISO\": \"2023-03-12T03:34:35+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 21.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 386270.22231\n }\n },\n {\n \"Date\": \"2023-03-13\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678648012,\n \"TimeISO\": \"2023-03-13T00:06:52+05:00\",\n \"AzimuthDegrees\": 123.94721,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 382995.32957\n },\n \"Moonset\": {\n \"Timestamp\": 1678678278,\n \"TimeISO\": \"2023-03-13T08:31:18+05:00\",\n \"AzimuthDegrees\": 233.31784,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 381620.77804\n },\n \"Meridian\": {\n \"Timestamp\": 1678663446,\n \"TimeISO\": \"2023-03-13T04:24:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 17,\n \"Direction\": \"S\",\n \"DistanceKm\": 382296.46854\n }\n },\n {\n \"Date\": \"2023-03-14\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678739357,\n \"TimeISO\": \"2023-03-14T01:29:17+05:00\",\n \"AzimuthDegrees\": 131.64699,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 378807.06957\n },\n \"Moonset\": {\n \"Timestamp\": 1678766372,\n \"TimeISO\": \"2023-03-14T08:59:32+05:00\",\n \"AzimuthDegrees\": 226.46039,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 377554.30425\n },\n \"Meridian\": {\n \"Timestamp\": 1678753085,\n \"TimeISO\": \"2023-03-14T05:18:05+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 378170.5856\n }\n },\n {\n \"Date\": \"2023-03-15\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678830525,\n \"TimeISO\": \"2023-03-15T02:48:45+05:00\",\n \"AzimuthDegrees\": 137.04254,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 374594.67251\n },\n \"Moonset\": {\n \"Timestamp\": 1678855238,\n \"TimeISO\": \"2023-03-15T09:40:38+05:00\",\n \"AzimuthDegrees\": 222.05405,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 373473.15308\n },\n \"Meridian\": {\n \"Timestamp\": 1678842992,\n \"TimeISO\": \"2023-03-15T06:16:32+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 374026.97617\n }\n },\n {\n \"Date\": \"2023-03-16\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678921072,\n \"TimeISO\": \"2023-03-16T03:57:52+05:00\",\n \"AzimuthDegrees\": 138.9114,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 370591.50029\n },\n \"Moonset\": {\n \"Timestamp\": 1678945166,\n \"TimeISO\": \"2023-03-16T10:39:26+05:00\",\n \"AzimuthDegrees\": 221.29657,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 369592.67725\n },\n \"Meridian\": {\n \"Timestamp\": 1678933092,\n \"TimeISO\": \"2023-03-16T07:18:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 370088.60593\n }\n },\n {\n \"Date\": \"2023-03-17\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679010663,\n \"TimeISO\": \"2023-03-17T04:51:03+05:00\",\n \"AzimuthDegrees\": 136.61246,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 367098.86901\n },\n \"Moonset\": {\n \"Timestamp\": 1679036174,\n \"TimeISO\": \"2023-03-17T11:56:14+05:00\",\n \"AzimuthDegrees\": 224.76797,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 366237.19817\n },\n \"Meridian\": {\n \"Timestamp\": 1679023251,\n \"TimeISO\": \"2023-03-17T08:20:51+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 11.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 366664.93323\n }\n },\n {\n \"Date\": \"2023-03-18\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679099326,\n \"TimeISO\": \"2023-03-18T05:28:46+05:00\",\n \"AzimuthDegrees\": 130.65168,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 364441.40172\n },\n \"Moonset\": {\n \"Timestamp\": 1679127890,\n \"TimeISO\": \"2023-03-18T13:24:50+05:00\",\n \"AzimuthDegrees\": 231.87919,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 363812.28579\n },\n \"Meridian\": {\n \"Timestamp\": 1679113319,\n \"TimeISO\": \"2023-03-18T09:21:59+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 14.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364117.71582\n }\n },\n {\n \"Date\": \"2023-03-19\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679187320,\n \"TimeISO\": \"2023-03-19T05:55:20+05:00\",\n \"AzimuthDegrees\": 122.15123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362929.11813\n },\n \"Moonset\": {\n \"Timestamp\": 1679219833,\n \"TimeISO\": \"2023-03-19T14:57:13+05:00\",\n \"AzimuthDegrees\": 241.42982,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 362712.81832\n },\n \"Meridian\": {\n \"Timestamp\": 1679203194,\n \"TimeISO\": \"2023-03-19T10:19:54+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 19.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 362798.79835\n }\n },\n {\n \"Date\": \"2023-03-20\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679274902,\n \"TimeISO\": \"2023-03-20T06:15:02+05:00\",\n \"AzimuthDegrees\": 112.14362,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362812.63242\n },\n \"Moonset\": {\n \"Timestamp\": 1679311702,\n \"TimeISO\": \"2023-03-20T16:28:22+05:00\",\n \"AzimuthDegrees\": 252.32977,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 363219.08698\n },\n \"Meridian\": {\n \"Timestamp\": 1679292852,\n \"TimeISO\": \"2023-03-20T11:14:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 25.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 362976.42916\n }\n },\n {\n \"Date\": \"2023-03-21\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679362254,\n \"TimeISO\": \"2023-03-21T06:30:54+05:00\",\n \"AzimuthDegrees\": 101.3864,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 364227.26916\n },\n \"Moonset\": {\n \"Timestamp\": 1679403400,\n \"TimeISO\": \"2023-03-21T17:56:40+05:00\",\n \"AzimuthDegrees\": 263.78338,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 365425.30425\n },\n \"Meridian\": {\n \"Timestamp\": 1679382325,\n \"TimeISO\": \"2023-03-21T12:05:25+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364770.21284\n }\n },\n {\n \"Date\": \"2023-03-22\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679449497,\n \"TimeISO\": \"2023-03-22T06:44:57+05:00\",\n \"AzimuthDegrees\": 90.43001,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 367149.18373\n },\n \"Moonset\": {\n \"Timestamp\": 1679494947,\n \"TimeISO\": \"2023-03-22T19:22:27+05:00\",\n \"AzimuthDegrees\": 275.21269,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 369210.60777\n },\n \"Meridian\": {\n \"Timestamp\": 1679471677,\n \"TimeISO\": \"2023-03-22T12:54:37+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 368112.88854\n }\n },\n {\n \"Date\": \"2023-03-23\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679536722,\n \"TimeISO\": \"2023-03-23T06:58:42+05:00\",\n \"AzimuthDegrees\": 79.70263,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 371383.43121\n },\n \"Moonset\": {\n \"Timestamp\": 1679586398,\n \"TimeISO\": \"2023-03-23T20:46:38+05:00\",\n \"AzimuthDegrees\": 286.16029,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 374253.93367\n },\n \"Meridian\": {\n \"Timestamp\": 1679560980,\n \"TimeISO\": \"2023-03-23T13:43:00+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 46.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 372750.53254\n }\n },\n {\n \"Date\": \"2023-03-24\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679624012,\n \"TimeISO\": \"2023-03-24T07:13:32+05:00\",\n \"AzimuthDegrees\": 69.57123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376588.54488\n },\n \"Moonset\": {\n \"Timestamp\": 1679677785,\n \"TimeISO\": \"2023-03-24T22:09:45+05:00\",\n \"AzimuthDegrees\": 296.21187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 380086.26614\n },\n \"Meridian\": {\n \"Timestamp\": 1679650299,\n \"TimeISO\": \"2023-03-24T14:31:39+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 52.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 378280.3871\n }\n },\n {\n \"Date\": \"2023-03-25\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679711456,\n \"TimeISO\": \"2023-03-25T07:30:56+05:00\",\n \"AzimuthDegrees\": 60.39425,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 382327.95573\n },\n \"Moonset\": {\n \"Timestamp\": 1679769074,\n \"TimeISO\": \"2023-03-25T23:31:14+05:00\",\n \"AzimuthDegrees\": 304.92579,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 386164.32159\n },\n \"Meridian\": {\n \"Timestamp\": 1679739679,\n \"TimeISO\": \"2023-03-25T15:21:19+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 58.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 384213.14123\n }\n },\n {\n \"Date\": \"2023-03-26\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": false,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679799169,\n \"TimeISO\": \"2023-03-26T07:52:49+05:00\",\n \"AzimuthDegrees\": 52.57818,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 388131.21721\n },\n \"Meridian\": {\n \"Timestamp\": 1679829144,\n \"TimeISO\": \"2023-03-26T16:12:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 62.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 390041.63582\n }\n },\n {\n \"Date\": \"2023-03-27\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679887300,\n \"TimeISO\": \"2023-03-27T08:21:40+05:00\",\n \"AzimuthDegrees\": 46.62563,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 393548.92562\n },\n \"Moonset\": {\n \"Timestamp\": 1679860134,\n \"TimeISO\": \"2023-03-27T00:48:54+05:00\",\n \"AzimuthDegrees\": 311.77602,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 391948.19405\n },\n \"Meridian\": {\n \"Timestamp\": 1679918681,\n \"TimeISO\": \"2023-03-27T17:04:41+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65,\n \"Direction\": \"S\",\n \"DistanceKm\": 395301.62304\n }\n },\n {\n \"Date\": \"2023-03-28\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679976010,\n \"TimeISO\": \"2023-03-28T09:00:10+05:00\",\n \"AzimuthDegrees\": 43.11087,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 398191.74953\n },\n \"Moonset\": {\n \"Timestamp\": 1679950741,\n \"TimeISO\": \"2023-03-28T01:59:01+05:00\",\n \"AzimuthDegrees\": 316.17036,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 396971.11487\n },\n \"Meridian\": {\n \"Timestamp\": 1680008246,\n \"TimeISO\": \"2023-03-28T17:57:26+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 399617.2651\n }\n },\n {\n \"Date\": \"2023-03-29\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1680065405,\n \"TimeISO\": \"2023-03-29T09:50:05+05:00\",\n \"AzimuthDegrees\": 42.48507,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 401753.75503\n },\n \"Moonset\": {\n \"Timestamp\": 1680040664,\n \"TimeISO\": \"2023-03-29T02:57:44+05:00\",\n \"AzimuthDegrees\": 317.64261,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 400892.49384\n },\n \"Meridian\": {\n \"Timestamp\": 1680097776,\n \"TimeISO\": \"2023-03-29T18:49:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 402729.21577\n }\n },\n {\n \"Date\": \"2023-03-30\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1680155438,\n \"TimeISO\": \"2023-03-30T10:50:38+05:00\",\n \"AzimuthDegrees\": 44.77953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404030.19632\n },\n \"Moonset\": {\n \"Timestamp\": 1680129800,\n \"TimeISO\": \"2023-03-30T03:43:20+05:00\",\n \"AzimuthDegrees\": 316.15045,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 403521.433\n },\n \"Meridian\": {\n \"Timestamp\": 1680187209,\n \"TimeISO\": \"2023-03-30T19:40:09+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 64.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 404505.37243\n }\n },\n {\n \"Date\": \"2023-03-31\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1680245913,\n \"TimeISO\": \"2023-03-31T11:58:33+05:00\",\n \"AzimuthDegrees\": 49.55137,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404935.96867\n },\n \"Moonset\": {\n \"Timestamp\": 1680218226,\n \"TimeISO\": \"2023-03-31T04:17:06+05:00\",\n \"AzimuthDegrees\": 312.1322,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404804.48186\n },\n \"Meridian\": {\n \"Timestamp\": 1680276504,\n \"TimeISO\": \"2023-03-31T20:28:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 61.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 404936.05398\n }\n }\n]" + }, + { + "name": "moonPositionMonthly — error", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=9999&month=3&precision=5", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPositionMonthly" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "year", + "value": "9999", + "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" + }, + { + "key": "month", + "value": "3", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 15:26:10 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "136" + } + ], + "cookie": [], + "body": "{\n \"Status\": 400,\n \"Message\": \"[400 Bad Request] Invalid parameter format: ephemeris segment only covers dates 1899-07-29 through 2053-10-09\"\n}" + } + ] + } + ] + }, + { + "name": "Moon Phase — Core information for year (phase)", "item": [ { "name": "Get moon phases table by specified year", @@ -462,7 +668,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonTableYear?utc=5&year=2025", + "raw": "{{baseUrl}}/v1/moonTableYear?utc=5&year=2023", "host": [ "{{baseUrl}}" ], @@ -478,7 +684,7 @@ }, { "key": "year", - "value": "2025", + "value": "2023", "description": "Year for request: YYYY. Current, if not specified" } ] @@ -673,7 +879,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/toJulianTimeByDate?utc=5&precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01&", + "raw": "{{baseUrl}}/v1/toJulianTimeByDate?precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01", "host": [ "{{baseUrl}}" ], @@ -682,11 +888,6 @@ "toJulianTimeByDate" ], "query": [ - { - "key": "utc", - "value": "5", - "description": "UTC in format UTC+7, UTC+09:30, -3" - }, { "key": "precision", "value": "5", @@ -721,11 +922,6 @@ "key": "second", "value": "01", "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" - }, - { - "key": "", - "value": null, - "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" } ] } @@ -803,7 +999,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" }, @@ -830,7 +1025,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/toJulianTimeByTimestamp?precision=5", + "raw": "{{baseUrl}}/v1/toJulianTimeByTimestamp?precision=10×tamp=1735693261", "host": [ "{{baseUrl}}" ], @@ -841,8 +1036,13 @@ "query": [ { "key": "precision", - "value": "5", + "value": "10", "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "timestamp", + "value": "1735693261", + "description": "Timestamp for calculations. Current, if not specified" } ] } @@ -895,7 +1095,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" }, @@ -981,7 +1180,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" }, @@ -996,6 +1194,228 @@ ] } ] + }, + { + "name": "Nearest events", + "item": [ + { + "name": "Next moon phase", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&format=ISO", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "nextMoonPhase" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "format", + "value": "ISO", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration" + } + ] + } + }, + "response": [ + { + "name": "nextMoonPhase — ISO format", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&format=ISO", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "nextMoonPhase" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "format", + "value": "ISO", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 15:28:59 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "163" + } + ], + "cookie": [], + "body": "{\n \"NewMoon\": \"2025-11-20T11:48:16+05:00\",\n \"FirstQuarter\": \"2025-11-28T11:44:00+05:00\",\n \"FullMoon\": \"2025-12-05T04:15:18+05:00\",\n \"LastQuarter\": \"2025-12-12T02:04:31+05:00\"\n}" + }, + { + "name": "nextMoonPhase — timestamp format", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&format=timestamp", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "nextMoonPhase" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "format", + "value": "timestamp", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 15:36:45 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "95" + } + ], + "cookie": [ + { + "expires": "Invalid Date", + "domain": "", + "path": "" + } + ], + "body": "{\n \"NewMoon\": 1763621296,\n \"FirstQuarter\": 1764312240,\n \"FullMoon\": 1764890118,\n \"LastQuarter\": 1765487071\n}" + }, + { + "name": "nextMoonPhase — duration format", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&format=duration", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "nextMoonPhase" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "format", + "value": "duration", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 15:37:46 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "82" + } + ], + "cookie": [], + "body": "{\n \"NewMoon\": 313830,\n \"FirstQuarter\": 1004774,\n \"FullMoon\": 1582652,\n \"LastQuarter\": 2179605\n}" + } + ] + } + ] } ] } \ No newline at end of file From 81315951543744dbb3f949fa14685900a6d2205f Mon Sep 17 00:00:00 2001 From: prostraction Date: Sun, 16 Nov 2025 22:07:59 +0500 Subject: [PATCH 06/18] feat: replaced TimeISO/Timestamp with Time --- internal/server/route-jtime.go | 13 +++++-- internal/server/route-phase.go | 6 +++- internal/server/route-position.go | 6 ++-- pkg/moon/moon.go | 8 ++--- pkg/position/position.go | 59 ++++++++++++++++--------------- 5 files changed, 54 insertions(+), 38 deletions(-) diff --git a/internal/server/route-jtime.go b/internal/server/route-jtime.go index 19aad48..45dd40d 100644 --- a/internal/server/route-jtime.go +++ b/internal/server/route-jtime.go @@ -23,7 +23,11 @@ func (s *Server) toJulianTimeByDateV1(c *fiber.Ctx) error { err := IsValidDate(year, month, day) if err != nil { c.Status(400) - return c.SendString("Validation error: " + err.Error()) + errPrintable := ErrorPrintable{ + Status: 400, + Message: "Validation error: " + err.Error(), + } + return c.JSON(errPrintable) } hour := StrToInt(c.Query("hour", strconv.Itoa(int(tNow.Hour()))), int(tNow.Hour()), 0, 23) @@ -65,8 +69,11 @@ func (s *Server) fromJulianTimeV1(c *fiber.Ctx) error { jtime, err := strconv.ParseFloat(jtStr, 64) if err != nil { c.Status(400) - c.JSON("missing required parameter: 'jtime' (float)") - return nil + errPrintable := ErrorPrintable{ + Status: 400, + Message: "missing required parameter: 'jtime' (float)", + } + return c.JSON(errPrintable) } t := jt.FromJulianDate(jtime, loc) diff --git a/internal/server/route-phase.go b/internal/server/route-phase.go index 1ea531b..ee99c41 100644 --- a/internal/server/route-phase.go +++ b/internal/server/route-phase.go @@ -71,7 +71,11 @@ func (s *Server) moonPhaseDatetV1(c *fiber.Ctx) error { err := IsValidDate(year, month, day) if err != nil { c.Status(400) - return c.SendString("Validation error: " + err.Error()) + errPrintable := ErrorPrintable{ + Status: 400, + Message: "Validation error: " + err.Error(), + } + return c.JSON(errPrintable) } hour := StrToInt(c.Query("hour", strconv.Itoa(int(tNow.Hour()))), int(tNow.Hour()), 0, 23) diff --git a/internal/server/route-position.go b/internal/server/route-position.go index fd50e40..71d1386 100644 --- a/internal/server/route-position.go +++ b/internal/server/route-position.go @@ -24,8 +24,10 @@ func (s *Server) moonPositionMonthly(c *fiber.Ctx) error { locationCords := parseCoords(latStr, lonStr) if !locationCords.IsValid { - c.Status(400) - return c.JSON("latitude and longitude is required for this method.") + e := ErrorPrintable{} + e.Status = 400 + e.Message = "latitude and longitude are required for this method." + return c.JSON(e) } if resp, err := s.positionCache.GetRisesMonthly(year, month, loc, precision, locationCords.Longitude, locationCords.Latitude); err == nil { diff --git a/pkg/moon/moon.go b/pkg/moon/moon.go index 8d355c5..ff237a1 100644 --- a/pkg/moon/moon.go +++ b/pkg/moon/moon.go @@ -43,24 +43,24 @@ func (c *Cache) MoonDetailed(tGiven time.Time, loc *time.Location, lang string, if err1 == nil && err2 == nil { if moonRiseYesterday.IsMoonRise { moonDaysDetailed.Day[0].Begin = new(time.Time) - *moonDaysDetailed.Day[0].Begin = moonRiseYesterday.Moonrise.TimeISO + moonDaysDetailed.Day[0].Begin = moonRiseYesterday.Moonrise.Time moonDaysDetailed.Day[0].IsBeginExists = true } if moonRiseToday.IsMoonRise { moonDaysDetailed.Day[0].End = new(time.Time) - *moonDaysDetailed.Day[0].End = moonRiseToday.Moonrise.TimeISO + moonDaysDetailed.Day[0].End = moonRiseToday.Moonrise.Time moonDaysDetailed.Day[0].IsEndExists = true } } if err2 == nil && err3 == nil { if moonRiseToday.IsMoonRise { moonDaysDetailed.Day[1].Begin = new(time.Time) - *moonDaysDetailed.Day[1].Begin = moonRiseToday.Moonrise.TimeISO + moonDaysDetailed.Day[1].Begin = moonRiseToday.Moonrise.Time moonDaysDetailed.Day[1].IsBeginExists = true } if moonRiseTomorrow.IsMoonRise { moonDaysDetailed.Day[1].End = new(time.Time) - *moonDaysDetailed.Day[1].End = moonRiseTomorrow.Moonrise.TimeISO + moonDaysDetailed.Day[1].End = moonRiseTomorrow.Moonrise.Time moonDaysDetailed.Day[1].IsEndExists = true } } diff --git a/pkg/position/position.go b/pkg/position/position.go index 8381ab7..9ca12ea 100644 --- a/pkg/position/position.go +++ b/pkg/position/position.go @@ -49,12 +49,12 @@ type Parameters struct { // resp for 1 day type MoonPosition struct { - Timestamp int64 `json:"Timestamp"` - TimeISO time.Time `json:"TimeISO,omitempty"` - AzimuthDegrees float64 `json:"AzimuthDegrees"` - AltitudeDegrees float64 `json:"AltitudeDegrees"` - Direction string `json:"Direction"` - DistanceKm float64 `json:"DistanceKm"` + Timestamp *int64 `json:"Timestamp,omitempty"` + Time *time.Time `json:"Time"` + AzimuthDegrees float64 `json:"AzimuthDegrees"` + AltitudeDegrees float64 `json:"AltitudeDegrees"` + Direction string `json:"Direction"` + DistanceKm float64 `json:"DistanceKm"` } type DayData struct { @@ -137,17 +137,17 @@ func (c *Cache) GetRisesMonthly(year, month int, loc *time.Location, precision i } for i := range monthResponse.Data { - if monthResponse.Data[i].Meridian != nil { - timestampToGoTime(monthResponse.Data[i].Meridian, loc) - monthResponse.Data[i].Meridian.TimeISO = time.Unix(monthResponse.Data[i].Meridian.Timestamp, 0) + if monthResponse.Data[i].Meridian != nil && monthResponse.Data[i].Meridian.Timestamp != nil { + monthResponse.Data[i].Meridian.Time = timestampToGoTime(monthResponse.Data[i].Meridian.Timestamp, loc) + monthResponse.Data[i].Meridian.Timestamp = nil } - if monthResponse.Data[i].Moonrise != nil { - timestampToGoTime(monthResponse.Data[i].Moonrise, loc) - monthResponse.Data[i].Moonrise.TimeISO = time.Unix(monthResponse.Data[i].Moonrise.Timestamp, 0) + if monthResponse.Data[i].Moonrise != nil && monthResponse.Data[i].Moonrise.Timestamp != nil { + monthResponse.Data[i].Moonrise.Time = timestampToGoTime(monthResponse.Data[i].Moonrise.Timestamp, loc) + monthResponse.Data[i].Moonrise.Timestamp = nil } if monthResponse.Data[i].Moonset != nil { - timestampToGoTime(monthResponse.Data[i].Moonset, loc) - monthResponse.Data[i].Moonset.TimeISO = time.Unix(monthResponse.Data[i].Moonset.Timestamp, 0) + monthResponse.Data[i].Moonset.Time = timestampToGoTime(monthResponse.Data[i].Moonset.Timestamp, loc) + monthResponse.Data[i].Moonset.Timestamp = nil } t := time.Date(year, jt.GetMonth(month), 1+i, 0, 0, 0, 0, time.UTC).Format("2006-01-02") monthResponse.Data[i].Day = &t @@ -206,17 +206,17 @@ func GetRisesDay(year, month, day int, loc *time.Location, precision int, locati if err := json.Unmarshal(body, &dayResponse); err != nil { return nil, fmt.Errorf("failed to unmarshal response: %w", err) } - if dayResponse.Data.Meridian != nil { - timestampToGoTime(dayResponse.Data.Meridian, loc) - dayResponse.Data.Meridian.Timestamp = dayResponse.Data.Meridian.TimeISO.Unix() + if dayResponse.Data.Meridian != nil && dayResponse.Data.Meridian.Timestamp != nil { + dayResponse.Data.Meridian.Time = timestampToGoTime(dayResponse.Data.Meridian.Timestamp, loc) + dayResponse.Data.Meridian.Timestamp = nil } - if dayResponse.Data.Moonrise != nil { - timestampToGoTime(dayResponse.Data.Moonrise, loc) - dayResponse.Data.Moonrise.Timestamp = dayResponse.Data.Moonrise.TimeISO.Unix() + if dayResponse.Data.Moonrise != nil && dayResponse.Data.Moonrise.Timestamp != nil { + dayResponse.Data.Moonrise.Time = timestampToGoTime(dayResponse.Data.Moonrise.Timestamp, loc) + dayResponse.Data.Moonrise.Timestamp = nil } if dayResponse.Data.Moonset != nil { - timestampToGoTime(dayResponse.Data.Moonset, loc) - dayResponse.Data.Moonset.Timestamp = dayResponse.Data.Moonset.TimeISO.Unix() + dayResponse.Data.Moonset.Time = timestampToGoTime(dayResponse.Data.Moonset.Timestamp, loc) + dayResponse.Data.Moonset.Timestamp = nil } return dayResponse.Data, nil @@ -317,8 +317,10 @@ func GetMoonPosition(tGiven time.Time, loc *time.Location, precision int, locati if err := json.Unmarshal(body, &pos); err != nil { return nil, fmt.Errorf("failed to unmarshal response: %w", err) } - if pos != nil { - timestampToGoTime(pos, loc) + if pos != nil && pos.Timestamp != nil { + t := pos.Timestamp + pos.Time = timestampToGoTime(t, loc) + pos.Timestamp = nil } return pos, nil @@ -335,10 +337,11 @@ func parseLocation(location []float64) (lat, lon float64, err error) { return lat, lon, nil } -func timestampToGoTime(ev *MoonPosition, loc *time.Location) { - utcTime := time.Unix(ev.Timestamp, 0).UTC() - ev.TimeISO = utcTime +func timestampToGoTime(ev *int64, loc *time.Location) *time.Time { + utcTime := time.Unix(*ev, 0).UTC() + time := utcTime if loc != nil { - ev.TimeISO = ev.TimeISO.In(loc) + time = time.In(loc) } + return &time } From 43e8b426baa8cccefbe3cce81a20f8679434ce0d Mon Sep 17 00:00:00 2001 From: prostraction Date: Sun, 16 Nov 2025 22:23:40 +0500 Subject: [PATCH 07/18] docs --- README.md | 111 ++++++++++++++++++++------------- doc/postman_collection | 71 ++++++++++++--------- internal/server/route-next.go | 2 +- internal/server/route-phase.go | 14 +++-- 4 files changed, 122 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index cfc62e7..c906fae 100644 --- a/README.md +++ b/README.md @@ -105,8 +105,7 @@ MoonPosition structure (Exists only if latitude and longitude are specified): | Response Variable | Type | Description | Example Value | | :--- | :--- | :--- | :--- | -|`MoonPosition.Timestamp` | `Integer [required]` | Unix timestamp of calculation | `1757962800` | -|`MoonPosition.TimeISO` | `String [required]` | ISO 8601 timestamp | `"2025-09-16T00:00:00+05:00"` | +|`MoonPosition.Time` | `String / int64 [required]` | Time in ISO (default) or specified timeFormat | `"2025-09-16T00:00:00+05:00"` | |`MoonPosition.AzimuthDegrees` | `Float [required]` | Compass direction (0°=North) | `57.1` | |`MoonPosition.AltitudeDegrees` | `Float [required]` | Angle above horizon (negative = below) | `8.8` | |`MoonPosition.Direction` | `String [required]` | Cardinal direction abbreviation | `"ENE"` | @@ -183,8 +182,7 @@ MoonPosition structure: | Response Variable | Type | Description | Example Value | | :--- | :--- | :--- | :--- | -|`MoonPosition.Timestamp` | `Integer [required]` | Moonrise Unix timestamp | `1758048970` | -|`MoonPosition.TimeISO` | `String [required]` | Moonrise ISO time | `"2025-09-16T23:56:10+05:00"` | +|`MoonPosition.Time` | `String / int64 [required]` | Time in ISO (default) or specified timeFormat | `"2025-09-16T00:00:00+05:00"` | |`MoonPosition.AzimuthDegrees` | `Float [required]` | Moonrise azimuth | `47.3` | |`MoonPosition.AltitudeDegrees` | `Float [required]` | Moonrise altitude | `-0.6` | |`MoonPosition.Direction` | `String [required]` | Moonrise direction | `"ENE"` | @@ -221,8 +219,7 @@ Response of: "Emoji": "♊" }, "MoonPosition": { - "Timestamp": 1757883600, - "TimeISO": "2025-09-15T02:00:00+05:00", + "Time": "2025-09-15T02:00:00+05:00", "AzimuthDegrees": 66.96877, "AltitudeDegrees": 17.50543, "Direction": "ENE", @@ -244,8 +241,7 @@ Response of: "Emoji": "♊" }, "MoonPosition": { - "Timestamp": 1757926800, - "TimeISO": "2025-09-15T14:00:00+05:00", + "Time": "2025-09-15T14:00:00+05:00", "AzimuthDegrees": 279.37345, "AltitudeDegrees": 29.04833, "Direction": "W", @@ -267,8 +263,7 @@ Response of: "Emoji": "♊" }, "MoonPosition": { - "Timestamp": 1757970000, - "TimeISO": "2025-09-16T02:00:00+05:00", + "Time": "2025-09-16T02:00:00+05:00", "AzimuthDegrees": 57.08873, "AltitudeDegrees": 8.79646, "Direction": "ENE", @@ -309,24 +304,21 @@ Response of: "IsMoonSet": true, "IsMeridian": true, "Moonrise": { - "Timestamp": 1757957865, - "TimeISO": "2025-09-15T22:37:45+05:00", + "Time": "2025-09-15T22:37:45+05:00", "AzimuthDegrees": 42.31555, "AltitudeDegrees": -0.56667, "Direction": "NE", "DistanceKm": 376365.00012 }, "Moonset": { - "Timestamp": 1757933213, - "TimeISO": "2025-09-15T15:46:53+05:00", + "Time": "2025-09-15T15:46:53+05:00", "AzimuthDegrees": 318.44328, "AltitudeDegrees": -0.56667, "Direction": "NW", "DistanceKm": 375398.22022 }, "Meridian": { - "Timestamp": 1757900413, - "TimeISO": "2025-09-15T06:40:13+05:00", + "Time": "2025-09-15T06:40:13+05:00", "AzimuthDegrees": 180, "AltitudeDegrees": 67.1, "Direction": "S", @@ -428,8 +420,7 @@ MoonPosition structure: | Response Variable | Type | Description | Example Value | | :--- | :--- | :--- | :--- | -|`MoonPosition.Timestamp` | `Integer [required]` | Moonrise Unix timestamp | `1758048970` | -|`MoonPosition.TimeISO` | `String [required]` | Moonrise ISO time | `"2025-09-16T23:56:10+05:00"` | +|`MoonPosition.Time` | `String / int64 [required]` | Time in ISO (default) or specified timeFormat | `"2025-09-16T00:00:00+05:00"` | |`MoonPosition.AzimuthDegrees` | `Float [required]` | Moonrise azimuth | `47.3` | |`MoonPosition.AltitudeDegrees` | `Float [required]` | Moonrise altitude | `-0.6` | |`MoonPosition.Direction` | `String [required]` | Moonrise direction | `"ENE"` | @@ -442,7 +433,6 @@ MoonPosition structure: #### Response example ```json - [ { "Date": "2023-03-01", @@ -450,24 +440,21 @@ MoonPosition structure: "IsMoonSet": true, "IsMeridian": true, "Moonrise": { - "Timestamp": 1677650857, - "TimeISO": "2023-03-01T11:07:37+05:00", + "Time": "2023-03-01T11:07:37+05:00", "AzimuthDegrees": 42.8762, "AltitudeDegrees": -0.56667, "Direction": "NE", "DistanceKm": 402636.75966 }, "Moonset": { - "Timestamp": 1677625807, - "TimeISO": "2023-03-01T04:10:07+05:00", + "Time": "2023-03-01T04:10:07+05:00", "AzimuthDegrees": 316.74613, "AltitudeDegrees": -0.56667, "Direction": "NW", "DistanceKm": 401798.89266 }, "Meridian": { - "Timestamp": 1677683130, - "TimeISO": "2023-03-01T20:05:30+05:00", + "Time": "2023-03-01T20:05:30+05:00", "AzimuthDegrees": 180, "AltitudeDegrees": 66.2, "Direction": "S", @@ -480,24 +467,21 @@ MoonPosition structure: "IsMoonSet": true, "IsMeridian": true, "Moonrise": { - "Timestamp": 1677740467, - "TimeISO": "2023-03-02T12:01:07+05:00", + "Time": "2023-03-02T12:01:07+05:00", "AzimuthDegrees": 43.38104, "AltitudeDegrees": -0.56667, "Direction": "NE", "DistanceKm": 404836.13007 }, "Moonset": { - "Timestamp": 1677715380, - "TimeISO": "2023-03-02T05:03:00+05:00", + "Time": "2023-03-02T05:03:00+05:00", "AzimuthDegrees": 317.04922, "AltitudeDegrees": -0.56667, "Direction": "NW", "DistanceKm": 404345.126 }, "Meridian": { - "Timestamp": 1677772596, - "TimeISO": "2023-03-02T20:56:36+05:00", + "Time": "2023-03-02T20:56:36+05:00", "AzimuthDegrees": 180, "AltitudeDegrees": 65.4, "Direction": "S", @@ -511,24 +495,21 @@ MoonPosition structure: "IsMoonSet": true, "IsMeridian": true, "Moonrise": { - "Timestamp": 1680245913, - "TimeISO": "2023-03-31T11:58:33+05:00", + "Time": "2023-03-31T11:58:33+05:00", "AzimuthDegrees": 49.55137, "AltitudeDegrees": -0.56667, "Direction": "NE", "DistanceKm": 404935.96867 }, "Moonset": { - "Timestamp": 1680218226, - "TimeISO": "2023-03-31T04:17:06+05:00", + "Time": "2023-03-31T04:17:06+05:00", "AzimuthDegrees": 312.1322, "AltitudeDegrees": -0.56667, "Direction": "NW", "DistanceKm": 404804.48186 }, "Meridian": { - "Timestamp": 1680276504, - "TimeISO": "2023-03-31T20:28:24+05:00", + "Time": "2023-03-31T20:28:24+05:00", "AzimuthDegrees": 180, "AltitudeDegrees": 61.6, "Direction": "S", @@ -551,7 +532,7 @@ The method returns the moon phases for the given year. The response contains an |`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` |`year` | `int [optional, default=]` | Format: YYYY Allowed range: [1, 9999] | `2025` -#### Response +#### Response example ```json [ @@ -592,7 +573,7 @@ The method returns the moon phases for the current year. The response contains a | :--- | :--- | :--- | :--- | |`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` -#### Response: +#### Response Response: as [GET /api/v1/moonTableYear](https://github.com/prostraction/moon#v1moontableyear-response) @@ -614,7 +595,7 @@ The method converts human date to julian time (UTC +0 timezone). |`minute` | `int [optional, default=]` | Format: m or mm. Allowed range: [0, 59] | `01` or `1` |`second` | `int [optional, default=]` | Format: s or ss. Allowed range: [0, 59] | `01` or `1` -#### Response: +#### Response example ```json { @@ -638,7 +619,7 @@ The method converts human date to julian time (UTC +0 timezone). |`timestamp` | `int [optional, default=]` | Timestamp for calculations. Current, if not specified | `1735693261` -#### Response: +#### Response example ```json { @@ -662,7 +643,7 @@ The method converts julian time to human time (UTC +0 timezone). |`jtime` | `float64 [required]` | Julian Time to convert (float64) | `2460676.5423726854` -#### Response: +#### Response example ```json { @@ -672,3 +653,49 @@ The method converts julian time to human time (UTC +0 timezone). } ``` +### GET /api/v1/nextMoonPhase + +The method returns next moon phase + +#### Params + + | Parameter | Type | Description | Example Value | +| :--- | :--- | :--- | :--- | +|`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` +|`timeFormat` | `string [optional]` | Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time. | `duration` + + +#### Response example: + +timeFormat: `ISO` + +```json +{ + "NewMoon": "2025-11-20T11:48:16+05:00", + "FirstQuarter": "2025-11-28T11:44:00+05:00", + "FullMoon": "2025-12-05T04:15:18+05:00", + "LastQuarter": "2025-12-12T02:04:31+05:00" +} +``` + +timeFormat: `timestamp` + +```json +{ + "NewMoon": 1763621296, + "FirstQuarter": 1764312240, + "FullMoon": 1764890118, + "LastQuarter": 1765487071 +} +``` + +timeFormat: `duration` + +```json +{ + "NewMoon": 313830, + "FirstQuarter": 1004774, + "FullMoon": 1582652, + "LastQuarter": 2179605 +} +``` \ No newline at end of file diff --git a/doc/postman_collection b/doc/postman_collection index 97636f6..024727e 100644 --- a/doc/postman_collection +++ b/doc/postman_collection @@ -189,7 +189,7 @@ } ], "cookie": [], - "body": "{\n \"BeginDay\": {\n \"MoonDays\": 0.85568,\n \"Illumination\": 0.35744,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735671600,\n \"TimeISO\": \"2025-01-01T00:00:00+05:00\",\n \"AzimuthDegrees\": 326.86255,\n \"AltitudeDegrees\": -62.68617,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 382464.56722\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 0.89806,\n \"Illumination\": 0.4137,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735675261,\n \"TimeISO\": \"2025-01-01T01:01:01+05:00\",\n \"AzimuthDegrees\": 356.08557,\n \"AltitudeDegrees\": -65.49632,\n \"Direction\": \"N\",\n \"DistanceKm\": 382316.55173\n }\n },\n \"EndDay\": {\n \"MoonDays\": 1.85568,\n \"Illumination\": 2.79491,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1735758000,\n \"TimeISO\": \"2025-01-02T00:00:00+05:00\",\n \"AzimuthDegrees\": 309.21002,\n \"AltitudeDegrees\": -54.25535,\n \"Direction\": \"NW\",\n \"DistanceKm\": 379194.7976\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2024-12-31T09:18:42+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-01T09:55:21+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-01-01T09:55:21+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-02T10:21:16+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\",\n \"Begin\": \"2024-12-31T03:27:49+05:00\",\n \"End\": \"2025-01-02T15:27:49+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1735707321,\n \"TimeISO\": \"2025-01-01T09:55:21+05:00\",\n \"AzimuthDegrees\": 133.52155,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 381054.53085\n },\n \"Moonset\": {\n \"Timestamp\": 1735734376,\n \"TimeISO\": \"2025-01-01T17:26:16+05:00\",\n \"AzimuthDegrees\": 228.55471,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 380040.1844\n },\n \"Meridian\": {\n \"Timestamp\": 1735720605,\n \"TimeISO\": \"2025-01-01T13:36:45+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 380550.46947\n }\n }\n}" + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 0.85568,\n \"Illumination\": 0.35744,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-01T00:00:00+05:00\",\n \"AzimuthDegrees\": 326.86255,\n \"AltitudeDegrees\": -62.68617,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 382464.56722\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 0.89806,\n \"Illumination\": 0.4137,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-01T01:01:01+05:00\",\n \"AzimuthDegrees\": 356.08557,\n \"AltitudeDegrees\": -65.49632,\n \"Direction\": \"N\",\n \"DistanceKm\": 382316.55173\n }\n },\n \"EndDay\": {\n \"MoonDays\": 1.85568,\n \"Illumination\": 2.79491,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-02T00:00:00+05:00\",\n \"AzimuthDegrees\": 309.21002,\n \"AltitudeDegrees\": -54.25535,\n \"Direction\": \"NW\",\n \"DistanceKm\": 379194.7976\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2024-12-31T09:18:42+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-01T09:55:21+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-01-01T09:55:21+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-02T10:21:16+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\",\n \"Begin\": \"2024-12-31T03:27:49+05:00\",\n \"End\": \"2025-01-02T15:27:49+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-01-01T09:55:21+05:00\",\n \"AzimuthDegrees\": 133.52155,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 381054.53085\n },\n \"Moonset\": {\n \"Time\": \"2025-01-01T17:26:16+05:00\",\n \"AzimuthDegrees\": 228.55471,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 380040.1844\n },\n \"Meridian\": {\n \"Time\": \"2025-01-01T13:36:45+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 380550.46947\n }\n }\n}" } ] }, @@ -313,7 +313,7 @@ } ], "cookie": [], - "body": "{\n \"BeginDay\": {\n \"MoonDays\": 13.27363,\n \"Illumination\": 93.79012,\n \"Phase\": {\n \"Name\": \"Waxing Gibbous\",\n \"NameLocalized\": \"Растущая Луна\",\n \"Emoji\": \"🌔\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762196400,\n \"TimeISO\": \"2025-11-04T00:00:00+05:00\",\n \"AzimuthDegrees\": 215.21834,\n \"AltitudeDegrees\": 42.12633,\n \"Direction\": \"SW\",\n \"DistanceKm\": 361308.10318\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 13.75159,\n \"Illumination\": 96.27365,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762237695,\n \"TimeISO\": \"2025-11-04T11:28:15+05:00\",\n \"AzimuthDegrees\": 12.34775,\n \"AltitudeDegrees\": -26.87274,\n \"Direction\": \"NNE\",\n \"DistanceKm\": 359579.90558\n }\n },\n \"EndDay\": {\n \"MoonDays\": 14.27363,\n \"Illumination\": 98.3017,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1762282800,\n \"TimeISO\": \"2025-11-05T00:00:00+05:00\",\n \"AzimuthDegrees\": 200.1139,\n \"AltitudeDegrees\": 52.28516,\n \"Direction\": \"SSW\",\n \"DistanceKm\": 358145.25242\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-11-03T15:31:43+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-04T15:47:07+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-11-04T15:47:07+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-05T16:06:53+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\",\n \"Begin\": \"2025-11-03T05:25:58+05:00\",\n \"End\": \"2025-11-05T17:25:58+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1762253227,\n \"TimeISO\": \"2025-11-04T15:47:07+05:00\",\n \"AzimuthDegrees\": 69.12186,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 359030.2047\n },\n \"Moonset\": {\n \"Timestamp\": 1762215573,\n \"TimeISO\": \"2025-11-04T05:19:33+05:00\",\n \"AzimuthDegrees\": 286.14788,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 360458.99957\n },\n \"Meridian\": {\n \"Timestamp\": 1762279703,\n \"TimeISO\": \"2025-11-04T23:08:23+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 53.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 358227.74281\n }\n }\n}" + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 13.27363,\n \"Illumination\": 93.79012,\n \"Phase\": {\n \"Name\": \"Waxing Gibbous\",\n \"NameLocalized\": \"Растущая Луна\",\n \"Emoji\": \"🌔\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-04T00:00:00+05:00\",\n \"AzimuthDegrees\": 215.21834,\n \"AltitudeDegrees\": 42.12633,\n \"Direction\": \"SW\",\n \"DistanceKm\": 361308.10318\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 13.75159,\n \"Illumination\": 96.27365,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-04T11:28:15+05:00\",\n \"AzimuthDegrees\": 12.34775,\n \"AltitudeDegrees\": -26.87274,\n \"Direction\": \"NNE\",\n \"DistanceKm\": 359579.90558\n }\n },\n \"EndDay\": {\n \"MoonDays\": 14.27363,\n \"Illumination\": 98.3017,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-05T00:00:00+05:00\",\n \"AzimuthDegrees\": 200.1139,\n \"AltitudeDegrees\": 52.28516,\n \"Direction\": \"SSW\",\n \"DistanceKm\": 358145.25242\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-11-03T15:31:43+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-04T15:47:07+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-11-04T15:47:07+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-05T16:06:53+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\",\n \"Begin\": \"2025-11-03T05:25:58+05:00\",\n \"End\": \"2025-11-05T17:25:58+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-11-04T15:47:07+05:00\",\n \"AzimuthDegrees\": 69.12186,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 359030.2047\n },\n \"Moonset\": {\n \"Time\": \"2025-11-04T05:19:33+05:00\",\n \"AzimuthDegrees\": 286.14788,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 360458.99957\n },\n \"Meridian\": {\n \"Time\": \"2025-11-04T23:08:23+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 53.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 358227.74281\n }\n }\n}" } ] }, @@ -447,7 +447,7 @@ } ], "cookie": [], - "body": "{\n \"BeginDay\": {\n \"MoonDays\": 23.53674,\n \"Illumination\": 38.2726,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1757962800,\n \"TimeISO\": \"2025-09-16T00:00:00+05:00\",\n \"AzimuthDegrees\": 57.08873,\n \"AltitudeDegrees\": 8.79646,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376559.88437\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 24.28786,\n \"Illumination\": 30.38659,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1758027697,\n \"TimeISO\": \"2025-09-16T18:01:37+05:00\",\n \"AzimuthDegrees\": 332.40261,\n \"AltitudeDegrees\": -8.94892,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 379152.9693\n }\n },\n \"EndDay\": {\n \"MoonDays\": 24.53674,\n \"Illumination\": 27.90729,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Timestamp\": 1758049200,\n \"TimeISO\": \"2025-09-17T00:00:00+05:00\",\n \"AzimuthDegrees\": 48.0345,\n \"AltitudeDegrees\": -0.14735,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380020.57994\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-09-15T22:37:45+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-09-16T23:56:10+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-09-16T23:56:10+05:00\",\n \"IsBeginExists\": true,\n \"IsEndExists\": false\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\",\n \"Begin\": \"2025-09-14T23:07:06+05:00\",\n \"End\": \"2025-09-17T11:07:06+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1758048970,\n \"TimeISO\": \"2025-09-16T23:56:10+05:00\",\n \"AzimuthDegrees\": 47.31764,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380011.3104\n },\n \"Moonset\": {\n \"Timestamp\": 1758022192,\n \"TimeISO\": \"2025-09-16T16:29:52+05:00\",\n \"AzimuthDegrees\": 314.4689,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 378931.34559\n },\n \"Meridian\": {\n \"Timestamp\": 1757990436,\n \"TimeISO\": \"2025-09-16T07:40:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 377658.15629\n }\n }\n}" + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 23.53674,\n \"Illumination\": 38.2726,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-16T00:00:00+05:00\",\n \"AzimuthDegrees\": 57.08873,\n \"AltitudeDegrees\": 8.79646,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376559.88437\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 24.28786,\n \"Illumination\": 30.38659,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-16T18:01:37+05:00\",\n \"AzimuthDegrees\": 332.40261,\n \"AltitudeDegrees\": -8.94892,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 379152.9693\n }\n },\n \"EndDay\": {\n \"MoonDays\": 24.53674,\n \"Illumination\": 27.90729,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-17T00:00:00+05:00\",\n \"AzimuthDegrees\": 48.0345,\n \"AltitudeDegrees\": -0.14735,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380020.57994\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-09-15T22:37:45+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-09-16T23:56:10+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-09-16T23:56:10+05:00\",\n \"IsBeginExists\": true,\n \"IsEndExists\": false\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\",\n \"Begin\": \"2025-09-14T23:07:06+05:00\",\n \"End\": \"2025-09-17T11:07:06+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-09-16T23:56:10+05:00\",\n \"AzimuthDegrees\": 47.31764,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380011.3104\n },\n \"Moonset\": {\n \"Time\": \"2025-09-16T16:29:52+05:00\",\n \"AzimuthDegrees\": 314.4689,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 378931.34559\n },\n \"Meridian\": {\n \"Time\": \"2025-09-16T07:40:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 377658.15629\n }\n }\n}" } ] } @@ -462,7 +462,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272", + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5", "host": [ "{{baseUrl}}" ], @@ -476,16 +476,6 @@ "value": "UTC+5", "description": "UTC in format UTC+7, UTC+09:30, -3" }, - { - "key": "lang", - "value": "ru", - "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" - }, - { - "key": "precision", - "value": "5", - "description": "How many digits after . will be in output. Allowed range: [1, 20]" - }, { "key": "latitude", "value": "51.1655", @@ -495,6 +485,21 @@ "key": "longitude", "value": "71.4272", "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "year", + "value": "2023", + "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" + }, + { + "key": "month", + "value": "3", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" } ] } @@ -578,7 +583,7 @@ "path": "" } ], - "body": "[\n {\n \"Date\": \"2023-03-01\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1677650857,\n \"TimeISO\": \"2023-03-01T11:07:37+05:00\",\n \"AzimuthDegrees\": 42.8762,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402636.75966\n },\n \"Moonset\": {\n \"Timestamp\": 1677625807,\n \"TimeISO\": \"2023-03-01T04:10:07+05:00\",\n \"AzimuthDegrees\": 316.74613,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 401798.89266\n },\n \"Meridian\": {\n \"Timestamp\": 1677683130,\n \"TimeISO\": \"2023-03-01T20:05:30+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 403572.05151\n }\n },\n {\n \"Date\": \"2023-03-02\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1677740467,\n \"TimeISO\": \"2023-03-02T12:01:07+05:00\",\n \"AzimuthDegrees\": 43.38104,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404836.13007\n },\n \"Moonset\": {\n \"Timestamp\": 1677715380,\n \"TimeISO\": \"2023-03-02T05:03:00+05:00\",\n \"AzimuthDegrees\": 317.04922,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404345.126\n },\n \"Meridian\": {\n \"Timestamp\": 1677772596,\n \"TimeISO\": \"2023-03-02T20:56:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405326.66664\n }\n },\n {\n \"Date\": \"2023-03-03\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1677830659,\n \"TimeISO\": \"2023-03-03T13:04:19+05:00\",\n \"AzimuthDegrees\": 46.62247,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405833.52922\n },\n \"Moonset\": {\n \"Timestamp\": 1677804219,\n \"TimeISO\": \"2023-03-03T05:43:39+05:00\",\n \"AzimuthDegrees\": 314.58446,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405661.87336\n },\n \"Meridian\": {\n \"Timestamp\": 1677861967,\n \"TimeISO\": \"2023-03-03T21:46:07+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 63.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405914.02206\n }\n },\n {\n \"Date\": \"2023-03-04\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1677921218,\n \"TimeISO\": \"2023-03-04T14:13:38+05:00\",\n \"AzimuthDegrees\": 52.09931,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405724.54322\n },\n \"Moonset\": {\n \"Timestamp\": 1677892432,\n \"TimeISO\": \"2023-03-04T06:13:52+05:00\",\n \"AzimuthDegrees\": 309.84464,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405870.41007\n },\n \"Meridian\": {\n \"Timestamp\": 1677951207,\n \"TimeISO\": \"2023-03-04T22:33:27+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 60.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 405470.71269\n }\n },\n {\n \"Date\": \"2023-03-05\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678011933,\n \"TimeISO\": \"2023-03-05T15:25:33+05:00\",\n \"AzimuthDegrees\": 59.20503,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 404665.50003\n },\n \"Moonset\": {\n \"Timestamp\": 1677980183,\n \"TimeISO\": \"2023-03-05T06:36:23+05:00\",\n \"AzimuthDegrees\": 303.43187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 405132.86805\n },\n \"Meridian\": {\n \"Timestamp\": 1678040308,\n \"TimeISO\": \"2023-03-05T23:18:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 55.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 404168.13916\n }\n },\n {\n \"Date\": \"2023-03-06\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": false,\n \"Moonrise\": {\n \"Timestamp\": 1678102672,\n \"TimeISO\": \"2023-03-06T16:37:52+05:00\",\n \"AzimuthDegrees\": 67.43208,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 402838.67123\n },\n \"Moonset\": {\n \"Timestamp\": 1678067624,\n \"TimeISO\": \"2023-03-06T06:53:44+05:00\",\n \"AzimuthDegrees\": 295.85243,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 403623.69367\n }\n },\n {\n \"Date\": \"2023-03-07\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678193387,\n \"TimeISO\": \"2023-03-07T17:49:47+05:00\",\n \"AzimuthDegrees\": 76.41149,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400418.46475\n },\n \"Moonset\": {\n \"Timestamp\": 1678154869,\n \"TimeISO\": \"2023-03-07T07:07:49+05:00\",\n \"AzimuthDegrees\": 287.47458,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 401508.60505\n },\n \"Meridian\": {\n \"Timestamp\": 1678129288,\n \"TimeISO\": \"2023-03-07T00:01:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 50.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 402183.01801\n }\n },\n {\n \"Date\": \"2023-03-08\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678284096,\n \"TimeISO\": \"2023-03-08T19:01:36+05:00\",\n \"AzimuthDegrees\": 85.87566,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"E\",\n \"DistanceKm\": 397547.14825\n },\n \"Moonset\": {\n \"Timestamp\": 1678242000,\n \"TimeISO\": \"2023-03-08T07:20:00+05:00\",\n \"AzimuthDegrees\": 278.56217,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 398928.36539\n },\n \"Meridian\": {\n \"Timestamp\": 1678218186,\n \"TimeISO\": \"2023-03-08T00:43:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 45.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 399673.27308\n }\n },\n {\n \"Date\": \"2023-03-09\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678374851,\n \"TimeISO\": \"2023-03-09T20:14:11+05:00\",\n \"AzimuthDegrees\": 95.60853,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 394323.19311\n },\n \"Moonset\": {\n \"Timestamp\": 1678329083,\n \"TimeISO\": \"2023-03-09T07:31:23+05:00\",\n \"AzimuthDegrees\": 269.31953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 395988.3718\n },\n \"Meridian\": {\n \"Timestamp\": 1678307052,\n \"TimeISO\": \"2023-03-09T01:24:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 396761.88573\n }\n },\n {\n \"Date\": \"2023-03-10\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678465722,\n \"TimeISO\": \"2023-03-10T21:28:42+05:00\",\n \"AzimuthDegrees\": 105.39738,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 390803.12388\n },\n \"Moonset\": {\n \"Timestamp\": 1678416177,\n \"TimeISO\": \"2023-03-10T07:42:57+05:00\",\n \"AzimuthDegrees\": 259.93073,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 392756.20131\n },\n \"Meridian\": {\n \"Timestamp\": 1678395944,\n \"TimeISO\": \"2023-03-10T02:05:44+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 33.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 393530.65092\n }\n },\n {\n \"Date\": \"2023-03-11\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678556769,\n \"TimeISO\": \"2023-03-11T22:46:09+05:00\",\n \"AzimuthDegrees\": 114.97706,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 387016.85411\n },\n \"Moonset\": {\n \"Timestamp\": 1678503344,\n \"TimeISO\": \"2023-03-11T07:55:44+05:00\",\n \"AzimuthDegrees\": 250.59757,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 389268.48137\n },\n \"Meridian\": {\n \"Timestamp\": 1678484929,\n \"TimeISO\": \"2023-03-11T02:48:49+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 27.5,\n \"Direction\": \"S\",\n \"DistanceKm\": 390025.04685\n }\n },\n {\n \"Date\": \"2023-03-12\",\n \"IsMoonRise\": false,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonset\": {\n \"Timestamp\": 1678590669,\n \"TimeISO\": \"2023-03-12T08:11:09+05:00\",\n \"AzimuthDegrees\": 241.58881,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 385546.82937\n },\n \"Meridian\": {\n \"Timestamp\": 1678574075,\n \"TimeISO\": \"2023-03-12T03:34:35+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 21.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 386270.22231\n }\n },\n {\n \"Date\": \"2023-03-13\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678648012,\n \"TimeISO\": \"2023-03-13T00:06:52+05:00\",\n \"AzimuthDegrees\": 123.94721,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 382995.32957\n },\n \"Moonset\": {\n \"Timestamp\": 1678678278,\n \"TimeISO\": \"2023-03-13T08:31:18+05:00\",\n \"AzimuthDegrees\": 233.31784,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 381620.77804\n },\n \"Meridian\": {\n \"Timestamp\": 1678663446,\n \"TimeISO\": \"2023-03-13T04:24:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 17,\n \"Direction\": \"S\",\n \"DistanceKm\": 382296.46854\n }\n },\n {\n \"Date\": \"2023-03-14\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678739357,\n \"TimeISO\": \"2023-03-14T01:29:17+05:00\",\n \"AzimuthDegrees\": 131.64699,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 378807.06957\n },\n \"Moonset\": {\n \"Timestamp\": 1678766372,\n \"TimeISO\": \"2023-03-14T08:59:32+05:00\",\n \"AzimuthDegrees\": 226.46039,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 377554.30425\n },\n \"Meridian\": {\n \"Timestamp\": 1678753085,\n \"TimeISO\": \"2023-03-14T05:18:05+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 378170.5856\n }\n },\n {\n \"Date\": \"2023-03-15\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678830525,\n \"TimeISO\": \"2023-03-15T02:48:45+05:00\",\n \"AzimuthDegrees\": 137.04254,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 374594.67251\n },\n \"Moonset\": {\n \"Timestamp\": 1678855238,\n \"TimeISO\": \"2023-03-15T09:40:38+05:00\",\n \"AzimuthDegrees\": 222.05405,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 373473.15308\n },\n \"Meridian\": {\n \"Timestamp\": 1678842992,\n \"TimeISO\": \"2023-03-15T06:16:32+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 374026.97617\n }\n },\n {\n \"Date\": \"2023-03-16\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1678921072,\n \"TimeISO\": \"2023-03-16T03:57:52+05:00\",\n \"AzimuthDegrees\": 138.9114,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 370591.50029\n },\n \"Moonset\": {\n \"Timestamp\": 1678945166,\n \"TimeISO\": \"2023-03-16T10:39:26+05:00\",\n \"AzimuthDegrees\": 221.29657,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 369592.67725\n },\n \"Meridian\": {\n \"Timestamp\": 1678933092,\n \"TimeISO\": \"2023-03-16T07:18:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 370088.60593\n }\n },\n {\n \"Date\": \"2023-03-17\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679010663,\n \"TimeISO\": \"2023-03-17T04:51:03+05:00\",\n \"AzimuthDegrees\": 136.61246,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 367098.86901\n },\n \"Moonset\": {\n \"Timestamp\": 1679036174,\n \"TimeISO\": \"2023-03-17T11:56:14+05:00\",\n \"AzimuthDegrees\": 224.76797,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 366237.19817\n },\n \"Meridian\": {\n \"Timestamp\": 1679023251,\n \"TimeISO\": \"2023-03-17T08:20:51+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 11.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 366664.93323\n }\n },\n {\n \"Date\": \"2023-03-18\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679099326,\n \"TimeISO\": \"2023-03-18T05:28:46+05:00\",\n \"AzimuthDegrees\": 130.65168,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 364441.40172\n },\n \"Moonset\": {\n \"Timestamp\": 1679127890,\n \"TimeISO\": \"2023-03-18T13:24:50+05:00\",\n \"AzimuthDegrees\": 231.87919,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 363812.28579\n },\n \"Meridian\": {\n \"Timestamp\": 1679113319,\n \"TimeISO\": \"2023-03-18T09:21:59+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 14.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364117.71582\n }\n },\n {\n \"Date\": \"2023-03-19\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679187320,\n \"TimeISO\": \"2023-03-19T05:55:20+05:00\",\n \"AzimuthDegrees\": 122.15123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362929.11813\n },\n \"Moonset\": {\n \"Timestamp\": 1679219833,\n \"TimeISO\": \"2023-03-19T14:57:13+05:00\",\n \"AzimuthDegrees\": 241.42982,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 362712.81832\n },\n \"Meridian\": {\n \"Timestamp\": 1679203194,\n \"TimeISO\": \"2023-03-19T10:19:54+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 19.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 362798.79835\n }\n },\n {\n \"Date\": \"2023-03-20\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679274902,\n \"TimeISO\": \"2023-03-20T06:15:02+05:00\",\n \"AzimuthDegrees\": 112.14362,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362812.63242\n },\n \"Moonset\": {\n \"Timestamp\": 1679311702,\n \"TimeISO\": \"2023-03-20T16:28:22+05:00\",\n \"AzimuthDegrees\": 252.32977,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 363219.08698\n },\n \"Meridian\": {\n \"Timestamp\": 1679292852,\n \"TimeISO\": \"2023-03-20T11:14:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 25.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 362976.42916\n }\n },\n {\n \"Date\": \"2023-03-21\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679362254,\n \"TimeISO\": \"2023-03-21T06:30:54+05:00\",\n \"AzimuthDegrees\": 101.3864,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 364227.26916\n },\n \"Moonset\": {\n \"Timestamp\": 1679403400,\n \"TimeISO\": \"2023-03-21T17:56:40+05:00\",\n \"AzimuthDegrees\": 263.78338,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 365425.30425\n },\n \"Meridian\": {\n \"Timestamp\": 1679382325,\n \"TimeISO\": \"2023-03-21T12:05:25+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364770.21284\n }\n },\n {\n \"Date\": \"2023-03-22\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679449497,\n \"TimeISO\": \"2023-03-22T06:44:57+05:00\",\n \"AzimuthDegrees\": 90.43001,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 367149.18373\n },\n \"Moonset\": {\n \"Timestamp\": 1679494947,\n \"TimeISO\": \"2023-03-22T19:22:27+05:00\",\n \"AzimuthDegrees\": 275.21269,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 369210.60777\n },\n \"Meridian\": {\n \"Timestamp\": 1679471677,\n \"TimeISO\": \"2023-03-22T12:54:37+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 368112.88854\n }\n },\n {\n \"Date\": \"2023-03-23\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679536722,\n \"TimeISO\": \"2023-03-23T06:58:42+05:00\",\n \"AzimuthDegrees\": 79.70263,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 371383.43121\n },\n \"Moonset\": {\n \"Timestamp\": 1679586398,\n \"TimeISO\": \"2023-03-23T20:46:38+05:00\",\n \"AzimuthDegrees\": 286.16029,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 374253.93367\n },\n \"Meridian\": {\n \"Timestamp\": 1679560980,\n \"TimeISO\": \"2023-03-23T13:43:00+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 46.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 372750.53254\n }\n },\n {\n \"Date\": \"2023-03-24\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679624012,\n \"TimeISO\": \"2023-03-24T07:13:32+05:00\",\n \"AzimuthDegrees\": 69.57123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376588.54488\n },\n \"Moonset\": {\n \"Timestamp\": 1679677785,\n \"TimeISO\": \"2023-03-24T22:09:45+05:00\",\n \"AzimuthDegrees\": 296.21187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 380086.26614\n },\n \"Meridian\": {\n \"Timestamp\": 1679650299,\n \"TimeISO\": \"2023-03-24T14:31:39+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 52.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 378280.3871\n }\n },\n {\n \"Date\": \"2023-03-25\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679711456,\n \"TimeISO\": \"2023-03-25T07:30:56+05:00\",\n \"AzimuthDegrees\": 60.39425,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 382327.95573\n },\n \"Moonset\": {\n \"Timestamp\": 1679769074,\n \"TimeISO\": \"2023-03-25T23:31:14+05:00\",\n \"AzimuthDegrees\": 304.92579,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 386164.32159\n },\n \"Meridian\": {\n \"Timestamp\": 1679739679,\n \"TimeISO\": \"2023-03-25T15:21:19+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 58.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 384213.14123\n }\n },\n {\n \"Date\": \"2023-03-26\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": false,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679799169,\n \"TimeISO\": \"2023-03-26T07:52:49+05:00\",\n \"AzimuthDegrees\": 52.57818,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 388131.21721\n },\n \"Meridian\": {\n \"Timestamp\": 1679829144,\n \"TimeISO\": \"2023-03-26T16:12:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 62.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 390041.63582\n }\n },\n {\n \"Date\": \"2023-03-27\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679887300,\n \"TimeISO\": \"2023-03-27T08:21:40+05:00\",\n \"AzimuthDegrees\": 46.62563,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 393548.92562\n },\n \"Moonset\": {\n \"Timestamp\": 1679860134,\n \"TimeISO\": \"2023-03-27T00:48:54+05:00\",\n \"AzimuthDegrees\": 311.77602,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 391948.19405\n },\n \"Meridian\": {\n \"Timestamp\": 1679918681,\n \"TimeISO\": \"2023-03-27T17:04:41+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65,\n \"Direction\": \"S\",\n \"DistanceKm\": 395301.62304\n }\n },\n {\n \"Date\": \"2023-03-28\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1679976010,\n \"TimeISO\": \"2023-03-28T09:00:10+05:00\",\n \"AzimuthDegrees\": 43.11087,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 398191.74953\n },\n \"Moonset\": {\n \"Timestamp\": 1679950741,\n \"TimeISO\": \"2023-03-28T01:59:01+05:00\",\n \"AzimuthDegrees\": 316.17036,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 396971.11487\n },\n \"Meridian\": {\n \"Timestamp\": 1680008246,\n \"TimeISO\": \"2023-03-28T17:57:26+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 399617.2651\n }\n },\n {\n \"Date\": \"2023-03-29\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1680065405,\n \"TimeISO\": \"2023-03-29T09:50:05+05:00\",\n \"AzimuthDegrees\": 42.48507,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 401753.75503\n },\n \"Moonset\": {\n \"Timestamp\": 1680040664,\n \"TimeISO\": \"2023-03-29T02:57:44+05:00\",\n \"AzimuthDegrees\": 317.64261,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 400892.49384\n },\n \"Meridian\": {\n \"Timestamp\": 1680097776,\n \"TimeISO\": \"2023-03-29T18:49:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 402729.21577\n }\n },\n {\n \"Date\": \"2023-03-30\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1680155438,\n \"TimeISO\": \"2023-03-30T10:50:38+05:00\",\n \"AzimuthDegrees\": 44.77953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404030.19632\n },\n \"Moonset\": {\n \"Timestamp\": 1680129800,\n \"TimeISO\": \"2023-03-30T03:43:20+05:00\",\n \"AzimuthDegrees\": 316.15045,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 403521.433\n },\n \"Meridian\": {\n \"Timestamp\": 1680187209,\n \"TimeISO\": \"2023-03-30T19:40:09+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 64.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 404505.37243\n }\n },\n {\n \"Date\": \"2023-03-31\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Timestamp\": 1680245913,\n \"TimeISO\": \"2023-03-31T11:58:33+05:00\",\n \"AzimuthDegrees\": 49.55137,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404935.96867\n },\n \"Moonset\": {\n \"Timestamp\": 1680218226,\n \"TimeISO\": \"2023-03-31T04:17:06+05:00\",\n \"AzimuthDegrees\": 312.1322,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404804.48186\n },\n \"Meridian\": {\n \"Timestamp\": 1680276504,\n \"TimeISO\": \"2023-03-31T20:28:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 61.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 404936.05398\n }\n }\n]" + "body": "[\n {\n \"Date\": \"2023-03-01\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-01T11:07:37+05:00\",\n \"AzimuthDegrees\": 42.8762,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402636.75966\n },\n \"Moonset\": {\n \"Time\": \"2023-03-01T04:10:07+05:00\",\n \"AzimuthDegrees\": 316.74613,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 401798.89266\n },\n \"Meridian\": {\n \"Time\": \"2023-03-01T20:05:30+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 403572.05151\n }\n },\n {\n \"Date\": \"2023-03-02\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-02T12:01:07+05:00\",\n \"AzimuthDegrees\": 43.38104,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404836.13007\n },\n \"Moonset\": {\n \"Time\": \"2023-03-02T05:03:00+05:00\",\n \"AzimuthDegrees\": 317.04922,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404345.126\n },\n \"Meridian\": {\n \"Time\": \"2023-03-02T20:56:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405326.66664\n }\n },\n {\n \"Date\": \"2023-03-03\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-03T13:04:19+05:00\",\n \"AzimuthDegrees\": 46.62247,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405833.52922\n },\n \"Moonset\": {\n \"Time\": \"2023-03-03T05:43:39+05:00\",\n \"AzimuthDegrees\": 314.58446,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405661.87336\n },\n \"Meridian\": {\n \"Time\": \"2023-03-03T21:46:07+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 63.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405914.02206\n }\n },\n {\n \"Date\": \"2023-03-04\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-04T14:13:38+05:00\",\n \"AzimuthDegrees\": 52.09931,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405724.54322\n },\n \"Moonset\": {\n \"Time\": \"2023-03-04T06:13:52+05:00\",\n \"AzimuthDegrees\": 309.84464,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405870.41007\n },\n \"Meridian\": {\n \"Time\": \"2023-03-04T22:33:27+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 60.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 405470.71269\n }\n },\n {\n \"Date\": \"2023-03-05\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-05T15:25:33+05:00\",\n \"AzimuthDegrees\": 59.20503,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 404665.50003\n },\n \"Moonset\": {\n \"Time\": \"2023-03-05T06:36:23+05:00\",\n \"AzimuthDegrees\": 303.43187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 405132.86805\n },\n \"Meridian\": {\n \"Time\": \"2023-03-05T23:18:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 55.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 404168.13916\n }\n },\n {\n \"Date\": \"2023-03-06\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": false,\n \"Moonrise\": {\n \"Time\": \"2023-03-06T16:37:52+05:00\",\n \"AzimuthDegrees\": 67.43208,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 402838.67123\n },\n \"Moonset\": {\n \"Time\": \"2023-03-06T06:53:44+05:00\",\n \"AzimuthDegrees\": 295.85243,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 403623.69367\n }\n },\n {\n \"Date\": \"2023-03-07\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-07T17:49:47+05:00\",\n \"AzimuthDegrees\": 76.41149,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400418.46475\n },\n \"Moonset\": {\n \"Time\": \"2023-03-07T07:07:49+05:00\",\n \"AzimuthDegrees\": 287.47458,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 401508.60505\n },\n \"Meridian\": {\n \"Time\": \"2023-03-07T00:01:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 50.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 402183.01801\n }\n },\n {\n \"Date\": \"2023-03-08\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-08T19:01:36+05:00\",\n \"AzimuthDegrees\": 85.87566,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"E\",\n \"DistanceKm\": 397547.14825\n },\n \"Moonset\": {\n \"Time\": \"2023-03-08T07:20:00+05:00\",\n \"AzimuthDegrees\": 278.56217,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 398928.36539\n },\n \"Meridian\": {\n \"Time\": \"2023-03-08T00:43:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 45.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 399673.27308\n }\n },\n {\n \"Date\": \"2023-03-09\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-09T20:14:11+05:00\",\n \"AzimuthDegrees\": 95.60853,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 394323.19311\n },\n \"Moonset\": {\n \"Time\": \"2023-03-09T07:31:23+05:00\",\n \"AzimuthDegrees\": 269.31953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 395988.3718\n },\n \"Meridian\": {\n \"Time\": \"2023-03-09T01:24:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 396761.88573\n }\n },\n {\n \"Date\": \"2023-03-10\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-10T21:28:42+05:00\",\n \"AzimuthDegrees\": 105.39738,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 390803.12388\n },\n \"Moonset\": {\n \"Time\": \"2023-03-10T07:42:57+05:00\",\n \"AzimuthDegrees\": 259.93073,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 392756.20131\n },\n \"Meridian\": {\n \"Time\": \"2023-03-10T02:05:44+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 33.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 393530.65092\n }\n },\n {\n \"Date\": \"2023-03-11\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-11T22:46:09+05:00\",\n \"AzimuthDegrees\": 114.97706,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 387016.85411\n },\n \"Moonset\": {\n \"Time\": \"2023-03-11T07:55:44+05:00\",\n \"AzimuthDegrees\": 250.59757,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 389268.48137\n },\n \"Meridian\": {\n \"Time\": \"2023-03-11T02:48:49+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 27.5,\n \"Direction\": \"S\",\n \"DistanceKm\": 390025.04685\n }\n },\n {\n \"Date\": \"2023-03-12\",\n \"IsMoonRise\": false,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonset\": {\n \"Time\": \"2023-03-12T08:11:09+05:00\",\n \"AzimuthDegrees\": 241.58881,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 385546.82937\n },\n \"Meridian\": {\n \"Time\": \"2023-03-12T03:34:35+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 21.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 386270.22231\n }\n },\n {\n \"Date\": \"2023-03-13\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-13T00:06:52+05:00\",\n \"AzimuthDegrees\": 123.94721,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 382995.32957\n },\n \"Moonset\": {\n \"Time\": \"2023-03-13T08:31:18+05:00\",\n \"AzimuthDegrees\": 233.31784,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 381620.77804\n },\n \"Meridian\": {\n \"Time\": \"2023-03-13T04:24:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 17,\n \"Direction\": \"S\",\n \"DistanceKm\": 382296.46854\n }\n },\n {\n \"Date\": \"2023-03-14\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-14T01:29:17+05:00\",\n \"AzimuthDegrees\": 131.64699,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 378807.06957\n },\n \"Moonset\": {\n \"Time\": \"2023-03-14T08:59:32+05:00\",\n \"AzimuthDegrees\": 226.46039,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 377554.30425\n },\n \"Meridian\": {\n \"Time\": \"2023-03-14T05:18:05+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 378170.5856\n }\n },\n {\n \"Date\": \"2023-03-15\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-15T02:48:45+05:00\",\n \"AzimuthDegrees\": 137.04254,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 374594.67251\n },\n \"Moonset\": {\n \"Time\": \"2023-03-15T09:40:38+05:00\",\n \"AzimuthDegrees\": 222.05405,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 373473.15308\n },\n \"Meridian\": {\n \"Time\": \"2023-03-15T06:16:32+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 374026.97617\n }\n },\n {\n \"Date\": \"2023-03-16\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-16T03:57:52+05:00\",\n \"AzimuthDegrees\": 138.9114,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 370591.50029\n },\n \"Moonset\": {\n \"Time\": \"2023-03-16T10:39:26+05:00\",\n \"AzimuthDegrees\": 221.29657,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 369592.67725\n },\n \"Meridian\": {\n \"Time\": \"2023-03-16T07:18:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 370088.60593\n }\n },\n {\n \"Date\": \"2023-03-17\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-17T04:51:03+05:00\",\n \"AzimuthDegrees\": 136.61246,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 367098.86901\n },\n \"Moonset\": {\n \"Time\": \"2023-03-17T11:56:14+05:00\",\n \"AzimuthDegrees\": 224.76797,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 366237.19817\n },\n \"Meridian\": {\n \"Time\": \"2023-03-17T08:20:51+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 11.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 366664.93323\n }\n },\n {\n \"Date\": \"2023-03-18\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-18T05:28:46+05:00\",\n \"AzimuthDegrees\": 130.65168,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 364441.40172\n },\n \"Moonset\": {\n \"Time\": \"2023-03-18T13:24:50+05:00\",\n \"AzimuthDegrees\": 231.87919,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 363812.28579\n },\n \"Meridian\": {\n \"Time\": \"2023-03-18T09:21:59+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 14.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364117.71582\n }\n },\n {\n \"Date\": \"2023-03-19\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-19T05:55:20+05:00\",\n \"AzimuthDegrees\": 122.15123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362929.11813\n },\n \"Moonset\": {\n \"Time\": \"2023-03-19T14:57:13+05:00\",\n \"AzimuthDegrees\": 241.42982,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 362712.81832\n },\n \"Meridian\": {\n \"Time\": \"2023-03-19T10:19:54+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 19.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 362798.79835\n }\n },\n {\n \"Date\": \"2023-03-20\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-20T06:15:02+05:00\",\n \"AzimuthDegrees\": 112.14362,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362812.63242\n },\n \"Moonset\": {\n \"Time\": \"2023-03-20T16:28:22+05:00\",\n \"AzimuthDegrees\": 252.32977,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 363219.08698\n },\n \"Meridian\": {\n \"Time\": \"2023-03-20T11:14:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 25.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 362976.42916\n }\n },\n {\n \"Date\": \"2023-03-21\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-21T06:30:54+05:00\",\n \"AzimuthDegrees\": 101.3864,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 364227.26916\n },\n \"Moonset\": {\n \"Time\": \"2023-03-21T17:56:40+05:00\",\n \"AzimuthDegrees\": 263.78338,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 365425.30425\n },\n \"Meridian\": {\n \"Time\": \"2023-03-21T12:05:25+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364770.21284\n }\n },\n {\n \"Date\": \"2023-03-22\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-22T06:44:57+05:00\",\n \"AzimuthDegrees\": 90.43001,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 367149.18373\n },\n \"Moonset\": {\n \"Time\": \"2023-03-22T19:22:27+05:00\",\n \"AzimuthDegrees\": 275.21269,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 369210.60777\n },\n \"Meridian\": {\n \"Time\": \"2023-03-22T12:54:37+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 368112.88854\n }\n },\n {\n \"Date\": \"2023-03-23\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-23T06:58:42+05:00\",\n \"AzimuthDegrees\": 79.70263,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 371383.43121\n },\n \"Moonset\": {\n \"Time\": \"2023-03-23T20:46:38+05:00\",\n \"AzimuthDegrees\": 286.16029,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 374253.93367\n },\n \"Meridian\": {\n \"Time\": \"2023-03-23T13:43:00+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 46.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 372750.53254\n }\n },\n {\n \"Date\": \"2023-03-24\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-24T07:13:32+05:00\",\n \"AzimuthDegrees\": 69.57123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376588.54488\n },\n \"Moonset\": {\n \"Time\": \"2023-03-24T22:09:45+05:00\",\n \"AzimuthDegrees\": 296.21187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 380086.26614\n },\n \"Meridian\": {\n \"Time\": \"2023-03-24T14:31:39+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 52.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 378280.3871\n }\n },\n {\n \"Date\": \"2023-03-25\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-25T07:30:56+05:00\",\n \"AzimuthDegrees\": 60.39425,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 382327.95573\n },\n \"Moonset\": {\n \"Time\": \"2023-03-25T23:31:14+05:00\",\n \"AzimuthDegrees\": 304.92579,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 386164.32159\n },\n \"Meridian\": {\n \"Time\": \"2023-03-25T15:21:19+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 58.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 384213.14123\n }\n },\n {\n \"Date\": \"2023-03-26\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": false,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-26T07:52:49+05:00\",\n \"AzimuthDegrees\": 52.57818,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 388131.21721\n },\n \"Meridian\": {\n \"Time\": \"2023-03-26T16:12:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 62.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 390041.63582\n }\n },\n {\n \"Date\": \"2023-03-27\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-27T08:21:40+05:00\",\n \"AzimuthDegrees\": 46.62563,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 393548.92562\n },\n \"Moonset\": {\n \"Time\": \"2023-03-27T00:48:54+05:00\",\n \"AzimuthDegrees\": 311.77602,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 391948.19405\n },\n \"Meridian\": {\n \"Time\": \"2023-03-27T17:04:41+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65,\n \"Direction\": \"S\",\n \"DistanceKm\": 395301.62304\n }\n },\n {\n \"Date\": \"2023-03-28\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-28T09:00:10+05:00\",\n \"AzimuthDegrees\": 43.11087,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 398191.74953\n },\n \"Moonset\": {\n \"Time\": \"2023-03-28T01:59:01+05:00\",\n \"AzimuthDegrees\": 316.17036,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 396971.11487\n },\n \"Meridian\": {\n \"Time\": \"2023-03-28T17:57:26+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 399617.2651\n }\n },\n {\n \"Date\": \"2023-03-29\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-29T09:50:05+05:00\",\n \"AzimuthDegrees\": 42.48507,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 401753.75503\n },\n \"Moonset\": {\n \"Time\": \"2023-03-29T02:57:44+05:00\",\n \"AzimuthDegrees\": 317.64261,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 400892.49384\n },\n \"Meridian\": {\n \"Time\": \"2023-03-29T18:49:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 402729.21577\n }\n },\n {\n \"Date\": \"2023-03-30\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-30T10:50:38+05:00\",\n \"AzimuthDegrees\": 44.77953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404030.19632\n },\n \"Moonset\": {\n \"Time\": \"2023-03-30T03:43:20+05:00\",\n \"AzimuthDegrees\": 316.15045,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 403521.433\n },\n \"Meridian\": {\n \"Time\": \"2023-03-30T19:40:09+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 64.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 404505.37243\n }\n },\n {\n \"Date\": \"2023-03-31\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-31T11:58:33+05:00\",\n \"AzimuthDegrees\": 49.55137,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404935.96867\n },\n \"Moonset\": {\n \"Time\": \"2023-03-31T04:17:06+05:00\",\n \"AzimuthDegrees\": 312.1322,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404804.48186\n },\n \"Meridian\": {\n \"Time\": \"2023-03-31T20:28:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 61.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 404936.05398\n }\n }\n]" }, { "name": "moonPositionMonthly — error", @@ -1211,7 +1216,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&format=ISO", + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=duration", "host": [ "{{baseUrl}}" ], @@ -1226,9 +1231,9 @@ "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" }, { - "key": "format", - "value": "ISO", - "description": "How the data will be formed. Available values: ISO (default), timestamp, duration" + "key": "timeFormat", + "value": "duration", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." } ] } @@ -1244,7 +1249,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&format=ISO", + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=ISO", "host": [ "{{baseUrl}}" ], @@ -1259,7 +1264,7 @@ "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" }, { - "key": "format", + "key": "timeFormat", "value": "ISO", "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." } @@ -1303,7 +1308,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&format=timestamp", + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=timestamp", "host": [ "{{baseUrl}}" ], @@ -1318,7 +1323,7 @@ "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" }, { - "key": "format", + "key": "timeFormat", "value": "timestamp", "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." } @@ -1358,7 +1363,7 @@ "body": "{\n \"NewMoon\": 1763621296,\n \"FirstQuarter\": 1764312240,\n \"FullMoon\": 1764890118,\n \"LastQuarter\": 1765487071\n}" }, { - "name": "nextMoonPhase — duration format", + "name": "nextMoonPhase — duration format", "originalRequest": { "method": "GET", "header": [], @@ -1367,7 +1372,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&format=duration", + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=duration", "host": [ "{{baseUrl}}" ], @@ -1382,7 +1387,7 @@ "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" }, { - "key": "format", + "key": "timeFormat", "value": "duration", "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." } @@ -1391,7 +1396,7 @@ }, "status": "OK", "code": 200, - "_postman_previewlanguage": null, + "_postman_previewlanguage": "json", "header": [ { "key": "Server", @@ -1403,14 +1408,22 @@ }, { "key": "Content-Type", - "value": "application/json" + "value": "application/json", + "description": "", + "type": "text" }, { "key": "Content-Length", "value": "82" } ], - "cookie": [], + "cookie": [ + { + "expires": "Invalid Date", + "domain": "", + "path": "" + } + ], "body": "{\n \"NewMoon\": 313830,\n \"FirstQuarter\": 1004774,\n \"FullMoon\": 1582652,\n \"LastQuarter\": 2179605\n}" } ] diff --git a/internal/server/route-next.go b/internal/server/route-next.go index 077bccb..970f909 100644 --- a/internal/server/route-next.go +++ b/internal/server/route-next.go @@ -17,7 +17,7 @@ func (s *Server) moonNextMoonPhaseV1(c *fiber.Ctx) error { np := s.moonCache.FindNearestPhase(tGiven) - format := c.Query("format", "ISO") + format := c.Query("timeFormat", "ISO") if strings.ToLower(format) == "timestamp" { npt := moon.NearestPhaseTimestamp{ NewMoon: np.NewMoon.Unix(), diff --git a/internal/server/route-phase.go b/internal/server/route-phase.go index ee99c41..e81a16d 100644 --- a/internal/server/route-phase.go +++ b/internal/server/route-phase.go @@ -27,7 +27,9 @@ func (s *Server) moonPhaseCurrentV1(c *fiber.Ctx) error { lonStr := c.Query("longitude", "no-value") locationCords := parseCoords(latStr, lonStr) - return s.moonPhaseV1(c, tGiven, precision, locationCords) + timeFormat := c.Query("timeFormat", "ISO") + + return s.moonPhaseV1(c, tGiven, precision, locationCords, timeFormat) } func (s *Server) moonPhaseTimestampV1(c *fiber.Ctx) error { @@ -52,7 +54,9 @@ func (s *Server) moonPhaseTimestampV1(c *fiber.Ctx) error { lonStr := c.Query("longitude", "no-value") locationCords := parseCoords(latStr, lonStr) - return s.moonPhaseV1(c, tGiven, precision, locationCords) + timeFormat := c.Query("timeFormat", "ISO") + + return s.moonPhaseV1(c, tGiven, precision, locationCords, timeFormat) } func (s *Server) moonPhaseDatetV1(c *fiber.Ctx) error { @@ -88,12 +92,14 @@ func (s *Server) moonPhaseDatetV1(c *fiber.Ctx) error { lonStr := c.Query("longitude", "no-value") locationCords := parseCoords(latStr, lonStr) + timeFormat := c.Query("timeFormat", "ISO") + tGiven := time.Date(year, jt.GetMonth(month), day, hour, minute, second, 0, time.Local) tGiven = tGiven.In(loc) - return s.moonPhaseV1(c, tGiven, precision, locationCords) + return s.moonPhaseV1(c, tGiven, precision, locationCords, timeFormat) } -func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, locationCords Coordinates) error { +func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, locationCords Coordinates, timeFormat string) error { lang := c.Query("lang", "en") utc := c.Query("utc", "UTC:+0") loc, _ := jt.SetTimezoneLocFromString(utc) From 1d4cc052f5ce41a7afbe43bc285149f52b2ebbdb Mon Sep 17 00:00:00 2001 From: prostraction Date: Sun, 16 Nov 2025 23:48:43 +0500 Subject: [PATCH 08/18] feat: timeFormat, closes #6 --- README.md | 433 +++++++- doc/postman_collection | 1445 ++++++++++++++++++++++----- internal/server/route-moon-table.go | 41 +- internal/server/route-next.go | 15 +- internal/server/route-phase.go | 14 +- internal/server/route-position.go | 4 +- pkg/julian-time/time.go | 1 - pkg/moon/days.go | 35 +- pkg/moon/models.go | 11 +- pkg/moon/moon.go | 12 +- pkg/position/position.go | 67 +- pkg/zodiac/models.go | 6 +- pkg/zodiac/zodiac.go | 26 +- 13 files changed, 1814 insertions(+), 296 deletions(-) diff --git a/README.md b/README.md index c906fae..9f71aa6 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ The method returns the Moon parameters for the specified day and time. If the da |`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` |`lang` | `string [optional, default="en"]` | Values available: ("en", "es", "fr", "de", "ru", "jp") | `es` |`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` |`latitude` | `float [optional, default=none]` | Latitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `51.1655` |`longitude` | `float [optional, default=none]` | Longitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `71.4272` |`year` | `int [optional, default=]` | Format: YYYY Allowed range: [1, 9999] | `2025` @@ -39,6 +40,7 @@ The method returns the Moon parameters for the specified day and time. If the da |`minute` | `int [optional, default=]` | Format: m or mm. Allowed range: [0, 59] | `01` or `1` |`second` | `int [optional, default=]` | Format: s or ss. Allowed range: [0, 59] | `01` or `1` + ---------------------------------------------------------------- #### Response: @@ -194,7 +196,7 @@ MoonPosition structure: ---------------------------------------------------------------- -#### Response example +#### Response examples Response of: ```GET /api/v1/moonPhaseDate?lang=ru&utc=5&latitude=51.1655&longitude=71.4272&year=2025&month=09&day=15&precision=5&hour=12&minute=0&second=0``` @@ -330,6 +332,269 @@ Response of:
+ +
+ timeFormat = 2006-01-02 15:04 + +```json +{ + "BeginDay": { + "MoonDays": 0.85568, + "Illumination": 0.35744, + "Phase": { + "Name": "New Moon", + "NameLocalized": "Новолуние", + "Emoji": "🌑", + "IsWaxing": true + }, + "Zodiac": { + "Name": "Virgo", + "NameLocalized": "Дева", + "Emoji": "♍" + }, + "MoonPosition": { + "Time": "2025-01-01 00:00", + "AzimuthDegrees": 326.86255, + "AltitudeDegrees": -62.68617, + "Direction": "NNW", + "DistanceKm": 382464.56722 + } + }, + "CurrentState": { + "MoonDays": 0.89806, + "Illumination": 0.4137, + "Phase": { + "Name": "New Moon", + "NameLocalized": "Новолуние", + "Emoji": "🌑", + "IsWaxing": true + }, + "Zodiac": { + "Name": "Virgo", + "NameLocalized": "Дева", + "Emoji": "♍" + }, + "MoonPosition": { + "Time": "2025-01-01 01:01", + "AzimuthDegrees": 356.08557, + "AltitudeDegrees": -65.49632, + "Direction": "N", + "DistanceKm": 382316.55173 + } + }, + "EndDay": { + "MoonDays": 1.85568, + "Illumination": 2.79491, + "Phase": { + "Name": "New Moon", + "NameLocalized": "Новолуние", + "Emoji": "🌑", + "IsWaxing": true + }, + "Zodiac": { + "Name": "Virgo", + "NameLocalized": "Дева", + "Emoji": "♍" + }, + "MoonPosition": { + "Time": "2025-01-02 00:00", + "AzimuthDegrees": 309.21002, + "AltitudeDegrees": -54.25535, + "Direction": "NW", + "DistanceKm": 379194.7976 + } + }, + "MoonDaysDetailed": { + "Count": 2, + "Day": [ + { + "Begin": "2024-12-31 09:18", + "IsBeginExists": true, + "End": "2025-01-01 09:55", + "IsEndExists": true + }, + { + "Begin": "2025-01-01 09:55", + "IsBeginExists": true, + "End": "2025-01-02 10:21", + "IsEndExists": true + } + ] + }, + "ZodiacDetailed": { + "Count": 1, + "Zodiac": [ + { + "Name": "Virgo", + "NameLocalized": "Дева", + "Emoji": "♍", + "Begin": "2024-12-31 03:27", + "End": "2025-01-02 15:27" + } + ] + }, + "MoonRiseAndSet": { + "IsMoonRise": true, + "IsMoonSet": true, + "IsMeridian": true, + "Moonrise": { + "Time": "2025-01-01 09:55", + "AzimuthDegrees": 133.52155, + "AltitudeDegrees": -0.56667, + "Direction": "SE", + "DistanceKm": 381054.53085 + }, + "Moonset": { + "Time": "2025-01-01 17:26", + "AzimuthDegrees": 228.55471, + "AltitudeDegrees": -0.56667, + "Direction": "SW", + "DistanceKm": 380040.1844 + }, + "Meridian": { + "Time": "2025-01-01 13:36", + "AzimuthDegrees": 180, + "AltitudeDegrees": 13.2, + "Direction": "S", + "DistanceKm": 380550.46947 + } + } +} +``` + +
+ +
+ timeFormat = timestamp + +```json +{ + "BeginDay": { + "MoonDays": 0.85568, + "Illumination": 0.35744, + "Phase": { + "Name": "New Moon", + "NameLocalized": "Новолуние", + "Emoji": "🌑", + "IsWaxing": true + }, + "Zodiac": { + "Name": "Virgo", + "NameLocalized": "Дева", + "Emoji": "♍" + }, + "MoonPosition": { + "Time": 1735671600, + "AzimuthDegrees": 326.86255, + "AltitudeDegrees": -62.68617, + "Direction": "NNW", + "DistanceKm": 382464.56722 + } + }, + "CurrentState": { + "MoonDays": 0.89806, + "Illumination": 0.4137, + "Phase": { + "Name": "New Moon", + "NameLocalized": "Новолуние", + "Emoji": "🌑", + "IsWaxing": true + }, + "Zodiac": { + "Name": "Virgo", + "NameLocalized": "Дева", + "Emoji": "♍" + }, + "MoonPosition": { + "Time": 1735675261, + "AzimuthDegrees": 356.08557, + "AltitudeDegrees": -65.49632, + "Direction": "N", + "DistanceKm": 382316.55173 + } + }, + "EndDay": { + "MoonDays": 1.85568, + "Illumination": 2.79491, + "Phase": { + "Name": "New Moon", + "NameLocalized": "Новолуние", + "Emoji": "🌑", + "IsWaxing": true + }, + "Zodiac": { + "Name": "Virgo", + "NameLocalized": "Дева", + "Emoji": "♍" + }, + "MoonPosition": { + "Time": 1735758000, + "AzimuthDegrees": 309.21002, + "AltitudeDegrees": -54.25535, + "Direction": "NW", + "DistanceKm": 379194.7976 + } + }, + "MoonDaysDetailed": { + "Count": 2, + "Day": [ + { + "Begin": 1735618722, + "IsBeginExists": true, + "End": 1735707321, + "IsEndExists": true + }, + { + "Begin": 1735707321, + "IsBeginExists": true, + "End": 1735795276, + "IsEndExists": true + } + ] + }, + "ZodiacDetailed": { + "Count": 1, + "Zodiac": [ + { + "Name": "Virgo", + "NameLocalized": "Дева", + "Emoji": "♍", + "Begin": 1735597669, + "End": 1735813669 + } + ] + }, + "MoonRiseAndSet": { + "IsMoonRise": true, + "IsMoonSet": true, + "IsMeridian": true, + "Moonrise": { + "Time": 1735707321, + "AzimuthDegrees": 133.52155, + "AltitudeDegrees": -0.56667, + "Direction": "SE", + "DistanceKm": 381054.53085 + }, + "Moonset": { + "Time": 1735734376, + "AzimuthDegrees": 228.55471, + "AltitudeDegrees": -0.56667, + "Direction": "SW", + "DistanceKm": 380040.1844 + }, + "Meridian": { + "Time": 1735720605, + "AzimuthDegrees": 180, + "AltitudeDegrees": 13.2, + "Direction": "S", + "DistanceKm": 380550.46947 + } + } +} +``` + +
+ ---------------------------------------------------------------- ### GET /api/v1/moonPhaseCurrent @@ -347,6 +612,7 @@ This is a synonym for the moonPhaseDate method without day and time Params. |`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` |`latitude` | `float [optional, default=none]` | Latitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `51.1655` |`longitude` | `float [optional, default=none]` | Longitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `71.4272` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` #### Response @@ -370,6 +636,7 @@ This is a synonym for the moonPhaseDate method but with timestamp instead of dat |`latitude` | `float [optional, default=none]` | Latitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `51.1655` |`longitude` | `float [optional, default=none]` | Longitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `71.4272` |`timestamp` | `int [optional, default=]` | Timestamp for calculations | `1758045697` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` #### Response @@ -391,6 +658,7 @@ The method returns Moon position for specified month. |`longitude` | `float [required]` | Longitude of viewer's place. Used for moon position calculations: ```MoonDaysDetailed```, ```MoonRiseAndSet```, and ```MoonPosition``` object | `71.4272` |`year` | `int [optional, default=]` | Format: YYYY Allowed range: [1, 9999] | `2025` |`month` | `int [optional, default=]` | Format: M or MM. Allowed range: [1, 12] | `01` or `1` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` #### Response @@ -432,6 +700,9 @@ MoonPosition structure: #### Response example +
+ JSON + ```json [ { @@ -519,6 +790,82 @@ MoonPosition structure: ] ``` +
+ +
+ timeFormat '2006-01-02 15:04' + +```json +[ + { + "Date": "2023-03-01", + "IsMoonRise": true, + "IsMoonSet": true, + "IsMeridian": true, + "Moonrise": { + "Time": "2023-03-01 11:07", + "AzimuthDegrees": 42.8762, + "AltitudeDegrees": -0.56667, + "Direction": "NE", + "DistanceKm": 402636.75966 + }, + "Moonset": { + "Time": "2023-03-01 04:10", + "AzimuthDegrees": 316.74613, + "AltitudeDegrees": -0.56667, + "Direction": "NW", + "DistanceKm": 401798.89266 + }, + "Meridian": { + "Time": "2023-03-01 20:05", + "AzimuthDegrees": 180, + "AltitudeDegrees": 66.2, + "Direction": "S", + "DistanceKm": 403572.05151 + } + }, + ... +``` + +
+ + +
+ timeFormat 'timestamp' + +```json +[ + { + "Date": "2023-03-01", + "IsMoonRise": true, + "IsMoonSet": true, + "IsMeridian": true, + "Moonrise": { + "Time": 1677650857, + "AzimuthDegrees": 42.8762, + "AltitudeDegrees": -0.56667, + "Direction": "NE", + "DistanceKm": 402636.75966 + }, + "Moonset": { + "Time": 1677625807, + "AzimuthDegrees": 316.74613, + "AltitudeDegrees": -0.56667, + "Direction": "NW", + "DistanceKm": 401798.89266 + }, + "Meridian": { + "Time": 1677683130, + "AzimuthDegrees": 180, + "AltitudeDegrees": 66.2, + "Direction": "S", + "DistanceKm": 403572.05151 + } + }, + ... +``` +
+ ---------------------------------------------------------------- ### GET /api/v1/moonTableYear @@ -531,9 +878,13 @@ The method returns the moon phases for the given year. The response contains an | :--- | :--- | :--- | :--- | |`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` |`year` | `int [optional, default=]` | Format: YYYY Allowed range: [1, 9999] | `2025` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` #### Response example +
+ JSON + ```json [ // first moon of the year @@ -561,6 +912,72 @@ The method returns the moon phases for the given year. The response contains an ] ``` +
+ +
+ timeFormat '2006-01-02 15:04' + +```json +[ + // first moon of the year + { + "NewMoon": "2022-12-23 15:17", + "FirstQuarter": "2022-12-30 06:24", + "FullMoon": "2023-01-07 04:09", + "LastQuarter": "2023-01-07 04:09" + }, + // second moon of the year + { + "NewMoon": "2023-01-22 01:55", + "FirstQuarter": "2023-01-28 20:05", + "FullMoon": "2023-02-05 23:30", + "LastQuarter": "2023-02-05 23:30" + }, +... + // last moon of the year + { + "NewMoon": "2023-12-13 04:32", + "FirstQuarter": "2023-12-19 23:42", + "FullMoon": "2023-12-27 05:33", + "LastQuarter": "2023-12-27 05:33" + } +] +``` + +
+ +
+ timeFormat 'timestamp' + +```json +[ + // first moon of the year + { + "NewMoon": 1671790676, + "FirstQuarter": 1672363444, + "FullMoon": 1673046595, + "LastQuarter": 1673046595 + }, + // second moon of the year + { + "NewMoon": 1674334530, + "FirstQuarter": 1674918315, + "FullMoon": 1675621844, + "LastQuarter": 1675621844 + }, +... + // last moon of the year + { + "NewMoon": 1702423927, + "FirstQuarter": 1703011321, + "FullMoon": 1703637223, + "LastQuarter": 1703637223 + } +] +``` + +
+ ---------------------------------------------------------------- ### GET /api/v1/moonTableCurrent @@ -572,6 +989,7 @@ The method returns the moon phases for the current year. The response contains a | Parameter | Type | Description | Example Value | | :--- | :--- | :--- | :--- | |`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` #### Response @@ -662,7 +1080,7 @@ The method returns next moon phase | Parameter | Type | Description | Example Value | | :--- | :--- | :--- | :--- | |`utc` | `string [optional, default="UTC+0"]` | UTC in format `UTC+7`, `UTC+09:30`, `-3` | `UTC+4` -|`timeFormat` | `string [optional]` | Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time. | `duration` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` #### Response example: @@ -678,6 +1096,17 @@ timeFormat: `ISO` } ``` +timeFormat: `Mon, 02 Jan 2006 15:04:05` + +```json +{ + "NewMoon": "Thu, 20 Nov 2025 11:48:16", + "FirstQuarter": "Fri, 28 Nov 2025 11:44:00", + "FullMoon": "Fri, 05 Dec 2025 04:15:18", + "LastQuarter": "Fri, 12 Dec 2025 02:04:31" +} +``` + timeFormat: `timestamp` ```json diff --git a/doc/postman_collection b/doc/postman_collection index 024727e..88ad1ca 100644 --- a/doc/postman_collection +++ b/doc/postman_collection @@ -15,7 +15,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPhaseDate?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&year=2025&month=01&day=01&hour=01&minute=01&second=01", + "raw": "{{baseUrl}}/v1/moonPhaseDate?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=timestamp", "host": [ "{{baseUrl}}" ], @@ -78,6 +78,11 @@ "key": "second", "value": "01", "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] }, @@ -190,67 +195,125 @@ ], "cookie": [], "body": "{\n \"BeginDay\": {\n \"MoonDays\": 0.85568,\n \"Illumination\": 0.35744,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-01T00:00:00+05:00\",\n \"AzimuthDegrees\": 326.86255,\n \"AltitudeDegrees\": -62.68617,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 382464.56722\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 0.89806,\n \"Illumination\": 0.4137,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-01T01:01:01+05:00\",\n \"AzimuthDegrees\": 356.08557,\n \"AltitudeDegrees\": -65.49632,\n \"Direction\": \"N\",\n \"DistanceKm\": 382316.55173\n }\n },\n \"EndDay\": {\n \"MoonDays\": 1.85568,\n \"Illumination\": 2.79491,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-02T00:00:00+05:00\",\n \"AzimuthDegrees\": 309.21002,\n \"AltitudeDegrees\": -54.25535,\n \"Direction\": \"NW\",\n \"DistanceKm\": 379194.7976\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2024-12-31T09:18:42+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-01T09:55:21+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-01-01T09:55:21+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-02T10:21:16+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\",\n \"Begin\": \"2024-12-31T03:27:49+05:00\",\n \"End\": \"2025-01-02T15:27:49+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-01-01T09:55:21+05:00\",\n \"AzimuthDegrees\": 133.52155,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 381054.53085\n },\n \"Moonset\": {\n \"Time\": \"2025-01-01T17:26:16+05:00\",\n \"AzimuthDegrees\": 228.55471,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 380040.1844\n },\n \"Meridian\": {\n \"Time\": \"2025-01-01T13:36:45+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 380550.46947\n }\n }\n}" - } - ] - }, - { - "name": "Get moon phase by current date", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/v1/moonPhaseCurrent?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "v1", - "moonPhaseCurrent" - ], - "query": [ - { - "key": "utc", - "value": "UTC+5", - "description": "UTC in format UTC+7, UTC+09:30, -3" - }, + }, + { + "name": "moonPhaseDate — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPhaseDate?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=2006-01-02 15:04", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPhaseDate" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "lang", + "value": "ru", + "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "year", + "value": "2025", + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" + }, + { + "key": "month", + "value": "01", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "day", + "value": "01", + "description": "Format: D or DD. Allowed range: [1, 31]. Current, if not specified" + }, + { + "key": "hour", + "value": "01", + "description": "Format: h or hh. Allowed range: [0, 23]. Current, if not specified" + }, + { + "key": "minute", + "value": "01", + "description": "Format: m or mm. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "second", + "value": "01", + "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ { - "key": "lang", - "value": "ru", - "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + "key": "Server", + "value": "Fiber" }, { - "key": "precision", - "value": "5", - "description": "How many digits after . will be in output. Allowed range: [1, 20]" + "key": "Date", + "value": "Sun, 16 Nov 2025 18:21:08 GMT" }, { - "key": "latitude", - "value": "51.1655", - "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" }, { - "key": "longitude", - "value": "71.4272", - "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + "key": "Content-Length", + "value": "1922" } - ] + ], + "cookie": [], + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 0.85568,\n \"Illumination\": 0.35744,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-01 00:00\",\n \"AzimuthDegrees\": 326.86255,\n \"AltitudeDegrees\": -62.68617,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 382464.56722\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 0.89806,\n \"Illumination\": 0.4137,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-01 01:01\",\n \"AzimuthDegrees\": 356.08557,\n \"AltitudeDegrees\": -65.49632,\n \"Direction\": \"N\",\n \"DistanceKm\": 382316.55173\n }\n },\n \"EndDay\": {\n \"MoonDays\": 1.85568,\n \"Illumination\": 2.79491,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-01-02 00:00\",\n \"AzimuthDegrees\": 309.21002,\n \"AltitudeDegrees\": -54.25535,\n \"Direction\": \"NW\",\n \"DistanceKm\": 379194.7976\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2024-12-31 09:18\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-01 09:55\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-01-01 09:55\",\n \"IsBeginExists\": true,\n \"End\": \"2025-01-02 10:21\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\",\n \"Begin\": \"2024-12-31 03:27\",\n \"End\": \"2025-01-02 15:27\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-01-01 09:55\",\n \"AzimuthDegrees\": 133.52155,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 381054.53085\n },\n \"Moonset\": {\n \"Time\": \"2025-01-01 17:26\",\n \"AzimuthDegrees\": 228.55471,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 380040.1844\n },\n \"Meridian\": {\n \"Time\": \"2025-01-01 13:36\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 380550.46947\n }\n }\n}" }, - "description": "Detailed information:\n\n[https://github.com/prostraction/moon?tab=readme-ov-file#get-v1moonphasedate](https://github.com/prostraction/moon?tab=readme-ov-file#get-v1moonphasedate)\n\nThe method returns 6 objects:\n\n- `BeginDay`, `CurrentState`, `EndDay` objects of the `MoonStat`structure to display the position of the moon at the beginning of the day, the specified time and the end of the day, respectively;\n \n- `MoonDaysDetailed (optional)`, a structure for determining the number of lunar days on a given day;\n \n- `ZodiacDetailed (required)`, a structure for determining which \n zodiac sign the moon is in on a given time interval, when it began and ended. It contains an array for each lunar day that falls on a given Earth day;\n \n- `MoonRiseAndSet (optional)`, a structure for determining the moonrise, moonset and meridian on a given day. Missing if no coords specified. Parts of this struct may be missing." - }, - "response": [ { - "name": "moonPhaseCurrent", + "name": "moonPhaseDate — timestamp timeFormat", "originalRequest": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPhaseCurrent?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272", + "raw": "{{baseUrl}}/v1/moonPhaseDate?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=timestamp", "host": [ "{{baseUrl}}" ], "path": [ "v1", - "moonPhaseCurrent" + "moonPhaseDate" ], "query": [ { @@ -277,6 +340,41 @@ "key": "longitude", "value": "71.4272", "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "year", + "value": "2025", + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" + }, + { + "key": "month", + "value": "01", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "day", + "value": "01", + "description": "Format: D or DD. Allowed range: [1, 31]. Current, if not specified" + }, + { + "key": "hour", + "value": "01", + "description": "Format: h or hh. Allowed range: [0, 23]. Current, if not specified" + }, + { + "key": "minute", + "value": "01", + "description": "Format: m or mm. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "second", + "value": "01", + "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] } @@ -287,49 +385,42 @@ "header": [ { "key": "Server", - "value": "nginx/1.24.0 (Ubuntu)" + "value": "Fiber" }, { "key": "Date", - "value": "Tue, 04 Nov 2025 11:28:16 GMT" + "value": "Sun, 16 Nov 2025 18:20:06 GMT" }, { "key": "Content-Type", "value": "application/json", + "name": "Content-Type", "description": "", "type": "text" }, { "key": "Content-Length", - "value": "2247" - }, - { - "key": "Connection", - "value": "keep-alive" - }, - { - "key": "Keep-Alive", - "value": "timeout=900" + "value": "1826" } ], "cookie": [], - "body": "{\n \"BeginDay\": {\n \"MoonDays\": 13.27363,\n \"Illumination\": 93.79012,\n \"Phase\": {\n \"Name\": \"Waxing Gibbous\",\n \"NameLocalized\": \"Растущая Луна\",\n \"Emoji\": \"🌔\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-04T00:00:00+05:00\",\n \"AzimuthDegrees\": 215.21834,\n \"AltitudeDegrees\": 42.12633,\n \"Direction\": \"SW\",\n \"DistanceKm\": 361308.10318\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 13.75159,\n \"Illumination\": 96.27365,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-04T11:28:15+05:00\",\n \"AzimuthDegrees\": 12.34775,\n \"AltitudeDegrees\": -26.87274,\n \"Direction\": \"NNE\",\n \"DistanceKm\": 359579.90558\n }\n },\n \"EndDay\": {\n \"MoonDays\": 14.27363,\n \"Illumination\": 98.3017,\n \"Phase\": {\n \"Name\": \"Full Moon\",\n \"NameLocalized\": \"Полнолуние\",\n \"Emoji\": \"🌕\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-05T00:00:00+05:00\",\n \"AzimuthDegrees\": 200.1139,\n \"AltitudeDegrees\": 52.28516,\n \"Direction\": \"SSW\",\n \"DistanceKm\": 358145.25242\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-11-03T15:31:43+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-04T15:47:07+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-11-04T15:47:07+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-05T16:06:53+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Aquarius\",\n \"NameLocalized\": \"Водолей\",\n \"Emoji\": \"♒\",\n \"Begin\": \"2025-11-03T05:25:58+05:00\",\n \"End\": \"2025-11-05T17:25:58+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-11-04T15:47:07+05:00\",\n \"AzimuthDegrees\": 69.12186,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 359030.2047\n },\n \"Moonset\": {\n \"Time\": \"2025-11-04T05:19:33+05:00\",\n \"AzimuthDegrees\": 286.14788,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 360458.99957\n },\n \"Meridian\": {\n \"Time\": \"2025-11-04T23:08:23+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 53.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 358227.74281\n }\n }\n}" + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 0.85568,\n \"Illumination\": 0.35744,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": 1735671600,\n \"AzimuthDegrees\": 326.86255,\n \"AltitudeDegrees\": -62.68617,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 382464.56722\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 0.89806,\n \"Illumination\": 0.4137,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": 1735675261,\n \"AzimuthDegrees\": 356.08557,\n \"AltitudeDegrees\": -65.49632,\n \"Direction\": \"N\",\n \"DistanceKm\": 382316.55173\n }\n },\n \"EndDay\": {\n \"MoonDays\": 1.85568,\n \"Illumination\": 2.79491,\n \"Phase\": {\n \"Name\": \"New Moon\",\n \"NameLocalized\": \"Новолуние\",\n \"Emoji\": \"🌑\",\n \"IsWaxing\": true\n },\n \"Zodiac\": {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\"\n },\n \"MoonPosition\": {\n \"Time\": 1735758000,\n \"AzimuthDegrees\": 309.21002,\n \"AltitudeDegrees\": -54.25535,\n \"Direction\": \"NW\",\n \"DistanceKm\": 379194.7976\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": 1735618722,\n \"IsBeginExists\": true,\n \"End\": 1735707321,\n \"IsEndExists\": true\n },\n {\n \"Begin\": 1735707321,\n \"IsBeginExists\": true,\n \"End\": 1735795276,\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Virgo\",\n \"NameLocalized\": \"Дева\",\n \"Emoji\": \"♍\",\n \"Begin\": 1735597669,\n \"End\": 1735813669\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1735707321,\n \"AzimuthDegrees\": 133.52155,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 381054.53085\n },\n \"Moonset\": {\n \"Time\": 1735734376,\n \"AzimuthDegrees\": 228.55471,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 380040.1844\n },\n \"Meridian\": {\n \"Time\": 1735720605,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 380550.46947\n }\n }\n}" } ] }, { - "name": "Get moon phase by timestamp", + "name": "Get moon phase by current date", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPhaseTimestamp?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272×tamp=1758045697", + "raw": "{{baseUrl}}/v1/moonPhaseCurrent?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&timeFormat=2006-01-02 15:04", "host": [ "{{baseUrl}}" ], "path": [ "v1", - "moonPhaseTimestamp" + "moonPhaseCurrent" ], "query": [ { @@ -358,9 +449,9 @@ "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" }, { - "key": "timestamp", - "value": "1758045697", - "description": "Timestamp for calculations. Current, if not specified" + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] }, @@ -368,18 +459,18 @@ }, "response": [ { - "name": "moonPhaseTimestamp", + "name": "moonPhaseCurrent", "originalRequest": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPhaseTimestamp?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272×tamp=1758045697", + "raw": "{{baseUrl}}/v1/moonPhaseCurrent?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272", "host": [ "{{baseUrl}}" ], "path": [ "v1", - "moonPhaseTimestamp" + "moonPhaseCurrent" ], "query": [ { @@ -406,11 +497,6 @@ "key": "longitude", "value": "71.4272", "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" - }, - { - "key": "timestamp", - "value": "1758045697", - "description": "Timestamp for calculations. Current, if not specified" } ] } @@ -425,7 +511,7 @@ }, { "key": "Date", - "value": "Tue, 04 Nov 2025 11:30:48 GMT" + "value": "Tue, 04 Nov 2025 11:28:16 GMT" }, { "key": "Content-Type", @@ -435,7 +521,7 @@ }, { "key": "Content-Length", - "value": "2239" + "value": "2247" }, { "key": "Connection", @@ -447,57 +533,502 @@ } ], "cookie": [], - "body": "{\n \"BeginDay\": {\n \"MoonDays\": 23.53674,\n \"Illumination\": 38.2726,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-16T00:00:00+05:00\",\n \"AzimuthDegrees\": 57.08873,\n \"AltitudeDegrees\": 8.79646,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376559.88437\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 24.28786,\n \"Illumination\": 30.38659,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-16T18:01:37+05:00\",\n \"AzimuthDegrees\": 332.40261,\n \"AltitudeDegrees\": -8.94892,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 379152.9693\n }\n },\n \"EndDay\": {\n \"MoonDays\": 24.53674,\n \"Illumination\": 27.90729,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-17T00:00:00+05:00\",\n \"AzimuthDegrees\": 48.0345,\n \"AltitudeDegrees\": -0.14735,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380020.57994\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-09-15T22:37:45+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-09-16T23:56:10+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-09-16T23:56:10+05:00\",\n \"IsBeginExists\": true,\n \"IsEndExists\": false\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\",\n \"Begin\": \"2025-09-14T23:07:06+05:00\",\n \"End\": \"2025-09-17T11:07:06+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-09-16T23:56:10+05:00\",\n \"AzimuthDegrees\": 47.31764,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380011.3104\n },\n \"Moonset\": {\n \"Time\": \"2025-09-16T16:29:52+05:00\",\n \"AzimuthDegrees\": 314.4689,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 378931.34559\n },\n \"Meridian\": {\n \"Time\": \"2025-09-16T07:40:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 377658.15629\n }\n }\n}" - } - ] - } - ] - }, - { - "name": "Moon — Detailed information for month", - "item": [ - { - "name": "Get moon rises, moon sets and meridians monthly", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "v1", - "moonPositionMonthly" - ], - "query": [ - { - "key": "utc", - "value": "UTC+5", - "description": "UTC in format UTC+7, UTC+09:30, -3" - }, - { - "key": "latitude", - "value": "51.1655", - "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" - }, - { - "key": "longitude", - "value": "71.4272", - "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" - }, - { - "key": "year", - "value": "2023", - "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" - }, - { - "key": "month", - "value": "3", - "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" - }, - { - "key": "precision", + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 25.27363,\n \"Illumination\": 19.40433,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-16T00:00:00+05:00\",\n \"AzimuthDegrees\": 56.78112,\n \"AltitudeDegrees\": -28.52163,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400130.90444\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 26.24569,\n \"Illumination\": 12.55125,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-16T23:19:46+05:00\",\n \"AzimuthDegrees\": 38.89192,\n \"AltitudeDegrees\": -42.30857,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402829.6361\n }\n },\n \"EndDay\": {\n \"MoonDays\": 26.27363,\n \"Illumination\": 12.37382,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-17T00:00:00+05:00\",\n \"AzimuthDegrees\": 50.22016,\n \"AltitudeDegrees\": -38.1823,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402895.89872\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-11-15T02:05:18+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-16T03:16:31+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-11-16T03:16:31+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-17T04:27:24+05:00\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\",\n \"Begin\": \"2025-11-15T17:25:58+05:00\",\n \"End\": \"2025-11-18T05:25:58+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-11-16T03:16:31+05:00\",\n \"AzimuthDegrees\": 97.01341,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 400556.57352\n },\n \"Moonset\": {\n \"Time\": \"2025-11-16T14:35:54+05:00\",\n \"AzimuthDegrees\": 258.70682,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 401909.79218\n },\n \"Meridian\": {\n \"Time\": \"2025-11-16T09:03:07+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 401269.77889\n }\n }\n}" + }, + { + "name": "moonPhaseCurrent — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPhaseCurrent?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&timeFormat=2006-01-02 15:04", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPhaseCurrent" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "lang", + "value": "ru", + "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 17:54:42 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "1889" + } + ], + "cookie": [], + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 25.27363,\n \"Illumination\": 19.40433,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-16 00:00\",\n \"AzimuthDegrees\": 56.78112,\n \"AltitudeDegrees\": -28.52163,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400130.90444\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 26.24558,\n \"Illumination\": 12.55199,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-16 23:19\",\n \"AzimuthDegrees\": 38.84242,\n \"AltitudeDegrees\": -42.32378,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402829.36032\n }\n },\n \"EndDay\": {\n \"MoonDays\": 26.27363,\n \"Illumination\": 12.37382,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-11-17 00:00\",\n \"AzimuthDegrees\": 50.22016,\n \"AltitudeDegrees\": -38.1823,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402895.89872\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-11-15 02:05\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-16 03:16\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-11-16 03:16\",\n \"IsBeginExists\": true,\n \"End\": \"2025-11-17 04:27\",\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\",\n \"Begin\": \"2025-11-15 17:25\",\n \"End\": \"2025-11-18 05:25\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-11-16 03:16\",\n \"AzimuthDegrees\": 97.01341,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 400556.57352\n },\n \"Moonset\": {\n \"Time\": \"2025-11-16 14:35\",\n \"AzimuthDegrees\": 258.70682,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 401909.79218\n },\n \"Meridian\": {\n \"Time\": \"2025-11-16 09:03\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 401269.77889\n }\n }\n}" + }, + { + "name": "moonPhaseCurrent — timestamp timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPhaseCurrent?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&timeFormat=timestamp", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPhaseCurrent" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "lang", + "value": "ru", + "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 17:59:09 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "1840" + } + ], + "cookie": [], + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 25.27363,\n \"Illumination\": 19.40433,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": 1763233200,\n \"AzimuthDegrees\": 56.78112,\n \"AltitudeDegrees\": -28.52163,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400130.90444\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 26.24535,\n \"Illumination\": 12.55346,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": 1763317156,\n \"AzimuthDegrees\": 38.74335,\n \"AltitudeDegrees\": -42.35414,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402828.80872\n }\n },\n \"EndDay\": {\n \"MoonDays\": 26.27363,\n \"Illumination\": 12.37382,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\"\n },\n \"MoonPosition\": {\n \"Time\": 1763319600,\n \"AzimuthDegrees\": 50.22016,\n \"AltitudeDegrees\": -38.1823,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402895.89872\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": 1763154318,\n \"IsBeginExists\": true,\n \"End\": 1763244991,\n \"IsEndExists\": true\n },\n {\n \"Begin\": 1763244991,\n \"IsBeginExists\": true,\n \"End\": 1763335644,\n \"IsEndExists\": true\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Cancer\",\n \"NameLocalized\": \"Рак\",\n \"Emoji\": \"♋\",\n \"Begin\": 1763209558,\n \"End\": 1763425558\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1763244991,\n \"AzimuthDegrees\": 97.01341,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 400556.57352\n },\n \"Moonset\": {\n \"Time\": 1763285754,\n \"AzimuthDegrees\": 258.70682,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 401909.79218\n },\n \"Meridian\": {\n \"Time\": 1763265787,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 401269.77889\n }\n }\n}" + } + ] + }, + { + "name": "Get moon phase by timestamp", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPhaseTimestamp?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272×tamp=1758045697&timeFormat=2006-01-02 15:04", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPhaseTimestamp" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "lang", + "value": "ru", + "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "timestamp", + "value": "1758045697", + "description": "Timestamp for calculations. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + }, + "description": "Detailed information:\n\n[https://github.com/prostraction/moon?tab=readme-ov-file#get-v1moonphasedate](https://github.com/prostraction/moon?tab=readme-ov-file#get-v1moonphasedate)\n\nThe method returns 6 objects:\n\n- `BeginDay`, `CurrentState`, `EndDay` objects of the `MoonStat`structure to display the position of the moon at the beginning of the day, the specified time and the end of the day, respectively;\n \n- `MoonDaysDetailed (optional)`, a structure for determining the number of lunar days on a given day;\n \n- `ZodiacDetailed (required)`, a structure for determining which \n zodiac sign the moon is in on a given time interval, when it began and ended. It contains an array for each lunar day that falls on a given Earth day;\n \n- `MoonRiseAndSet (optional)`, a structure for determining the moonrise, moonset and meridian on a given day. Missing if no coords specified. Parts of this struct may be missing." + }, + "response": [ + { + "name": "moonPhaseTimestamp", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPhaseTimestamp?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272×tamp=1758045697", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPhaseTimestamp" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "lang", + "value": "ru", + "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "timestamp", + "value": "1758045697", + "description": "Timestamp for calculations. Current, if not specified" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "nginx/1.24.0 (Ubuntu)" + }, + { + "key": "Date", + "value": "Tue, 04 Nov 2025 11:30:48 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "2239" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Keep-Alive", + "value": "timeout=900" + } + ], + "cookie": [], + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 23.53674,\n \"Illumination\": 38.2726,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-16T00:00:00+05:00\",\n \"AzimuthDegrees\": 57.08873,\n \"AltitudeDegrees\": 8.79646,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376559.88437\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 24.28786,\n \"Illumination\": 30.38659,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-16T18:01:37+05:00\",\n \"AzimuthDegrees\": 332.40261,\n \"AltitudeDegrees\": -8.94892,\n \"Direction\": \"NNW\",\n \"DistanceKm\": 379152.9693\n }\n },\n \"EndDay\": {\n \"MoonDays\": 24.53674,\n \"Illumination\": 27.90729,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-17T00:00:00+05:00\",\n \"AzimuthDegrees\": 48.0345,\n \"AltitudeDegrees\": -0.14735,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380020.57994\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-09-15T22:37:45+05:00\",\n \"IsBeginExists\": true,\n \"End\": \"2025-09-16T23:56:10+05:00\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-09-16T23:56:10+05:00\",\n \"IsBeginExists\": true,\n \"IsEndExists\": false\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\",\n \"Begin\": \"2025-09-14T23:07:06+05:00\",\n \"End\": \"2025-09-17T11:07:06+05:00\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-09-16T23:56:10+05:00\",\n \"AzimuthDegrees\": 47.31764,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380011.3104\n },\n \"Moonset\": {\n \"Time\": \"2025-09-16T16:29:52+05:00\",\n \"AzimuthDegrees\": 314.4689,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 378931.34559\n },\n \"Meridian\": {\n \"Time\": \"2025-09-16T07:40:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 377658.15629\n }\n }\n}" + }, + { + "name": "moonPhaseTimestamp — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPhaseTimestamp?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272×tamp=1758045697&timeFormat=2006-01-02 15:04", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPhaseTimestamp" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "lang", + "value": "ru", + "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "timestamp", + "value": "1758045697", + "description": "Timestamp for calculations. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 18:23:44 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "1983" + } + ], + "cookie": [], + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 23.53674,\n \"Illumination\": 38.2726,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-16 00:00\",\n \"AzimuthDegrees\": 57.08873,\n \"AltitudeDegrees\": 8.79646,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376559.88437\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 24.49619,\n \"Illumination\": 28.30607,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-16 23:01\",\n \"AzimuthDegrees\": 36.77206,\n \"AltitudeDegrees\": -5.96783,\n \"Direction\": \"NE\",\n \"DistanceKm\": 379879.05898\n }\n },\n \"EndDay\": {\n \"MoonDays\": 24.53674,\n \"Illumination\": 27.90729,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": \"2025-09-17 00:00\",\n \"AzimuthDegrees\": 48.0345,\n \"AltitudeDegrees\": -0.14735,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380020.57994\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": \"2025-09-15 22:37\",\n \"IsBeginExists\": true,\n \"End\": \"2025-09-16 23:56\",\n \"IsEndExists\": true\n },\n {\n \"Begin\": \"2025-09-16 23:56\",\n \"IsBeginExists\": true,\n \"IsEndExists\": false\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\",\n \"Begin\": \"2025-09-14 23:07\",\n \"End\": \"2025-09-17 11:07\"\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2025-09-16 23:56\",\n \"AzimuthDegrees\": 47.31764,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380011.3104\n },\n \"Moonset\": {\n \"Time\": \"2025-09-16 16:29\",\n \"AzimuthDegrees\": 314.4689,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 378931.34559\n },\n \"Meridian\": {\n \"Time\": \"2025-09-16 07:40\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 377658.15629\n }\n }\n}" + }, + { + "name": "moonPhaseTimestamp — timestamp timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPhaseTimestamp?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272×tamp=1758045697&timeFormat=timestamp", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPhaseTimestamp" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "lang", + "value": "ru", + "description": "Values available: (\"en\", \"es\", \"fr\", \"de\", \"ru\", \"jp\")" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "timestamp", + "value": "1758045697", + "description": "Timestamp for calculations. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 18:24:22 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "1895" + } + ], + "cookie": [], + "body": "{\n \"BeginDay\": {\n \"MoonDays\": 23.53674,\n \"Illumination\": 38.2726,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": 1757962800,\n \"AzimuthDegrees\": 57.08873,\n \"AltitudeDegrees\": 8.79646,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376559.88437\n }\n },\n \"CurrentState\": {\n \"MoonDays\": 24.49619,\n \"Illumination\": 28.30607,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": 1758045697,\n \"AzimuthDegrees\": 36.77206,\n \"AltitudeDegrees\": -5.96783,\n \"Direction\": \"NE\",\n \"DistanceKm\": 379879.05898\n }\n },\n \"EndDay\": {\n \"MoonDays\": 24.53674,\n \"Illumination\": 27.90729,\n \"Phase\": {\n \"Name\": \"Waning Crescent\",\n \"NameLocalized\": \"Убывающий серп\",\n \"Emoji\": \"🌘\",\n \"IsWaxing\": false\n },\n \"Zodiac\": {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\"\n },\n \"MoonPosition\": {\n \"Time\": 1758049200,\n \"AzimuthDegrees\": 48.0345,\n \"AltitudeDegrees\": -0.14735,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380020.57994\n }\n },\n \"MoonDaysDetailed\": {\n \"Count\": 2,\n \"Day\": [\n {\n \"Begin\": 1757957865,\n \"IsBeginExists\": true,\n \"End\": 1758048970,\n \"IsEndExists\": true\n },\n {\n \"Begin\": 1758048970,\n \"IsBeginExists\": true,\n \"IsEndExists\": false\n }\n ]\n },\n \"ZodiacDetailed\": {\n \"Count\": 1,\n \"Zodiac\": [\n {\n \"Name\": \"Gemini\",\n \"NameLocalized\": \"Близнецы\",\n \"Emoji\": \"♊\",\n \"Begin\": 1757873226,\n \"End\": 1758089226\n }\n ]\n },\n \"MoonRiseAndSet\": {\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1758048970,\n \"AzimuthDegrees\": 47.31764,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 380011.3104\n },\n \"Moonset\": {\n \"Time\": 1758022192,\n \"AzimuthDegrees\": 314.4689,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 378931.34559\n },\n \"Meridian\": {\n \"Time\": 1757990436,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 377658.15629\n }\n }\n}" + } + ] + } + ] + }, + { + "name": "Moon — Detailed information for month", + "item": [ + { + "name": "Get moon rises, moon sets and meridians monthly", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPositionMonthly" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "year", + "value": "2023", + "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" + }, + { + "key": "month", + "value": "3", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "precision", "value": "5", "description": "How many digits after . will be in output. Allowed range: [1, 20]" } @@ -506,12 +1037,92 @@ }, "response": [ { - "name": "moonPositionMonthly — ok", + "name": "moonPositionMonthly — ok", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPositionMonthly" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "year", + "value": "2023", + "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" + }, + { + "key": "month", + "value": "3", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 15:25:52 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "17334" + } + ], + "cookie": [ + { + "expires": "Invalid Date", + "domain": "", + "path": "" + } + ], + "body": "[\n {\n \"Date\": \"2023-03-01\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-01T11:07:37+05:00\",\n \"AzimuthDegrees\": 42.8762,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402636.75966\n },\n \"Moonset\": {\n \"Time\": \"2023-03-01T04:10:07+05:00\",\n \"AzimuthDegrees\": 316.74613,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 401798.89266\n },\n \"Meridian\": {\n \"Time\": \"2023-03-01T20:05:30+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 403572.05151\n }\n },\n {\n \"Date\": \"2023-03-02\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-02T12:01:07+05:00\",\n \"AzimuthDegrees\": 43.38104,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404836.13007\n },\n \"Moonset\": {\n \"Time\": \"2023-03-02T05:03:00+05:00\",\n \"AzimuthDegrees\": 317.04922,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404345.126\n },\n \"Meridian\": {\n \"Time\": \"2023-03-02T20:56:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405326.66664\n }\n },\n {\n \"Date\": \"2023-03-03\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-03T13:04:19+05:00\",\n \"AzimuthDegrees\": 46.62247,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405833.52922\n },\n \"Moonset\": {\n \"Time\": \"2023-03-03T05:43:39+05:00\",\n \"AzimuthDegrees\": 314.58446,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405661.87336\n },\n \"Meridian\": {\n \"Time\": \"2023-03-03T21:46:07+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 63.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405914.02206\n }\n },\n {\n \"Date\": \"2023-03-04\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-04T14:13:38+05:00\",\n \"AzimuthDegrees\": 52.09931,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405724.54322\n },\n \"Moonset\": {\n \"Time\": \"2023-03-04T06:13:52+05:00\",\n \"AzimuthDegrees\": 309.84464,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405870.41007\n },\n \"Meridian\": {\n \"Time\": \"2023-03-04T22:33:27+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 60.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 405470.71269\n }\n },\n {\n \"Date\": \"2023-03-05\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-05T15:25:33+05:00\",\n \"AzimuthDegrees\": 59.20503,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 404665.50003\n },\n \"Moonset\": {\n \"Time\": \"2023-03-05T06:36:23+05:00\",\n \"AzimuthDegrees\": 303.43187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 405132.86805\n },\n \"Meridian\": {\n \"Time\": \"2023-03-05T23:18:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 55.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 404168.13916\n }\n },\n {\n \"Date\": \"2023-03-06\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": false,\n \"Moonrise\": {\n \"Time\": \"2023-03-06T16:37:52+05:00\",\n \"AzimuthDegrees\": 67.43208,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 402838.67123\n },\n \"Moonset\": {\n \"Time\": \"2023-03-06T06:53:44+05:00\",\n \"AzimuthDegrees\": 295.85243,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 403623.69367\n }\n },\n {\n \"Date\": \"2023-03-07\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-07T17:49:47+05:00\",\n \"AzimuthDegrees\": 76.41149,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400418.46475\n },\n \"Moonset\": {\n \"Time\": \"2023-03-07T07:07:49+05:00\",\n \"AzimuthDegrees\": 287.47458,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 401508.60505\n },\n \"Meridian\": {\n \"Time\": \"2023-03-07T00:01:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 50.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 402183.01801\n }\n },\n {\n \"Date\": \"2023-03-08\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-08T19:01:36+05:00\",\n \"AzimuthDegrees\": 85.87566,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"E\",\n \"DistanceKm\": 397547.14825\n },\n \"Moonset\": {\n \"Time\": \"2023-03-08T07:20:00+05:00\",\n \"AzimuthDegrees\": 278.56217,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 398928.36539\n },\n \"Meridian\": {\n \"Time\": \"2023-03-08T00:43:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 45.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 399673.27308\n }\n },\n {\n \"Date\": \"2023-03-09\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-09T20:14:11+05:00\",\n \"AzimuthDegrees\": 95.60853,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 394323.19311\n },\n \"Moonset\": {\n \"Time\": \"2023-03-09T07:31:23+05:00\",\n \"AzimuthDegrees\": 269.31953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 395988.3718\n },\n \"Meridian\": {\n \"Time\": \"2023-03-09T01:24:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 396761.88573\n }\n },\n {\n \"Date\": \"2023-03-10\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-10T21:28:42+05:00\",\n \"AzimuthDegrees\": 105.39738,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 390803.12388\n },\n \"Moonset\": {\n \"Time\": \"2023-03-10T07:42:57+05:00\",\n \"AzimuthDegrees\": 259.93073,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 392756.20131\n },\n \"Meridian\": {\n \"Time\": \"2023-03-10T02:05:44+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 33.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 393530.65092\n }\n },\n {\n \"Date\": \"2023-03-11\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-11T22:46:09+05:00\",\n \"AzimuthDegrees\": 114.97706,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 387016.85411\n },\n \"Moonset\": {\n \"Time\": \"2023-03-11T07:55:44+05:00\",\n \"AzimuthDegrees\": 250.59757,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 389268.48137\n },\n \"Meridian\": {\n \"Time\": \"2023-03-11T02:48:49+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 27.5,\n \"Direction\": \"S\",\n \"DistanceKm\": 390025.04685\n }\n },\n {\n \"Date\": \"2023-03-12\",\n \"IsMoonRise\": false,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonset\": {\n \"Time\": \"2023-03-12T08:11:09+05:00\",\n \"AzimuthDegrees\": 241.58881,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 385546.82937\n },\n \"Meridian\": {\n \"Time\": \"2023-03-12T03:34:35+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 21.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 386270.22231\n }\n },\n {\n \"Date\": \"2023-03-13\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-13T00:06:52+05:00\",\n \"AzimuthDegrees\": 123.94721,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 382995.32957\n },\n \"Moonset\": {\n \"Time\": \"2023-03-13T08:31:18+05:00\",\n \"AzimuthDegrees\": 233.31784,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 381620.77804\n },\n \"Meridian\": {\n \"Time\": \"2023-03-13T04:24:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 17,\n \"Direction\": \"S\",\n \"DistanceKm\": 382296.46854\n }\n },\n {\n \"Date\": \"2023-03-14\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-14T01:29:17+05:00\",\n \"AzimuthDegrees\": 131.64699,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 378807.06957\n },\n \"Moonset\": {\n \"Time\": \"2023-03-14T08:59:32+05:00\",\n \"AzimuthDegrees\": 226.46039,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 377554.30425\n },\n \"Meridian\": {\n \"Time\": \"2023-03-14T05:18:05+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 378170.5856\n }\n },\n {\n \"Date\": \"2023-03-15\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-15T02:48:45+05:00\",\n \"AzimuthDegrees\": 137.04254,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 374594.67251\n },\n \"Moonset\": {\n \"Time\": \"2023-03-15T09:40:38+05:00\",\n \"AzimuthDegrees\": 222.05405,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 373473.15308\n },\n \"Meridian\": {\n \"Time\": \"2023-03-15T06:16:32+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 374026.97617\n }\n },\n {\n \"Date\": \"2023-03-16\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-16T03:57:52+05:00\",\n \"AzimuthDegrees\": 138.9114,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 370591.50029\n },\n \"Moonset\": {\n \"Time\": \"2023-03-16T10:39:26+05:00\",\n \"AzimuthDegrees\": 221.29657,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 369592.67725\n },\n \"Meridian\": {\n \"Time\": \"2023-03-16T07:18:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 370088.60593\n }\n },\n {\n \"Date\": \"2023-03-17\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-17T04:51:03+05:00\",\n \"AzimuthDegrees\": 136.61246,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 367098.86901\n },\n \"Moonset\": {\n \"Time\": \"2023-03-17T11:56:14+05:00\",\n \"AzimuthDegrees\": 224.76797,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 366237.19817\n },\n \"Meridian\": {\n \"Time\": \"2023-03-17T08:20:51+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 11.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 366664.93323\n }\n },\n {\n \"Date\": \"2023-03-18\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-18T05:28:46+05:00\",\n \"AzimuthDegrees\": 130.65168,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 364441.40172\n },\n \"Moonset\": {\n \"Time\": \"2023-03-18T13:24:50+05:00\",\n \"AzimuthDegrees\": 231.87919,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 363812.28579\n },\n \"Meridian\": {\n \"Time\": \"2023-03-18T09:21:59+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 14.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364117.71582\n }\n },\n {\n \"Date\": \"2023-03-19\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-19T05:55:20+05:00\",\n \"AzimuthDegrees\": 122.15123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362929.11813\n },\n \"Moonset\": {\n \"Time\": \"2023-03-19T14:57:13+05:00\",\n \"AzimuthDegrees\": 241.42982,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 362712.81832\n },\n \"Meridian\": {\n \"Time\": \"2023-03-19T10:19:54+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 19.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 362798.79835\n }\n },\n {\n \"Date\": \"2023-03-20\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-20T06:15:02+05:00\",\n \"AzimuthDegrees\": 112.14362,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362812.63242\n },\n \"Moonset\": {\n \"Time\": \"2023-03-20T16:28:22+05:00\",\n \"AzimuthDegrees\": 252.32977,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 363219.08698\n },\n \"Meridian\": {\n \"Time\": \"2023-03-20T11:14:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 25.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 362976.42916\n }\n },\n {\n \"Date\": \"2023-03-21\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-21T06:30:54+05:00\",\n \"AzimuthDegrees\": 101.3864,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 364227.26916\n },\n \"Moonset\": {\n \"Time\": \"2023-03-21T17:56:40+05:00\",\n \"AzimuthDegrees\": 263.78338,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 365425.30425\n },\n \"Meridian\": {\n \"Time\": \"2023-03-21T12:05:25+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364770.21284\n }\n },\n {\n \"Date\": \"2023-03-22\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-22T06:44:57+05:00\",\n \"AzimuthDegrees\": 90.43001,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 367149.18373\n },\n \"Moonset\": {\n \"Time\": \"2023-03-22T19:22:27+05:00\",\n \"AzimuthDegrees\": 275.21269,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 369210.60777\n },\n \"Meridian\": {\n \"Time\": \"2023-03-22T12:54:37+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 368112.88854\n }\n },\n {\n \"Date\": \"2023-03-23\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-23T06:58:42+05:00\",\n \"AzimuthDegrees\": 79.70263,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 371383.43121\n },\n \"Moonset\": {\n \"Time\": \"2023-03-23T20:46:38+05:00\",\n \"AzimuthDegrees\": 286.16029,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 374253.93367\n },\n \"Meridian\": {\n \"Time\": \"2023-03-23T13:43:00+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 46.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 372750.53254\n }\n },\n {\n \"Date\": \"2023-03-24\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-24T07:13:32+05:00\",\n \"AzimuthDegrees\": 69.57123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376588.54488\n },\n \"Moonset\": {\n \"Time\": \"2023-03-24T22:09:45+05:00\",\n \"AzimuthDegrees\": 296.21187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 380086.26614\n },\n \"Meridian\": {\n \"Time\": \"2023-03-24T14:31:39+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 52.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 378280.3871\n }\n },\n {\n \"Date\": \"2023-03-25\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-25T07:30:56+05:00\",\n \"AzimuthDegrees\": 60.39425,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 382327.95573\n },\n \"Moonset\": {\n \"Time\": \"2023-03-25T23:31:14+05:00\",\n \"AzimuthDegrees\": 304.92579,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 386164.32159\n },\n \"Meridian\": {\n \"Time\": \"2023-03-25T15:21:19+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 58.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 384213.14123\n }\n },\n {\n \"Date\": \"2023-03-26\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": false,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-26T07:52:49+05:00\",\n \"AzimuthDegrees\": 52.57818,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 388131.21721\n },\n \"Meridian\": {\n \"Time\": \"2023-03-26T16:12:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 62.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 390041.63582\n }\n },\n {\n \"Date\": \"2023-03-27\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-27T08:21:40+05:00\",\n \"AzimuthDegrees\": 46.62563,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 393548.92562\n },\n \"Moonset\": {\n \"Time\": \"2023-03-27T00:48:54+05:00\",\n \"AzimuthDegrees\": 311.77602,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 391948.19405\n },\n \"Meridian\": {\n \"Time\": \"2023-03-27T17:04:41+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65,\n \"Direction\": \"S\",\n \"DistanceKm\": 395301.62304\n }\n },\n {\n \"Date\": \"2023-03-28\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-28T09:00:10+05:00\",\n \"AzimuthDegrees\": 43.11087,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 398191.74953\n },\n \"Moonset\": {\n \"Time\": \"2023-03-28T01:59:01+05:00\",\n \"AzimuthDegrees\": 316.17036,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 396971.11487\n },\n \"Meridian\": {\n \"Time\": \"2023-03-28T17:57:26+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 399617.2651\n }\n },\n {\n \"Date\": \"2023-03-29\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-29T09:50:05+05:00\",\n \"AzimuthDegrees\": 42.48507,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 401753.75503\n },\n \"Moonset\": {\n \"Time\": \"2023-03-29T02:57:44+05:00\",\n \"AzimuthDegrees\": 317.64261,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 400892.49384\n },\n \"Meridian\": {\n \"Time\": \"2023-03-29T18:49:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 402729.21577\n }\n },\n {\n \"Date\": \"2023-03-30\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-30T10:50:38+05:00\",\n \"AzimuthDegrees\": 44.77953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404030.19632\n },\n \"Moonset\": {\n \"Time\": \"2023-03-30T03:43:20+05:00\",\n \"AzimuthDegrees\": 316.15045,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 403521.433\n },\n \"Meridian\": {\n \"Time\": \"2023-03-30T19:40:09+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 64.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 404505.37243\n }\n },\n {\n \"Date\": \"2023-03-31\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-31T11:58:33+05:00\",\n \"AzimuthDegrees\": 49.55137,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404935.96867\n },\n \"Moonset\": {\n \"Time\": \"2023-03-31T04:17:06+05:00\",\n \"AzimuthDegrees\": 312.1322,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404804.48186\n },\n \"Meridian\": {\n \"Time\": \"2023-03-31T20:28:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 61.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 404936.05398\n }\n }\n]" + }, + { + "name": "moonPositionMonthly — error", "originalRequest": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5", + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=9999&month=3&precision=5", "host": [ "{{baseUrl}}" ], @@ -537,7 +1148,7 @@ }, { "key": "year", - "value": "2023", + "value": "9999", "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" }, { @@ -553,8 +1164,8 @@ ] } }, - "status": "OK", - "code": 200, + "status": "Bad Request", + "code": 400, "_postman_previewlanguage": "json", "header": [ { @@ -563,35 +1174,107 @@ }, { "key": "Date", - "value": "Sun, 16 Nov 2025 15:25:52 GMT" + "value": "Sun, 16 Nov 2025 15:26:10 GMT" }, { "key": "Content-Type", "value": "application/json", + "name": "Content-Type", "description": "", "type": "text" }, { "key": "Content-Length", - "value": "17334" + "value": "136" } ], - "cookie": [ + "cookie": [], + "body": "{\n \"Status\": 400,\n \"Message\": \"[400 Bad Request] Invalid parameter format: ephemeris segment only covers dates 1899-07-29 through 2053-10-09\"\n}" + }, + { + "name": "moonPositionMonthly — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5&timeFormat=2006-01-02 15:04", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonPositionMonthly" + ], + "query": [ + { + "key": "utc", + "value": "UTC+5", + "description": "UTC in format UTC+7, UTC+09:30, -3" + }, + { + "key": "latitude", + "value": "51.1655", + "description": "Latitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "longitude", + "value": "71.4272", + "description": "Longitude of viewer's place. Used for moon position calculations: MoonDaysDetailed, MoonRiseAndSet, and MoonPosition object" + }, + { + "key": "year", + "value": "2023", + "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" + }, + { + "key": "month", + "value": "3", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": [ { - "expires": "Invalid Date", - "domain": "", - "path": "" + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 18:28:02 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "14184" } ], - "body": "[\n {\n \"Date\": \"2023-03-01\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-01T11:07:37+05:00\",\n \"AzimuthDegrees\": 42.8762,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402636.75966\n },\n \"Moonset\": {\n \"Time\": \"2023-03-01T04:10:07+05:00\",\n \"AzimuthDegrees\": 316.74613,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 401798.89266\n },\n \"Meridian\": {\n \"Time\": \"2023-03-01T20:05:30+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 403572.05151\n }\n },\n {\n \"Date\": \"2023-03-02\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-02T12:01:07+05:00\",\n \"AzimuthDegrees\": 43.38104,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404836.13007\n },\n \"Moonset\": {\n \"Time\": \"2023-03-02T05:03:00+05:00\",\n \"AzimuthDegrees\": 317.04922,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404345.126\n },\n \"Meridian\": {\n \"Time\": \"2023-03-02T20:56:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405326.66664\n }\n },\n {\n \"Date\": \"2023-03-03\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-03T13:04:19+05:00\",\n \"AzimuthDegrees\": 46.62247,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405833.52922\n },\n \"Moonset\": {\n \"Time\": \"2023-03-03T05:43:39+05:00\",\n \"AzimuthDegrees\": 314.58446,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405661.87336\n },\n \"Meridian\": {\n \"Time\": \"2023-03-03T21:46:07+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 63.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405914.02206\n }\n },\n {\n \"Date\": \"2023-03-04\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-04T14:13:38+05:00\",\n \"AzimuthDegrees\": 52.09931,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405724.54322\n },\n \"Moonset\": {\n \"Time\": \"2023-03-04T06:13:52+05:00\",\n \"AzimuthDegrees\": 309.84464,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405870.41007\n },\n \"Meridian\": {\n \"Time\": \"2023-03-04T22:33:27+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 60.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 405470.71269\n }\n },\n {\n \"Date\": \"2023-03-05\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-05T15:25:33+05:00\",\n \"AzimuthDegrees\": 59.20503,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 404665.50003\n },\n \"Moonset\": {\n \"Time\": \"2023-03-05T06:36:23+05:00\",\n \"AzimuthDegrees\": 303.43187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 405132.86805\n },\n \"Meridian\": {\n \"Time\": \"2023-03-05T23:18:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 55.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 404168.13916\n }\n },\n {\n \"Date\": \"2023-03-06\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": false,\n \"Moonrise\": {\n \"Time\": \"2023-03-06T16:37:52+05:00\",\n \"AzimuthDegrees\": 67.43208,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 402838.67123\n },\n \"Moonset\": {\n \"Time\": \"2023-03-06T06:53:44+05:00\",\n \"AzimuthDegrees\": 295.85243,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 403623.69367\n }\n },\n {\n \"Date\": \"2023-03-07\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-07T17:49:47+05:00\",\n \"AzimuthDegrees\": 76.41149,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400418.46475\n },\n \"Moonset\": {\n \"Time\": \"2023-03-07T07:07:49+05:00\",\n \"AzimuthDegrees\": 287.47458,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 401508.60505\n },\n \"Meridian\": {\n \"Time\": \"2023-03-07T00:01:28+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 50.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 402183.01801\n }\n },\n {\n \"Date\": \"2023-03-08\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-08T19:01:36+05:00\",\n \"AzimuthDegrees\": 85.87566,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"E\",\n \"DistanceKm\": 397547.14825\n },\n \"Moonset\": {\n \"Time\": \"2023-03-08T07:20:00+05:00\",\n \"AzimuthDegrees\": 278.56217,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 398928.36539\n },\n \"Meridian\": {\n \"Time\": \"2023-03-08T00:43:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 45.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 399673.27308\n }\n },\n {\n \"Date\": \"2023-03-09\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-09T20:14:11+05:00\",\n \"AzimuthDegrees\": 95.60853,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 394323.19311\n },\n \"Moonset\": {\n \"Time\": \"2023-03-09T07:31:23+05:00\",\n \"AzimuthDegrees\": 269.31953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 395988.3718\n },\n \"Meridian\": {\n \"Time\": \"2023-03-09T01:24:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 396761.88573\n }\n },\n {\n \"Date\": \"2023-03-10\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-10T21:28:42+05:00\",\n \"AzimuthDegrees\": 105.39738,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 390803.12388\n },\n \"Moonset\": {\n \"Time\": \"2023-03-10T07:42:57+05:00\",\n \"AzimuthDegrees\": 259.93073,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 392756.20131\n },\n \"Meridian\": {\n \"Time\": \"2023-03-10T02:05:44+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 33.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 393530.65092\n }\n },\n {\n \"Date\": \"2023-03-11\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-11T22:46:09+05:00\",\n \"AzimuthDegrees\": 114.97706,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 387016.85411\n },\n \"Moonset\": {\n \"Time\": \"2023-03-11T07:55:44+05:00\",\n \"AzimuthDegrees\": 250.59757,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 389268.48137\n },\n \"Meridian\": {\n \"Time\": \"2023-03-11T02:48:49+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 27.5,\n \"Direction\": \"S\",\n \"DistanceKm\": 390025.04685\n }\n },\n {\n \"Date\": \"2023-03-12\",\n \"IsMoonRise\": false,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonset\": {\n \"Time\": \"2023-03-12T08:11:09+05:00\",\n \"AzimuthDegrees\": 241.58881,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 385546.82937\n },\n \"Meridian\": {\n \"Time\": \"2023-03-12T03:34:35+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 21.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 386270.22231\n }\n },\n {\n \"Date\": \"2023-03-13\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-13T00:06:52+05:00\",\n \"AzimuthDegrees\": 123.94721,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 382995.32957\n },\n \"Moonset\": {\n \"Time\": \"2023-03-13T08:31:18+05:00\",\n \"AzimuthDegrees\": 233.31784,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 381620.77804\n },\n \"Meridian\": {\n \"Time\": \"2023-03-13T04:24:06+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 17,\n \"Direction\": \"S\",\n \"DistanceKm\": 382296.46854\n }\n },\n {\n \"Date\": \"2023-03-14\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-14T01:29:17+05:00\",\n \"AzimuthDegrees\": 131.64699,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 378807.06957\n },\n \"Moonset\": {\n \"Time\": \"2023-03-14T08:59:32+05:00\",\n \"AzimuthDegrees\": 226.46039,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 377554.30425\n },\n \"Meridian\": {\n \"Time\": \"2023-03-14T05:18:05+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 378170.5856\n }\n },\n {\n \"Date\": \"2023-03-15\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-15T02:48:45+05:00\",\n \"AzimuthDegrees\": 137.04254,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 374594.67251\n },\n \"Moonset\": {\n \"Time\": \"2023-03-15T09:40:38+05:00\",\n \"AzimuthDegrees\": 222.05405,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 373473.15308\n },\n \"Meridian\": {\n \"Time\": \"2023-03-15T06:16:32+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 374026.97617\n }\n },\n {\n \"Date\": \"2023-03-16\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-16T03:57:52+05:00\",\n \"AzimuthDegrees\": 138.9114,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 370591.50029\n },\n \"Moonset\": {\n \"Time\": \"2023-03-16T10:39:26+05:00\",\n \"AzimuthDegrees\": 221.29657,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 369592.67725\n },\n \"Meridian\": {\n \"Time\": \"2023-03-16T07:18:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 370088.60593\n }\n },\n {\n \"Date\": \"2023-03-17\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-17T04:51:03+05:00\",\n \"AzimuthDegrees\": 136.61246,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 367098.86901\n },\n \"Moonset\": {\n \"Time\": \"2023-03-17T11:56:14+05:00\",\n \"AzimuthDegrees\": 224.76797,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 366237.19817\n },\n \"Meridian\": {\n \"Time\": \"2023-03-17T08:20:51+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 11.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 366664.93323\n }\n },\n {\n \"Date\": \"2023-03-18\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-18T05:28:46+05:00\",\n \"AzimuthDegrees\": 130.65168,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 364441.40172\n },\n \"Moonset\": {\n \"Time\": \"2023-03-18T13:24:50+05:00\",\n \"AzimuthDegrees\": 231.87919,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 363812.28579\n },\n \"Meridian\": {\n \"Time\": \"2023-03-18T09:21:59+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 14.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364117.71582\n }\n },\n {\n \"Date\": \"2023-03-19\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-19T05:55:20+05:00\",\n \"AzimuthDegrees\": 122.15123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362929.11813\n },\n \"Moonset\": {\n \"Time\": \"2023-03-19T14:57:13+05:00\",\n \"AzimuthDegrees\": 241.42982,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 362712.81832\n },\n \"Meridian\": {\n \"Time\": \"2023-03-19T10:19:54+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 19.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 362798.79835\n }\n },\n {\n \"Date\": \"2023-03-20\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-20T06:15:02+05:00\",\n \"AzimuthDegrees\": 112.14362,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362812.63242\n },\n \"Moonset\": {\n \"Time\": \"2023-03-20T16:28:22+05:00\",\n \"AzimuthDegrees\": 252.32977,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 363219.08698\n },\n \"Meridian\": {\n \"Time\": \"2023-03-20T11:14:12+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 25.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 362976.42916\n }\n },\n {\n \"Date\": \"2023-03-21\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-21T06:30:54+05:00\",\n \"AzimuthDegrees\": 101.3864,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 364227.26916\n },\n \"Moonset\": {\n \"Time\": \"2023-03-21T17:56:40+05:00\",\n \"AzimuthDegrees\": 263.78338,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 365425.30425\n },\n \"Meridian\": {\n \"Time\": \"2023-03-21T12:05:25+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364770.21284\n }\n },\n {\n \"Date\": \"2023-03-22\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-22T06:44:57+05:00\",\n \"AzimuthDegrees\": 90.43001,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 367149.18373\n },\n \"Moonset\": {\n \"Time\": \"2023-03-22T19:22:27+05:00\",\n \"AzimuthDegrees\": 275.21269,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 369210.60777\n },\n \"Meridian\": {\n \"Time\": \"2023-03-22T12:54:37+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 368112.88854\n }\n },\n {\n \"Date\": \"2023-03-23\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-23T06:58:42+05:00\",\n \"AzimuthDegrees\": 79.70263,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 371383.43121\n },\n \"Moonset\": {\n \"Time\": \"2023-03-23T20:46:38+05:00\",\n \"AzimuthDegrees\": 286.16029,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 374253.93367\n },\n \"Meridian\": {\n \"Time\": \"2023-03-23T13:43:00+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 46.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 372750.53254\n }\n },\n {\n \"Date\": \"2023-03-24\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-24T07:13:32+05:00\",\n \"AzimuthDegrees\": 69.57123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376588.54488\n },\n \"Moonset\": {\n \"Time\": \"2023-03-24T22:09:45+05:00\",\n \"AzimuthDegrees\": 296.21187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 380086.26614\n },\n \"Meridian\": {\n \"Time\": \"2023-03-24T14:31:39+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 52.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 378280.3871\n }\n },\n {\n \"Date\": \"2023-03-25\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-25T07:30:56+05:00\",\n \"AzimuthDegrees\": 60.39425,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 382327.95573\n },\n \"Moonset\": {\n \"Time\": \"2023-03-25T23:31:14+05:00\",\n \"AzimuthDegrees\": 304.92579,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 386164.32159\n },\n \"Meridian\": {\n \"Time\": \"2023-03-25T15:21:19+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 58.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 384213.14123\n }\n },\n {\n \"Date\": \"2023-03-26\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": false,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-26T07:52:49+05:00\",\n \"AzimuthDegrees\": 52.57818,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 388131.21721\n },\n \"Meridian\": {\n \"Time\": \"2023-03-26T16:12:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 62.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 390041.63582\n }\n },\n {\n \"Date\": \"2023-03-27\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-27T08:21:40+05:00\",\n \"AzimuthDegrees\": 46.62563,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 393548.92562\n },\n \"Moonset\": {\n \"Time\": \"2023-03-27T00:48:54+05:00\",\n \"AzimuthDegrees\": 311.77602,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 391948.19405\n },\n \"Meridian\": {\n \"Time\": \"2023-03-27T17:04:41+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65,\n \"Direction\": \"S\",\n \"DistanceKm\": 395301.62304\n }\n },\n {\n \"Date\": \"2023-03-28\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-28T09:00:10+05:00\",\n \"AzimuthDegrees\": 43.11087,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 398191.74953\n },\n \"Moonset\": {\n \"Time\": \"2023-03-28T01:59:01+05:00\",\n \"AzimuthDegrees\": 316.17036,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 396971.11487\n },\n \"Meridian\": {\n \"Time\": \"2023-03-28T17:57:26+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 399617.2651\n }\n },\n {\n \"Date\": \"2023-03-29\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-29T09:50:05+05:00\",\n \"AzimuthDegrees\": 42.48507,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 401753.75503\n },\n \"Moonset\": {\n \"Time\": \"2023-03-29T02:57:44+05:00\",\n \"AzimuthDegrees\": 317.64261,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 400892.49384\n },\n \"Meridian\": {\n \"Time\": \"2023-03-29T18:49:36+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 402729.21577\n }\n },\n {\n \"Date\": \"2023-03-30\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-30T10:50:38+05:00\",\n \"AzimuthDegrees\": 44.77953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404030.19632\n },\n \"Moonset\": {\n \"Time\": \"2023-03-30T03:43:20+05:00\",\n \"AzimuthDegrees\": 316.15045,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 403521.433\n },\n \"Meridian\": {\n \"Time\": \"2023-03-30T19:40:09+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 64.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 404505.37243\n }\n },\n {\n \"Date\": \"2023-03-31\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-31T11:58:33+05:00\",\n \"AzimuthDegrees\": 49.55137,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404935.96867\n },\n \"Moonset\": {\n \"Time\": \"2023-03-31T04:17:06+05:00\",\n \"AzimuthDegrees\": 312.1322,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404804.48186\n },\n \"Meridian\": {\n \"Time\": \"2023-03-31T20:28:24+05:00\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 61.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 404936.05398\n }\n }\n]" + "cookie": [], + "body": "[\n {\n \"Date\": \"2023-03-01\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-01 11:07\",\n \"AzimuthDegrees\": 42.8762,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402636.75966\n },\n \"Moonset\": {\n \"Time\": \"2023-03-01 04:10\",\n \"AzimuthDegrees\": 316.74613,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 401798.89266\n },\n \"Meridian\": {\n \"Time\": \"2023-03-01 20:05\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 403572.05151\n }\n },\n {\n \"Date\": \"2023-03-02\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-02 12:01\",\n \"AzimuthDegrees\": 43.38104,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404836.13007\n },\n \"Moonset\": {\n \"Time\": \"2023-03-02 05:03\",\n \"AzimuthDegrees\": 317.04922,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404345.126\n },\n \"Meridian\": {\n \"Time\": \"2023-03-02 20:56\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405326.66664\n }\n },\n {\n \"Date\": \"2023-03-03\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-03 13:04\",\n \"AzimuthDegrees\": 46.62247,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405833.52922\n },\n \"Moonset\": {\n \"Time\": \"2023-03-03 05:43\",\n \"AzimuthDegrees\": 314.58446,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405661.87336\n },\n \"Meridian\": {\n \"Time\": \"2023-03-03 21:46\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 63.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405914.02206\n }\n },\n {\n \"Date\": \"2023-03-04\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-04 14:13\",\n \"AzimuthDegrees\": 52.09931,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405724.54322\n },\n \"Moonset\": {\n \"Time\": \"2023-03-04 06:13\",\n \"AzimuthDegrees\": 309.84464,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405870.41007\n },\n \"Meridian\": {\n \"Time\": \"2023-03-04 22:33\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 60.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 405470.71269\n }\n },\n {\n \"Date\": \"2023-03-05\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-05 15:25\",\n \"AzimuthDegrees\": 59.20503,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 404665.50003\n },\n \"Moonset\": {\n \"Time\": \"2023-03-05 06:36\",\n \"AzimuthDegrees\": 303.43187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 405132.86805\n },\n \"Meridian\": {\n \"Time\": \"2023-03-05 23:18\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 55.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 404168.13916\n }\n },\n {\n \"Date\": \"2023-03-06\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": false,\n \"Moonrise\": {\n \"Time\": \"2023-03-06 16:37\",\n \"AzimuthDegrees\": 67.43208,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 402838.67123\n },\n \"Moonset\": {\n \"Time\": \"2023-03-06 06:53\",\n \"AzimuthDegrees\": 295.85243,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 403623.69367\n }\n },\n {\n \"Date\": \"2023-03-07\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-07 17:49\",\n \"AzimuthDegrees\": 76.41149,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400418.46475\n },\n \"Moonset\": {\n \"Time\": \"2023-03-07 07:07\",\n \"AzimuthDegrees\": 287.47458,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 401508.60505\n },\n \"Meridian\": {\n \"Time\": \"2023-03-07 00:01\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 50.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 402183.01801\n }\n },\n {\n \"Date\": \"2023-03-08\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-08 19:01\",\n \"AzimuthDegrees\": 85.87566,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"E\",\n \"DistanceKm\": 397547.14825\n },\n \"Moonset\": {\n \"Time\": \"2023-03-08 07:20\",\n \"AzimuthDegrees\": 278.56217,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 398928.36539\n },\n \"Meridian\": {\n \"Time\": \"2023-03-08 00:43\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 45.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 399673.27308\n }\n },\n {\n \"Date\": \"2023-03-09\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-09 20:14\",\n \"AzimuthDegrees\": 95.60853,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 394323.19311\n },\n \"Moonset\": {\n \"Time\": \"2023-03-09 07:31\",\n \"AzimuthDegrees\": 269.31953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 395988.3718\n },\n \"Meridian\": {\n \"Time\": \"2023-03-09 01:24\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 396761.88573\n }\n },\n {\n \"Date\": \"2023-03-10\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-10 21:28\",\n \"AzimuthDegrees\": 105.39738,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 390803.12388\n },\n \"Moonset\": {\n \"Time\": \"2023-03-10 07:42\",\n \"AzimuthDegrees\": 259.93073,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 392756.20131\n },\n \"Meridian\": {\n \"Time\": \"2023-03-10 02:05\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 33.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 393530.65092\n }\n },\n {\n \"Date\": \"2023-03-11\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-11 22:46\",\n \"AzimuthDegrees\": 114.97706,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 387016.85411\n },\n \"Moonset\": {\n \"Time\": \"2023-03-11 07:55\",\n \"AzimuthDegrees\": 250.59757,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 389268.48137\n },\n \"Meridian\": {\n \"Time\": \"2023-03-11 02:48\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 27.5,\n \"Direction\": \"S\",\n \"DistanceKm\": 390025.04685\n }\n },\n {\n \"Date\": \"2023-03-12\",\n \"IsMoonRise\": false,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonset\": {\n \"Time\": \"2023-03-12 08:11\",\n \"AzimuthDegrees\": 241.58881,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 385546.82937\n },\n \"Meridian\": {\n \"Time\": \"2023-03-12 03:34\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 21.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 386270.22231\n }\n },\n {\n \"Date\": \"2023-03-13\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-13 00:06\",\n \"AzimuthDegrees\": 123.94721,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 382995.32957\n },\n \"Moonset\": {\n \"Time\": \"2023-03-13 08:31\",\n \"AzimuthDegrees\": 233.31784,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 381620.77804\n },\n \"Meridian\": {\n \"Time\": \"2023-03-13 04:24\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 17,\n \"Direction\": \"S\",\n \"DistanceKm\": 382296.46854\n }\n },\n {\n \"Date\": \"2023-03-14\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-14 01:29\",\n \"AzimuthDegrees\": 131.64699,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 378807.06957\n },\n \"Moonset\": {\n \"Time\": \"2023-03-14 08:59\",\n \"AzimuthDegrees\": 226.46039,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 377554.30425\n },\n \"Meridian\": {\n \"Time\": \"2023-03-14 05:18\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 378170.5856\n }\n },\n {\n \"Date\": \"2023-03-15\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-15 02:48\",\n \"AzimuthDegrees\": 137.04254,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 374594.67251\n },\n \"Moonset\": {\n \"Time\": \"2023-03-15 09:40\",\n \"AzimuthDegrees\": 222.05405,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 373473.15308\n },\n \"Meridian\": {\n \"Time\": \"2023-03-15 06:16\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 374026.97617\n }\n },\n {\n \"Date\": \"2023-03-16\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-16 03:57\",\n \"AzimuthDegrees\": 138.9114,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 370591.50029\n },\n \"Moonset\": {\n \"Time\": \"2023-03-16 10:39\",\n \"AzimuthDegrees\": 221.29657,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 369592.67725\n },\n \"Meridian\": {\n \"Time\": \"2023-03-16 07:18\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 370088.60593\n }\n },\n {\n \"Date\": \"2023-03-17\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-17 04:51\",\n \"AzimuthDegrees\": 136.61246,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 367098.86901\n },\n \"Moonset\": {\n \"Time\": \"2023-03-17 11:56\",\n \"AzimuthDegrees\": 224.76797,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 366237.19817\n },\n \"Meridian\": {\n \"Time\": \"2023-03-17 08:20\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 11.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 366664.93323\n }\n },\n {\n \"Date\": \"2023-03-18\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-18 05:28\",\n \"AzimuthDegrees\": 130.65168,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 364441.40172\n },\n \"Moonset\": {\n \"Time\": \"2023-03-18 13:24\",\n \"AzimuthDegrees\": 231.87919,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 363812.28579\n },\n \"Meridian\": {\n \"Time\": \"2023-03-18 09:21\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 14.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364117.71582\n }\n },\n {\n \"Date\": \"2023-03-19\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-19 05:55\",\n \"AzimuthDegrees\": 122.15123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362929.11813\n },\n \"Moonset\": {\n \"Time\": \"2023-03-19 14:57\",\n \"AzimuthDegrees\": 241.42982,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 362712.81832\n },\n \"Meridian\": {\n \"Time\": \"2023-03-19 10:19\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 19.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 362798.79835\n }\n },\n {\n \"Date\": \"2023-03-20\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-20 06:15\",\n \"AzimuthDegrees\": 112.14362,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362812.63242\n },\n \"Moonset\": {\n \"Time\": \"2023-03-20 16:28\",\n \"AzimuthDegrees\": 252.32977,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 363219.08698\n },\n \"Meridian\": {\n \"Time\": \"2023-03-20 11:14\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 25.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 362976.42916\n }\n },\n {\n \"Date\": \"2023-03-21\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-21 06:30\",\n \"AzimuthDegrees\": 101.3864,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 364227.26916\n },\n \"Moonset\": {\n \"Time\": \"2023-03-21 17:56\",\n \"AzimuthDegrees\": 263.78338,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 365425.30425\n },\n \"Meridian\": {\n \"Time\": \"2023-03-21 12:05\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364770.21284\n }\n },\n {\n \"Date\": \"2023-03-22\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-22 06:44\",\n \"AzimuthDegrees\": 90.43001,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 367149.18373\n },\n \"Moonset\": {\n \"Time\": \"2023-03-22 19:22\",\n \"AzimuthDegrees\": 275.21269,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 369210.60777\n },\n \"Meridian\": {\n \"Time\": \"2023-03-22 12:54\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 368112.88854\n }\n },\n {\n \"Date\": \"2023-03-23\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-23 06:58\",\n \"AzimuthDegrees\": 79.70263,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 371383.43121\n },\n \"Moonset\": {\n \"Time\": \"2023-03-23 20:46\",\n \"AzimuthDegrees\": 286.16029,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 374253.93367\n },\n \"Meridian\": {\n \"Time\": \"2023-03-23 13:43\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 46.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 372750.53254\n }\n },\n {\n \"Date\": \"2023-03-24\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-24 07:13\",\n \"AzimuthDegrees\": 69.57123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376588.54488\n },\n \"Moonset\": {\n \"Time\": \"2023-03-24 22:09\",\n \"AzimuthDegrees\": 296.21187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 380086.26614\n },\n \"Meridian\": {\n \"Time\": \"2023-03-24 14:31\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 52.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 378280.3871\n }\n },\n {\n \"Date\": \"2023-03-25\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-25 07:30\",\n \"AzimuthDegrees\": 60.39425,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 382327.95573\n },\n \"Moonset\": {\n \"Time\": \"2023-03-25 23:31\",\n \"AzimuthDegrees\": 304.92579,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 386164.32159\n },\n \"Meridian\": {\n \"Time\": \"2023-03-25 15:21\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 58.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 384213.14123\n }\n },\n {\n \"Date\": \"2023-03-26\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": false,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-26 07:52\",\n \"AzimuthDegrees\": 52.57818,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 388131.21721\n },\n \"Meridian\": {\n \"Time\": \"2023-03-26 16:12\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 62.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 390041.63582\n }\n },\n {\n \"Date\": \"2023-03-27\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-27 08:21\",\n \"AzimuthDegrees\": 46.62563,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 393548.92562\n },\n \"Moonset\": {\n \"Time\": \"2023-03-27 00:48\",\n \"AzimuthDegrees\": 311.77602,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 391948.19405\n },\n \"Meridian\": {\n \"Time\": \"2023-03-27 17:04\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65,\n \"Direction\": \"S\",\n \"DistanceKm\": 395301.62304\n }\n },\n {\n \"Date\": \"2023-03-28\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-28 09:00\",\n \"AzimuthDegrees\": 43.11087,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 398191.74953\n },\n \"Moonset\": {\n \"Time\": \"2023-03-28 01:59\",\n \"AzimuthDegrees\": 316.17036,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 396971.11487\n },\n \"Meridian\": {\n \"Time\": \"2023-03-28 17:57\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 399617.2651\n }\n },\n {\n \"Date\": \"2023-03-29\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-29 09:50\",\n \"AzimuthDegrees\": 42.48507,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 401753.75503\n },\n \"Moonset\": {\n \"Time\": \"2023-03-29 02:57\",\n \"AzimuthDegrees\": 317.64261,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 400892.49384\n },\n \"Meridian\": {\n \"Time\": \"2023-03-29 18:49\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 402729.21577\n }\n },\n {\n \"Date\": \"2023-03-30\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-30 10:50\",\n \"AzimuthDegrees\": 44.77953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404030.19632\n },\n \"Moonset\": {\n \"Time\": \"2023-03-30 03:43\",\n \"AzimuthDegrees\": 316.15045,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 403521.433\n },\n \"Meridian\": {\n \"Time\": \"2023-03-30 19:40\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 64.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 404505.37243\n }\n },\n {\n \"Date\": \"2023-03-31\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": \"2023-03-31 11:58\",\n \"AzimuthDegrees\": 49.55137,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404935.96867\n },\n \"Moonset\": {\n \"Time\": \"2023-03-31 04:17\",\n \"AzimuthDegrees\": 312.1322,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404804.48186\n },\n \"Meridian\": {\n \"Time\": \"2023-03-31 20:28\",\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 61.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 404936.05398\n }\n }\n]" }, { - "name": "moonPositionMonthly — error", + "name": "moonPositionMonthly — timestamp timeFormat", "originalRequest": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=9999&month=3&precision=5", + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5&timeFormat=timestamp", "host": [ "{{baseUrl}}" ], @@ -617,7 +1300,7 @@ }, { "key": "year", - "value": "9999", + "value": "2023", "description": "Format: YYYY Allowed range: [1899, 2053]. Current, if not specified" }, { @@ -629,12 +1312,17 @@ "key": "precision", "value": "5", "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] } }, - "status": "Bad Request", - "code": 400, + "status": "OK", + "code": 200, "_postman_previewlanguage": "json", "header": [ { @@ -643,67 +1331,196 @@ }, { "key": "Date", - "value": "Sun, 16 Nov 2025 15:26:10 GMT" + "value": "Sun, 16 Nov 2025 18:27:20 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "13464" + } + ], + "cookie": [ + { + "expires": "Invalid Date", + "domain": "", + "path": "" + } + ], + "body": "[\n {\n \"Date\": \"2023-03-01\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1677650857,\n \"AzimuthDegrees\": 42.8762,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 402636.75966\n },\n \"Moonset\": {\n \"Time\": 1677625807,\n \"AzimuthDegrees\": 316.74613,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 401798.89266\n },\n \"Meridian\": {\n \"Time\": 1677683130,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 403572.05151\n }\n },\n {\n \"Date\": \"2023-03-02\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1677740467,\n \"AzimuthDegrees\": 43.38104,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404836.13007\n },\n \"Moonset\": {\n \"Time\": 1677715380,\n \"AzimuthDegrees\": 317.04922,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404345.126\n },\n \"Meridian\": {\n \"Time\": 1677772596,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405326.66664\n }\n },\n {\n \"Date\": \"2023-03-03\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1677830659,\n \"AzimuthDegrees\": 46.62247,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405833.52922\n },\n \"Moonset\": {\n \"Time\": 1677804219,\n \"AzimuthDegrees\": 314.58446,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405661.87336\n },\n \"Meridian\": {\n \"Time\": 1677861967,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 63.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 405914.02206\n }\n },\n {\n \"Date\": \"2023-03-04\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1677921218,\n \"AzimuthDegrees\": 52.09931,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 405724.54322\n },\n \"Moonset\": {\n \"Time\": 1677892432,\n \"AzimuthDegrees\": 309.84464,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 405870.41007\n },\n \"Meridian\": {\n \"Time\": 1677951207,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 60.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 405470.71269\n }\n },\n {\n \"Date\": \"2023-03-05\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678011933,\n \"AzimuthDegrees\": 59.20503,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 404665.50003\n },\n \"Moonset\": {\n \"Time\": 1677980183,\n \"AzimuthDegrees\": 303.43187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 405132.86805\n },\n \"Meridian\": {\n \"Time\": 1678040308,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 55.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 404168.13916\n }\n },\n {\n \"Date\": \"2023-03-06\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": false,\n \"Moonrise\": {\n \"Time\": 1678102672,\n \"AzimuthDegrees\": 67.43208,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 402838.67123\n },\n \"Moonset\": {\n \"Time\": 1678067624,\n \"AzimuthDegrees\": 295.85243,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 403623.69367\n }\n },\n {\n \"Date\": \"2023-03-07\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678193387,\n \"AzimuthDegrees\": 76.41149,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 400418.46475\n },\n \"Moonset\": {\n \"Time\": 1678154869,\n \"AzimuthDegrees\": 287.47458,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 401508.60505\n },\n \"Meridian\": {\n \"Time\": 1678129288,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 50.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 402183.01801\n }\n },\n {\n \"Date\": \"2023-03-08\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678284096,\n \"AzimuthDegrees\": 85.87566,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"E\",\n \"DistanceKm\": 397547.14825\n },\n \"Moonset\": {\n \"Time\": 1678242000,\n \"AzimuthDegrees\": 278.56217,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 398928.36539\n },\n \"Meridian\": {\n \"Time\": 1678218186,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 45.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 399673.27308\n }\n },\n {\n \"Date\": \"2023-03-09\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678374851,\n \"AzimuthDegrees\": 95.60853,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 394323.19311\n },\n \"Moonset\": {\n \"Time\": 1678329083,\n \"AzimuthDegrees\": 269.31953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 395988.3718\n },\n \"Meridian\": {\n \"Time\": 1678307052,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 396761.88573\n }\n },\n {\n \"Date\": \"2023-03-10\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678465722,\n \"AzimuthDegrees\": 105.39738,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 390803.12388\n },\n \"Moonset\": {\n \"Time\": 1678416177,\n \"AzimuthDegrees\": 259.93073,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 392756.20131\n },\n \"Meridian\": {\n \"Time\": 1678395944,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 33.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 393530.65092\n }\n },\n {\n \"Date\": \"2023-03-11\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678556769,\n \"AzimuthDegrees\": 114.97706,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 387016.85411\n },\n \"Moonset\": {\n \"Time\": 1678503344,\n \"AzimuthDegrees\": 250.59757,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 389268.48137\n },\n \"Meridian\": {\n \"Time\": 1678484929,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 27.5,\n \"Direction\": \"S\",\n \"DistanceKm\": 390025.04685\n }\n },\n {\n \"Date\": \"2023-03-12\",\n \"IsMoonRise\": false,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonset\": {\n \"Time\": 1678590669,\n \"AzimuthDegrees\": 241.58881,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 385546.82937\n },\n \"Meridian\": {\n \"Time\": 1678574075,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 21.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 386270.22231\n }\n },\n {\n \"Date\": \"2023-03-13\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678648012,\n \"AzimuthDegrees\": 123.94721,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 382995.32957\n },\n \"Moonset\": {\n \"Time\": 1678678278,\n \"AzimuthDegrees\": 233.31784,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 381620.77804\n },\n \"Meridian\": {\n \"Time\": 1678663446,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 17,\n \"Direction\": \"S\",\n \"DistanceKm\": 382296.46854\n }\n },\n {\n \"Date\": \"2023-03-14\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678739357,\n \"AzimuthDegrees\": 131.64699,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 378807.06957\n },\n \"Moonset\": {\n \"Time\": 1678766372,\n \"AzimuthDegrees\": 226.46039,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 377554.30425\n },\n \"Meridian\": {\n \"Time\": 1678753085,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 13.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 378170.5856\n }\n },\n {\n \"Date\": \"2023-03-15\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678830525,\n \"AzimuthDegrees\": 137.04254,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 374594.67251\n },\n \"Moonset\": {\n \"Time\": 1678855238,\n \"AzimuthDegrees\": 222.05405,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 373473.15308\n },\n \"Meridian\": {\n \"Time\": 1678842992,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 374026.97617\n }\n },\n {\n \"Date\": \"2023-03-16\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1678921072,\n \"AzimuthDegrees\": 138.9114,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 370591.50029\n },\n \"Moonset\": {\n \"Time\": 1678945166,\n \"AzimuthDegrees\": 221.29657,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 369592.67725\n },\n \"Meridian\": {\n \"Time\": 1678933092,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 10.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 370088.60593\n }\n },\n {\n \"Date\": \"2023-03-17\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679010663,\n \"AzimuthDegrees\": 136.61246,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 367098.86901\n },\n \"Moonset\": {\n \"Time\": 1679036174,\n \"AzimuthDegrees\": 224.76797,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 366237.19817\n },\n \"Meridian\": {\n \"Time\": 1679023251,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 11.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 366664.93323\n }\n },\n {\n \"Date\": \"2023-03-18\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679099326,\n \"AzimuthDegrees\": 130.65168,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SE\",\n \"DistanceKm\": 364441.40172\n },\n \"Moonset\": {\n \"Time\": 1679127890,\n \"AzimuthDegrees\": 231.87919,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"SW\",\n \"DistanceKm\": 363812.28579\n },\n \"Meridian\": {\n \"Time\": 1679113319,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 14.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364117.71582\n }\n },\n {\n \"Date\": \"2023-03-19\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679187320,\n \"AzimuthDegrees\": 122.15123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362929.11813\n },\n \"Moonset\": {\n \"Time\": 1679219833,\n \"AzimuthDegrees\": 241.42982,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 362712.81832\n },\n \"Meridian\": {\n \"Time\": 1679203194,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 19.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 362798.79835\n }\n },\n {\n \"Date\": \"2023-03-20\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679274902,\n \"AzimuthDegrees\": 112.14362,\n \"AltitudeDegrees\": -0.56666,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 362812.63242\n },\n \"Moonset\": {\n \"Time\": 1679311702,\n \"AzimuthDegrees\": 252.32977,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WSW\",\n \"DistanceKm\": 363219.08698\n },\n \"Meridian\": {\n \"Time\": 1679292852,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 25.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 362976.42916\n }\n },\n {\n \"Date\": \"2023-03-21\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679362254,\n \"AzimuthDegrees\": 101.3864,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ESE\",\n \"DistanceKm\": 364227.26916\n },\n \"Moonset\": {\n \"Time\": 1679403400,\n \"AzimuthDegrees\": 263.78338,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 365425.30425\n },\n \"Meridian\": {\n \"Time\": 1679382325,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 32.8,\n \"Direction\": \"S\",\n \"DistanceKm\": 364770.21284\n }\n },\n {\n \"Date\": \"2023-03-22\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679449497,\n \"AzimuthDegrees\": 90.43001,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 367149.18373\n },\n \"Moonset\": {\n \"Time\": 1679494947,\n \"AzimuthDegrees\": 275.21269,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"W\",\n \"DistanceKm\": 369210.60777\n },\n \"Meridian\": {\n \"Time\": 1679471677,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 39.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 368112.88854\n }\n },\n {\n \"Date\": \"2023-03-23\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679536722,\n \"AzimuthDegrees\": 79.70263,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"E\",\n \"DistanceKm\": 371383.43121\n },\n \"Moonset\": {\n \"Time\": 1679586398,\n \"AzimuthDegrees\": 286.16029,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 374253.93367\n },\n \"Meridian\": {\n \"Time\": 1679560980,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 46.7,\n \"Direction\": \"S\",\n \"DistanceKm\": 372750.53254\n }\n },\n {\n \"Date\": \"2023-03-24\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679624012,\n \"AzimuthDegrees\": 69.57123,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 376588.54488\n },\n \"Moonset\": {\n \"Time\": 1679677785,\n \"AzimuthDegrees\": 296.21187,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"WNW\",\n \"DistanceKm\": 380086.26614\n },\n \"Meridian\": {\n \"Time\": 1679650299,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 52.9,\n \"Direction\": \"S\",\n \"DistanceKm\": 378280.3871\n }\n },\n {\n \"Date\": \"2023-03-25\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679711456,\n \"AzimuthDegrees\": 60.39425,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"ENE\",\n \"DistanceKm\": 382327.95573\n },\n \"Moonset\": {\n \"Time\": 1679769074,\n \"AzimuthDegrees\": 304.92579,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 386164.32159\n },\n \"Meridian\": {\n \"Time\": 1679739679,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 58.2,\n \"Direction\": \"S\",\n \"DistanceKm\": 384213.14123\n }\n },\n {\n \"Date\": \"2023-03-26\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": false,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679799169,\n \"AzimuthDegrees\": 52.57818,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 388131.21721\n },\n \"Meridian\": {\n \"Time\": 1679829144,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 62.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 390041.63582\n }\n },\n {\n \"Date\": \"2023-03-27\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679887300,\n \"AzimuthDegrees\": 46.62563,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 393548.92562\n },\n \"Moonset\": {\n \"Time\": 1679860134,\n \"AzimuthDegrees\": 311.77602,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 391948.19405\n },\n \"Meridian\": {\n \"Time\": 1679918681,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 65,\n \"Direction\": \"S\",\n \"DistanceKm\": 395301.62304\n }\n },\n {\n \"Date\": \"2023-03-28\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1679976010,\n \"AzimuthDegrees\": 43.11087,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 398191.74953\n },\n \"Moonset\": {\n \"Time\": 1679950741,\n \"AzimuthDegrees\": 316.17036,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 396971.11487\n },\n \"Meridian\": {\n \"Time\": 1680008246,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.3,\n \"Direction\": \"S\",\n \"DistanceKm\": 399617.2651\n }\n },\n {\n \"Date\": \"2023-03-29\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1680065405,\n \"AzimuthDegrees\": 42.48507,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 401753.75503\n },\n \"Moonset\": {\n \"Time\": 1680040664,\n \"AzimuthDegrees\": 317.64261,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 400892.49384\n },\n \"Meridian\": {\n \"Time\": 1680097776,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 66.1,\n \"Direction\": \"S\",\n \"DistanceKm\": 402729.21577\n }\n },\n {\n \"Date\": \"2023-03-30\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1680155438,\n \"AzimuthDegrees\": 44.77953,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404030.19632\n },\n \"Moonset\": {\n \"Time\": 1680129800,\n \"AzimuthDegrees\": 316.15045,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 403521.433\n },\n \"Meridian\": {\n \"Time\": 1680187209,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 64.4,\n \"Direction\": \"S\",\n \"DistanceKm\": 404505.37243\n }\n },\n {\n \"Date\": \"2023-03-31\",\n \"IsMoonRise\": true,\n \"IsMoonSet\": true,\n \"IsMeridian\": true,\n \"Moonrise\": {\n \"Time\": 1680245913,\n \"AzimuthDegrees\": 49.55137,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NE\",\n \"DistanceKm\": 404935.96867\n },\n \"Moonset\": {\n \"Time\": 1680218226,\n \"AzimuthDegrees\": 312.1322,\n \"AltitudeDegrees\": -0.56667,\n \"Direction\": \"NW\",\n \"DistanceKm\": 404804.48186\n },\n \"Meridian\": {\n \"Time\": 1680276504,\n \"AzimuthDegrees\": 180,\n \"AltitudeDegrees\": 61.6,\n \"Direction\": \"S\",\n \"DistanceKm\": 404936.05398\n }\n }\n]" + } + ] + } + ] + }, + { + "name": "Moon Phase — Core information for year (phase)", + "item": [ + { + "name": "Get moon phases table by specified year", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonTableYear?utc=5&year=2023&timeFormat=2006-01-02 15:04", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonTableYear" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "year", + "value": "2023", + "description": "Year for request: YYYY. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + }, + "description": "Detailed information:\n\n[https://github.com/prostraction/moon?tab=readme-ov-file#get-v1moontableyear](https://github.com/prostraction/moon?tab=readme-ov-file#get-v1moontableyear)\n\nThe method returns the moon phases for the given year. The response contains an array for each month, each element of which contains the time of the new moon, first quarter, full moon, last quarter." + }, + "response": [ + { + "name": "moonTableYear", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonTableYear?utc=5&year=2025", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonTableYear" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "year", + "value": "2025", + "description": "Year for request: YYYY. Current, if not specified" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "nginx/1.24.0 (Ubuntu)" + }, + { + "key": "Date", + "value": "Tue, 04 Nov 2025 11:31:11 GMT" }, { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" }, { "key": "Content-Length", - "value": "136" + "value": "2133" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Keep-Alive", + "value": "timeout=900" + } + ], + "cookie": [], + "body": "[\n {\n \"NewMoon\": \"2024-12-31T03:27:49+05:00\",\n \"FirstQuarter\": \"2025-01-07T04:34:13+05:00\",\n \"FullMoon\": \"2025-01-14T03:27:44+05:00\",\n \"LastQuarter\": \"2025-01-22T01:42:58+05:00\"\n },\n {\n \"NewMoon\": \"2025-01-29T17:37:18+05:00\",\n \"FirstQuarter\": \"2025-02-05T12:39:54+05:00\",\n \"FullMoon\": \"2025-02-12T18:54:26+05:00\",\n \"LastQuarter\": \"2025-02-20T22:54:12+05:00\"\n },\n {\n \"NewMoon\": \"2025-02-28T05:46:43+05:00\",\n \"FirstQuarter\": \"2025-03-06T21:17:10+05:00\",\n \"FullMoon\": \"2025-03-14T11:55:56+05:00\",\n \"LastQuarter\": \"2025-03-22T17:09:32+05:00\"\n },\n {\n \"NewMoon\": \"2025-03-29T16:00:07+05:00\",\n \"FirstQuarter\": \"2025-04-05T07:05:24+05:00\",\n \"FullMoon\": \"2025-04-13T05:23:56+05:00\",\n \"LastQuarter\": \"2025-04-21T07:24:10+05:00\"\n },\n {\n \"NewMoon\": \"2025-04-28T00:33:22+05:00\",\n \"FirstQuarter\": \"2025-05-04T18:40:59+05:00\",\n \"FullMoon\": \"2025-05-12T21:58:06+05:00\",\n \"LastQuarter\": \"2025-05-20T17:38:39+05:00\"\n },\n {\n \"NewMoon\": \"2025-05-27T08:04:25+05:00\",\n \"FirstQuarter\": \"2025-06-03T08:18:31+05:00\",\n \"FullMoon\": \"2025-06-11T12:46:06+05:00\",\n \"LastQuarter\": \"2025-06-19T00:39:04+05:00\"\n },\n {\n \"NewMoon\": \"2025-06-25T15:33:30+05:00\",\n \"FirstQuarter\": \"2025-07-02T23:55:45+05:00\",\n \"FullMoon\": \"2025-07-11T01:38:54+05:00\",\n \"LastQuarter\": \"2025-07-18T05:42:32+05:00\"\n },\n {\n \"NewMoon\": \"2025-07-25T00:12:27+05:00\",\n \"FirstQuarter\": \"2025-08-01T17:07:25+05:00\",\n \"FullMoon\": \"2025-08-09T12:57:07+05:00\",\n \"LastQuarter\": \"2025-08-16T10:14:41+05:00\"\n },\n {\n \"NewMoon\": \"2025-08-23T11:07:06+05:00\",\n \"FirstQuarter\": \"2025-08-31T11:05:52+05:00\",\n \"FullMoon\": \"2025-09-07T23:10:41+05:00\",\n \"LastQuarter\": \"2025-09-14T15:47:01+05:00\"\n },\n {\n \"NewMoon\": \"2025-09-22T00:54:36+05:00\",\n \"FirstQuarter\": \"2025-09-30T04:51:14+05:00\",\n \"FullMoon\": \"2025-10-07T08:48:57+05:00\",\n \"LastQuarter\": \"2025-10-13T23:39:48+05:00\"\n },\n {\n \"NewMoon\": \"2025-10-21T17:25:58+05:00\",\n \"FirstQuarter\": \"2025-10-29T21:21:02+05:00\",\n \"FullMoon\": \"2025-11-05T18:20:26+05:00\",\n \"LastQuarter\": \"2025-11-12T10:55:00+05:00\"\n },\n {\n \"NewMoon\": \"2025-11-20T11:48:16+05:00\",\n \"FirstQuarter\": \"2025-11-28T11:44:00+05:00\",\n \"FullMoon\": \"2025-12-05T04:15:18+05:00\",\n \"LastQuarter\": \"2025-12-12T02:04:31+05:00\"\n },\n {\n \"NewMoon\": \"2025-12-20T06:44:25+05:00\",\n \"FirstQuarter\": \"2025-12-27T23:35:58+05:00\",\n \"FullMoon\": \"2026-01-03T15:04:15+05:00\",\n \"LastQuarter\": \"2026-01-10T20:51:36+05:00\"\n }\n]" + }, + { + "name": "moonTableYear — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/moonTableYear?utc=5&year=2023&timeFormat=2006-01-02 15:04", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonTableYear" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "year", + "value": "2023", + "description": "Year for request: YYYY. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 18:36:08 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "1665" } ], "cookie": [], - "body": "{\n \"Status\": 400,\n \"Message\": \"[400 Bad Request] Invalid parameter format: ephemeris segment only covers dates 1899-07-29 through 2053-10-09\"\n}" - } - ] - } - ] - }, - { - "name": "Moon Phase — Core information for year (phase)", - "item": [ - { - "name": "Get moon phases table by specified year", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/v1/moonTableYear?utc=5&year=2023", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "v1", - "moonTableYear" - ], - "query": [ - { - "key": "utc", - "value": "5", - "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" - }, - { - "key": "year", - "value": "2023", - "description": "Year for request: YYYY. Current, if not specified" - } - ] + "body": "[\n {\n \"NewMoon\": \"2022-12-23 15:17\",\n \"FirstQuarter\": \"2022-12-30 06:24\",\n \"FullMoon\": \"2023-01-07 04:09\",\n \"LastQuarter\": \"2023-01-07 04:09\"\n },\n {\n \"NewMoon\": \"2023-01-22 01:55\",\n \"FirstQuarter\": \"2023-01-28 20:05\",\n \"FullMoon\": \"2023-02-05 23:30\",\n \"LastQuarter\": \"2023-02-05 23:30\"\n },\n {\n \"NewMoon\": \"2023-02-20 12:09\",\n \"FirstQuarter\": \"2023-02-27 12:36\",\n \"FullMoon\": \"2023-03-07 17:42\",\n \"LastQuarter\": \"2023-03-07 17:42\"\n },\n {\n \"NewMoon\": \"2023-03-21 22:26\",\n \"FirstQuarter\": \"2023-03-29 07:03\",\n \"FullMoon\": \"2023-04-06 09:37\",\n \"LastQuarter\": \"2023-04-06 09:37\"\n },\n {\n \"NewMoon\": \"2023-04-20 09:15\",\n \"FirstQuarter\": \"2023-04-28 02:12\",\n \"FullMoon\": \"2023-05-05 22:36\",\n \"LastQuarter\": \"2023-05-05 22:36\"\n },\n {\n \"NewMoon\": \"2023-05-19 20:55\",\n \"FirstQuarter\": \"2023-05-27 20:30\",\n \"FullMoon\": \"2023-06-04 08:43\",\n \"LastQuarter\": \"2023-06-04 08:43\"\n },\n {\n \"NewMoon\": \"2023-06-18 09:39\",\n \"FirstQuarter\": \"2023-06-26 12:59\",\n \"FullMoon\": \"2023-07-03 16:40\",\n \"LastQuarter\": \"2023-07-03 16:40\"\n },\n {\n \"NewMoon\": \"2023-07-17 23:33\",\n \"FirstQuarter\": \"2023-07-26 03:01\",\n \"FullMoon\": \"2023-08-01 23:33\",\n \"LastQuarter\": \"2023-08-01 23:33\"\n },\n {\n \"NewMoon\": \"2023-08-16 14:38\",\n \"FirstQuarter\": \"2023-08-24 14:34\",\n \"FullMoon\": \"2023-08-31 06:37\",\n \"LastQuarter\": \"2023-08-31 06:37\"\n },\n {\n \"NewMoon\": \"2023-09-15 06:40\",\n \"FirstQuarter\": \"2023-09-23 00:03\",\n \"FullMoon\": \"2023-09-29 14:58\",\n \"LastQuarter\": \"2023-09-29 14:58\"\n },\n {\n \"NewMoon\": \"2023-10-14 22:55\",\n \"FirstQuarter\": \"2023-10-22 08:10\",\n \"FullMoon\": \"2023-10-29 01:24\",\n \"LastQuarter\": \"2023-10-29 01:24\"\n },\n {\n \"NewMoon\": \"2023-11-13 14:27\",\n \"FirstQuarter\": \"2023-11-20 15:45\",\n \"FullMoon\": \"2023-11-27 14:16\",\n \"LastQuarter\": \"2023-11-27 14:16\"\n },\n {\n \"NewMoon\": \"2023-12-13 04:32\",\n \"FirstQuarter\": \"2023-12-19 23:42\",\n \"FullMoon\": \"2023-12-27 05:33\",\n \"LastQuarter\": \"2023-12-27 05:33\"\n }\n]" }, - "description": "Detailed information:\n\n[https://github.com/prostraction/moon?tab=readme-ov-file#get-v1moontableyear](https://github.com/prostraction/moon?tab=readme-ov-file#get-v1moontableyear)\n\nThe method returns the moon phases for the given year. The response contains an array for each month, each element of which contains the time of the new moon, first quarter, full moon, last quarter." - }, - "response": [ { - "name": "moonTableYear", + "name": "moonTableYear — timestamp timeFormat", "originalRequest": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonTableYear?utc=5&year=2025", + "raw": "{{baseUrl}}/v1/moonTableYear?utc=5&year=2023&timeFormat=timestamp", "host": [ "{{baseUrl}}" ], @@ -719,45 +1536,40 @@ }, { "key": "year", - "value": "2025", + "value": "2023", "description": "Year for request: YYYY. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] } }, "status": "OK", "code": 200, - "_postman_previewlanguage": "json", + "_postman_previewlanguage": null, "header": [ { "key": "Server", - "value": "nginx/1.24.0 (Ubuntu)" + "value": "Fiber" }, { "key": "Date", - "value": "Tue, 04 Nov 2025 11:31:11 GMT" + "value": "Sun, 16 Nov 2025 18:36:46 GMT" }, { "key": "Content-Type", - "value": "application/json", - "description": "", - "type": "text" + "value": "application/json" }, { "key": "Content-Length", - "value": "2133" - }, - { - "key": "Connection", - "value": "keep-alive" - }, - { - "key": "Keep-Alive", - "value": "timeout=900" + "value": "1249" } ], "cookie": [], - "body": "[\n {\n \"NewMoon\": \"2024-12-31T03:27:49+05:00\",\n \"FirstQuarter\": \"2025-01-07T04:34:13+05:00\",\n \"FullMoon\": \"2025-01-14T03:27:44+05:00\",\n \"LastQuarter\": \"2025-01-22T01:42:58+05:00\"\n },\n {\n \"NewMoon\": \"2025-01-29T17:37:18+05:00\",\n \"FirstQuarter\": \"2025-02-05T12:39:54+05:00\",\n \"FullMoon\": \"2025-02-12T18:54:26+05:00\",\n \"LastQuarter\": \"2025-02-20T22:54:12+05:00\"\n },\n {\n \"NewMoon\": \"2025-02-28T05:46:43+05:00\",\n \"FirstQuarter\": \"2025-03-06T21:17:10+05:00\",\n \"FullMoon\": \"2025-03-14T11:55:56+05:00\",\n \"LastQuarter\": \"2025-03-22T17:09:32+05:00\"\n },\n {\n \"NewMoon\": \"2025-03-29T16:00:07+05:00\",\n \"FirstQuarter\": \"2025-04-05T07:05:24+05:00\",\n \"FullMoon\": \"2025-04-13T05:23:56+05:00\",\n \"LastQuarter\": \"2025-04-21T07:24:10+05:00\"\n },\n {\n \"NewMoon\": \"2025-04-28T00:33:22+05:00\",\n \"FirstQuarter\": \"2025-05-04T18:40:59+05:00\",\n \"FullMoon\": \"2025-05-12T21:58:06+05:00\",\n \"LastQuarter\": \"2025-05-20T17:38:39+05:00\"\n },\n {\n \"NewMoon\": \"2025-05-27T08:04:25+05:00\",\n \"FirstQuarter\": \"2025-06-03T08:18:31+05:00\",\n \"FullMoon\": \"2025-06-11T12:46:06+05:00\",\n \"LastQuarter\": \"2025-06-19T00:39:04+05:00\"\n },\n {\n \"NewMoon\": \"2025-06-25T15:33:30+05:00\",\n \"FirstQuarter\": \"2025-07-02T23:55:45+05:00\",\n \"FullMoon\": \"2025-07-11T01:38:54+05:00\",\n \"LastQuarter\": \"2025-07-18T05:42:32+05:00\"\n },\n {\n \"NewMoon\": \"2025-07-25T00:12:27+05:00\",\n \"FirstQuarter\": \"2025-08-01T17:07:25+05:00\",\n \"FullMoon\": \"2025-08-09T12:57:07+05:00\",\n \"LastQuarter\": \"2025-08-16T10:14:41+05:00\"\n },\n {\n \"NewMoon\": \"2025-08-23T11:07:06+05:00\",\n \"FirstQuarter\": \"2025-08-31T11:05:52+05:00\",\n \"FullMoon\": \"2025-09-07T23:10:41+05:00\",\n \"LastQuarter\": \"2025-09-14T15:47:01+05:00\"\n },\n {\n \"NewMoon\": \"2025-09-22T00:54:36+05:00\",\n \"FirstQuarter\": \"2025-09-30T04:51:14+05:00\",\n \"FullMoon\": \"2025-10-07T08:48:57+05:00\",\n \"LastQuarter\": \"2025-10-13T23:39:48+05:00\"\n },\n {\n \"NewMoon\": \"2025-10-21T17:25:58+05:00\",\n \"FirstQuarter\": \"2025-10-29T21:21:02+05:00\",\n \"FullMoon\": \"2025-11-05T18:20:26+05:00\",\n \"LastQuarter\": \"2025-11-12T10:55:00+05:00\"\n },\n {\n \"NewMoon\": \"2025-11-20T11:48:16+05:00\",\n \"FirstQuarter\": \"2025-11-28T11:44:00+05:00\",\n \"FullMoon\": \"2025-12-05T04:15:18+05:00\",\n \"LastQuarter\": \"2025-12-12T02:04:31+05:00\"\n },\n {\n \"NewMoon\": \"2025-12-20T06:44:25+05:00\",\n \"FirstQuarter\": \"2025-12-27T23:35:58+05:00\",\n \"FullMoon\": \"2026-01-03T15:04:15+05:00\",\n \"LastQuarter\": \"2026-01-10T20:51:36+05:00\"\n }\n]" + "body": "[\n {\n \"NewMoon\": 1671790676,\n \"FirstQuarter\": 1672363444,\n \"FullMoon\": 1673046595,\n \"LastQuarter\": 1673046595\n },\n {\n \"NewMoon\": 1674334530,\n \"FirstQuarter\": 1674918315,\n \"FullMoon\": 1675621844,\n \"LastQuarter\": 1675621844\n },\n {\n \"NewMoon\": 1676876945,\n \"FirstQuarter\": 1677483389,\n \"FullMoon\": 1678192969,\n \"LastQuarter\": 1678192969\n },\n {\n \"NewMoon\": 1679419604,\n \"FirstQuarter\": 1680055425,\n \"FullMoon\": 1680755838,\n \"LastQuarter\": 1680755838\n },\n {\n \"NewMoon\": 1681964148,\n \"FirstQuarter\": 1682629943,\n \"FullMoon\": 1683308207,\n \"LastQuarter\": 1683308207\n },\n {\n \"NewMoon\": 1684511756,\n \"FirstQuarter\": 1685201420,\n \"FullMoon\": 1685850236,\n \"LastQuarter\": 1685850236\n },\n {\n \"NewMoon\": 1687063150,\n \"FirstQuarter\": 1687766370,\n \"FullMoon\": 1688384430,\n \"LastQuarter\": 1688384430\n },\n {\n \"NewMoon\": 1689618786,\n \"FirstQuarter\": 1690322484,\n \"FullMoon\": 1690914806,\n \"LastQuarter\": 1690914806\n },\n {\n \"NewMoon\": 1692178729,\n \"FirstQuarter\": 1692869661,\n \"FullMoon\": 1693445825,\n \"LastQuarter\": 1693445825\n },\n {\n \"NewMoon\": 1694742005,\n \"FirstQuarter\": 1695409414,\n \"FullMoon\": 1695981501,\n \"LastQuarter\": 1695981501\n },\n {\n \"NewMoon\": 1697306108,\n \"FirstQuarter\": 1697944245,\n \"FullMoon\": 1698524667,\n \"LastQuarter\": 1698524667\n },\n {\n \"NewMoon\": 1699867635,\n \"FirstQuarter\": 1700477136,\n \"FullMoon\": 1701076601,\n \"LastQuarter\": 1701076601\n },\n {\n \"NewMoon\": 1702423927,\n \"FirstQuarter\": 1703011321,\n \"FullMoon\": 1703637223,\n \"LastQuarter\": 1703637223\n }\n]" } ] }, @@ -779,7 +1591,7 @@ } }, "url": { - "raw": "{{baseUrl}}/v1/moonTableCurrent?utc=5", + "raw": "{{baseUrl}}/v1/moonTableCurrent?utc=5&timeFormat=2006-01-02 15:04", "host": [ "{{baseUrl}}" ], @@ -792,6 +1604,11 @@ "key": "utc", "value": "5", "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] }, @@ -863,6 +1680,128 @@ ], "cookie": [], "body": "[\n {\n \"NewMoon\": \"2024-12-31T03:27:49+05:00\",\n \"FirstQuarter\": \"2025-01-07T04:34:13+05:00\",\n \"FullMoon\": \"2025-01-14T03:27:44+05:00\",\n \"LastQuarter\": \"2025-01-22T01:42:58+05:00\"\n },\n {\n \"NewMoon\": \"2025-01-29T17:37:18+05:00\",\n \"FirstQuarter\": \"2025-02-05T12:39:54+05:00\",\n \"FullMoon\": \"2025-02-12T18:54:26+05:00\",\n \"LastQuarter\": \"2025-02-20T22:54:12+05:00\"\n },\n {\n \"NewMoon\": \"2025-02-28T05:46:43+05:00\",\n \"FirstQuarter\": \"2025-03-06T21:17:10+05:00\",\n \"FullMoon\": \"2025-03-14T11:55:56+05:00\",\n \"LastQuarter\": \"2025-03-22T17:09:32+05:00\"\n },\n {\n \"NewMoon\": \"2025-03-29T16:00:07+05:00\",\n \"FirstQuarter\": \"2025-04-05T07:05:24+05:00\",\n \"FullMoon\": \"2025-04-13T05:23:56+05:00\",\n \"LastQuarter\": \"2025-04-21T07:24:10+05:00\"\n },\n {\n \"NewMoon\": \"2025-04-28T00:33:22+05:00\",\n \"FirstQuarter\": \"2025-05-04T18:40:59+05:00\",\n \"FullMoon\": \"2025-05-12T21:58:06+05:00\",\n \"LastQuarter\": \"2025-05-20T17:38:39+05:00\"\n },\n {\n \"NewMoon\": \"2025-05-27T08:04:25+05:00\",\n \"FirstQuarter\": \"2025-06-03T08:18:31+05:00\",\n \"FullMoon\": \"2025-06-11T12:46:06+05:00\",\n \"LastQuarter\": \"2025-06-19T00:39:04+05:00\"\n },\n {\n \"NewMoon\": \"2025-06-25T15:33:30+05:00\",\n \"FirstQuarter\": \"2025-07-02T23:55:45+05:00\",\n \"FullMoon\": \"2025-07-11T01:38:54+05:00\",\n \"LastQuarter\": \"2025-07-18T05:42:32+05:00\"\n },\n {\n \"NewMoon\": \"2025-07-25T00:12:27+05:00\",\n \"FirstQuarter\": \"2025-08-01T17:07:25+05:00\",\n \"FullMoon\": \"2025-08-09T12:57:07+05:00\",\n \"LastQuarter\": \"2025-08-16T10:14:41+05:00\"\n },\n {\n \"NewMoon\": \"2025-08-23T11:07:06+05:00\",\n \"FirstQuarter\": \"2025-08-31T11:05:52+05:00\",\n \"FullMoon\": \"2025-09-07T23:10:41+05:00\",\n \"LastQuarter\": \"2025-09-14T15:47:01+05:00\"\n },\n {\n \"NewMoon\": \"2025-09-22T00:54:36+05:00\",\n \"FirstQuarter\": \"2025-09-30T04:51:14+05:00\",\n \"FullMoon\": \"2025-10-07T08:48:57+05:00\",\n \"LastQuarter\": \"2025-10-13T23:39:48+05:00\"\n },\n {\n \"NewMoon\": \"2025-10-21T17:25:58+05:00\",\n \"FirstQuarter\": \"2025-10-29T21:21:02+05:00\",\n \"FullMoon\": \"2025-11-05T18:20:26+05:00\",\n \"LastQuarter\": \"2025-11-12T10:55:00+05:00\"\n },\n {\n \"NewMoon\": \"2025-11-20T11:48:16+05:00\",\n \"FirstQuarter\": \"2025-11-28T11:44:00+05:00\",\n \"FullMoon\": \"2025-12-05T04:15:18+05:00\",\n \"LastQuarter\": \"2025-12-12T02:04:31+05:00\"\n },\n {\n \"NewMoon\": \"2025-12-20T06:44:25+05:00\",\n \"FirstQuarter\": \"2025-12-27T23:35:58+05:00\",\n \"FullMoon\": \"2026-01-03T15:04:15+05:00\",\n \"LastQuarter\": \"2026-01-10T20:51:36+05:00\"\n }\n]" + }, + { + "name": "moonTableCurrent — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/v1/moonTableCurrent?utc=5&timeFormat=2006-01-02 15:04", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonTableCurrent" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 18:37:44 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "1665" + } + ], + "cookie": [], + "body": "[\n {\n \"NewMoon\": \"2024-12-31 03:27\",\n \"FirstQuarter\": \"2025-01-07 04:34\",\n \"FullMoon\": \"2025-01-14 03:27\",\n \"LastQuarter\": \"2025-01-14 03:27\"\n },\n {\n \"NewMoon\": \"2025-01-29 17:37\",\n \"FirstQuarter\": \"2025-02-05 12:39\",\n \"FullMoon\": \"2025-02-12 18:54\",\n \"LastQuarter\": \"2025-02-12 18:54\"\n },\n {\n \"NewMoon\": \"2025-02-28 05:46\",\n \"FirstQuarter\": \"2025-03-06 21:17\",\n \"FullMoon\": \"2025-03-14 11:55\",\n \"LastQuarter\": \"2025-03-14 11:55\"\n },\n {\n \"NewMoon\": \"2025-03-29 16:00\",\n \"FirstQuarter\": \"2025-04-05 07:05\",\n \"FullMoon\": \"2025-04-13 05:23\",\n \"LastQuarter\": \"2025-04-13 05:23\"\n },\n {\n \"NewMoon\": \"2025-04-28 00:33\",\n \"FirstQuarter\": \"2025-05-04 18:40\",\n \"FullMoon\": \"2025-05-12 21:58\",\n \"LastQuarter\": \"2025-05-12 21:58\"\n },\n {\n \"NewMoon\": \"2025-05-27 08:04\",\n \"FirstQuarter\": \"2025-06-03 08:18\",\n \"FullMoon\": \"2025-06-11 12:46\",\n \"LastQuarter\": \"2025-06-11 12:46\"\n },\n {\n \"NewMoon\": \"2025-06-25 15:33\",\n \"FirstQuarter\": \"2025-07-02 23:55\",\n \"FullMoon\": \"2025-07-11 01:38\",\n \"LastQuarter\": \"2025-07-11 01:38\"\n },\n {\n \"NewMoon\": \"2025-07-25 00:12\",\n \"FirstQuarter\": \"2025-08-01 17:07\",\n \"FullMoon\": \"2025-08-09 12:57\",\n \"LastQuarter\": \"2025-08-09 12:57\"\n },\n {\n \"NewMoon\": \"2025-08-23 11:07\",\n \"FirstQuarter\": \"2025-08-31 11:05\",\n \"FullMoon\": \"2025-09-07 23:10\",\n \"LastQuarter\": \"2025-09-07 23:10\"\n },\n {\n \"NewMoon\": \"2025-09-22 00:54\",\n \"FirstQuarter\": \"2025-09-30 04:51\",\n \"FullMoon\": \"2025-10-07 08:48\",\n \"LastQuarter\": \"2025-10-07 08:48\"\n },\n {\n \"NewMoon\": \"2025-10-21 17:25\",\n \"FirstQuarter\": \"2025-10-29 21:21\",\n \"FullMoon\": \"2025-11-05 18:20\",\n \"LastQuarter\": \"2025-11-05 18:20\"\n },\n {\n \"NewMoon\": \"2025-11-20 11:48\",\n \"FirstQuarter\": \"2025-11-28 11:44\",\n \"FullMoon\": \"2025-12-05 04:15\",\n \"LastQuarter\": \"2025-12-05 04:15\"\n },\n {\n \"NewMoon\": \"2025-12-20 06:44\",\n \"FirstQuarter\": \"2025-12-27 23:35\",\n \"FullMoon\": \"2026-01-03 15:04\",\n \"LastQuarter\": \"2026-01-03 15:04\"\n }\n]" + }, + { + "name": "moonTableCurrent — timestamp timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/v1/moonTableCurrent?utc=5&timeFormat=timestamp", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "moonTableCurrent" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 18:38:09 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "1249" + } + ], + "cookie": [], + "body": "[\n {\n \"NewMoon\": 1735597669,\n \"FirstQuarter\": 1736206453,\n \"FullMoon\": 1736807264,\n \"LastQuarter\": 1736807264\n },\n {\n \"NewMoon\": 1738154238,\n \"FirstQuarter\": 1738741194,\n \"FullMoon\": 1739368466,\n \"LastQuarter\": 1739368466\n },\n {\n \"NewMoon\": 1740703603,\n \"FirstQuarter\": 1741277830,\n \"FullMoon\": 1741935356,\n \"LastQuarter\": 1741935356\n },\n {\n \"NewMoon\": 1743246007,\n \"FirstQuarter\": 1743818724,\n \"FullMoon\": 1744503836,\n \"LastQuarter\": 1744503836\n },\n {\n \"NewMoon\": 1745782402,\n \"FirstQuarter\": 1746366059,\n \"FullMoon\": 1747069086,\n \"LastQuarter\": 1747069086\n },\n {\n \"NewMoon\": 1748315065,\n \"FirstQuarter\": 1748920711,\n \"FullMoon\": 1749627966,\n \"LastQuarter\": 1749627966\n },\n {\n \"NewMoon\": 1750847610,\n \"FirstQuarter\": 1751482545,\n \"FullMoon\": 1752179934,\n \"LastQuarter\": 1752179934\n },\n {\n \"NewMoon\": 1753384347,\n \"FirstQuarter\": 1754050045,\n \"FullMoon\": 1754726227,\n \"LastQuarter\": 1754726227\n },\n {\n \"NewMoon\": 1755929226,\n \"FirstQuarter\": 1756620352,\n \"FullMoon\": 1757268641,\n \"LastQuarter\": 1757268641\n },\n {\n \"NewMoon\": 1758484476,\n \"FirstQuarter\": 1759189874,\n \"FullMoon\": 1759808937,\n \"LastQuarter\": 1759808937\n },\n {\n \"NewMoon\": 1761049558,\n \"FirstQuarter\": 1761754862,\n \"FullMoon\": 1762348826,\n \"LastQuarter\": 1762348826\n },\n {\n \"NewMoon\": 1763621296,\n \"FirstQuarter\": 1764312240,\n \"FullMoon\": 1764890118,\n \"LastQuarter\": 1764890118\n },\n {\n \"NewMoon\": 1766195065,\n \"FirstQuarter\": 1766860558,\n \"FullMoon\": 1767434655,\n \"LastQuarter\": 1767434655\n }\n]" } ] } @@ -1216,7 +2155,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=duration", + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=Mon, 02 Jan 2006 15:04:05", "host": [ "{{baseUrl}}" ], @@ -1232,15 +2171,15 @@ }, { "key": "timeFormat", - "value": "duration", - "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." + "value": "Mon, 02 Jan 2006 15:04:05", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time." } ] } }, "response": [ { - "name": "nextMoonPhase — ISO format", + "name": "nextMoonPhase — custom format", "originalRequest": { "method": "GET", "header": [], @@ -1249,7 +2188,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=ISO", + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=Mon, 02 Jan 2006 15:04:05", "host": [ "{{baseUrl}}" ], @@ -1265,8 +2204,8 @@ }, { "key": "timeFormat", - "value": "ISO", - "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." + "value": "Mon, 02 Jan 2006 15:04:05", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time." } ] } @@ -1281,7 +2220,7 @@ }, { "key": "Date", - "value": "Sun, 16 Nov 2025 15:28:59 GMT" + "value": "Sun, 16 Nov 2025 18:17:07 GMT" }, { "key": "Content-Type", @@ -1296,10 +2235,10 @@ } ], "cookie": [], - "body": "{\n \"NewMoon\": \"2025-11-20T11:48:16+05:00\",\n \"FirstQuarter\": \"2025-11-28T11:44:00+05:00\",\n \"FullMoon\": \"2025-12-05T04:15:18+05:00\",\n \"LastQuarter\": \"2025-12-12T02:04:31+05:00\"\n}" + "body": "{\n \"NewMoon\": \"Thu, 20 Nov 2025 11:48:16\",\n \"FirstQuarter\": \"Fri, 28 Nov 2025 11:44:00\",\n \"FullMoon\": \"Fri, 05 Dec 2025 04:15:18\",\n \"LastQuarter\": \"Fri, 12 Dec 2025 02:04:31\"\n}" }, { - "name": "nextMoonPhase — timestamp format", + "name": "nextMoonPhase — duration format", "originalRequest": { "method": "GET", "header": [], @@ -1308,7 +2247,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=timestamp", + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=duration", "host": [ "{{baseUrl}}" ], @@ -1324,8 +2263,8 @@ }, { "key": "timeFormat", - "value": "timestamp", - "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." + "value": "duration", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time." } ] } @@ -1340,7 +2279,7 @@ }, { "key": "Date", - "value": "Sun, 16 Nov 2025 15:36:45 GMT" + "value": "Sun, 16 Nov 2025 15:37:46 GMT" }, { "key": "Content-Type", @@ -1350,7 +2289,7 @@ }, { "key": "Content-Length", - "value": "95" + "value": "82" } ], "cookie": [ @@ -1360,10 +2299,10 @@ "path": "" } ], - "body": "{\n \"NewMoon\": 1763621296,\n \"FirstQuarter\": 1764312240,\n \"FullMoon\": 1764890118,\n \"LastQuarter\": 1765487071\n}" + "body": "{\n \"NewMoon\": 313830,\n \"FirstQuarter\": 1004774,\n \"FullMoon\": 1582652,\n \"LastQuarter\": 2179605\n}" }, { - "name": "nextMoonPhase — duration format", + "name": "nextMoonPhase — ISO format", "originalRequest": { "method": "GET", "header": [], @@ -1372,7 +2311,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=duration", + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5", "host": [ "{{baseUrl}}" ], @@ -1388,8 +2327,9 @@ }, { "key": "timeFormat", - "value": "duration", - "description": "How the data will be formed. Available values: ISO (default), timestamp, duration. Duration is seconds between current date and event time." + "value": "ISO", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.", + "disabled": true } ] } @@ -1404,17 +2344,76 @@ }, { "key": "Date", - "value": "Sun, 16 Nov 2025 15:37:46 GMT" + "value": "Sun, 16 Nov 2025 15:28:59 GMT" }, { "key": "Content-Type", "value": "application/json", + "name": "Content-Type", "description": "", "type": "text" }, { "key": "Content-Length", - "value": "82" + "value": "163" + } + ], + "cookie": [], + "body": "{\n \"NewMoon\": \"2025-11-20T11:48:16+05:00\",\n \"FirstQuarter\": \"2025-11-28T11:44:00+05:00\",\n \"FullMoon\": \"2025-12-05T04:15:18+05:00\",\n \"LastQuarter\": \"2025-12-12T02:04:31+05:00\"\n}" + }, + { + "name": "nextMoonPhase — timestamp format", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=timestamp", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "nextMoonPhase" + ], + "query": [ + { + "key": "utc", + "value": "5", + "description": "Timezone. Example: \"UTC+5\", \"5\", \"-10:30\"" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time." + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Sun, 16 Nov 2025 15:36:45 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "95" } ], "cookie": [ @@ -1424,7 +2423,7 @@ "path": "" } ], - "body": "{\n \"NewMoon\": 313830,\n \"FirstQuarter\": 1004774,\n \"FullMoon\": 1582652,\n \"LastQuarter\": 2179605\n}" + "body": "{\n \"NewMoon\": 1763621296,\n \"FirstQuarter\": 1764312240,\n \"FullMoon\": 1764890118,\n \"LastQuarter\": 1765487071\n}" } ] } diff --git a/internal/server/route-moon-table.go b/internal/server/route-moon-table.go index 17127d1..8f01f90 100644 --- a/internal/server/route-moon-table.go +++ b/internal/server/route-moon-table.go @@ -2,7 +2,9 @@ package server import ( jt "moon/pkg/julian-time" + "moon/pkg/moon" "strconv" + "strings" "time" "github.com/gofiber/fiber/v2" @@ -16,8 +18,10 @@ func (s *Server) moonTableCurrentV1(c *fiber.Ctx) error { log.Println(err) }*/ + timeFormat := c.Query("timeFormat", "ISO") + tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, loc) - return s.moonTableV1(c, tGiven) + return s.moonTableV1(c, timeFormat, tGiven) } func (s *Server) moonTableYearV1(c *fiber.Ctx) error { @@ -27,14 +31,41 @@ func (s *Server) moonTableYearV1(c *fiber.Ctx) error { log.Println(err) }*/ + timeFormat := c.Query("timeFormat", "ISO") + tNow := time.Now() year := StrToInt(c.Query("year", strconv.Itoa(tNow.Year())), tNow.Year(), 1, 9999) tGiven := time.Date(year, time.January, 1, 0, 0, 0, 0, loc) - return s.moonTableV1(c, tGiven) + return s.moonTableV1(c, timeFormat, tGiven) } -func (s *Server) moonTableV1(c *fiber.Ctx, tGiven time.Time) error { - resp := s.moonCache.GenerateMoonTable(tGiven) - return c.JSON(resp.Elems) +func (s *Server) moonTableV1(c *fiber.Ctx, timeFormat string, tGiven time.Time) error { + table := s.moonCache.GenerateMoonTable(tGiven) + + if strings.ToLower(timeFormat) == "timestamp" { + val := []moon.NearestPhaseTimestamp{} + for i := range table.Elems { + val = append(val, moon.NearestPhaseTimestamp{ + NewMoon: table.Elems[i].NewMoon.Unix(), + FirstQuarter: table.Elems[i].FirstQuarter.Unix(), + FullMoon: table.Elems[i].FullMoon.Unix(), + LastQuarter: table.Elems[i].FullMoon.Unix(), + }) + } + return c.JSON(val) + } else if strings.ToLower(timeFormat) != "iso" { + val := []moon.NearestPhaseString{} + for i := range table.Elems { + val = append(val, moon.NearestPhaseString{ + NewMoon: table.Elems[i].NewMoon.Format(timeFormat), + FirstQuarter: table.Elems[i].FirstQuarter.Format(timeFormat), + FullMoon: table.Elems[i].FullMoon.Format(timeFormat), + LastQuarter: table.Elems[i].FullMoon.Format(timeFormat), + }) + } + return c.JSON(val) + } + + return c.JSON(table.Elems) } diff --git a/internal/server/route-next.go b/internal/server/route-next.go index 970f909..b1eeddc 100644 --- a/internal/server/route-next.go +++ b/internal/server/route-next.go @@ -26,9 +26,7 @@ func (s *Server) moonNextMoonPhaseV1(c *fiber.Ctx) error { LastQuarter: np.LastQuarter.Unix(), } return c.JSON(npt) - } - - if strings.ToLower(format) == "duration" { + } else if strings.ToLower(format) == "duration" { npd := moon.NearestPhaseTimestamp{ NewMoon: np.NewMoon.Unix() - tGiven.Unix(), FirstQuarter: np.FirstQuarter.Unix() - tGiven.Unix(), @@ -36,7 +34,16 @@ func (s *Server) moonNextMoonPhaseV1(c *fiber.Ctx) error { LastQuarter: np.LastQuarter.Unix() - tGiven.Unix(), } return c.JSON(npd) + } else if strings.ToLower(format) == "iso" { + return c.JSON(np) + } else { + npc := moon.NearestPhaseString{ + NewMoon: np.NewMoon.Format(format), + FirstQuarter: np.FirstQuarter.Format(format), + FullMoon: np.FullMoon.Format(format), + LastQuarter: np.LastQuarter.Format(format), + } + return c.JSON(npc) } - return c.JSON(np) } diff --git a/internal/server/route-phase.go b/internal/server/route-phase.go index e81a16d..950a38b 100644 --- a/internal/server/route-phase.go +++ b/internal/server/route-phase.go @@ -132,32 +132,32 @@ func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, loca resp.CurrentState.Illumination = ToFixed(resp.info.IlluminationCurrent*100, precision) resp.EndDay.Illumination = ToFixed(resp.info.IlluminationEndDay*100, precision) - resp.ZodiacDetailed, resp.BeginDay.Zodiac, resp.CurrentState.Zodiac, resp.EndDay.Zodiac = zodiac.CurrentZodiacs(tGiven, loc, lang, s.moonCache.CreateMoonTable(tGiven).Elems) + resp.ZodiacDetailed, resp.BeginDay.Zodiac, resp.CurrentState.Zodiac, resp.EndDay.Zodiac = zodiac.CurrentZodiacs(tGiven, loc, lang, timeFormat, s.moonCache.CreateMoonTable(tGiven).Elems) if locationCords.IsValid { - resp.MoonRiseAndSet, err = s.positionCache.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision, locationCords.Longitude, locationCords.Latitude) + resp.MoonRiseAndSet, err = s.positionCache.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision, timeFormat, locationCords.Longitude, locationCords.Latitude) if err != nil { log.Error(err) } - resp.MoonDaysDetailed = s.moonCache.MoonDetailed(tGiven, loc, lang, locationCords.Longitude, locationCords.Latitude) + resp.MoonDaysDetailed = s.moonCache.MoonDetailed(tGiven, loc, lang, timeFormat, locationCords.Longitude, locationCords.Latitude) newT := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, tGiven.Location()) - resp.BeginDay.Position, err = pos.GetMoonPosition(newT, newT.Location(), precision, locationCords.Longitude, locationCords.Latitude) + resp.BeginDay.Position, err = pos.GetMoonPosition(newT, newT.Location(), precision, timeFormat, locationCords.Longitude, locationCords.Latitude) if err != nil { log.Error(err) } - resp.CurrentState.Position, err = pos.GetMoonPosition(tGiven, tGiven.Location(), precision, locationCords.Longitude, locationCords.Latitude) + resp.CurrentState.Position, err = pos.GetMoonPosition(tGiven, tGiven.Location(), precision, timeFormat, locationCords.Longitude, locationCords.Latitude) if err != nil { log.Error(err) } - resp.EndDay.Position, err = pos.GetMoonPosition(newT.AddDate(0, 0, 1), newT.AddDate(0, 0, 1).Location(), precision, locationCords.Longitude, locationCords.Latitude) + resp.EndDay.Position, err = pos.GetMoonPosition(newT.AddDate(0, 0, 1), newT.AddDate(0, 0, 1).Location(), precision, timeFormat, locationCords.Longitude, locationCords.Latitude) if err != nil { log.Error(err) } } else { - resp.MoonRiseAndSet, err = s.positionCache.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision) + resp.MoonRiseAndSet, err = s.positionCache.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision, timeFormat) } if err != nil && err.Error() != "no location prodived" { diff --git a/internal/server/route-position.go b/internal/server/route-position.go index 71d1386..e581f83 100644 --- a/internal/server/route-position.go +++ b/internal/server/route-position.go @@ -23,6 +23,8 @@ func (s *Server) moonPositionMonthly(c *fiber.Ctx) error { lonStr := c.Query("longitude", "no-value") locationCords := parseCoords(latStr, lonStr) + timeFormat := c.Query("timeFormat", "ISO") + if !locationCords.IsValid { e := ErrorPrintable{} e.Status = 400 @@ -30,7 +32,7 @@ func (s *Server) moonPositionMonthly(c *fiber.Ctx) error { return c.JSON(e) } - if resp, err := s.positionCache.GetRisesMonthly(year, month, loc, precision, locationCords.Longitude, locationCords.Latitude); err == nil { + if resp, err := s.positionCache.GetRisesMonthly(year, month, loc, precision, timeFormat, locationCords.Longitude, locationCords.Latitude); err == nil { return c.JSON(resp) } else { e := ErrorPrintable{} diff --git a/pkg/julian-time/time.go b/pkg/julian-time/time.go index 1c49519..0404c08 100644 --- a/pkg/julian-time/time.go +++ b/pkg/julian-time/time.go @@ -18,7 +18,6 @@ const ( SecondsPerMinute = 60.0 ) -// looking cool, but mismatch with nasa func ToJulianDate(t time.Time) float64 { year := t.Year() month := int(t.Month()) diff --git a/pkg/moon/days.go b/pkg/moon/days.go index bb810b1..f81763e 100644 --- a/pkg/moon/days.go +++ b/pkg/moon/days.go @@ -2,6 +2,7 @@ package moon import ( "math" + "strings" "time" il "moon/pkg/illumination" @@ -92,9 +93,10 @@ func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { return moonTable } -func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, moonTable []*MoonTableElement) time.Time { +func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, timeFormat string, moonTable []*MoonTableElement) *any { + var time any = time.Time{} if len(moonTable) == 0 { - return time.Time{} + return &time } for i := range moonTable { elem := moonTable[i] @@ -102,19 +104,42 @@ func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, moonTable if tGiven.After(elem.NewMoon) && tGiven.Before(elem.LastQuarter) { t := elem.NewMoon t = t.Add(duration) - return t + if strings.ToLower(timeFormat) == "timestamp" { + var tRet any = t.Unix() + return &tRet + } + + if strings.ToLower(timeFormat) != "iso" { + var tRet any = t.Format(timeFormat) + return &tRet + } + + var tRet any = t + return &tRet } if i < len(moonTable)-1 { elem2 := moonTable[i+1] if tGiven.After(elem.LastQuarter) && tGiven.Before(elem2.NewMoon) { t := elem.NewMoon t = t.Add(duration) - return t + + if strings.ToLower(timeFormat) == "timestamp" { + var tRet any = t.Unix() + return &tRet + } + + if strings.ToLower(timeFormat) != "iso" { + var tRet any = t.Format(timeFormat) + return &tRet + } + + var tRet any = t + return &tRet } } } } - return time.Time{} + return &time } func (c *Cache) FindNearestPhase(tGiven time.Time) NearestPhase { diff --git a/pkg/moon/models.go b/pkg/moon/models.go index 16784a8..aa36ca3 100644 --- a/pkg/moon/models.go +++ b/pkg/moon/models.go @@ -29,9 +29,9 @@ type Cache struct { } type MoonDay struct { - Begin *time.Time `json:"Begin,omitempty"` + Begin *any `json:"Begin,omitempty"` IsBeginExists bool - End *time.Time `json:"End,omitempty"` + End *any `json:"End,omitempty"` IsEndExists bool } @@ -53,3 +53,10 @@ type NearestPhaseTimestamp struct { FullMoon int64 LastQuarter int64 } + +type NearestPhaseString struct { + NewMoon string + FirstQuarter string + FullMoon string + LastQuarter string +} diff --git a/pkg/moon/moon.go b/pkg/moon/moon.go index ff237a1..3ffa090 100644 --- a/pkg/moon/moon.go +++ b/pkg/moon/moon.go @@ -23,7 +23,7 @@ func (c *Cache) CurrentMoonDays(tGiven time.Time, loc *time.Location) (time.Dura return beginMoonDays, currentMoonDays, endMoonDays } -func (c *Cache) MoonDetailed(tGiven time.Time, loc *time.Location, lang string, longitude float64, latitude float64) *MoonDaysDetailed { +func (c *Cache) MoonDetailed(tGiven time.Time, loc *time.Location, lang string, timeFormat string, longitude float64, latitude float64) *MoonDaysDetailed { if loc == nil { loc = time.UTC } @@ -36,30 +36,26 @@ func (c *Cache) MoonDetailed(tGiven time.Time, loc *time.Location, lang string, moonDaysDetailed.Day = make([]MoonDay, 2) moonDaysDetailed.Count = 2 - moonRiseYesterday, err1 := pos.GetRisesDay(dayYesterday.Year(), int(dayYesterday.Month()), dayYesterday.Day(), loc, 2, longitude, latitude) - moonRiseToday, err2 := pos.GetRisesDay(dayToday.Year(), int(dayToday.Month()), dayToday.Day(), loc, 2, longitude, latitude) - moonRiseTomorrow, err3 := pos.GetRisesDay(dayTomorrow.Year(), int(dayTomorrow.Month()), dayTomorrow.Day(), loc, 2, longitude, latitude) + moonRiseYesterday, err1 := pos.GetRisesDay(dayYesterday.Year(), int(dayYesterday.Month()), dayYesterday.Day(), loc, 2, timeFormat, longitude, latitude) + moonRiseToday, err2 := pos.GetRisesDay(dayToday.Year(), int(dayToday.Month()), dayToday.Day(), loc, 2, timeFormat, longitude, latitude) + moonRiseTomorrow, err3 := pos.GetRisesDay(dayTomorrow.Year(), int(dayTomorrow.Month()), dayTomorrow.Day(), loc, 2, timeFormat, longitude, latitude) if err1 == nil && err2 == nil { if moonRiseYesterday.IsMoonRise { - moonDaysDetailed.Day[0].Begin = new(time.Time) moonDaysDetailed.Day[0].Begin = moonRiseYesterday.Moonrise.Time moonDaysDetailed.Day[0].IsBeginExists = true } if moonRiseToday.IsMoonRise { - moonDaysDetailed.Day[0].End = new(time.Time) moonDaysDetailed.Day[0].End = moonRiseToday.Moonrise.Time moonDaysDetailed.Day[0].IsEndExists = true } } if err2 == nil && err3 == nil { if moonRiseToday.IsMoonRise { - moonDaysDetailed.Day[1].Begin = new(time.Time) moonDaysDetailed.Day[1].Begin = moonRiseToday.Moonrise.Time moonDaysDetailed.Day[1].IsBeginExists = true } if moonRiseTomorrow.IsMoonRise { - moonDaysDetailed.Day[1].End = new(time.Time) moonDaysDetailed.Day[1].End = moonRiseTomorrow.Moonrise.Time moonDaysDetailed.Day[1].IsEndExists = true } diff --git a/pkg/position/position.go b/pkg/position/position.go index 9ca12ea..7aca8a3 100644 --- a/pkg/position/position.go +++ b/pkg/position/position.go @@ -49,12 +49,12 @@ type Parameters struct { // resp for 1 day type MoonPosition struct { - Timestamp *int64 `json:"Timestamp,omitempty"` - Time *time.Time `json:"Time"` - AzimuthDegrees float64 `json:"AzimuthDegrees"` - AltitudeDegrees float64 `json:"AltitudeDegrees"` - Direction string `json:"Direction"` - DistanceKm float64 `json:"DistanceKm"` + Timestamp *int64 `json:"Timestamp,omitempty"` + Time *any `json:"Time"` + AzimuthDegrees float64 `json:"AzimuthDegrees"` + AltitudeDegrees float64 `json:"AltitudeDegrees"` + Direction string `json:"Direction"` + DistanceKm float64 `json:"DistanceKm"` } type DayData struct { @@ -67,7 +67,7 @@ type DayData struct { Meridian *MoonPosition `json:"Meridian,omitempty"` } -func (c *Cache) GetRisesMonthly(year, month int, loc *time.Location, precision int, location ...float64) (*[]DayData, error) { +func (c *Cache) GetRisesMonthly(year, month int, loc *time.Location, precision int, timeFormat string, location ...float64) (*[]DayData, error) { lat, lon, err := parseLocation(location) if err != nil { return nil, err @@ -93,6 +93,8 @@ func (c *Cache) GetRisesMonthly(year, month int, loc *time.Location, precision i strKey.WriteString(strconv.FormatFloat(lat, 'e', precision, 64)) strKey.WriteString("-") strKey.WriteString(strconv.FormatFloat(lon, 'e', precision, 64)) + strKey.WriteString("-") + strKey.WriteString(timeFormat) if c.CacheMonthly != nil && c.CacheMonthly[strKey.String()] != nil { return c.CacheMonthly[strKey.String()], nil @@ -138,15 +140,18 @@ func (c *Cache) GetRisesMonthly(year, month int, loc *time.Location, precision i for i := range monthResponse.Data { if monthResponse.Data[i].Meridian != nil && monthResponse.Data[i].Meridian.Timestamp != nil { - monthResponse.Data[i].Meridian.Time = timestampToGoTime(monthResponse.Data[i].Meridian.Timestamp, loc) + var t any = timestampToGoTime(monthResponse.Data[i].Meridian.Timestamp, timeFormat, loc) + monthResponse.Data[i].Meridian.Time = &t monthResponse.Data[i].Meridian.Timestamp = nil } if monthResponse.Data[i].Moonrise != nil && monthResponse.Data[i].Moonrise.Timestamp != nil { - monthResponse.Data[i].Moonrise.Time = timestampToGoTime(monthResponse.Data[i].Moonrise.Timestamp, loc) + var t any = timestampToGoTime(monthResponse.Data[i].Moonrise.Timestamp, timeFormat, loc) + monthResponse.Data[i].Moonrise.Time = &t monthResponse.Data[i].Moonrise.Timestamp = nil } if monthResponse.Data[i].Moonset != nil { - monthResponse.Data[i].Moonset.Time = timestampToGoTime(monthResponse.Data[i].Moonset.Timestamp, loc) + var t any = timestampToGoTime(monthResponse.Data[i].Moonset.Timestamp, timeFormat, loc) + monthResponse.Data[i].Moonset.Time = &t monthResponse.Data[i].Moonset.Timestamp = nil } t := time.Date(year, jt.GetMonth(month), 1+i, 0, 0, 0, 0, time.UTC).Format("2006-01-02") @@ -160,7 +165,7 @@ func (c *Cache) GetRisesMonthly(year, month int, loc *time.Location, precision i return &monthResponse.Data, nil } -func GetRisesDay(year, month, day int, loc *time.Location, precision int, location ...float64) (*DayData, error) { +func GetRisesDay(year, month, day int, loc *time.Location, precision int, timeFormat string, location ...float64) (*DayData, error) { lat, lon, err := parseLocation(location) if err != nil { return nil, err @@ -207,22 +212,25 @@ func GetRisesDay(year, month, day int, loc *time.Location, precision int, locati return nil, fmt.Errorf("failed to unmarshal response: %w", err) } if dayResponse.Data.Meridian != nil && dayResponse.Data.Meridian.Timestamp != nil { - dayResponse.Data.Meridian.Time = timestampToGoTime(dayResponse.Data.Meridian.Timestamp, loc) + var t any = timestampToGoTime(dayResponse.Data.Meridian.Timestamp, timeFormat, loc) + dayResponse.Data.Meridian.Time = &t dayResponse.Data.Meridian.Timestamp = nil } if dayResponse.Data.Moonrise != nil && dayResponse.Data.Moonrise.Timestamp != nil { - dayResponse.Data.Moonrise.Time = timestampToGoTime(dayResponse.Data.Moonrise.Timestamp, loc) + var t any = timestampToGoTime(dayResponse.Data.Moonrise.Timestamp, timeFormat, loc) + dayResponse.Data.Moonrise.Time = &t dayResponse.Data.Moonrise.Timestamp = nil } if dayResponse.Data.Moonset != nil { - dayResponse.Data.Moonset.Time = timestampToGoTime(dayResponse.Data.Moonset.Timestamp, loc) + var t any = timestampToGoTime(dayResponse.Data.Moonset.Timestamp, timeFormat, loc) + dayResponse.Data.Moonset.Time = &t dayResponse.Data.Moonset.Timestamp = nil } return dayResponse.Data, nil } -func (c *Cache) GetRisesDay(year, month, day int, loc *time.Location, precision int, location ...float64) (*DayData, error) { +func (c *Cache) GetRisesDay(year, month, day int, loc *time.Location, precision int, timeFormat string, location ...float64) (*DayData, error) { lat, lon, err := parseLocation(location) if err != nil { return nil, err @@ -250,12 +258,14 @@ func (c *Cache) GetRisesDay(year, month, day int, loc *time.Location, precision strKey.WriteString(strconv.FormatFloat(lat, 'e', precision, 64)) strKey.WriteString("-") strKey.WriteString(strconv.FormatFloat(lon, 'e', precision, 64)) + strKey.WriteString("-") + strKey.WriteString(timeFormat) if c.CacheDaily != nil && c.CacheDaily[strKey.String()] != nil { return c.CacheDaily[strKey.String()], nil } - dayResponse, err := GetRisesDay(year, month, day, loc, precision, location...) + dayResponse, err := GetRisesDay(year, month, day, loc, precision, timeFormat, location...) if err != nil { return nil, err } @@ -267,7 +277,7 @@ func (c *Cache) GetRisesDay(year, month, day int, loc *time.Location, precision return dayResponse, nil } -func GetMoonPosition(tGiven time.Time, loc *time.Location, precision int, location ...float64) (*MoonPosition, error) { +func GetMoonPosition(tGiven time.Time, loc *time.Location, precision int, timeFormat string, location ...float64) (*MoonPosition, error) { lat, lon, err := parseLocation(location) if err != nil { return nil, err @@ -318,8 +328,8 @@ func GetMoonPosition(tGiven time.Time, loc *time.Location, precision int, locati return nil, fmt.Errorf("failed to unmarshal response: %w", err) } if pos != nil && pos.Timestamp != nil { - t := pos.Timestamp - pos.Time = timestampToGoTime(t, loc) + var t any = timestampToGoTime(pos.Timestamp, timeFormat, loc) + pos.Time = &t pos.Timestamp = nil } @@ -337,11 +347,26 @@ func parseLocation(location []float64) (lat, lon float64, err error) { return lat, lon, nil } -func timestampToGoTime(ev *int64, loc *time.Location) *time.Time { +func timestampToGoTime(ev *int64, timeFormat string, loc *time.Location) *any { + if ev == nil { + return nil + } utcTime := time.Unix(*ev, 0).UTC() time := utcTime if loc != nil { time = time.In(loc) } - return &time + + if strings.ToLower(timeFormat) == "timestamp" { + var t any = time.Unix() + return &t + } + + if strings.ToLower(timeFormat) != "iso" { + var t any = time.Format(timeFormat) + return &t + } + + var t any = time + return &t } diff --git a/pkg/zodiac/models.go b/pkg/zodiac/models.go index 9eaee83..bac5dec 100644 --- a/pkg/zodiac/models.go +++ b/pkg/zodiac/models.go @@ -1,13 +1,11 @@ package zodiac -import "time" - type ZodiacDetailed struct { Name string NameLocalized string Emoji string - Begin time.Time - End time.Time + Begin *any + End *any } type Zodiacs struct { diff --git a/pkg/zodiac/zodiac.go b/pkg/zodiac/zodiac.go index 55e373e..3f3331c 100644 --- a/pkg/zodiac/zodiac.go +++ b/pkg/zodiac/zodiac.go @@ -6,7 +6,7 @@ import ( "time" ) -func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, moonTable []*moon.MoonTableElement) (*Zodiacs, Zodiac, Zodiac, Zodiac) { +func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeFormat string, moonTable []*moon.MoonTableElement) (*Zodiacs, Zodiac, Zodiac, Zodiac) { zods := new(Zodiacs) zodiacBegin := Zodiac{} @@ -28,11 +28,11 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, moonTable zods.Count = 1 zodBegin := zodiacPositionBegin * jt.Fminute / 360 * 30. * 30. zodEnd := (zodiacPositionEnd + 1) * jt.Fminute / 360 * 30. * 30. - tBegin := moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin)*time.Minute, moonTable) - tEnd := moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd)*time.Minute, moonTable) + var tBegin any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin)*time.Minute, timeFormat, moonTable) + var tEnd any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd)*time.Minute, timeFormat, moonTable) zods.Zodiac = make([]ZodiacDetailed, 1) - zods.Zodiac[0].Begin = tBegin - zods.Zodiac[0].End = tEnd + zods.Zodiac[0].Begin = &tBegin + zods.Zodiac[0].End = &tEnd zods.Zodiac[0].Name, zods.Zodiac[0].Emoji = getZodiacResp(zodiacPositionBegin) zods.Zodiac[0].NameLocalized = getZodiacRespLocalized(zodiacPositionBegin, lang) } else { @@ -40,11 +40,11 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, moonTable zodBegin1 := (zodiacPositionBegin) * jt.Fminute / 360 * 30. * 30. zodEnd1 := (zodiacPositionBegin + 1) * jt.Fminute / 360 * 30. * 30. - tBegin1 := moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin1)*time.Minute, moonTable) - tEnd1 := moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd1)*time.Minute, moonTable) + var tBegin1 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin1)*time.Minute, timeFormat, moonTable) + var tEnd1 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd1)*time.Minute, timeFormat, moonTable) zods.Zodiac = make([]ZodiacDetailed, 2) - zods.Zodiac[0].Begin = tBegin1 - zods.Zodiac[0].End = tEnd1 + zods.Zodiac[0].Begin = &tBegin1 + zods.Zodiac[0].End = &tEnd1 zods.Zodiac[0].Name, zods.Zodiac[0].Emoji = getZodiacResp(zodiacPositionBegin) zods.Zodiac[0].NameLocalized = getZodiacRespLocalized(zodiacPositionBegin, lang) @@ -55,10 +55,10 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, moonTable zodBegin2 := (zodiacPositionEnd) * jt.Fminute / 360 * 30. * 30. zodEnd2 := (zodiacPositionEnd + 1) * jt.Fminute / 360 * 30. * 30. - tBegin2 := moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin2)*time.Minute, moonTable) - tEnd2 := moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd2)*time.Minute, moonTable) - zods.Zodiac[1].Begin = tBegin2 - zods.Zodiac[1].End = tEnd2 + var tBegin2 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin2)*time.Minute, timeFormat, moonTable) + var tEnd2 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd2)*time.Minute, timeFormat, moonTable) + zods.Zodiac[1].Begin = &tBegin2 + zods.Zodiac[1].End = &tEnd2 zods.Zodiac[1].Name, zods.Zodiac[1].Emoji = getZodiacResp(zodiacPositionEnd) zods.Zodiac[1].NameLocalized = getZodiacRespLocalized(zodiacPositionEnd, lang) } From c26a140b83bbfd48df454f99d276b831234d0501 Mon Sep 17 00:00:00 2001 From: prostraction Date: Sun, 16 Nov 2025 23:49:17 +0500 Subject: [PATCH 09/18] chore: bump version --- internal/server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/server/server.go b/internal/server/server.go index 5069ca9..bc7a8b3 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -102,5 +102,5 @@ func (s *Server) NewRouter() *fiber.App { } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc2") + return c.JSON("1.2.0rc3") } From dd6029c2d28b8b55bb667f7b41a702f151cc3f94 Mon Sep 17 00:00:00 2001 From: prostraction Date: Mon, 17 Nov 2025 00:07:58 +0500 Subject: [PATCH 10/18] docs: Postman update --- doc/postman_collection | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/postman_collection b/doc/postman_collection index 88ad1ca..f3d3401 100644 --- a/doc/postman_collection +++ b/doc/postman_collection @@ -15,7 +15,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPhaseDate?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=timestamp", + "raw": "{{baseUrl}}/v1/moonPhaseDate?utc=UTC+5&lang=ru&precision=5&latitude=51.1655&longitude=71.4272&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=2006-01-02 15:04", "host": [ "{{baseUrl}}" ], @@ -81,7 +81,7 @@ }, { "key": "timeFormat", - "value": "timestamp", + "value": "2006-01-02 15:04", "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] @@ -993,7 +993,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5", + "raw": "{{baseUrl}}/v1/moonPositionMonthly?utc=UTC+5&latitude=51.1655&longitude=71.4272&year=2023&month=3&precision=5&timeFormat=2006-01-02 15:04", "host": [ "{{baseUrl}}" ], @@ -1031,6 +1031,11 @@ "key": "precision", "value": "5", "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "timeFormat", + "value": "2006-01-02 15:04", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] } From b6f93674078073b20da02991ec9e4cf791f02353 Mon Sep 17 00:00:00 2001 From: prostraction <47314760+prostraction@users.noreply.github.com> Date: Mon, 17 Nov 2025 07:33:20 +0300 Subject: [PATCH 11/18] feat: timeFormat for jtime methods #5 --- README.md | 69 ++++- doc/postman_collection | 536 +++++++++++++++++++++++++++++---- internal/server/models.go | 5 +- internal/server/route-jtime.go | 42 ++- internal/server/server.go | 2 +- 5 files changed, 569 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index 9f71aa6..fcb1293 100644 --- a/README.md +++ b/README.md @@ -1006,6 +1006,7 @@ The method converts human date to julian time (UTC +0 timezone). | Parameter | Type | Description | Example Value | | :--- | :--- | :--- | :--- | |`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` |`year` | `int [optional, default=]` | Format: YYYY Allowed range: [1, 9999] | `2025` |`month` | `int [optional, default=]` | Format: M or MM. Allowed range: [1, 12] | `01` or `1` |`day` | `int [optional, default=]` | Format: D or DD. Allowed range: [1, 31] | `01` or `1` @@ -1013,12 +1014,29 @@ The method converts human date to julian time (UTC +0 timezone). |`minute` | `int [optional, default=]` | Format: m or mm. Allowed range: [0, 59] | `01` or `1` |`second` | `int [optional, default=]` | Format: s or ss. Allowed range: [0, 59] | `01` or `1` -#### Response example +#### Response examples + +```json +{ + "CivilDate": "2025-01-01T01:01:01Z", + "JulianDate": 2460676.54237 +} +``` + +timeFormat: `Mon, 02 Jan 2006 15:04:05` ```json { - "CivilDate": "2025-01-01 01:01:01 +0000 UTC", - "CivilDateTimestamp": 1735693261, + "CivilDate": "Wed, 01 Jan 2025 01:01:01", + "JulianDate": 2460676.54237 +} +``` + +timeFormat: `timestamp` + +```json +{ + "CivilDate": 1735693261, "JulianDate": 2460676.54237 } ``` @@ -1035,18 +1053,34 @@ The method converts human date to julian time (UTC +0 timezone). | :--- | :--- | :--- | :--- | |`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` |`timestamp` | `int [optional, default=]` | Timestamp for calculations. Current, if not specified | `1735693261` - +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` #### Response example ```json { - "CivilDate": "2025-01-01 01:01:01 +0000 UTC", - "CivilDateTimestamp": 1735693261, + "CivilDate": "2025-01-01T01:01:01Z", + "JulianDate": 2460676.54237 +} +``` + +timeFormat: `Mon, 02 Jan 2006 15:04:05` + +```json +{ + "CivilDate": "Wed, 01 Jan 2025 01:01:01", "JulianDate": 2460676.54237 } ``` +timeFormat: `timestamp` + +```json +{ + "CivilDate": 1735693261, + "JulianDate": 2460676.54237 +} + ---------------------------------------------------------------- ### GET /api/v1/fromJulianTime @@ -1059,14 +1093,31 @@ The method converts julian time to human time (UTC +0 timezone). | :--- | :--- | :--- | :--- | |`precision` | `int [optional, default=2]` | How many digits after ```.``` will be in output. Allowed range: [1, 20] | `5` |`jtime` | `float64 [required]` | Julian Time to convert (float64) | `2460676.5423726854` +|`timeFormat` | `string [optional]` | How the data will be formed. Available values: ISO (default), timestamp, duration or Go format. Duration is seconds between current date and event time.| `duration` +#### Response examples -#### Response example +```json +{ + "CivilDate": "2025-01-01T01:01:01Z", + "JulianDate": 2460676.54237 +} +``` + +timeFormat: `Mon, 02 Jan 2006 15:04:05` + +```json +{ + "CivilDate": "Wed, 01 Jan 2025 01:01:01", + "JulianDate": 2460676.54237 +} +``` + +timeFormat: `timestamp` ```json { - "CivilDate": "2025-01-01 01:01:01 +0000 UTC", - "CivilDateTimestamp": 1735693261, + "CivilDate": 1735693261, "JulianDate": 2460676.54237 } ``` diff --git a/doc/postman_collection b/doc/postman_collection index f3d3401..dea4ad2 100644 --- a/doc/postman_collection +++ b/doc/postman_collection @@ -1,9 +1,9 @@ { "info": { - "_postman_id": "9d158224-dba6-4890-ad63-22f987406d00", + "_postman_id": "026b2bd4-85f5-43d9-8f57-f1d26a4e548a", "name": "Moon Collection V1", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "19971618" + "_exporter_id": "37785427" }, "item": [ { @@ -878,7 +878,7 @@ }, "status": "OK", "code": 200, - "_postman_previewlanguage": null, + "_postman_previewlanguage": "Text", "header": [ { "key": "Server", @@ -1251,7 +1251,7 @@ }, "status": "OK", "code": 200, - "_postman_previewlanguage": null, + "_postman_previewlanguage": "Text", "header": [ { "key": "Server", @@ -1497,7 +1497,7 @@ }, "status": "OK", "code": 200, - "_postman_previewlanguage": null, + "_postman_previewlanguage": "Text", "header": [ { "key": "Server", @@ -1554,7 +1554,7 @@ }, "status": "OK", "code": 200, - "_postman_previewlanguage": null, + "_postman_previewlanguage": "Text", "header": [ { "key": "Server", @@ -1625,15 +1625,6 @@ "originalRequest": { "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { "raw": "{{baseUrl}}/v1/moonTableCurrent?utc=5", "host": [ @@ -1691,15 +1682,6 @@ "originalRequest": { "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { "raw": "{{baseUrl}}/v1/moonTableCurrent?utc=5&timeFormat=2006-01-02 15:04", "host": [ @@ -1725,7 +1707,7 @@ }, "status": "OK", "code": 200, - "_postman_previewlanguage": null, + "_postman_previewlanguage": "Text", "header": [ { "key": "Server", @@ -1752,15 +1734,6 @@ "originalRequest": { "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { "raw": "{{baseUrl}}/v1/moonTableCurrent?utc=5&timeFormat=timestamp", "host": [ @@ -1786,7 +1759,7 @@ }, "status": "OK", "code": 200, - "_postman_previewlanguage": null, + "_postman_previewlanguage": "Text", "header": [ { "key": "Server", @@ -1828,7 +1801,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/toJulianTimeByDate?precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01", + "raw": "{{baseUrl}}/v1/toJulianTimeByDate?precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=02 Jan 2006 15:04:05", "host": [ "{{baseUrl}}" ], @@ -1871,6 +1844,11 @@ "key": "second", "value": "01", "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "02 Jan 2006 15:04:05", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] } @@ -1886,7 +1864,93 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/toJulianTimeByDate?precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01", + "raw": "{{baseUrl}}/v1/toJulianTimeByDate?precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=ISO", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "toJulianTimeByDate" + ], + "query": [ + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "year", + "value": "2025", + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" + }, + { + "key": "month", + "value": "01", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "day", + "value": "01", + "description": "Format: D or DD. Allowed range: [1, 31]. Current, if not specified" + }, + { + "key": "hour", + "value": "01", + "description": "Format: h or hh. Allowed range: [0, 23]. Current, if not specified" + }, + { + "key": "minute", + "value": "01", + "description": "Format: m or mm. Allowed range: [0, 59] Current, if not specified" + }, + { + "key": "second", + "value": "01", + "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "ISO", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Mon, 17 Nov 2025 04:26:30 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "63" + } + ], + "cookie": [], + "body": "{\n \"CivilDate\": \"2025-01-01T01:01:01Z\",\n \"JulianDate\": 2460676.54237\n}" + }, + { + "name": "toJulianTimeByDate — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/toJulianTimeByDate?precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=Mon, 02 Jan 2006 15:04:05", "host": [ "{{baseUrl}}" ], @@ -1929,6 +1993,11 @@ "key": "second", "value": "01", "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "Mon, 02 Jan 2006 15:04:05", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] } @@ -1943,21 +2012,111 @@ }, { "key": "Date", - "value": "Tue, 04 Nov 2025 15:16:26 GMT" + "value": "Mon, 17 Nov 2025 04:23:36 GMT" }, { "key": "Content-Type", "value": "application/json", + "name": "Content-Type", "description": "", "type": "text" }, { "key": "Content-Length", - "value": "104" + "value": "68" } ], "cookie": [], - "body": "{\n \"CivilDate\": \"2025-01-01 01:01:01 +0000 UTC\",\n \"CivilDateTimestamp\": 1735693261,\n \"JulianDate\": 2460676.54237\n}" + "body": "{\n \"CivilDate\": \"Wed, 01 Jan 2025 01:01:01\",\n \"JulianDate\": 2460676.54237\n}" + }, + { + "name": "toJulianTimeByDate — timestamp timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/toJulianTimeByDate?precision=5&year=2025&month=01&day=01&hour=01&minute=01&second=01&timeFormat=timestamp", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "toJulianTimeByDate" + ], + "query": [ + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "year", + "value": "2025", + "description": "Format: YYYY Allowed range: [1, 9999]. Current, if not specified" + }, + { + "key": "month", + "value": "01", + "description": "Format: M or MM. Allowed range: [1, 12]. Current, if not specified" + }, + { + "key": "day", + "value": "01", + "description": "Format: D or DD. Allowed range: [1, 31]. Current, if not specified" + }, + { + "key": "hour", + "value": "01", + "description": "Format: h or hh. Allowed range: [0, 23]. Current, if not specified" + }, + { + "key": "minute", + "value": "01", + "description": "Format: m or mm. Allowed range: [0, 59] Current, if not specified" + }, + { + "key": "second", + "value": "01", + "description": "Format: s or ss. Allowed range: [0, 59]. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Mon, 17 Nov 2025 04:24:35 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "51" + } + ], + "cookie": [], + "body": "{\n \"CivilDate\": 1735693261,\n \"JulianDate\": 2460676.54237\n}" } ] }, @@ -1974,7 +2133,7 @@ "raw": "" }, "url": { - "raw": "{{baseUrl}}/v1/toJulianTimeByTimestamp?precision=10×tamp=1735693261", + "raw": "{{baseUrl}}/v1/toJulianTimeByTimestamp?precision=10×tamp=1735693261&timeFormat=Mon, 02 Jan 2006 15:04:05", "host": [ "{{baseUrl}}" ], @@ -1992,6 +2151,11 @@ "key": "timestamp", "value": "1735693261", "description": "Timestamp for calculations. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "Mon, 02 Jan 2006 15:04:05", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] } @@ -2039,21 +2203,155 @@ }, { "key": "Date", - "value": "Tue, 04 Nov 2025 15:23:01 GMT" + "value": "Mon, 17 Nov 2025 04:26:15 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "68" + } + ], + "cookie": [], + "body": "{\n \"CivilDate\": \"2025-01-01T01:01:01Z\",\n \"JulianDate\": 2460676.5423726854\n}" + }, + { + "name": "toJulianTimeByTimestamp — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/toJulianTimeByTimestamp?precision=10×tamp=1735693261&timeFormat=Mon, 02 Jan 2006 15:04:05", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "toJulianTimeByTimestamp" + ], + "query": [ + { + "key": "precision", + "value": "10", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "timestamp", + "value": "1735693261", + "description": "Timestamp for calculations. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "Mon, 02 Jan 2006 15:04:05", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Mon, 17 Nov 2025 04:27:18 GMT" }, { "key": "Content-Type", "value": "application/json", + "name": "Content-Type", "description": "", "type": "text" }, { "key": "Content-Length", - "value": "109" + "value": "73" } ], "cookie": [], - "body": "{\n \"CivilDate\": \"2025-01-01 01:01:01 +0000 UTC\",\n \"CivilDateTimestamp\": 1735693261,\n \"JulianDate\": 2460676.5423726854\n}" + "body": "{\n \"CivilDate\": \"Wed, 01 Jan 2025 01:01:01\",\n \"JulianDate\": 2460676.5423726854\n}" + }, + { + "name": "toJulianTimeByTimestamp — timestamp timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/v1/toJulianTimeByTimestamp?precision=10×tamp=1735693261&timeFormat=timestamp", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "toJulianTimeByTimestamp" + ], + "query": [ + { + "key": "precision", + "value": "10", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "timestamp", + "value": "1735693261", + "description": "Timestamp for calculations. Current, if not specified" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Mon, 17 Nov 2025 04:27:47 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "56" + } + ], + "cookie": [ + { + "expires": "Invalid Date", + "domain": "", + "path": "" + } + ], + "body": "{\n \"CivilDate\": 1735693261,\n \"JulianDate\": 2460676.5423726854\n}" } ] }, @@ -2063,7 +2361,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/v1/fromJulianTime?precision=5&jtime=2460676.5423726854", + "raw": "{{baseUrl}}/v1/fromJulianTime?precision=5&jtime=2460676.5423726854&timeFormat=Mon, 02 Jan 2006 15:04:05", "host": [ "{{baseUrl}}" ], @@ -2081,6 +2379,11 @@ "key": "jtime", "value": "2460676.5423726854", "description": "Julian Time to convert (float64)" + }, + { + "key": "timeFormat", + "value": "Mon, 02 Jan 2006 15:04:05", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" } ] } @@ -2124,21 +2427,142 @@ }, { "key": "Date", - "value": "Tue, 04 Nov 2025 15:17:41 GMT" + "value": "Mon, 17 Nov 2025 04:28:25 GMT" }, { "key": "Content-Type", "value": "application/json", + "name": "Content-Type", "description": "", "type": "text" }, { "key": "Content-Length", - "value": "104" + "value": "63" } ], "cookie": [], - "body": "{\n \"CivilDate\": \"2025-01-01 01:01:01 +0000 UTC\",\n \"CivilDateTimestamp\": 1735693261,\n \"JulianDate\": 2460676.54237\n}" + "body": "{\n \"CivilDate\": \"2025-01-01T01:01:01Z\",\n \"JulianDate\": 2460676.54237\n}" + }, + { + "name": "fromJulianTime — custom timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/fromJulianTime?precision=5&jtime=2460676.5423726854&timeFormat=Mon, 02 Jan 2006 15:04:05", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "fromJulianTime" + ], + "query": [ + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "jtime", + "value": "2460676.5423726854", + "description": "Julian Time to convert (float64)" + }, + { + "key": "timeFormat", + "value": "Mon, 02 Jan 2006 15:04:05", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Mon, 17 Nov 2025 04:30:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "68" + } + ], + "cookie": [], + "body": "{\n \"CivilDate\": \"Wed, 01 Jan 2025 01:01:01\",\n \"JulianDate\": 2460676.54237\n}" + }, + { + "name": "fromJulianTime — timestamp timeFormat", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/v1/fromJulianTime?precision=5&jtime=2460676.5423726854&timeFormat=timestamp", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "v1", + "fromJulianTime" + ], + "query": [ + { + "key": "precision", + "value": "5", + "description": "How many digits after . will be in output. Allowed range: [1, 20]" + }, + { + "key": "jtime", + "value": "2460676.5423726854", + "description": "Julian Time to convert (float64)" + }, + { + "key": "timeFormat", + "value": "timestamp", + "description": "Supports: \"ISO\" (default), \"timestamp\" any Go time format directives (without UTC), for example: \"Mon, 02 Jan 2006 15:04:05\"" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Server", + "value": "Fiber" + }, + { + "key": "Date", + "value": "Mon, 17 Nov 2025 04:30:57 GMT" + }, + { + "key": "Content-Type", + "value": "application/json", + "name": "Content-Type", + "description": "", + "type": "text" + }, + { + "key": "Content-Length", + "value": "51" + } + ], + "cookie": [], + "body": "{\n \"CivilDate\": 1735693261,\n \"JulianDate\": 2460676.54237\n}" } ] } @@ -2188,10 +2612,6 @@ "originalRequest": { "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=Mon, 02 Jan 2006 15:04:05", "host": [ @@ -2247,10 +2667,6 @@ "originalRequest": { "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=duration", "host": [ @@ -2311,10 +2727,6 @@ "originalRequest": { "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5", "host": [ @@ -2371,10 +2783,6 @@ "originalRequest": { "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{baseUrl}}/v1/nextMoonPhase?utc=5&timeFormat=timestamp", "host": [ diff --git a/internal/server/models.go b/internal/server/models.go index b650ec7..2a0cbfb 100644 --- a/internal/server/models.go +++ b/internal/server/models.go @@ -56,7 +56,6 @@ type Coordinates struct { } type JulianTimeResp struct { - CivilDate string - CivilDateTimestamp int64 - JulianDate float64 + CivilDate *any + JulianDate float64 } diff --git a/internal/server/route-jtime.go b/internal/server/route-jtime.go index 45dd40d..8d06397 100644 --- a/internal/server/route-jtime.go +++ b/internal/server/route-jtime.go @@ -2,6 +2,7 @@ package server import ( "strconv" + "strings" "time" "github.com/gofiber/fiber/v2" @@ -20,6 +21,8 @@ func (s *Server) toJulianTimeByDateV1(c *fiber.Ctx) error { month := StrToInt(c.Query("month", strconv.Itoa(int(tNow.Month()))), int(tNow.Month()), 1, 12) day := StrToInt(c.Query("day", strconv.Itoa(int(tNow.Day()))), int(tNow.Day()), 1, 31) + timeFormat := c.Query("timeFormat", "ISO") + err := IsValidDate(year, month, day) if err != nil { c.Status(400) @@ -35,7 +38,9 @@ func (s *Server) toJulianTimeByDateV1(c *fiber.Ctx) error { second := StrToInt(c.Query("second", strconv.Itoa(int(tNow.Second()))), int(tNow.Second()), 0, 59) tGiven := time.Date(year, jt.GetMonth(month), day, hour, minute, second, 0, time.UTC) - return s.toJulianTimeV1(c, tGiven, precision) + + resp := s.toJulianTimeV1(c, tGiven, timeFormat, precision) + return c.JSON(resp) } func (s *Server) toJulianTimeByTimestampV1(c *fiber.Ctx) error { @@ -45,20 +50,32 @@ func (s *Server) toJulianTimeByTimestampV1(c *fiber.Ctx) error { t = time.Now().Unix() } + timeFormat := c.Query("timeFormat", "ISO") precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) tm := time.Unix(t, 0) tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, time.Local) tGiven = tGiven.In(time.UTC) - return s.toJulianTimeV1(c, tGiven, precision) + + resp := s.toJulianTimeV1(c, tGiven, timeFormat, precision) + return c.JSON(resp) } -func (s *Server) toJulianTimeV1(c *fiber.Ctx, tGiven time.Time, precision int) error { +func (s *Server) toJulianTimeV1(c *fiber.Ctx, tGiven time.Time, timeFormat string, precision int) JulianTimeResp { resp := JulianTimeResp{} - resp.CivilDate = tGiven.String() - resp.CivilDateTimestamp = tGiven.Unix() + + var t any + if strings.ToLower(timeFormat) == "timestamp" { + t = tGiven.Unix() + } else if strings.ToLower(timeFormat) != "iso" { + t = tGiven.Format(timeFormat) + } else { + t = tGiven + } + + resp.CivilDate = &t resp.JulianDate = ToFixed(jt.ToJulianDate(tGiven), precision) - return c.JSON(resp) + return resp } func (s *Server) fromJulianTimeV1(c *fiber.Ctx) error { @@ -79,10 +96,19 @@ func (s *Server) fromJulianTimeV1(c *fiber.Ctx) error { t := jt.FromJulianDate(jtime, loc) precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) + timeFormat := c.Query("timeFormat", "ISO") + + var tFormat any + if strings.ToLower(timeFormat) == "timestamp" { + tFormat = t.Unix() + } else if strings.ToLower(timeFormat) != "iso" { + tFormat = t.Format(timeFormat) + } else { + tFormat = t + } resp := JulianTimeResp{} - resp.CivilDate = t.String() - resp.CivilDateTimestamp = t.Unix() + resp.CivilDate = &tFormat resp.JulianDate = ToFixed(jtime, precision) return c.JSON(resp) diff --git a/internal/server/server.go b/internal/server/server.go index bc7a8b3..3024cb2 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -102,5 +102,5 @@ func (s *Server) NewRouter() *fiber.App { } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc3") + return c.JSON("1.2.0rc4") } From 807d390052513c67fc09793d849d722f11953824 Mon Sep 17 00:00:00 2001 From: prostraction Date: Wed, 19 Nov 2025 01:26:57 +0500 Subject: [PATCH 12/18] feat: correct moon days, correct zodiacs. closes #5 --- internal/server/server.go | 2 +- pkg/moon/days.go | 45 ++++++++++++++++++++++++++++++++++----- pkg/moon/moon.go | 8 ++++--- pkg/zodiac/zodiac.go | 26 +++++++++++----------- 4 files changed, 59 insertions(+), 22 deletions(-) diff --git a/internal/server/server.go b/internal/server/server.go index bc7a8b3..d8ad868 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -102,5 +102,5 @@ func (s *Server) NewRouter() *fiber.App { } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc3") + return c.JSON("1.2.0rc5") } diff --git a/pkg/moon/days.go b/pkg/moon/days.go index f81763e..a997344 100644 --- a/pkg/moon/days.go +++ b/pkg/moon/days.go @@ -94,14 +94,14 @@ func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { } func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, timeFormat string, moonTable []*MoonTableElement) *any { - var time any = time.Time{} + var tt any = time.Time{} if len(moonTable) == 0 { - return &time + return &tt } for i := range moonTable { elem := moonTable[i] if elem.t1 != elem.t2 { - if tGiven.After(elem.NewMoon) && tGiven.Before(elem.LastQuarter) { + if tGiven.After(elem.NewMoon) && tGiven.Before(elem.NewMoon.Add(time.Hour*24*32)) { t := elem.NewMoon t = t.Add(duration) if strings.ToLower(timeFormat) == "timestamp" { @@ -139,7 +139,7 @@ func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, timeFormat } } } - return &time + return &tt } func (c *Cache) FindNearestPhase(tGiven time.Time) NearestPhase { @@ -162,16 +162,51 @@ func (c *Cache) FindNearestPhase(tGiven time.Time) NearestPhase { return np } +/* func GetMoonDays(tGiven time.Time, table []*MoonTableElement) time.Duration { var moonDays time.Duration for i := range table { elem := table[i] if elem.t1 != elem.t2 { - if tGiven.After(elem.NewMoon) /*&& tGiven.Before(elem.TFull)*/ { + if tGiven.After(elem.NewMoon) { moonDays = tGiven.Sub(elem.NewMoon) } } } return moonDays +}*/ + +func GetMoonDaysPrecise(tGiven time.Time, table []*MoonTableElement) (time.Duration, time.Duration, time.Duration) { + for i := range table { + elem := table[i] + if elem.t1 != elem.t2 { + if tGiven.After(elem.NewMoon) && tGiven.Before(elem.NewMoon.Add(time.Hour*24*32)) { + var elem2 *MoonTableElement + if i < len(table)-1 { + elem2 = table[i+1] // new next moon + } else { + elem2 = new(MoonTableElement) // new next moon approx (fix?) + elem2.NewMoon = elem.NewMoon.AddDate(0, 0, 29) + elem2.NewMoon = elem.NewMoon.Add(time.Hour * 12) + } + moonMonDays := elem2.NewMoon.Sub(elem.NewMoon) // moon month + + eartbeg := tGiven.Add(-tGiven.Sub(elem.NewMoon)) + eartend := time.Date(eartbeg.Year(), eartbeg.Month()+1, eartbeg.Day(), eartbeg.Hour(), eartbeg.Minute(), eartbeg.Second(), 0, tGiven.Location()) + eartMon := eartend.Unix() - eartbeg.Unix() // earth month + + beginDay := elem.NewMoon + day := time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*24.)) + + for tGiven.Sub(beginDay) > day { + beginDay = beginDay.Add(day) + } + + endDay := beginDay.Add(day) + return beginDay.Sub(elem.NewMoon) % moonMonDays, endDay.Sub(elem.NewMoon) % moonMonDays, moonMonDays + } + } + } + return time.Duration(0), time.Duration(0), time.Duration(0) } diff --git a/pkg/moon/moon.go b/pkg/moon/moon.go index 3ffa090..68f77a8 100644 --- a/pkg/moon/moon.go +++ b/pkg/moon/moon.go @@ -16,9 +16,11 @@ func (c *Cache) CurrentMoonDays(tGiven time.Time, loc *time.Location) (time.Dura dayEndTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day()+1, 0, 0, 0, 0, loc) moonTable := c.CreateMoonTable(tGiven) - beginMoonDays := GetMoonDays(dayBeginTime, moonTable.Elems) - currentMoonDays := GetMoonDays(tGiven, moonTable.Elems) - endMoonDays := GetMoonDays(dayEndTime, moonTable.Elems) + + beginMoonDays, endMoonDays, _ := GetMoonDaysPrecise(dayBeginTime, moonTable.Elems) + + dayProgress := float64(tGiven.Sub(dayBeginTime)) / float64(dayEndTime.Sub(dayBeginTime)) + currentMoonDays := beginMoonDays + time.Duration(float64(endMoonDays-beginMoonDays)*dayProgress) return beginMoonDays, currentMoonDays, endMoonDays } diff --git a/pkg/zodiac/zodiac.go b/pkg/zodiac/zodiac.go index 3f3331c..c034a29 100644 --- a/pkg/zodiac/zodiac.go +++ b/pkg/zodiac/zodiac.go @@ -16,18 +16,18 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma dayBeginTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, loc) dayEndTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day()+1, 0, 0, 0, 0, loc) - beginMoonDays := moon.GetMoonDays(dayBeginTime, moonTable) - currentMoonDays := moon.GetMoonDays(tGiven, moonTable) - endMoonDays := moon.GetMoonDays(dayEndTime, moonTable) + beginMoonDays, endMoonDays, monthDays := moon.GetMoonDaysPrecise(dayBeginTime, moonTable) + dayProgress := float64(tGiven.Sub(dayBeginTime)) / float64(dayEndTime.Sub(dayBeginTime)) + currentMoonDays := beginMoonDays + time.Duration(float64(endMoonDays-beginMoonDays)*dayProgress) - zodiacPositionBegin := int((beginMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 - zodiacPositionCurrent := int((currentMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 - zodiacPositionEnd := int((endMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 + zodiacPositionBegin := int((beginMoonDays.Minutes()/jt.Fminute*360.)/30./(monthDays.Hours()/24.)) % 12 + zodiacPositionCurrent := int((currentMoonDays.Minutes()/jt.Fminute*360.)/30./(monthDays.Hours()/24.)) % 12 + zodiacPositionEnd := int((endMoonDays.Minutes()/jt.Fminute*360.)/30./(monthDays.Hours()/24.)) % 12 if zodiacPositionBegin == zodiacPositionEnd { zods.Count = 1 - zodBegin := zodiacPositionBegin * jt.Fminute / 360 * 30. * 30. - zodEnd := (zodiacPositionEnd + 1) * jt.Fminute / 360 * 30. * 30. + zodBegin := zodiacPositionBegin * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) + zodEnd := (zodiacPositionEnd + 1) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) var tBegin any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin)*time.Minute, timeFormat, moonTable) var tEnd any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd)*time.Minute, timeFormat, moonTable) zods.Zodiac = make([]ZodiacDetailed, 1) @@ -37,8 +37,8 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma zods.Zodiac[0].NameLocalized = getZodiacRespLocalized(zodiacPositionBegin, lang) } else { zods.Count = 2 - zodBegin1 := (zodiacPositionBegin) * jt.Fminute / 360 * 30. * 30. - zodEnd1 := (zodiacPositionBegin + 1) * jt.Fminute / 360 * 30. * 30. + zodBegin1 := (zodiacPositionBegin) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) + zodEnd1 := (zodiacPositionBegin + 1) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) var tBegin1 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin1)*time.Minute, timeFormat, moonTable) var tEnd1 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd1)*time.Minute, timeFormat, moonTable) @@ -50,11 +50,11 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma if int(endMoonDays.Minutes()/jt.Fminute) == 0 { endMoonDays += (beginMoonDays + 24*time.Hour) - zodiacPositionEnd = int((endMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 + zodiacPositionEnd = int((endMoonDays.Minutes() / float64(jt.Fminute/360*30.*(monthDays.Hours()/24.)))) % 12 } - zodBegin2 := (zodiacPositionEnd) * jt.Fminute / 360 * 30. * 30. - zodEnd2 := (zodiacPositionEnd + 1) * jt.Fminute / 360 * 30. * 30. + zodBegin2 := (zodiacPositionEnd) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) + zodEnd2 := (zodiacPositionEnd + 1) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) var tBegin2 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin2)*time.Minute, timeFormat, moonTable) var tEnd2 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd2)*time.Minute, timeFormat, moonTable) zods.Zodiac[1].Begin = &tBegin2 From 5234781b6198cb4c2d80479aa601e71feebdcb14 Mon Sep 17 00:00:00 2001 From: prostraction Date: Thu, 20 Nov 2025 21:13:57 +0500 Subject: [PATCH 13/18] fix: zodiac calculation --- internal/server/server.go | 2 +- pkg/zodiac/zodiac.go | 27 +++++++++++++-------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/internal/server/server.go b/internal/server/server.go index d8ad868..af60e69 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -102,5 +102,5 @@ func (s *Server) NewRouter() *fiber.App { } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc5") + return c.JSON("1.2.0rc6") } diff --git a/pkg/zodiac/zodiac.go b/pkg/zodiac/zodiac.go index c034a29..e60f034 100644 --- a/pkg/zodiac/zodiac.go +++ b/pkg/zodiac/zodiac.go @@ -1,6 +1,7 @@ package zodiac import ( + "log" jt "moon/pkg/julian-time" moon "moon/pkg/moon" "time" @@ -14,20 +15,18 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma zodiacEnd := Zodiac{} dayBeginTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, loc) - dayEndTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day()+1, 0, 0, 0, 0, loc) + beginMoonDays, endMoonDays, _ := moon.GetMoonDaysPrecise(dayBeginTime, moonTable) - beginMoonDays, endMoonDays, monthDays := moon.GetMoonDaysPrecise(dayBeginTime, moonTable) - dayProgress := float64(tGiven.Sub(dayBeginTime)) / float64(dayEndTime.Sub(dayBeginTime)) - currentMoonDays := beginMoonDays + time.Duration(float64(endMoonDays-beginMoonDays)*dayProgress) + zodiacPositionBegin := int((beginMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 + zodiacPositionCurrent := int(((beginMoonDays.Minutes()+float64(tGiven.Hour()*60)+float64(tGiven.Minute()))/jt.Fminute*360.)/30./30.) % 12 + zodiacPositionEnd := int(((beginMoonDays.Minutes()+1440)/jt.Fminute*360.)/30./30.) % 12 - zodiacPositionBegin := int((beginMoonDays.Minutes()/jt.Fminute*360.)/30./(monthDays.Hours()/24.)) % 12 - zodiacPositionCurrent := int((currentMoonDays.Minutes()/jt.Fminute*360.)/30./(monthDays.Hours()/24.)) % 12 - zodiacPositionEnd := int((endMoonDays.Minutes()/jt.Fminute*360.)/30./(monthDays.Hours()/24.)) % 12 + log.Println(zodiacPositionBegin, zodiacPositionEnd) if zodiacPositionBegin == zodiacPositionEnd { zods.Count = 1 - zodBegin := zodiacPositionBegin * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) - zodEnd := (zodiacPositionEnd + 1) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) + zodBegin := zodiacPositionBegin * jt.Fminute / 360 * 30. * 30. + zodEnd := (zodiacPositionEnd + 1) * jt.Fminute / 360 * 30. * 30. var tBegin any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin)*time.Minute, timeFormat, moonTable) var tEnd any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd)*time.Minute, timeFormat, moonTable) zods.Zodiac = make([]ZodiacDetailed, 1) @@ -37,8 +36,8 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma zods.Zodiac[0].NameLocalized = getZodiacRespLocalized(zodiacPositionBegin, lang) } else { zods.Count = 2 - zodBegin1 := (zodiacPositionBegin) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) - zodEnd1 := (zodiacPositionBegin + 1) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) + zodBegin1 := (zodiacPositionBegin) * jt.Fminute / 360 * 30. * 30. + zodEnd1 := (zodiacPositionBegin + 1) * jt.Fminute / 360 * 30. * 30. var tBegin1 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin1)*time.Minute, timeFormat, moonTable) var tEnd1 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd1)*time.Minute, timeFormat, moonTable) @@ -50,11 +49,11 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma if int(endMoonDays.Minutes()/jt.Fminute) == 0 { endMoonDays += (beginMoonDays + 24*time.Hour) - zodiacPositionEnd = int((endMoonDays.Minutes() / float64(jt.Fminute/360*30.*(monthDays.Hours()/24.)))) % 12 + zodiacPositionEnd = int((endMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 } - zodBegin2 := (zodiacPositionEnd) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) - zodEnd2 := (zodiacPositionEnd + 1) * int(float64(jt.Fminute/360*30.*(monthDays.Hours()/24.))) + zodBegin2 := (zodiacPositionEnd) * jt.Fminute / 360 * 30. * 30. + zodEnd2 := (zodiacPositionEnd + 1) * jt.Fminute / 360 * 30. * 30. var tBegin2 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin2)*time.Minute, timeFormat, moonTable) var tEnd2 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd2)*time.Minute, timeFormat, moonTable) zods.Zodiac[1].Begin = &tBegin2 From 49693c14aec7c26dfec4871f78c5b8e502a35864 Mon Sep 17 00:00:00 2001 From: prostraction <47314760+prostraction@users.noreply.github.com> Date: Fri, 21 Nov 2025 07:52:20 +0300 Subject: [PATCH 14/18] fix: current moon days --- internal/server/server.go | 2 +- pkg/moon/days.go | 20 +++++++++++++------- pkg/moon/moon.go | 10 ++-------- pkg/zodiac/zodiac.go | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/internal/server/server.go b/internal/server/server.go index af60e69..aca475c 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -102,5 +102,5 @@ func (s *Server) NewRouter() *fiber.App { } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc6") + return c.JSON("1.2.0rc7") } diff --git a/pkg/moon/days.go b/pkg/moon/days.go index a997344..30e8f9e 100644 --- a/pkg/moon/days.go +++ b/pkg/moon/days.go @@ -177,11 +177,12 @@ func GetMoonDays(tGiven time.Time, table []*MoonTableElement) time.Duration { return moonDays }*/ -func GetMoonDaysPrecise(tGiven time.Time, table []*MoonTableElement) (time.Duration, time.Duration, time.Duration) { +func GetMoonDaysPrecise(tGiven time.Time, table []*MoonTableElement) (time.Duration, time.Duration, time.Duration, time.Duration) { + tBegin := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, tGiven.Location()) for i := range table { elem := table[i] if elem.t1 != elem.t2 { - if tGiven.After(elem.NewMoon) && tGiven.Before(elem.NewMoon.Add(time.Hour*24*32)) { + if tBegin.After(elem.NewMoon) && tBegin.Before(elem.NewMoon.Add(time.Hour*24*32)) { var elem2 *MoonTableElement if i < len(table)-1 { elem2 = table[i+1] // new next moon @@ -192,21 +193,26 @@ func GetMoonDaysPrecise(tGiven time.Time, table []*MoonTableElement) (time.Durat } moonMonDays := elem2.NewMoon.Sub(elem.NewMoon) // moon month - eartbeg := tGiven.Add(-tGiven.Sub(elem.NewMoon)) - eartend := time.Date(eartbeg.Year(), eartbeg.Month()+1, eartbeg.Day(), eartbeg.Hour(), eartbeg.Minute(), eartbeg.Second(), 0, tGiven.Location()) + eartbeg := tBegin.Add(-tGiven.Sub(elem.NewMoon)) + eartend := time.Date(eartbeg.Year(), eartbeg.Month()+1, eartbeg.Day(), eartbeg.Hour(), eartbeg.Minute(), eartbeg.Second(), 0, tBegin.Location()) eartMon := eartend.Unix() - eartbeg.Unix() // earth month beginDay := elem.NewMoon + currentDay := elem.NewMoon day := time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*24.)) - for tGiven.Sub(beginDay) > day { + for tBegin.Sub(beginDay) > day { beginDay = beginDay.Add(day) + currentDay = currentDay.Add(day) } + currentDay = currentDay.Add(time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Hour())))) + currentDay = currentDay.Add(time.Minute * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Minute())))) + endDay := beginDay.Add(day) - return beginDay.Sub(elem.NewMoon) % moonMonDays, endDay.Sub(elem.NewMoon) % moonMonDays, moonMonDays + return beginDay.Sub(elem.NewMoon) % moonMonDays, currentDay.Sub(elem.NewMoon) % moonMonDays, endDay.Sub(elem.NewMoon) % moonMonDays, moonMonDays } } } - return time.Duration(0), time.Duration(0), time.Duration(0) + return time.Duration(0), time.Duration(0), time.Duration(0), time.Duration(0) } diff --git a/pkg/moon/moon.go b/pkg/moon/moon.go index 68f77a8..22f7901 100644 --- a/pkg/moon/moon.go +++ b/pkg/moon/moon.go @@ -12,15 +12,9 @@ func (c *Cache) CurrentMoonDays(tGiven time.Time, loc *time.Location) (time.Dura loc = time.UTC } - dayBeginTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, loc) - dayEndTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day()+1, 0, 0, 0, 0, loc) - + currentDayTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), tGiven.Hour(), tGiven.Minute(), tGiven.Second(), tGiven.Nanosecond(), loc) moonTable := c.CreateMoonTable(tGiven) - - beginMoonDays, endMoonDays, _ := GetMoonDaysPrecise(dayBeginTime, moonTable.Elems) - - dayProgress := float64(tGiven.Sub(dayBeginTime)) / float64(dayEndTime.Sub(dayBeginTime)) - currentMoonDays := beginMoonDays + time.Duration(float64(endMoonDays-beginMoonDays)*dayProgress) + beginMoonDays, currentMoonDays, endMoonDays, _ := GetMoonDaysPrecise(currentDayTime, moonTable.Elems) return beginMoonDays, currentMoonDays, endMoonDays } diff --git a/pkg/zodiac/zodiac.go b/pkg/zodiac/zodiac.go index e60f034..e708b06 100644 --- a/pkg/zodiac/zodiac.go +++ b/pkg/zodiac/zodiac.go @@ -15,7 +15,7 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma zodiacEnd := Zodiac{} dayBeginTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, loc) - beginMoonDays, endMoonDays, _ := moon.GetMoonDaysPrecise(dayBeginTime, moonTable) + beginMoonDays, _, endMoonDays, _ := moon.GetMoonDaysPrecise(dayBeginTime, moonTable) zodiacPositionBegin := int((beginMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 zodiacPositionCurrent := int(((beginMoonDays.Minutes()+float64(tGiven.Hour()*60)+float64(tGiven.Minute()))/jt.Fminute*360.)/30./30.) % 12 From 11bd22ce49c4548b4af37b39709e09910eddbba3 Mon Sep 17 00:00:00 2001 From: prostraction Date: Fri, 21 Nov 2025 21:53:04 +0500 Subject: [PATCH 15/18] feat: started refactoring --- internal/server/server.go | 23 ++++++++++++----------- pkg/illumination/illumination.go | 1 - pkg/julian-time/time.go | 24 +++--------------------- pkg/moon/days.go | 9 +++++---- pkg/zodiac/zodiac.go | 3 --- 5 files changed, 20 insertions(+), 40 deletions(-) diff --git a/internal/server/server.go b/internal/server/server.go index af60e69..bfa057c 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -3,6 +3,7 @@ package server import ( "moon/pkg/moon" "moon/pkg/position" + "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" @@ -15,10 +16,15 @@ type Server struct { func (s *Server) NewRouter() *fiber.App { app := fiber.New(fiber.Config{ - Prefork: true, - ServerHeader: "Fiber", - CaseSensitive: false, - StrictRouting: true, + Prefork: true, + ServerHeader: "", + CaseSensitive: false, + StrictRouting: false, + ReadTimeout: 60 * time.Second, + WriteTimeout: 60 * time.Second, + DisableKeepalive: true, + DisableStartupMessage: true, + DisablePreParseMultipartForm: true, }) app.Use(cors.New(cors.Config{ @@ -43,12 +49,7 @@ func (s *Server) NewRouter() *fiber.App { // maybe rename moonPhase -> phase later // moon phase for day: - // - begin, current, end of the day: - // - moon days, illumintaion, phase, zodiac - // - moon days by time - // - zodiacs by time - // - rise, set and meridian: - // - time, direction, azimuth, exists + app.Get("/v1/moonPhaseCurrent", s.moonPhaseCurrentV1) app.Get("/v1/moonPhaseTimestamp", s.moonPhaseTimestampV1) app.Get("/v1/moonPhaseDate", s.moonPhaseDatetV1) @@ -102,5 +103,5 @@ func (s *Server) NewRouter() *fiber.App { } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc6") + return c.JSON("1.2.0") } diff --git a/pkg/illumination/illumination.go b/pkg/illumination/illumination.go index 922b5f7..1a349a6 100644 --- a/pkg/illumination/illumination.go +++ b/pkg/illumination/illumination.go @@ -61,7 +61,6 @@ func BinarySearchIllumination(jdTimeBegin, jdTimeEnd float64, loc *time.Location for it < 50 { mid = low + (high-low)/2.0 illum = GetCurrentMoonIllumination(jt.FromJulianDate(mid, loc), loc) - //log.Printf("Direction: %v, Iteration %d: low=%.6f, high=%.6f, mid=%.6f, illum=%.6f", direction, it, low, high, mid, illum) if math.Abs(illum-0.5) < 0.0001 { return mid } diff --git a/pkg/julian-time/time.go b/pkg/julian-time/time.go index 0404c08..29257bb 100644 --- a/pkg/julian-time/time.go +++ b/pkg/julian-time/time.go @@ -22,7 +22,6 @@ func ToJulianDate(t time.Time) float64 { year := t.Year() month := int(t.Month()) - // Calculate fractional day including time fractionalDay := (float64(t.Hour()) + float64(t.Minute())/MinutesPerHour + float64(t.Second())/(MinutesPerHour*SecondsPerMinute)) / HoursPerDay @@ -61,18 +60,15 @@ func SetTimezoneLocFromString(utc string) (*time.Location, error) { re := regexp.MustCompile(`[^a-zA-Z0-9:+-\-]`) utc = re.ReplaceAllString(utc, "") - // Remove "UTC" prefix if present and convert to lowercase for case-insensitive matching normalized := strings.ToLower(utc) normalized = strings.TrimPrefix(normalized, "utc") normalized = strings.TrimPrefix(normalized, "gmt") normalized = strings.TrimSpace(normalized) - // Handle cases like "UTC", "+0", "-0", "0" if normalized == "" || normalized == "+0" || normalized == "-0" || normalized == "0" { return time.UTC, nil } - // Check if it starts with + or - sign := 1 if strings.HasPrefix(normalized, "+") { sign = 1 @@ -85,7 +81,6 @@ func SetTimezoneLocFromString(utc string) (*time.Location, error) { var hours, minutes int var err error - // Handle cases with colon separator (e.g., "05:30", "5:30") if strings.Contains(normalized, ":") { parts := strings.Split(normalized, ":") if len(parts) != 2 { @@ -103,16 +98,15 @@ func SetTimezoneLocFromString(utc string) (*time.Location, error) { } } else { - // Handle cases without colon switch len(normalized) { - case 1, 2: // Just hours (e.g., "5", "12") + case 1, 2: hours, err = strconv.Atoi(normalized) if err != nil { return time.UTC, fmt.Errorf("invalid hours: %s", normalized) } minutes = 0 - case 3: // Hours + minutes (e.g., "530" -> 5 hours, 30 minutes) + case 3: hours, err = strconv.Atoi(normalized[:1]) if err != nil { return time.UTC, fmt.Errorf("invalid hours: %s", normalized[:1]) @@ -122,7 +116,7 @@ func SetTimezoneLocFromString(utc string) (*time.Location, error) { return time.UTC, fmt.Errorf("invalid minutes: %s", normalized[1:]) } - case 4: // Hours + minutes (e.g., "0530" -> 5 hours, 30 minutes) + case 4: hours, err = strconv.Atoi(normalized[:2]) if err != nil { return time.UTC, fmt.Errorf("invalid hours: %s", normalized[:2]) @@ -137,15 +131,12 @@ func SetTimezoneLocFromString(utc string) (*time.Location, error) { } } - // Validate hours range if hours < 0 || hours > 23 { return time.UTC, fmt.Errorf("hours out of range (0-23): %d", hours) } - // Calculate total seconds offset totalSeconds := sign * (hours*3600 + minutes*60) - // Create location name locationName := fmt.Sprintf("UTC%s%d:%02d", m.GetSignPrefix(sign), hours, minutes) if minutes == 0 { locationName = fmt.Sprintf("UTC%s%d", m.GetSignPrefix(sign), hours) @@ -166,19 +157,15 @@ func GetTimeFromLocation(loc *time.Location) (hours int, minutes int, err error) if utc == "" { return 0, 0, errors.New("empty timezone string") } - - // Remove "UTC" prefix if present and convert to lowercase for case-insensitive matching normalized := strings.ToLower(utc) normalized = strings.TrimPrefix(normalized, "utc") normalized = strings.TrimPrefix(normalized, "gmt") normalized = strings.TrimSpace(normalized) - // Handle cases like "+5", "-3", etc. if normalized == "" || normalized == "+0" || normalized == "-0" || normalized == "0" { return 0, 0, nil } - // Check if it starts with + or - var sign int = 1 if strings.HasPrefix(normalized, "+") { sign = 1 @@ -188,7 +175,6 @@ func GetTimeFromLocation(loc *time.Location) (hours int, minutes int, err error) normalized = normalized[1:] } - // Handle cases with colon separator (e.g., "05:30", "5:30") if strings.Contains(normalized, ":") { parts := strings.Split(normalized, ":") if len(parts) != 2 { @@ -208,8 +194,6 @@ func GetTimeFromLocation(loc *time.Location) (hours int, minutes int, err error) return sign * hours, minutes, nil } - // Handle cases without colon (e.g., "0530", "530", "5") - // Check if it's just hours (e.g., "5") if len(normalized) <= 2 { hours, err := strconv.Atoi(normalized) if err != nil { @@ -218,7 +202,6 @@ func GetTimeFromLocation(loc *time.Location) (hours int, minutes int, err error) return sign * hours, minutes, nil } - // Handle cases like "0530" (4 digits) if len(normalized) == 4 { hoursStr := normalized[:2] minutesStr := normalized[2:] @@ -236,7 +219,6 @@ func GetTimeFromLocation(loc *time.Location) (hours int, minutes int, err error) return sign * hours, minutes, nil } - // Handle cases like "530" (3 digits - hours + minutes) if len(normalized) == 3 { hoursStr := normalized[:1] minutesStr := normalized[1:] diff --git a/pkg/moon/days.go b/pkg/moon/days.go index a997344..3a155ef 100644 --- a/pkg/moon/days.go +++ b/pkg/moon/days.go @@ -8,6 +8,8 @@ import ( il "moon/pkg/illumination" jt "moon/pkg/julian-time" phase "moon/pkg/phase" + + "github.com/gofiber/fiber/v2/log" ) func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { @@ -102,8 +104,7 @@ func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, timeFormat elem := moonTable[i] if elem.t1 != elem.t2 { if tGiven.After(elem.NewMoon) && tGiven.Before(elem.NewMoon.Add(time.Hour*24*32)) { - t := elem.NewMoon - t = t.Add(duration) + t := elem.NewMoon.Add(duration) if strings.ToLower(timeFormat) == "timestamp" { var tRet any = t.Unix() return &tRet @@ -120,8 +121,7 @@ func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, timeFormat if i < len(moonTable)-1 { elem2 := moonTable[i+1] if tGiven.After(elem.LastQuarter) && tGiven.Before(elem2.NewMoon) { - t := elem.NewMoon - t = t.Add(duration) + t := elem.NewMoon.Add(duration) if strings.ToLower(timeFormat) == "timestamp" { var tRet any = t.Unix() @@ -186,6 +186,7 @@ func GetMoonDaysPrecise(tGiven time.Time, table []*MoonTableElement) (time.Durat if i < len(table)-1 { elem2 = table[i+1] // new next moon } else { + log.Error("if i < len(table)-1 { // fix it, it is required") elem2 = new(MoonTableElement) // new next moon approx (fix?) elem2.NewMoon = elem.NewMoon.AddDate(0, 0, 29) elem2.NewMoon = elem.NewMoon.Add(time.Hour * 12) diff --git a/pkg/zodiac/zodiac.go b/pkg/zodiac/zodiac.go index e60f034..c424222 100644 --- a/pkg/zodiac/zodiac.go +++ b/pkg/zodiac/zodiac.go @@ -1,7 +1,6 @@ package zodiac import ( - "log" jt "moon/pkg/julian-time" moon "moon/pkg/moon" "time" @@ -21,8 +20,6 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma zodiacPositionCurrent := int(((beginMoonDays.Minutes()+float64(tGiven.Hour()*60)+float64(tGiven.Minute()))/jt.Fminute*360.)/30./30.) % 12 zodiacPositionEnd := int(((beginMoonDays.Minutes()+1440)/jt.Fminute*360.)/30./30.) % 12 - log.Println(zodiacPositionBegin, zodiacPositionEnd) - if zodiacPositionBegin == zodiacPositionEnd { zods.Count = 1 zodBegin := zodiacPositionBegin * jt.Fminute / 360 * 30. * 30. From bc0833aeda12b93ec7304cf968247819ad89e20d Mon Sep 17 00:00:00 2001 From: prostraction Date: Sat, 22 Nov 2025 21:45:03 +0500 Subject: [PATCH 16/18] fix: utc; feat: refactor --- internal/server/models.go | 4 +- internal/server/route-moon-table.go | 24 ++-- internal/server/route-next.go | 5 +- internal/server/route-phase.go | 50 ++++---- internal/server/server.go | 7 +- pkg/julian-time/time.go | 1 - pkg/moon/days.go | 157 ++++++++++------------- pkg/moon/models.go | 43 ++++--- pkg/moon/moon.go | 186 ++++++++++++++-------------- pkg/moon/moon_test.go | 128 ++----------------- pkg/phase/models.go | 13 ++ pkg/phase/phase.go | 18 ++- pkg/position/position.go | 2 + pkg/zodiac/zodiac.go | 27 ++-- 14 files changed, 287 insertions(+), 378 deletions(-) diff --git a/internal/server/models.go b/internal/server/models.go index 2a0cbfb..f7f9fef 100644 --- a/internal/server/models.go +++ b/internal/server/models.go @@ -16,7 +16,7 @@ type ErrorPrintable struct { type MoonStat struct { MoonDays float64 Illumination float64 - Phase phase.PhaseResp + Phase *phase.Phase Zodiac zodiac.Zodiac Position *pos.MoonPosition `json:"MoonPosition,omitempty"` } @@ -46,7 +46,7 @@ type MoonPhaseResponse struct { MoonRiseAndSet *pos.DayData `json:"MoonRiseAndSet,omitempty"` - info *FullInfo + //info *FullInfo } type Coordinates struct { diff --git a/internal/server/route-moon-table.go b/internal/server/route-moon-table.go index 8f01f90..9f71f3d 100644 --- a/internal/server/route-moon-table.go +++ b/internal/server/route-moon-table.go @@ -41,31 +41,31 @@ func (s *Server) moonTableYearV1(c *fiber.Ctx) error { } func (s *Server) moonTableV1(c *fiber.Ctx, timeFormat string, tGiven time.Time) error { - table := s.moonCache.GenerateMoonTable(tGiven) + moonTable := moon.CreateMoonTable(tGiven) if strings.ToLower(timeFormat) == "timestamp" { val := []moon.NearestPhaseTimestamp{} - for i := range table.Elems { + for i := range moonTable.Elems { val = append(val, moon.NearestPhaseTimestamp{ - NewMoon: table.Elems[i].NewMoon.Unix(), - FirstQuarter: table.Elems[i].FirstQuarter.Unix(), - FullMoon: table.Elems[i].FullMoon.Unix(), - LastQuarter: table.Elems[i].FullMoon.Unix(), + NewMoon: moonTable.Elems[i].NewMoon.Unix(), + FirstQuarter: moonTable.Elems[i].FirstQuarter.Unix(), + FullMoon: moonTable.Elems[i].FullMoon.Unix(), + LastQuarter: moonTable.Elems[i].FullMoon.Unix(), }) } return c.JSON(val) } else if strings.ToLower(timeFormat) != "iso" { val := []moon.NearestPhaseString{} - for i := range table.Elems { + for i := range moonTable.Elems { val = append(val, moon.NearestPhaseString{ - NewMoon: table.Elems[i].NewMoon.Format(timeFormat), - FirstQuarter: table.Elems[i].FirstQuarter.Format(timeFormat), - FullMoon: table.Elems[i].FullMoon.Format(timeFormat), - LastQuarter: table.Elems[i].FullMoon.Format(timeFormat), + NewMoon: moonTable.Elems[i].NewMoon.Format(timeFormat), + FirstQuarter: moonTable.Elems[i].FirstQuarter.Format(timeFormat), + FullMoon: moonTable.Elems[i].FullMoon.Format(timeFormat), + LastQuarter: moonTable.Elems[i].FullMoon.Format(timeFormat), }) } return c.JSON(val) } - return c.JSON(table.Elems) + return c.JSON(moonTable.Elems) } diff --git a/internal/server/route-next.go b/internal/server/route-next.go index b1eeddc..e1bef56 100644 --- a/internal/server/route-next.go +++ b/internal/server/route-next.go @@ -12,10 +12,11 @@ import ( func (s *Server) moonNextMoonPhaseV1(c *fiber.Ctx) error { utc := c.Query("utc", "UTC:+0") loc, _ := jt.SetTimezoneLocFromString(utc) - tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, time.Local) + tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond(), time.Local) tGiven = tGiven.In(loc) - np := s.moonCache.FindNearestPhase(tGiven) + moonTable := moon.CreateMoonTable(tGiven) + np := moon.FindNearestPhase(tGiven, moonTable) format := c.Query("timeFormat", "ISO") if strings.ToLower(format) == "timestamp" { diff --git a/internal/server/route-phase.go b/internal/server/route-phase.go index 950a38b..b299386 100644 --- a/internal/server/route-phase.go +++ b/internal/server/route-phase.go @@ -2,6 +2,7 @@ package server import ( jt "moon/pkg/julian-time" + "moon/pkg/moon" "moon/pkg/phase" pos "moon/pkg/position" "moon/pkg/zodiac" @@ -100,46 +101,51 @@ func (s *Server) moonPhaseDatetV1(c *fiber.Ctx) error { } func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, locationCords Coordinates, timeFormat string) error { + var err error + lang := c.Query("lang", "en") utc := c.Query("utc", "UTC:+0") - loc, _ := jt.SetTimezoneLocFromString(utc) - /*if err != nil { - log.Println(err) - }*/ + + loc, err := jt.SetTimezoneLocFromString(utc) + if err != nil { + log.Trace(err) + } resp := MoonPhaseResponse{} - resp.EndDay = new(MoonStat) - resp.CurrentState = new(MoonStat) resp.BeginDay = new(MoonStat) - resp.info = new(FullInfo) + resp.CurrentState = new(MoonStat) + resp.EndDay = new(MoonStat) - var err error + moonTable := moon.CreateMoonTable(tGiven) + + // moon days + day := moon.CurrentMoonDays(tGiven, loc, moonTable) - var beginDuration, currentDuration, endDuration time.Duration - beginDuration, currentDuration, endDuration = s.moonCache.CurrentMoonDays(tGiven, loc) + resp.BeginDay.MoonDays = ToFixed(day.Begin.Minutes()/jt.Fminute, precision) + resp.CurrentState.MoonDays = ToFixed(day.Current.Minutes()/jt.Fminute, precision) + resp.EndDay.MoonDays = ToFixed(day.End.Minutes()/jt.Fminute, precision) - resp.info.MoonDaysBegin = beginDuration.Minutes() / jt.Fminute - resp.info.MoonDaysCurrent = currentDuration.Minutes() / jt.Fminute - resp.info.MoonDaysEnd = endDuration.Minutes() / jt.Fminute + // phase && illum + phase := phase.CurrentMoonPhase(tGiven, lang) - resp.BeginDay.MoonDays = ToFixed(resp.info.MoonDaysBegin, precision) - resp.CurrentState.MoonDays = ToFixed(resp.info.MoonDaysCurrent, precision) - resp.EndDay.MoonDays = ToFixed(resp.info.MoonDaysEnd, precision) + resp.BeginDay.Illumination = ToFixed(phase.Illumination.BeginDay*100, precision) + resp.CurrentState.Illumination = ToFixed(phase.Illumination.Current*100, precision) + resp.EndDay.Illumination = ToFixed(phase.Illumination.EndDay*100, precision) - resp.info.IlluminationCurrent, resp.info.IlluminationBeginDay, resp.info.IlluminationEndDay, resp.CurrentState.Phase, resp.BeginDay.Phase, resp.EndDay.Phase = phase.CurrentMoonPhase(tGiven, lang) + resp.BeginDay.Phase = phase.BeginDay + resp.CurrentState.Phase = phase.Current + resp.EndDay.Phase = phase.EndDay - resp.BeginDay.Illumination = ToFixed(resp.info.IlluminationBeginDay*100, precision) - resp.CurrentState.Illumination = ToFixed(resp.info.IlluminationCurrent*100, precision) - resp.EndDay.Illumination = ToFixed(resp.info.IlluminationEndDay*100, precision) + // zodiac - resp.ZodiacDetailed, resp.BeginDay.Zodiac, resp.CurrentState.Zodiac, resp.EndDay.Zodiac = zodiac.CurrentZodiacs(tGiven, loc, lang, timeFormat, s.moonCache.CreateMoonTable(tGiven).Elems) + resp.ZodiacDetailed, resp.BeginDay.Zodiac, resp.CurrentState.Zodiac, resp.EndDay.Zodiac = zodiac.CurrentZodiacs(tGiven, loc, lang, timeFormat, moonTable.Elems) if locationCords.IsValid { resp.MoonRiseAndSet, err = s.positionCache.GetRisesDay(tGiven.Year(), int(tGiven.Month()), tGiven.Day(), tGiven.Location(), precision, timeFormat, locationCords.Longitude, locationCords.Latitude) if err != nil { log.Error(err) } - resp.MoonDaysDetailed = s.moonCache.MoonDetailed(tGiven, loc, lang, timeFormat, locationCords.Longitude, locationCords.Latitude) + resp.MoonDaysDetailed = moon.MoonDetailed(tGiven, loc, lang, timeFormat, locationCords.Longitude, locationCords.Latitude) newT := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, tGiven.Location()) resp.BeginDay.Position, err = pos.GetMoonPosition(newT, newT.Location(), precision, timeFormat, locationCords.Longitude, locationCords.Latitude) diff --git a/internal/server/server.go b/internal/server/server.go index bfa057c..34eefc9 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1,7 +1,6 @@ package server import ( - "moon/pkg/moon" "moon/pkg/position" "time" @@ -10,7 +9,7 @@ import ( ) type Server struct { - moonCache *moon.Cache + //moonCache *moon.Cache positionCache *position.Cache } @@ -97,11 +96,11 @@ func (s *Server) NewRouter() *fiber.App { app.Get("/v1/version", s.versionV1) app.Get("/api/v1/version", s.versionV1) - s.moonCache = new(moon.Cache) + //s.moonCache = new(moon.Cache) s.positionCache = new(position.Cache) return app } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0") + return c.JSON("1.2.0rc8") } diff --git a/pkg/julian-time/time.go b/pkg/julian-time/time.go index 29257bb..49e90bd 100644 --- a/pkg/julian-time/time.go +++ b/pkg/julian-time/time.go @@ -153,7 +153,6 @@ func GetTimeFromLocation(loc *time.Location) (hours int, minutes int, err error) re := regexp.MustCompile(`[^a-zA-Z0-9:+-\-]`) utc = re.ReplaceAllString(utc, "") - // Handle empty string if utc == "" { return 0, 0, errors.New("empty timezone string") } diff --git a/pkg/moon/days.go b/pkg/moon/days.go index c73b6af..a030fde 100644 --- a/pkg/moon/days.go +++ b/pkg/moon/days.go @@ -12,11 +12,8 @@ import ( "github.com/gofiber/fiber/v2/log" ) -func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { - t := time.Date(timeGiven.Year(), 0, 0, 0, 0, 0, 0, timeGiven.Location()) - if c.moonTable != nil && c.moonTable[t.String()] != nil { - return c.moonTable[t.String()] - } +// to do +func CreateMoonTable(timeGiven time.Time) *MoonTable { moonTable := new(MoonTable) var l int @@ -26,7 +23,7 @@ func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { phaset = make([]float64, 0) - // Tabulate new and full moons surrounding the year + // tabulate new and full moons surrounding the year k1 = math.Floor((float64(timeGiven.Year()) - 1900) * 12.3685) minx = 0 isNext := true @@ -59,16 +56,9 @@ func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { mp := phaset[l] if mp < 0 { mp = -mp - - elem.t1 = mp - elem.t2 = lastnew - lastnew = mp } - elem.t1 = mp - elem.t2 = lastnew - firstQuarterTime := il.BinarySearchIllumination(lastnew, mp, timeGiven.Location(), true) elem.FirstQuarter = jt.FromJulianDate(firstQuarterTime, timeGiven.Location()) @@ -78,7 +68,7 @@ func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { elem.NewMoon = jt.FromJulianDate(lastnew, timeGiven.Location()) elem.FullMoon = jt.FromJulianDate(mp, timeGiven.Location()) - if elem.t1 != elem.t2 { + if mp != lastnew { moonTable.Elems = append(moonTable.Elems, elem) } @@ -86,15 +76,11 @@ func (c *Cache) CreateMoonTable(timeGiven time.Time) *MoonTable { break } } - if c.moonTable == nil { - c.moonTable = make(map[string]*MoonTable) - } - if c.moonTable[t.String()] == nil { - c.moonTable[t.String()] = moonTable - } + return moonTable } +// to do func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, timeFormat string, moonTable []*MoonTableElement) *any { var tt any = time.Time{} if len(moonTable) == 0 { @@ -102,9 +88,26 @@ func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, timeFormat } for i := range moonTable { elem := moonTable[i] - if elem.t1 != elem.t2 { - if tGiven.After(elem.NewMoon) && tGiven.Before(elem.NewMoon.Add(time.Hour*24*32)) { + if tGiven.After(elem.NewMoon) && tGiven.Before(elem.NewMoon.Add(time.Hour*24*32)) { + t := elem.NewMoon.Add(duration) + if strings.ToLower(timeFormat) == "timestamp" { + var tRet any = t.Unix() + return &tRet + } + + if strings.ToLower(timeFormat) != "iso" { + var tRet any = t.Format(timeFormat) + return &tRet + } + + var tRet any = t + return &tRet + } + if i < len(moonTable)-1 { + elem2 := moonTable[i+1] + if tGiven.After(elem.LastQuarter) && tGiven.Before(elem2.NewMoon) { t := elem.NewMoon.Add(duration) + if strings.ToLower(timeFormat) == "timestamp" { var tRet any = t.Unix() return &tRet @@ -118,102 +121,74 @@ func BeginMoonDayToEarthDay(tGiven time.Time, duration time.Duration, timeFormat var tRet any = t return &tRet } - if i < len(moonTable)-1 { - elem2 := moonTable[i+1] - if tGiven.After(elem.LastQuarter) && tGiven.Before(elem2.NewMoon) { - t := elem.NewMoon.Add(duration) - - if strings.ToLower(timeFormat) == "timestamp" { - var tRet any = t.Unix() - return &tRet - } - - if strings.ToLower(timeFormat) != "iso" { - var tRet any = t.Format(timeFormat) - return &tRet - } - - var tRet any = t - return &tRet - } - } } } return &tt } -func (c *Cache) FindNearestPhase(tGiven time.Time) NearestPhase { +// to do +func FindNearestPhase(tGiven time.Time, moonTable *MoonTable) NearestPhase { + if moonTable == nil { + return NearestPhase{} + } + var np NearestPhase - table := c.CreateMoonTable(tGiven) - if val, err := c.SearchPhase(tGiven, table, NewMoon); err == nil { + if val, err := SearchPhase(tGiven, moonTable, NewMoon); err == nil { np.NewMoon = val } - if val, err := c.SearchPhase(tGiven, table, FirstQuarter); err == nil { + if val, err := SearchPhase(tGiven, moonTable, FirstQuarter); err == nil { np.FirstQuarter = val } - if val, err := c.SearchPhase(tGiven, table, FullMoon); err == nil { + if val, err := SearchPhase(tGiven, moonTable, FullMoon); err == nil { np.FullMoon = val } - if val, err := c.SearchPhase(tGiven, table, LastQuarter); err == nil { + if val, err := SearchPhase(tGiven, moonTable, LastQuarter); err == nil { np.LastQuarter = val } return np } -/* -func GetMoonDays(tGiven time.Time, table []*MoonTableElement) time.Duration { - var moonDays time.Duration - for i := range table { - elem := table[i] - - if elem.t1 != elem.t2 { - if tGiven.After(elem.NewMoon) { - moonDays = tGiven.Sub(elem.NewMoon) - } - } - } - return moonDays -}*/ - -func GetMoonDaysPrecise(tGiven time.Time, table []*MoonTableElement) (time.Duration, time.Duration, time.Duration, time.Duration) { +func GetMoonDays(tGiven time.Time, table []*MoonTableElement) (MoonDaysInDay, time.Duration) { tBegin := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, tGiven.Location()) for i := range table { elem := table[i] - if elem.t1 != elem.t2 { - if tBegin.After(elem.NewMoon) && tBegin.Before(elem.NewMoon.Add(time.Hour*24*32)) { - var elem2 *MoonTableElement - if i < len(table)-1 { - elem2 = table[i+1] // new next moon - } else { - log.Error("if i < len(table)-1 { // fix it, it is required") - elem2 = new(MoonTableElement) // new next moon approx (fix?) - elem2.NewMoon = elem.NewMoon.AddDate(0, 0, 29) - elem2.NewMoon = elem.NewMoon.Add(time.Hour * 12) - } - moonMonDays := elem2.NewMoon.Sub(elem.NewMoon) // moon month + if tBegin.After(elem.NewMoon) && tBegin.Before(elem.NewMoon.Add(time.Hour*24*32)) { + var elem2 *MoonTableElement + if i < len(table)-1 { + elem2 = table[i+1] // new next moon + } else { + log.Error("if i < len(table)-1 { // fix it, it is required") + elem2 = new(MoonTableElement) // new next moon approx (fix?) to do + elem2.NewMoon = elem.NewMoon.AddDate(0, 0, 29) + elem2.NewMoon = elem.NewMoon.Add(time.Hour * 12) + } + moonMonDays := elem2.NewMoon.Sub(elem.NewMoon) // moon month - eartbeg := tBegin.Add(-tGiven.Sub(elem.NewMoon)) - eartend := time.Date(eartbeg.Year(), eartbeg.Month()+1, eartbeg.Day(), eartbeg.Hour(), eartbeg.Minute(), eartbeg.Second(), 0, tBegin.Location()) - eartMon := eartend.Unix() - eartbeg.Unix() // earth month + eartbeg := tBegin.Add(-tGiven.Sub(elem.NewMoon)) + eartend := time.Date(eartbeg.Year(), eartbeg.Month()+1, eartbeg.Day(), eartbeg.Hour(), eartbeg.Minute(), eartbeg.Second(), 0, tBegin.Location()) + eartMon := eartend.Unix() - eartbeg.Unix() // earth month - beginDay := elem.NewMoon - currentDay := elem.NewMoon - day := time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*24.)) + beginDay := elem.NewMoon + currentDay := elem.NewMoon + day := time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*24.)) - for tBegin.Sub(beginDay) > day { - beginDay = beginDay.Add(day) - currentDay = currentDay.Add(day) - } + for tBegin.Sub(beginDay) > day { + beginDay = beginDay.Add(day) + currentDay = currentDay.Add(day) + } - currentDay = currentDay.Add(time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Hour())))) - currentDay = currentDay.Add(time.Minute * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Minute())))) + currentDay = currentDay.Add(time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Hour())))) + currentDay = currentDay.Add(time.Minute * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Minute())))) - endDay := beginDay.Add(day) - return beginDay.Sub(elem.NewMoon) % moonMonDays, currentDay.Sub(elem.NewMoon) % moonMonDays, endDay.Sub(elem.NewMoon) % moonMonDays, moonMonDays - } + endDay := beginDay.Add(day) + return MoonDaysInDay{ + Begin: beginDay.Sub(elem.NewMoon) % moonMonDays, + Current: currentDay.Sub(elem.NewMoon) % moonMonDays, + End: endDay.Sub(elem.NewMoon) % moonMonDays, + }, moonMonDays } } - return time.Duration(0), time.Duration(0), time.Duration(0), time.Duration(0) + return MoonDaysInDay{time.Duration(0), time.Duration(0), time.Duration(0)}, time.Duration(0) } diff --git a/pkg/moon/models.go b/pkg/moon/models.go index aa36ca3..0b09a9c 100644 --- a/pkg/moon/models.go +++ b/pkg/moon/models.go @@ -2,15 +2,14 @@ package moon import "time" -type EnumPhase int - -const ( - NewMoon EnumPhase = iota - FirstQuarter - FullMoon - LastQuarter -) +type MoonDaysInDay struct { + Begin time.Duration + Current time.Duration + End time.Duration +} +// used by: +// all moon package type MoonTable struct { Elems []*MoonTableElement } @@ -20,12 +19,22 @@ type MoonTableElement struct { FirstQuarter time.Time FullMoon time.Time LastQuarter time.Time - t1 float64 - t2 float64 } -type Cache struct { - moonTable map[string]*MoonTable +type EnumPhase int + +const ( + NewMoon EnumPhase = iota + FirstQuarter + FullMoon + LastQuarter +) + +// used by: +// - phase methods +type MoonDaysDetailed struct { + Count int + Day []MoonDay } type MoonDay struct { @@ -35,25 +44,21 @@ type MoonDay struct { IsEndExists bool } -type MoonDaysDetailed struct { - Count int - Day []MoonDay -} - +// used by: +// - route-phase +// - rounte-moon-table type NearestPhase struct { NewMoon time.Time FirstQuarter time.Time FullMoon time.Time LastQuarter time.Time } - type NearestPhaseTimestamp struct { NewMoon int64 FirstQuarter int64 FullMoon int64 LastQuarter int64 } - type NearestPhaseString struct { NewMoon string FirstQuarter string diff --git a/pkg/moon/moon.go b/pkg/moon/moon.go index 22f7901..9b18a9a 100644 --- a/pkg/moon/moon.go +++ b/pkg/moon/moon.go @@ -7,19 +7,23 @@ import ( "time" ) -func (c *Cache) CurrentMoonDays(tGiven time.Time, loc *time.Location) (time.Duration, time.Duration, time.Duration) { +// to do +func CurrentMoonDays(tGiven time.Time, loc *time.Location, moonTable *MoonTable) MoonDaysInDay { + var mday MoonDaysInDay + if moonTable == nil { + return mday + } + if loc == nil { loc = time.UTC } - currentDayTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), tGiven.Hour(), tGiven.Minute(), tGiven.Second(), tGiven.Nanosecond(), loc) - moonTable := c.CreateMoonTable(tGiven) - beginMoonDays, currentMoonDays, endMoonDays, _ := GetMoonDaysPrecise(currentDayTime, moonTable.Elems) - return beginMoonDays, currentMoonDays, endMoonDays + mday, _ = GetMoonDays(currentDayTime, moonTable.Elems) + return mday } -func (c *Cache) MoonDetailed(tGiven time.Time, loc *time.Location, lang string, timeFormat string, longitude float64, latitude float64) *MoonDaysDetailed { +func MoonDetailed(tGiven time.Time, loc *time.Location, lang string, timeFormat string, longitude float64, latitude float64) *MoonDaysDetailed { if loc == nil { loc = time.UTC } @@ -64,11 +68,7 @@ func (c *Cache) MoonDetailed(tGiven time.Time, loc *time.Location, lang string, return moonDaysDetailed } -func (c *Cache) GenerateMoonTable(tGiven time.Time) *MoonTable { - return c.CreateMoonTable(tGiven) -} - -func (c *Cache) SearchPhase(tGiven time.Time, moonTable *MoonTable, phase EnumPhase) (t time.Time, err error) { +func SearchPhase(tGiven time.Time, moonTable *MoonTable, phase EnumPhase) (t time.Time, err error) { if moonTable == nil { err = errors.New("passed empty moonTable to SearchNewMoon") log.Println(err.Error()) @@ -77,117 +77,115 @@ func (c *Cache) SearchPhase(tGiven time.Time, moonTable *MoonTable, phase EnumPh err = errors.New("not found") for i := range moonTable.Elems { elem := moonTable.Elems[i] - if elem.t1 != elem.t2 { - elemSearch1 := elem.NewMoon - if tGiven.Before(elemSearch1) { - elemSearch1 = time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day()-1, 0, 0, 0, 0, tGiven.Location()) + elemSearch1 := elem.NewMoon + if tGiven.Before(elemSearch1) { + elemSearch1 = time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day()-1, 0, 0, 0, 0, tGiven.Location()) + } + elemSearch2 := elem.LastQuarter + + // range current phase: + if tGiven.After(elemSearch1) && tGiven.Before(elemSearch2) { + // found in current phase + switch phase { + case NewMoon: + if tGiven.Before(elem.NewMoon) { + return elem.NewMoon, nil + } + case FirstQuarter: + if tGiven.Before(elem.FirstQuarter) { + return elem.FirstQuarter, nil + } + case FullMoon: + if tGiven.Before(elem.FullMoon) { + return elem.FullMoon, nil + } + case LastQuarter: + if tGiven.Before(elem.LastQuarter) { + return elem.LastQuarter, nil + } } - elemSearch2 := elem.LastQuarter - - // range current phase: - if tGiven.After(elemSearch1) && tGiven.Before(elemSearch2) { - // found in current phase + // found in next phase + if i < len(moonTable.Elems)-1 { + // use values if in table switch phase { case NewMoon: - if tGiven.Before(elem.NewMoon) { - return elem.NewMoon, nil - } + return moonTable.Elems[i+1].NewMoon, nil case FirstQuarter: - if tGiven.Before(elem.FirstQuarter) { - return elem.FirstQuarter, nil - } + return moonTable.Elems[i+1].FirstQuarter, nil case FullMoon: - if tGiven.Before(elem.FullMoon) { - return elem.FullMoon, nil - } + return moonTable.Elems[i+1].FullMoon, nil case LastQuarter: - if tGiven.Before(elem.LastQuarter) { - return elem.LastQuarter, nil - } + return moonTable.Elems[i+1].LastQuarter, nil } - // found in next phase - if i < len(moonTable.Elems)-1 { - // use values if in table + } else { + // create table for next table + newT := time.Date(tGiven.Year()+1, 0, 0, 0, 0, 0, 0, tGiven.Location()) + newMoonTable := CreateMoonTable(newT) + + if newMoonTable != nil && newMoonTable.Elems != nil && len(newMoonTable.Elems) > 0 { switch phase { case NewMoon: - return moonTable.Elems[i+1].NewMoon, nil + if tGiven.Before(newMoonTable.Elems[0].NewMoon) { + return newMoonTable.Elems[0].NewMoon, nil + } case FirstQuarter: - return moonTable.Elems[i+1].FirstQuarter, nil + if tGiven.Before(newMoonTable.Elems[0].FirstQuarter) { + return newMoonTable.Elems[0].FirstQuarter, nil + } case FullMoon: - return moonTable.Elems[i+1].FullMoon, nil + if tGiven.Before(newMoonTable.Elems[0].FullMoon) { + return newMoonTable.Elems[0].FullMoon, nil + } case LastQuarter: - return moonTable.Elems[i+1].LastQuarter, nil - } - } else { - // create table for next table - newT := time.Date(tGiven.Year()+1, 0, 0, 0, 0, 0, 0, tGiven.Location()) - newMoonTable := c.CreateMoonTable(newT) - - if newMoonTable != nil && newMoonTable.Elems != nil && len(newMoonTable.Elems) > 0 { - switch phase { - case NewMoon: - if tGiven.Before(newMoonTable.Elems[0].NewMoon) { - return newMoonTable.Elems[0].NewMoon, nil - } - case FirstQuarter: - if tGiven.Before(newMoonTable.Elems[0].FirstQuarter) { - return newMoonTable.Elems[0].FirstQuarter, nil - } - case FullMoon: - if tGiven.Before(newMoonTable.Elems[0].FullMoon) { - return newMoonTable.Elems[0].FullMoon, nil - } - case LastQuarter: - if tGiven.Before(newMoonTable.Elems[0].LastQuarter) { - return newMoonTable.Elems[0].LastQuarter, nil - } + if tGiven.Before(newMoonTable.Elems[0].LastQuarter) { + return newMoonTable.Elems[0].LastQuarter, nil } } } } - // range next phase - if i < len(moonTable.Elems)-1 { - // try to find in current table - elem2 := moonTable.Elems[i+1] + } + // range next phase + if i < len(moonTable.Elems)-1 { + // try to find in current table + elem2 := moonTable.Elems[i+1] + elemSearch1 = elem.LastQuarter + + switch phase { + case NewMoon: + elemSearch2 = elem2.NewMoon + case FirstQuarter: + elemSearch2 = elem2.FirstQuarter + case FullMoon: + elemSearch2 = elem2.FullMoon + case LastQuarter: + elemSearch2 = elem2.LastQuarter + } + + if tGiven.After(elemSearch1) && tGiven.Before(elemSearch2) { + return elemSearch2, nil + } + + } else { + // try to find in next table + newT := time.Date(tGiven.Year()+1, 0, 0, 0, 0, 0, 0, tGiven.Location()) + newMoonTable := CreateMoonTable(newT) + if newMoonTable != nil && newMoonTable.Elems != nil && len(newMoonTable.Elems) > 0 { elemSearch1 = elem.LastQuarter switch phase { case NewMoon: - elemSearch2 = elem2.NewMoon + elemSearch2 = newMoonTable.Elems[0].NewMoon case FirstQuarter: - elemSearch2 = elem2.FirstQuarter + elemSearch2 = newMoonTable.Elems[0].FirstQuarter case FullMoon: - elemSearch2 = elem2.FullMoon + elemSearch2 = newMoonTable.Elems[0].FullMoon case LastQuarter: - elemSearch2 = elem2.LastQuarter + elemSearch2 = newMoonTable.Elems[0].LastQuarter } if tGiven.After(elemSearch1) && tGiven.Before(elemSearch2) { return elemSearch2, nil } - - } else { - // try to find in next table - newT := time.Date(tGiven.Year()+1, 0, 0, 0, 0, 0, 0, tGiven.Location()) - newMoonTable := c.CreateMoonTable(newT) - if newMoonTable != nil && newMoonTable.Elems != nil && len(newMoonTable.Elems) > 0 { - elemSearch1 = elem.LastQuarter - - switch phase { - case NewMoon: - elemSearch2 = newMoonTable.Elems[0].NewMoon - case FirstQuarter: - elemSearch2 = newMoonTable.Elems[0].FirstQuarter - case FullMoon: - elemSearch2 = newMoonTable.Elems[0].FullMoon - case LastQuarter: - elemSearch2 = newMoonTable.Elems[0].LastQuarter - } - - if tGiven.After(elemSearch1) && tGiven.Before(elemSearch2) { - return elemSearch2, nil - } - } } } } diff --git a/pkg/moon/moon_test.go b/pkg/moon/moon_test.go index 743374f..715152e 100644 --- a/pkg/moon/moon_test.go +++ b/pkg/moon/moon_test.go @@ -43,8 +43,6 @@ func TestSearchPhase_NextPhase(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, }, }, @@ -61,16 +59,12 @@ func TestSearchPhase_NextPhase(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 3.0, - t2: 4.0, }, }, }, @@ -87,16 +81,12 @@ func TestSearchPhase_NextPhase(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 3.0, - t2: 4.0, }, }, }, @@ -113,16 +103,12 @@ func TestSearchPhase_NextPhase(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 3.0, - t2: 4.0, }, }, }, @@ -139,39 +125,18 @@ func TestSearchPhase_NextPhase(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, }, }, phase: NewMoon, wantTime: time.Date(2023, 1, 1, 0, 0, 0, 0, location), }, - { - name: "element with t1 == t2 should be skipped", - tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), - moonTable: &MoonTable{ - Elems: []*MoonTableElement{ - { - NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), - FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), - FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), - LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 1.0, - }, - }, - }, - phase: NewMoon, - wantError: errors.New("not found"), - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cache := &Cache{} - gotTime, err := cache.SearchPhase(tt.tGiven, tt.moonTable, tt.phase) + gotTime, err := SearchPhase(tt.tGiven, tt.moonTable, tt.phase) if tt.wantError != nil { if err == nil { t.Errorf("Test: %v, SearchPhase() error = %v, wantErr %v", tt.name, err, tt.wantError) @@ -203,16 +168,12 @@ func TestSearchPhase_AllPhaseTypes(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 3.0, - t2: 4.0, }, }, } @@ -249,11 +210,9 @@ func TestSearchPhase_AllPhaseTypes(t *testing.T) { }, } - cache := &Cache{} - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := cache.SearchPhase(tt.tGiven, moonTable, tt.phase) + result, err := SearchPhase(tt.tGiven, moonTable, tt.phase) if err != nil { t.Errorf("SearchPhase() for %s failed with error: %v", tt.name, err) @@ -287,24 +246,18 @@ func TestSearchPhase_ComplexScenarios(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 3.0, - t2: 4.0, }, { NewMoon: time.Date(2023, 3, 3, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 3, 10, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 3, 17, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 3, 24, 0, 0, 0, 0, location), - t1: 5.0, - t2: 6.0, }, }, }, @@ -321,16 +274,12 @@ func TestSearchPhase_ComplexScenarios(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 3.0, - t2: 4.0, }, }, }, @@ -339,11 +288,9 @@ func TestSearchPhase_ComplexScenarios(t *testing.T) { }, } - cache := &Cache{} - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := cache.SearchPhase(tt.tGiven, tt.moonTable, tt.phase) + result, err := SearchPhase(tt.tGiven, tt.moonTable, tt.phase) if err != nil { t.Errorf("SearchPhase() for %s failed with error: %v", tt.name, err) @@ -378,16 +325,12 @@ func TestSearchPhase_EdgeCases(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 3.0, - t2: 4.0, }, }, }, @@ -404,48 +347,18 @@ func TestSearchPhase_EdgeCases(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 3.0, - t2: 4.0, }, }, }, phase: NewMoon, wantTime: time.Date(2023, 2, 1, 0, 0, 0, 0, location), }, - { - name: "all elements skipped due to t1 == t2", - tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), - moonTable: &MoonTable{ - Elems: []*MoonTableElement{ - { - NewMoon: time.Date(2023, 1, 1, 0, 0, 0, 0, location), - FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), - FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), - LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 1.0, - }, - { - NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), - FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), - FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), - LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 2.0, - t2: 2.0, - }, - }, - }, - phase: NewMoon, - wantError: errors.New("not found"), - }, { name: "mixed valid and invalid elements", tGiven: time.Date(2023, 1, 10, 0, 0, 0, 0, location), @@ -456,16 +369,12 @@ func TestSearchPhase_EdgeCases(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 1.0, }, { NewMoon: time.Date(2023, 2, 1, 0, 0, 0, 0, location), FirstQuarter: time.Date(2023, 2, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 2, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 2, 22, 0, 0, 0, 0, location), - t1: 2.0, - t2: 3.0, }, }, }, @@ -476,9 +385,7 @@ func TestSearchPhase_EdgeCases(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cache := &Cache{} - - gotTime, err := cache.SearchPhase(tt.tGiven, tt.moonTable, tt.phase) + gotTime, err := SearchPhase(tt.tGiven, tt.moonTable, tt.phase) if tt.wantError != nil { if err == nil { @@ -523,8 +430,6 @@ func TestSearchPhase_TimeLocation(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, moscowLocation), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, moscowLocation), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, moscowLocation), - t1: 1.0, - t2: 2.0, }, }, }, @@ -541,8 +446,6 @@ func TestSearchPhase_TimeLocation(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, newYorkLocation), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, newYorkLocation), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, newYorkLocation), - t1: 1.0, - t2: 2.0, }, }, }, @@ -559,8 +462,6 @@ func TestSearchPhase_TimeLocation(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, utcLocation), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, utcLocation), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, utcLocation), - t1: 1.0, - t2: 2.0, }, }, }, @@ -571,9 +472,7 @@ func TestSearchPhase_TimeLocation(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cache := &Cache{} - - result, err := cache.SearchPhase(tt.tGiven, tt.moonTable, tt.phase) + result, err := SearchPhase(tt.tGiven, tt.moonTable, tt.phase) if err != nil { t.Errorf("SearchPhase() for %s failed with error: %v", tt.name, err) @@ -602,8 +501,6 @@ func TestSearchPhase_PhaseOrder(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, }, } @@ -635,11 +532,9 @@ func TestSearchPhase_PhaseOrder(t *testing.T) { }, } - cache := &Cache{} - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := cache.SearchPhase(tt.tGiven, moonTable, tt.phase) + result, err := SearchPhase(tt.tGiven, moonTable, tt.phase) if err != nil { t.Errorf("SearchPhase() for %s failed with error: %v", tt.name, err) @@ -664,8 +559,6 @@ func TestSearchPhase_SingleElement(t *testing.T) { FirstQuarter: time.Date(2023, 1, 8, 0, 0, 0, 0, location), FullMoon: time.Date(2023, 1, 15, 0, 0, 0, 0, location), LastQuarter: time.Date(2023, 1, 22, 0, 0, 0, 0, location), - t1: 1.0, - t2: 2.0, }, }, } @@ -697,11 +590,9 @@ func TestSearchPhase_SingleElement(t *testing.T) { }, } - cache := &Cache{} - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := cache.SearchPhase(tt.tGiven, singleElementTable, tt.phase) + result, err := SearchPhase(tt.tGiven, singleElementTable, tt.phase) if tt.wantError != nil { if err == nil { @@ -735,17 +626,14 @@ func TestSearchPhase_Performance(t *testing.T) { FirstQuarter: baseTime.AddDate(0, 0, 7), FullMoon: baseTime.AddDate(0, 0, 14), LastQuarter: baseTime.AddDate(0, 0, 21), - t1: float64(i * 2), - t2: float64(i*2 + 1), } elems = append(elems, elem) } largeMoonTable := &MoonTable{Elems: elems} - cache := &Cache{} tGiven := time.Date(2023, 6, 15, 0, 0, 0, 0, location) - result, err := cache.SearchPhase(tGiven, largeMoonTable, FullMoon) + result, err := SearchPhase(tGiven, largeMoonTable, FullMoon) if err != nil { t.Errorf("SearchPhase() with large table failed: %v", err) diff --git a/pkg/phase/models.go b/pkg/phase/models.go index 9e41a55..e1d4288 100644 --- a/pkg/phase/models.go +++ b/pkg/phase/models.go @@ -1,8 +1,21 @@ package phase type PhaseResp struct { + BeginDay *Phase + Current *Phase + EndDay *Phase + Illumination Illumination +} + +type Phase struct { Name string NameLocalized string Emoji string IsWaxing bool } + +type Illumination struct { + BeginDay float64 + Current float64 + EndDay float64 +} diff --git a/pkg/phase/phase.go b/pkg/phase/phase.go index 23a57d6..f2d26f2 100644 --- a/pkg/phase/phase.go +++ b/pkg/phase/phase.go @@ -9,7 +9,8 @@ import ( type illumFunc func(tGiven time.Time, loc *time.Location) float64 -func CurrentMoonPhase(tGiven time.Time, lang string) (float64, float64, float64, PhaseResp, PhaseResp, PhaseResp) { +func CurrentMoonPhase(tGiven time.Time, lang string) *PhaseResp { + pr := new(PhaseResp) newT := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), tGiven.Hour(), tGiven.Minute(), tGiven.Second(), 0, time.UTC) currentMoonIllumination, currentMoonIlluminationBefore, currentMoonIlluminationAfter := currentMoonPhaseCalc(newT, time.FixedZone("UTC+12", -12*60*60), il.GetCurrentMoonIllumination) @@ -38,10 +39,18 @@ func CurrentMoonPhase(tGiven time.Time, lang string) (float64, float64, float64, moonPhaseEnd.IsWaxing = false } - return currentMoonIllumination, dayBeginMoonIllumination, dayEndMoonIllumination, moonPhaseCurrent, moonPhaseBegin, moonPhaseEnd + pr.BeginDay = &moonPhaseBegin + pr.Current = &moonPhaseCurrent + pr.EndDay = &moonPhaseEnd + + pr.Illumination.BeginDay = dayBeginMoonIllumination + pr.Illumination.Current = currentMoonIllumination + pr.Illumination.EndDay = dayEndMoonIllumination + + return pr } -func GetMoonPhase(before, current, after float64, lang string) PhaseResp { +func GetMoonPhase(before, current, after float64, lang string) Phase { phaseName, phangeNameLocalized, phaseEmoji := "", "", "" switch { case current > 0.05 && current < 0.45 && current < after: @@ -69,7 +78,7 @@ func GetMoonPhase(before, current, after float64, lang string) PhaseResp { phangeNameLocalized = getMoonPhasesLocalized(lang, 7) phaseName, phaseEmoji = getMoonPhases(7) } - return PhaseResp{Name: phaseName, NameLocalized: phangeNameLocalized, Emoji: phaseEmoji} + return Phase{Name: phaseName, NameLocalized: phangeNameLocalized, Emoji: phaseEmoji} } func getMoonPhases(position int) (string, string) { @@ -113,6 +122,7 @@ func currentMoonPhaseCalc(tGiven time.Time, loc *time.Location, calcF illumFunc) func Truephase(k, phase float64) float64 { var t, t2, t3, pt, m, mprime, f float64 + // to do SynMonth := 29.53058868 // Synodic month (mean time from new to next new Moon) k += phase // Add phase to new moon time diff --git a/pkg/position/position.go b/pkg/position/position.go index 7aca8a3..09eaf47 100644 --- a/pkg/position/position.go +++ b/pkg/position/position.go @@ -277,6 +277,8 @@ func (c *Cache) GetRisesDay(year, month, day int, loc *time.Location, precision return dayResponse, nil } +// to do: refactor here. +// 3 req to 1 day is not ok func GetMoonPosition(tGiven time.Time, loc *time.Location, precision int, timeFormat string, location ...float64) (*MoonPosition, error) { lat, lon, err := parseLocation(location) if err != nil { diff --git a/pkg/zodiac/zodiac.go b/pkg/zodiac/zodiac.go index e624279..421b3ad 100644 --- a/pkg/zodiac/zodiac.go +++ b/pkg/zodiac/zodiac.go @@ -13,12 +13,14 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma zodiacCurrent := Zodiac{} zodiacEnd := Zodiac{} + var day moon.MoonDaysInDay + dayBeginTime := time.Date(tGiven.Year(), tGiven.Month(), tGiven.Day(), 0, 0, 0, 0, loc) - beginMoonDays, _, endMoonDays, _ := moon.GetMoonDaysPrecise(dayBeginTime, moonTable) + day, _ = moon.GetMoonDays(dayBeginTime, moonTable) - zodiacPositionBegin := int((beginMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 - zodiacPositionCurrent := int(((beginMoonDays.Minutes()+float64(tGiven.Hour()*60)+float64(tGiven.Minute()))/jt.Fminute*360.)/30./30.) % 12 - zodiacPositionEnd := int(((beginMoonDays.Minutes()+1440)/jt.Fminute*360.)/30./30.) % 12 + zodiacPositionBegin := int((day.Begin.Minutes()/jt.Fminute*360.)/30./30.) % 12 + zodiacPositionCurrent := int(((day.Begin.Minutes()+float64(tGiven.Hour()*60)+float64(tGiven.Minute()))/jt.Fminute*360.)/30./30.) % 12 + zodiacPositionEnd := int(((day.Begin.Minutes()+1440)/jt.Fminute*360.)/30./30.) % 12 if zodiacPositionBegin == zodiacPositionEnd { zods.Count = 1 @@ -38,21 +40,32 @@ func CurrentZodiacs(tGiven time.Time, loc *time.Location, lang string, timeForma var tBegin1 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin1)*time.Minute, timeFormat, moonTable) var tEnd1 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd1)*time.Minute, timeFormat, moonTable) + zods.Zodiac = make([]ZodiacDetailed, 2) zods.Zodiac[0].Begin = &tBegin1 zods.Zodiac[0].End = &tEnd1 zods.Zodiac[0].Name, zods.Zodiac[0].Emoji = getZodiacResp(zodiacPositionBegin) zods.Zodiac[0].NameLocalized = getZodiacRespLocalized(zodiacPositionBegin, lang) - if int(endMoonDays.Minutes()/jt.Fminute) == 0 { - endMoonDays += (beginMoonDays + 24*time.Hour) - zodiacPositionEnd = int((endMoonDays.Minutes()/jt.Fminute*360.)/30./30.) % 12 + if int(day.End.Minutes()/jt.Fminute) == 0 { + day.End += (day.Begin + 24*time.Hour) + zodiacPositionEnd = int((day.End.Minutes()/jt.Fminute*360.)/30./30.) % 12 } zodBegin2 := (zodiacPositionEnd) * jt.Fminute / 360 * 30. * 30. zodEnd2 := (zodiacPositionEnd + 1) * jt.Fminute / 360 * 30. * 30. + + // to do: test + // when last sign comes to first sign + // zodBegin2 shild be == zodEnd1 + if zodBegin2 == 0 { + zodBegin2 = zodEnd1 + zodEnd2 += zodEnd1 + } + var tBegin2 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodBegin2)*time.Minute, timeFormat, moonTable) var tEnd2 any = moon.BeginMoonDayToEarthDay(tGiven, time.Duration(zodEnd2)*time.Minute, timeFormat, moonTable) + zods.Zodiac[1].Begin = &tBegin2 zods.Zodiac[1].End = &tEnd2 zods.Zodiac[1].Name, zods.Zodiac[1].Emoji = getZodiacResp(zodiacPositionEnd) From 8c1b5504d1a68dc5b1c166a3f4c9f839f78c32a2 Mon Sep 17 00:00:00 2001 From: prostraction Date: Sat, 22 Nov 2025 21:46:38 +0500 Subject: [PATCH 17/18] fix: version --- internal/server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/server/server.go b/internal/server/server.go index 34eefc9..c17a73a 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -102,5 +102,5 @@ func (s *Server) NewRouter() *fiber.App { } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc8") + return c.JSON("1.2.0rc7-hotfix") } From 5bfd5a367753995d21c9b14df6a186cc5f3a222d Mon Sep 17 00:00:00 2001 From: prostraction Date: Wed, 26 Nov 2025 22:34:45 +0500 Subject: [PATCH 18/18] fix: critical fix for corrent moon day handle --- internal/cache/cache.go | 7 ++++ internal/server/route-next.go | 54 ++++++++++++++++++++++++++++ internal/server/route-phase.go | 33 +++++++++-------- internal/server/server.go | 4 ++- pkg/moon/days.go | 17 +++++---- pkg/moon/models.go | 15 ++++++++ pkg/moon/moon.go | 65 ++++++++++++++++++++++++++++++++-- 7 files changed, 169 insertions(+), 26 deletions(-) create mode 100644 internal/cache/cache.go diff --git a/internal/cache/cache.go b/internal/cache/cache.go new file mode 100644 index 0000000..c374047 --- /dev/null +++ b/internal/cache/cache.go @@ -0,0 +1,7 @@ +package cache + +type moonCache struct { +} + +type positionCache struct { +} diff --git a/internal/server/route-next.go b/internal/server/route-next.go index e1bef56..6cd0a90 100644 --- a/internal/server/route-next.go +++ b/internal/server/route-next.go @@ -3,10 +3,12 @@ package server import ( jt "moon/pkg/julian-time" "moon/pkg/moon" + "strconv" "strings" "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" ) func (s *Server) moonNextMoonPhaseV1(c *fiber.Ctx) error { @@ -46,5 +48,57 @@ func (s *Server) moonNextMoonPhaseV1(c *fiber.Ctx) error { } return c.JSON(npc) } +} + +func (s *Server) moonNextMoonDayV1(c *fiber.Ctx) error { + utc := c.Query("utc", "UTC:+0") + loc, _ := jt.SetTimezoneLocFromString(utc) + day := c.Query("day", "not-set") + if day == "not-set" { + e := ErrorPrintable{Status: 400, Message: "day is required for this method."} + return c.JSON(e) + } + dayInt, err := strconv.Atoi(day) + if err != nil { + e := ErrorPrintable{Status: 400, Message: "day is required to be int."} + return c.JSON(e) + } + if dayInt < 0 || dayInt > 30 { + e := ErrorPrintable{Status: 400, Message: "day is required to be [0, 30]. No other days allowed!"} + return c.JSON(e) + } + + tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond(), time.Local) + tGiven = tGiven.In(loc) + + moonTable := moon.CreateMoonTable(tGiven) + resp, err := moon.SearchMoonDay(tGiven, moonTable, dayInt) + if err != nil { + log.Error(err) + e := ErrorPrintable{Status: 500, Message: "bad things happen: " + err.Error()} + return c.JSON(e) + } + format := c.Query("timeFormat", "ISO") + if strings.ToLower(format) == "timestamp" { + resp := moon.SeachMoonDayRespTimestamp{ + From: resp.From.Unix(), + To: resp.To.Unix(), + } + return c.JSON(resp) + } else if strings.ToLower(format) == "duration" { + resp := moon.SeachMoonDayRespTimestamp{ + From: resp.From.Unix() - tGiven.Unix(), + To: resp.To.Unix() - tGiven.Unix(), + } + return c.JSON(resp) + } else if strings.ToLower(format) == "iso" { + return c.JSON(resp) + } else { + resp := moon.SeachMoonDayRespString{ + From: resp.From.Format(format), + To: resp.To.Format(format), + } + return c.JSON(resp) + } } diff --git a/internal/server/route-phase.go b/internal/server/route-phase.go index b299386..00ecf35 100644 --- a/internal/server/route-phase.go +++ b/internal/server/route-phase.go @@ -16,10 +16,10 @@ import ( /* MOON PHASE */ func (s *Server) moonPhaseCurrentV1(c *fiber.Ctx) error { utc := c.Query("utc", "UTC:+0") - loc, _ := jt.SetTimezoneLocFromString(utc) - /*if err != nil { - log.Println(err) - }*/ + loc, err := jt.SetTimezoneLocFromString(utc) + if err != nil { + log.Trace(err) + } tGiven := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), 0, time.Local) tGiven = tGiven.In(loc) precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) @@ -35,19 +35,19 @@ func (s *Server) moonPhaseCurrentV1(c *fiber.Ctx) error { func (s *Server) moonPhaseTimestampV1(c *fiber.Ctx) error { utc := c.Query("utc", "UTC:+0") - loc, _ := jt.SetTimezoneLocFromString(utc) - /*if err != nil { - log.Println(err) - }*/ + loc, err := jt.SetTimezoneLocFromString(utc) + if err != nil { + log.Trace(err) + } tStr := c.Query("timestamp", strconv.FormatInt(time.Now().Unix(), 10)) t, err := strconv.ParseInt(tStr, 10, 64) if err != nil { t = time.Now().Unix() } + tm := time.Unix(t, 0) - tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, time.Local) - tGiven = tGiven.In(loc) + tGiven := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute(), tm.Second(), 0, time.Local).In(loc) precision := StrToInt(c.Query("precision", "2"), 2, 0, 20) @@ -62,10 +62,10 @@ func (s *Server) moonPhaseTimestampV1(c *fiber.Ctx) error { func (s *Server) moonPhaseDatetV1(c *fiber.Ctx) error { utc := c.Query("utc", "UTC:+0") - loc, _ := jt.SetTimezoneLocFromString(utc) - /*if err != nil { - log.Println(err) - }*/ + loc, err := jt.SetTimezoneLocFromString(utc) + if err != nil { + log.Trace(err) + } tNow := time.Now() @@ -73,7 +73,7 @@ func (s *Server) moonPhaseDatetV1(c *fiber.Ctx) error { month := StrToInt(c.Query("month", strconv.Itoa(int(tNow.Month()))), int(tNow.Month()), 1, 12) day := StrToInt(c.Query("day", strconv.Itoa(int(tNow.Day()))), int(tNow.Day()), 1, 31) - err := IsValidDate(year, month, day) + err = IsValidDate(year, month, day) if err != nil { c.Status(400) errPrintable := ErrorPrintable{ @@ -136,8 +136,7 @@ func (s *Server) moonPhaseV1(c *fiber.Ctx, tGiven time.Time, precision int, loca resp.CurrentState.Phase = phase.Current resp.EndDay.Phase = phase.EndDay - // zodiac - + // zodiac TO DO refactor resp.ZodiacDetailed, resp.BeginDay.Zodiac, resp.CurrentState.Zodiac, resp.EndDay.Zodiac = zodiac.CurrentZodiacs(tGiven, loc, lang, timeFormat, moonTable.Elems) if locationCords.IsValid { diff --git a/internal/server/server.go b/internal/server/server.go index c17a73a..756d335 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -72,7 +72,9 @@ func (s *Server) NewRouter() *fiber.App { // methods when? app.Get("/v1/nextMoonPhase", s.moonNextMoonPhaseV1) + app.Get("/v1/nextMoonDay", s.moonNextMoonDayV1) app.Get("/api/v1/nextMoonPhase", s.moonNextMoonPhaseV1) + app.Get("/api/v1/nextMoonDay", s.moonNextMoonDayV1) //app.Get("/v1/nextMoonEclipse") //app.Get("/v1/nextSunEclipse") @@ -102,5 +104,5 @@ func (s *Server) NewRouter() *fiber.App { } func (s *Server) versionV1(c *fiber.Ctx) error { - return c.JSON("1.2.0rc7-hotfix") + return c.JSON("1.2.0rc7-hotfix-2") } diff --git a/pkg/moon/days.go b/pkg/moon/days.go index a030fde..e9202ae 100644 --- a/pkg/moon/days.go +++ b/pkg/moon/days.go @@ -8,8 +8,6 @@ import ( il "moon/pkg/illumination" jt "moon/pkg/julian-time" phase "moon/pkg/phase" - - "github.com/gofiber/fiber/v2/log" ) // to do @@ -159,11 +157,15 @@ func GetMoonDays(tGiven time.Time, table []*MoonTableElement) (MoonDaysInDay, ti if i < len(table)-1 { elem2 = table[i+1] // new next moon } else { - log.Error("if i < len(table)-1 { // fix it, it is required") - elem2 = new(MoonTableElement) // new next moon approx (fix?) to do - elem2.NewMoon = elem.NewMoon.AddDate(0, 0, 29) - elem2.NewMoon = elem.NewMoon.Add(time.Hour * 12) + newTGiven := time.Date(tGiven.Year()+1, time.January, 1, 0, 0, 0, 0, tGiven.Location()) + newT := CreateMoonTable(newTGiven) + if newT != nil && newT.Elems != nil && len(newT.Elems) > 0 { + return GetMoonDays(tGiven, newT.Elems) + } else { + /// return err + } } + moonMonDays := elem2.NewMoon.Sub(elem.NewMoon) // moon month eartbeg := tBegin.Add(-tGiven.Sub(elem.NewMoon)) @@ -178,6 +180,9 @@ func GetMoonDays(tGiven time.Time, table []*MoonTableElement) (MoonDaysInDay, ti beginDay = beginDay.Add(day) currentDay = currentDay.Add(day) } + last := tBegin.Sub(beginDay) + beginDay = beginDay.Add(last) + currentDay = currentDay.Add(last) currentDay = currentDay.Add(time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Hour())))) currentDay = currentDay.Add(time.Minute * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Minute())))) diff --git a/pkg/moon/models.go b/pkg/moon/models.go index 0b09a9c..6c77583 100644 --- a/pkg/moon/models.go +++ b/pkg/moon/models.go @@ -65,3 +65,18 @@ type NearestPhaseString struct { FullMoon string LastQuarter string } + +type SeachMoonDayResp struct { + From time.Time + To time.Time +} + +type SeachMoonDayRespTimestamp struct { + From int64 + To int64 +} + +type SeachMoonDayRespString struct { + From string + To string +} diff --git a/pkg/moon/moon.go b/pkg/moon/moon.go index 9b18a9a..b492a2d 100644 --- a/pkg/moon/moon.go +++ b/pkg/moon/moon.go @@ -2,9 +2,10 @@ package moon import ( "errors" - "log" pos "moon/pkg/position" "time" + + log "github.com/gofiber/fiber/v2/log" ) // to do @@ -71,7 +72,7 @@ func MoonDetailed(tGiven time.Time, loc *time.Location, lang string, timeFormat func SearchPhase(tGiven time.Time, moonTable *MoonTable, phase EnumPhase) (t time.Time, err error) { if moonTable == nil { err = errors.New("passed empty moonTable to SearchNewMoon") - log.Println(err.Error()) + log.Debug(err.Error()) return } err = errors.New("not found") @@ -191,3 +192,63 @@ func SearchPhase(tGiven time.Time, moonTable *MoonTable, phase EnumPhase) (t tim } return } + +func SearchMoonDay(tGiven time.Time, moonTable *MoonTable, moonDay int) (SeachMoonDayResp, error) { + t := tGiven + var resp SeachMoonDayResp + var err error + if moonTable == nil { + err = errors.New("passed empty moonTable to SearchNewMoon") + log.Error(err.Error()) + return resp, err + } + err = errors.New("not found") + iters := 0 + for i := range moonTable.Elems { + elem := moonTable.Elems[i] + if t.After(elem.NewMoon) && t.Before(elem.NewMoon.Add(time.Hour*24*32)) { + var elem2 *MoonTableElement + if i < len(moonTable.Elems)-1 { + elem2 = moonTable.Elems[i+1] // new next moon + } else { + newTGiven := time.Date(tGiven.Year()+1, time.January, 1, 0, 0, 0, 0, tGiven.Location()) + newT := CreateMoonTable(newTGiven) + if newT != nil && newT.Elems != nil && len(newT.Elems) > 0 { + return SearchMoonDay(tGiven, newT, moonDay) + } else { + ///// return err + } + } + moonMonDays := elem2.NewMoon.Sub(elem.NewMoon) // moon month + if moonDay > int(moonMonDays) { + break // to do + } + eartbeg := t.Add(-t.Sub(elem.NewMoon)) + eartend := time.Date(eartbeg.Year(), eartbeg.Month()+1, eartbeg.Day(), eartbeg.Hour(), eartbeg.Minute(), eartbeg.Second(), 0, t.Location()) + eartMon := eartend.Unix() - eartbeg.Unix() // earth month + + beginDay := elem.NewMoon + //currentDay := elem.NewMoon + day := time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*24.)) + //for t.Sub(beginDay) > day { + // beginDay = beginDay.Add(day) + // currentDay = currentDay.Add(day) + //} + log.Debug(beginDay) + beginDay = beginDay.Add(time.Duration(moonDay) * day) + + //currentDay = currentDay.Add(time.Hour * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Hour())))) + //currentDay = currentDay.Add(time.Minute * time.Duration(int64(moonMonDays.Seconds()/float64(eartMon)*float64(tGiven.Minute())))) + + endDay := beginDay.Add(day) + + resp.From = beginDay + resp.To = endDay + return resp, nil + } + t = time.Date(t.Year()+1, 0, 0, 0, 0, 0, 0, t.Location()) + moonTable = CreateMoonTable(t) + iters++ + } + return resp, err +}