当设计全球化后端服务时,时间处理是一个关键的考虑因素。以下是一个全面的设计方案,涵盖了数据库存储和 API 接口设计:
CREATE TABLE events (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
start_time TIMESTAMP WITH TIME ZONE NOT NULL,
end_time TIMESTAMP WITH TIME ZONE NOT NULL,
user_id INTEGER NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
preferred_timezone VARCHAR(50) NOT NULL DEFAULT 'UTC',
-- 其他用户字段
);
import "time"
func storeEvent(title string, startTime, endTime time.Time, userID int) error {
// 确保时间是 UTC
startTimeUTC := startTime.UTC()
endTimeUTC := endTime.UTC()
// 存储到数据库
_, err := db.Exec("INSERT INTO events (title, start_time, end_time, user_id) VALUES ($1, $2, $3, $4)",
title, startTimeUTC, endTimeUTC, userID)
return err
}
func getUserEvents(userID int) ([]Event, error) {
var events []Event
// 从数据库获取事件
rows, err := db.Query("SELECT id, title, start_time, end_time FROM events WHERE user_id = $1", userID)
if err != nil {
return nil, err
}
defer rows.Close()
user, err := getUserById(userID)
if err != nil {
return nil, err
}
loc, err := time.LoadLocation(user.PreferredTimezone)
if err != nil {
loc = time.UTC
}
for rows.Next() {
var e Event
var startTime, endTime time.Time
err := rows.Scan(&e.ID, &e.Title, &startTime, &endTime)
if err != nil {
return nil, err
}
e.StartTime = startTime.In(loc)
e.EndTime = endTime.In(loc)
events = append(events, e)
}
return events, nil
}
type CreateEventRequest struct {
Title string `json:"title"`
StartTime string `json:"start_time"` // 格式: "2023-08-15T14:30:00+08:00"
EndTime string `json:"end_time"` // 格式: "2023-08-15T16:30:00+08:00"
}
func handleCreateEvent(w http.ResponseWriter, r *http.Request) {
var req CreateEventRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
startTime, err := time.Parse(time.RFC3339, req.StartTime)
if err != nil {
http.Error(w, "Invalid start time format", http.StatusBadRequest)
return
}
endTime, err := time.Parse(time.RFC3339, req.EndTime)
if err != nil {
http.Error(w, "Invalid end time format", http.StatusBadRequest)
return
}
// 处理创建事件的逻辑
// ...
}
type EventResponse struct {
ID int `json:"id"`
Title string `json:"title"`
StartTime string `json:"start_time"`
EndTime string `json:"end_time"`
TimeZone string `json:"time_zone"`
}
func handleGetEvents(w http.ResponseWriter, r *http.Request) {
userID := getUserIDFromRequest(r)
events, err := getUserEvents(userID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
user, err := getUserById(userID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var response []EventResponse
for _, e := range events {
response = append(response, EventResponse{
ID: e.ID,
Title: e.Title,
StartTime: e.StartTime.Format(time.RFC3339),
EndTime: e.EndTime.Format(time.RFC3339),
TimeZone: user.PreferredTimezone,
})
}
json.NewEncoder(w).Encode(response)
}
func getEventsInRange(startDate, endDate time.Time, userID int) ([]Event, error) {
startUTC := startDate.UTC()
endUTC := endDate.UTC()
query := `SELECT id, title, start_time, end_time
FROM events
WHERE user_id = $1 AND start_time >= $2 AND end_time <= $3`
// 执行查询并处理结果
// ...
}
func updateUserTimeZone(userID int, newTimeZone string) error {
_, err := db.Exec("UPDATE users SET preferred_timezone = $1 WHERE id = $2", newTimeZone, userID)
return err
}
通过这种设计,后端服务将能够有效地处理全球化环境中的时间存储和展示问题。它保证了数据的一致性(通过使用 UTC),同时提供了灵活性(通过用户首选时区),使得系统能够适应不同地理位置用户的需求。记住,时间处理是一个复杂的话题,可能需要根据具体的应用场景和需求进行进一步的调整和优化。