Go by Example: http Request

requirement
A web service provides weather API: GET http://example.server/weather?date=2021-06-29
query parameter: date: string of date
rsponse data is of JSON format:
   {
     "status": 0,
     "result": {
       "high": 30,
       "low":  20,
       "rain": 0.75
     }
   }

Unfortunately, it does not provide APIs to get a range of data.
Please complete func quoteApi() so it can take year and month as arguments,
returning a slice of results.

Bonus: sending requests concurrency
package main
import (
    "context"
    "encoding/json"
    "errors"
    "flag"
    "fmt"
    "net/http"
    "sync"
    "sync/atomic"
    "time"
)
var (
    month int
    year  int
)
type Weather struct {
    High float64 `json:"high"`
    Low  float64 `json:"low"`
    Rain float64 `json:"rain"`
}
const API = "http://example.server/weather"
const TIME_LAYOUT = "2006-1"

the last day for day count of month

func getLastDay(year int, month int) (int, error) {
    t, err := time.Parse(TIME_LAYOUT, fmt.Sprintf("%d-%v", year, month))
    lastDay := t.AddDate(0, 1, 0).Add(-time.Nanosecond)
    if err != nil {
        return -1, err
    }
    return lastDay.Day(), nil
}

get single day weather struct from API

func getWeather(ctx context.Context, api string, year, month, day int) (Weather, error) {

%02d -> ensure the total lenght of 2 with 0 padding prefix

    url := fmt.Sprintf("%s?date=%d-%02d-%02d", api, year, month, day)
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
        return Weather{}, err
    }
    res, err := http.DefaultClient.Do(req)
    if err != nil {
        return Weather{}, err
    }
    if res.StatusCode != 200 {
        return Weather{}, fmt.Errorf("Non-OK HTTP status: %v", res.StatusCode)
    }
    defer res.Body.Close()
    var w Weather
    err = json.NewDecoder(res.Body).Decode(&w)
    if err != nil {
        return Weather{}, err
    }
    return w, nil
}

the success req count

func quoteApi(api string, year int, month time.Month) ([]Weather, error) {
    lastDay, err := getLastDay(year, int(month))
    if err != nil {
        return nil, err
    }
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    ret := make([]Weather, lastDay)
    var wg sync.WaitGroup
    var start int32 = 0

cancel other waiting request if meet one error

    counter := atomic.LoadInt32(&start)
    for day := 1; day <= lastDay; day++ {
        wg.Add(1)
        go func(ctx context.Context, day int) {
            w, err := getWeather(ctx, api, year, int(month), day+1)
            if err != nil {
                fmt.Println(err)

if only got partial data

                cancel()
                wg.Done()
                return
            }
            atomic.AddInt32(&counter, 1)
            ret[day-1] = w // assign
            wg.Done()
        }(ctx, day)
    }
    wg.Wait()
    if int(counter) != lastDay {
        cancel()
        return nil, errors.New("incomplete data")
    }
    return ret, nil
}
func init() {
    flag.IntVar(&year, "y", 0, "year value")
    flag.IntVar(&month, "m", 0, "month value")
}
func main() {
    flag.Parse()
    quoteApi(API, year, time.Month(month))
}
$ go run http-req.go -m 2 -y 2023

Next example: http.RoundTripper.