package rain import ( "context" "fmt" "log" "os" "path" "regexp" "strconv" "strings" "time" "weatherstation/internal/database" ) var ( // Matches ...///////.bin // Example: /tiles/China/CMPA_RT_China_0P01_HOR-PRE_GISJPG_Tiles/20251007/01/00/7/40/102.bin tileRE = regexp.MustCompile(`(?i)/tiles/.+?/([^/]+)/([0-9]{8})/([0-9]{2})/([0-9]{2})/([0-9]+)/([0-9]+)/([0-9]+)\.bin$`) ) // TileRef references a CMPA rain tile. type TileRef struct { Product string DT time.Time // nominal time in Asia/Shanghai (UTC+8) Z, Y, X int } // ParseCMPATileURL parses a CMPA tile URL/path and extracts product, time (UTC+8), z/y/x. // The timestamp in the path is UTC; we convert to Asia/Shanghai by adding 8h. func ParseCMPATileURL(u string) (TileRef, error) { p := u if i := strings.IndexAny(p, "?#"); i >= 0 { p = p[:i] } p = path.Clean(p) m := tileRE.FindStringSubmatch(p) if len(m) == 0 { return TileRef{}, fmt.Errorf("unrecognized CMPA tile path: %s", u) } product := m[1] yyyymmdd := m[2] hh := m[3] mm := m[4] z := mustAtoi(m[5]) y := mustAtoi(m[6]) x := mustAtoi(m[7]) // Parse as UTC then shift to CST(+8) utcT, err := time.ParseInLocation("20060102 15 04", fmt.Sprintf("%s %s %s", yyyymmdd, hh, mm), time.UTC) if err != nil { return TileRef{}, fmt.Errorf("parse utc time: %w", err) } loc, _ := time.LoadLocation("Asia/Shanghai") if loc == nil { loc = time.FixedZone("CST", 8*3600) } dt := utcT.In(loc) return TileRef{Product: product, DT: dt, Z: z, Y: y, X: x}, nil } func mustAtoi(s string) int { n, _ := strconv.Atoi(s); return n } // StoreTileBytes parses the URL, computes metadata and upserts into rain_tiles. func StoreTileBytes(ctx context.Context, urlOrPath string, data []byte) error { ref, err := ParseCMPATileURL(urlOrPath) if err != nil { return err } // 可选:强制 +8 小时(若上游传入的时间已为 UTC 且未转换,可用此开关修正) if strings.EqualFold(strings.TrimSpace(strings.ToLower(os.Getenv("RAIN_FORCE_SHIFT8"))), "1") { ref.DT = ref.DT.Add(8 * time.Hour) } // 可选调试:打印解析出的时间(CST)与 URL if strings.EqualFold(strings.TrimSpace(strings.ToLower(os.Getenv("RAIN_DEBUG"))), "1") { log.Printf("[rain] store tile: url=%s -> product=%s dt(local)=%s z=%d y=%d x=%d", urlOrPath, ref.Product, ref.DT.Format("2006-01-02 15:04:05 -0700"), ref.Z, ref.Y, ref.X) } db := database.GetDB() return database.UpsertRainTile(ctx, db, ref.Product, ref.DT, ref.Z, ref.Y, ref.X, 256, 256, data) }