diff --git a/README.md b/README.md index a0c2541..031cbd3 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ As a fan of [TimeAPI.io](https://www.timeapi.io/swagger/index.html), but not thi ## Usage ```shell -neo@matrix:~$ curl -s -H "Accept: application/json" "http://localhost:9100/Time/current/zone?timeZone=Europe/Berlin" | jq +neo@matrix:~$ curl -s -H "Accept: application/json" "http://localhost:9100/Europe/Berlin" | jq { "year": 2023, "month": 6, diff --git a/apiTime/api.go b/apiTime/api.go deleted file mode 100644 index f4fb061..0000000 --- a/apiTime/api.go +++ /dev/null @@ -1,140 +0,0 @@ -package apiTime - -import "git.0x0001f346.de/andreas/api" - -var tags []string = []string{"Time"} - -func GetComponents() *api.Components { - components := &api.Components{ - Schemas: map[string]*api.ComponentsSchema{ - "CurrentTime": { - Properties: map[string]*api.ComponentsProperty{ - "year": { - Type: "integer", - Description: "Year", - Format: "int64", - Example: 2020, - }, - "month": { - Type: "integer", - Description: "Month", - Format: "int64", - Example: 12, - }, - "day": { - Type: "integer", - Description: "Day", - Format: "int64", - Example: 13, - }, - "hour": { - Type: "integer", - Description: "Hour of the day in range 0-24", - Format: "int64", - Example: 9, - }, - "minute": { - Type: "integer", - Description: "Minute", - Format: "int64", - Example: 30, - }, - "seconds": { - Type: "integer", - Description: "Second", - Format: "int64", - Example: 30, - }, - "milliSeconds": { - Type: "integer", - Description: "Milliseconds", - Format: "int64", - Example: 123, - }, - "dateTime": { - Type: "string", - Description: "Full date time", - Format: "date-time", - Example: "2020-12-13T09:30:30+01:00", - }, - "date": { - Type: "string", - Description: "Date string", - Example: "13/12/2020", - }, - "time": { - Type: "string", - Description: "Time string", - Example: "09:30", - }, - "timeZone": { - Type: "string", - Description: "TimeZone of the result", - Example: "Europe/Berlin", - }, - "dayOfWeek": { - Type: "string", - Description: "The day of the week", - Example: "Monday", - }, - "dstActive": { - Type: "boolean", - Description: "Boolean describing whether DST is applied and active in that timezone", - Example: false, - }, - }, - AdditionalProperties: false, - }, - }, - } - - return components -} - -func GetPaths() map[string]*api.Path { - paths := map[string]*api.Path{ - "/Time/current/zone": { - GET: &api.Method{ - Function: TimeCurrentZone, - Tags: tags, - Summary: "Gets the current time of a time zone.", - Parameters: []*api.Parameters{ - { - Name: "timeZone", - In: "query", - Description: "Full IANA time zone names.", - Schema: &api.ParametersSchema{ - Type: "string", - }, - Example: "Europe/Berlin", - AllowReserved: true, - }, - }, - Responses: map[int]*api.Response{ - 200: { - Description: "Current time", - Content: map[string]*api.ResponseContent{ - "application/json": { - Schema: &api.ResponseSchema{ - Ref: "#/components/schemas/CurrentTime", - }, - }, - }, - }, - 400: { - Description: "Error message", - Content: map[string]*api.ResponseContent{ - "application/json": { - Schema: &api.ResponseSchema{ - Type: "string", - }, - }, - }, - }, - }, - }, - }, - } - - return paths -} diff --git a/apiTime/time.go b/apiTime/time.go deleted file mode 100644 index f7e160c..0000000 --- a/apiTime/time.go +++ /dev/null @@ -1,81 +0,0 @@ -package apiTime - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "strconv" - "time" -) - -type dateTimeResponse struct { - Year int `json:"year,omitempty"` - Month int `json:"month,omitempty"` - Day int `json:"day,omitempty"` - Hour int `json:"hour,omitempty"` - Minute int `json:"minute,omitempty"` - Seconds int `json:"seconds,omitempty"` - MilliSeconds int `json:"milliSeconds,omitempty"` - DateTime string `json:"dateTime,omitempty"` - Date string `json:"date,omitempty"` - Time string `json:"time,omitempty"` - TimeZone string `json:"timeZone,omitempty"` - DayOfWeek string `json:"dayOfWeek,omitempty"` - DstActive bool `json:"dstActive,omitempty"` -} - -func TimeCurrentZone(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Language", "en") - w.Header().Set("Content-Type", "application/json; charset=utf-8") - - timeZone := "" - - vals := r.URL.Query() - parameters, ok := vals["timeZone"] - if ok && len(parameters) == 1 { - timeZone = parameters[0] - } - - if timeZone == "" { - w.WriteHeader(400) - fmt.Fprint(w, "{\"error\": \"invalid value for timeZone\"}") - return - } - - loc, err := time.LoadLocation(timeZone) - if err != nil { - w.WriteHeader(400) - fmt.Fprint(w, "{\"error\": \"invalid value for timeZone\"}") - return - } - - log.Printf("%q %q\n", loc.String(), r.RemoteAddr) - - w.WriteHeader(200) - fmt.Fprint(w, getDateTimeResponseAsJSONString(loc)) -} - -func getDateTimeResponse(loc *time.Location) dateTimeResponse { - t := time.Now().In(loc) - return dateTimeResponse{ - Year: t.Year(), - Month: int(t.Month()), - Day: t.Day(), - Hour: t.Hour(), - Minute: t.Minute(), - Seconds: t.Second(), - MilliSeconds: func(i int64, err error) int { return int(i) }(strconv.ParseInt(strconv.Itoa(t.Nanosecond())[:3], 10, 0)), - DateTime: t.Format(time.RFC3339), - Date: fmt.Sprintf("%02d/%02d/%04d", t.Month(), t.Day(), t.Year()), - Time: fmt.Sprintf("%02d:%02d", t.Hour(), t.Minute()), - TimeZone: loc.String(), - DayOfWeek: t.Weekday().String(), - DstActive: t.IsDST(), - } -} - -func getDateTimeResponseAsJSONString(loc *time.Location) string { - r, _ := json.Marshal(getDateTimeResponse(loc)) - return string(r) -} diff --git a/main.go b/main.go index 079f961..0fe8515 100644 --- a/main.go +++ b/main.go @@ -1,83 +1,34 @@ package main import ( + "encoding/json" "fmt" "log" "net/http" - - "git.0x0001f346.de/andreas/Datetime-API/apiTime" - "git.0x0001f346.de/andreas/api" - "github.com/gorilla/mux" + "strconv" + "time" ) -const apiTitle string = "Datetime-API" -const apiDescription string = "A simple API to get the current time for a given time zone." -const apiVersion string = "0.1" -const portToListenOn int = 9100 +var portToListenOn int = 9100 +var defaultTZ string = "Europe/Berlin" + +type DateTimeResponse struct { + Year int `json:"year,omitempty"` + Month int `json:"month,omitempty"` + Day int `json:"day,omitempty"` + Hour int `json:"hour,omitempty"` + Minute int `json:"minute,omitempty"` + Seconds int `json:"seconds,omitempty"` + MilliSeconds int `json:"milliSeconds,omitempty"` + DateTime string `json:"dateTime,omitempty"` + Date string `json:"date,omitempty"` + Time string `json:"time,omitempty"` + TimeZone string `json:"timeZone,omitempty"` + DayOfWeek string `json:"dayOfWeek,omitempty"` + DstActive bool `json:"dstActive,omitempty"` +} func main() { - API := buildAPI() - router := mux.NewRouter() - - router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Language", "en") - w.Header().Set("Content-Type", "application/json; charset=utf-8") - fmt.Fprint(w, API.ToJSONString()) - }).Methods("GET") - - for route, path := range API.Paths { - if path.DELETE != nil { - if path.DELETE.Function == nil { - continue - } - router.HandleFunc(route, path.DELETE.Function).Methods("DELETE") - } - if path.GET != nil { - if path.GET.Function == nil { - continue - } - router.HandleFunc(route, path.GET.Function).Methods("GET") - } - if path.PATCH != nil { - if path.PATCH.Function == nil { - continue - } - router.HandleFunc(route, path.PATCH.Function).Methods("PATCH") - } - if path.POST != nil { - if path.POST.Function == nil { - continue - } - router.HandleFunc(route, path.POST.Function).Methods("POST") - } - if path.PUT != nil { - if path.PUT.Function == nil { - continue - } - router.HandleFunc(route, path.PUT.Function).Methods("PUT") - } - } - - printStartupBanner() - - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", portToListenOn), router)) -} - -func buildAPI() api.API { - fullAPI := api.GetAPIPrototype(apiTitle, apiDescription, apiVersion) - - for route, path := range apiTime.GetPaths() { - fullAPI.Paths[route] = path - } - - for name, schema := range apiTime.GetComponents().Schemas { - fullAPI.Components.Schemas[name] = schema - } - - return fullAPI -} - -func printStartupBanner() { fmt.Println("********************************************") fmt.Println("* *") fmt.Println("* git.0x0001f346.de/andreas/Datetime-API *") @@ -86,4 +37,51 @@ func printStartupBanner() { fmt.Println("") fmt.Printf("Listening: http://localhost:%d\n", portToListenOn) fmt.Println("") + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + loc, _ := time.LoadLocation(defaultTZ) + if r.URL.String() != "/" { + newloc, err := time.LoadLocation(r.URL.String()[1:]) + if err == nil { + loc = newloc + } + } + + log.Printf( + "%q %q\n", + loc.String(), + r.RemoteAddr, + ) + + w.Header().Add("Content-Language", "en") + w.Header().Add("Content-Type", "application/json; charset=utf-8") + w.Header().Add("Server", "git.0x0001f346.de/andreas/Datetime-API") + fmt.Fprint(w, GetDateTimeResponseAsJSONString(loc)) + }) + + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", portToListenOn), nil)) +} + +func GetDateTimeResponse(loc *time.Location) DateTimeResponse { + t := time.Now().In(loc) + return DateTimeResponse{ + Year: t.Year(), + Month: int(t.Month()), + Day: t.Day(), + Hour: t.Hour(), + Minute: t.Minute(), + Seconds: t.Second(), + MilliSeconds: func(i int64, err error) int { return int(i) }(strconv.ParseInt(strconv.Itoa(t.Nanosecond())[:3], 10, 0)), + DateTime: t.Format(time.RFC3339), + Date: fmt.Sprintf("%02d/%02d/%04d", t.Month(), t.Day(), t.Year()), + Time: fmt.Sprintf("%02d:%02d", t.Hour(), t.Minute()), + TimeZone: loc.String(), + DayOfWeek: t.Weekday().String(), + DstActive: t.IsDST(), + } +} + +func GetDateTimeResponseAsJSONString(loc *time.Location) string { + r, _ := json.Marshal(GetDateTimeResponse(loc)) + return string(r) }