Files
server/app/http/helpers/timezone.go
T
2026-01-16 15:49:34 +08:00

214 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package helpers
import (
"goravel/app/utils"
"time"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/str"
)
// GetCurrentTimezone 获取当前请求的时区
// 优先从请求头 X-Timezone 或 Timezone 获取
// 如果请求头没有或时区无效,使用配置的默认时区
func GetCurrentTimezone(ctx http.Context) string {
// 优先从 X-Timezone 请求头获取
timezone := ctx.Request().Header("X-Timezone", "")
if timezone == "" {
// 尝试从 Timezone 请求头获取
timezone = ctx.Request().Header("Timezone", "")
}
if timezone == "" {
// 尝试从查询参数获取
timezone = ctx.Request().Input("timezone")
}
// 如果从请求中获取到了时区,规范化并返回
if timezone != "" {
return NormalizeTimezone(timezone)
}
// 如果都没有,使用配置的默认时区
defaultTimezone := facades.Config().GetString("app.timezone", carbon.UTC)
return NormalizeTimezone(defaultTimezone)
}
// isValidTimezone 验证时区是否有效
func isValidTimezone(timezone string) bool {
if timezone == "" {
return false
}
// 尝试加载时区
_, err := time.LoadLocation(timezone)
return err == nil
}
// NormalizeTimezone 规范化时区名称(处理常见别名)
func NormalizeTimezone(timezone string) string {
timezone = str.Of(timezone).Trim().String()
if str.Of(timezone).IsEmpty() {
return carbon.UTC
}
// 转换为标准时区名称
timezoneMap := map[string]string{
"UTC": "UTC",
"GMT": "UTC",
"PST": "America/Los_Angeles",
"PDT": "America/Los_Angeles",
"EST": "America/New_York",
"EDT": "America/New_York",
"CST": "America/Chicago",
"CDT": "America/Chicago",
"MST": "America/Denver",
"MDT": "America/Denver",
"Beijing": "Asia/Shanghai",
"Shanghai": "Asia/Shanghai",
"Hong Kong": "Asia/Hong_Kong",
"Tokyo": "Asia/Tokyo",
"Seoul": "Asia/Seoul",
"Singapore": "Asia/Singapore",
"London": "Europe/London",
"Paris": "Europe/Paris",
"Berlin": "Europe/Berlin",
"Moscow": "Europe/Moscow",
"Sydney": "Australia/Sydney",
"Melbourne": "Australia/Melbourne",
}
if normalized, ok := timezoneMap[timezone]; ok {
return normalized
}
// 如果时区有效,直接返回
if isValidTimezone(timezone) {
return timezone
}
// 默认返回 UTC
return carbon.UTC
}
// ConvertTimeToTimezone 将时间字符串转换为指定时区
// 返回转换后的时间字符串
func ConvertTimeToTimezone(timeStr string, timezone string) string {
if timeStr == "" {
return ""
}
// 规范化时区
timezone = NormalizeTimezone(timezone)
// 解析时间字符串
dt := carbon.Parse(timeStr)
if dt.IsZero() {
return timeStr
}
// 转换时区并返回格式化的字符串
return dt.SetTimezone(timezone).ToDateTimeString()
}
// ConvertTimeByContext 根据请求头中的时区转换时间字符串
// 返回转换后的时间字符串
func ConvertTimeByContext(ctx http.Context, timeStr string) string {
if timeStr == "" {
return ""
}
timezone := GetCurrentTimezone(ctx)
return ConvertTimeToTimezone(timeStr, timezone)
}
// ConvertTimeToUTC 将本地时区的时间字符串转换为 UTC 时间字符串(用于数据库查询)
// timeStr: 前端传入的时间字符串(本地时区格式,如 "2025-11-25 14:00:00"
// ctx: 请求上下文,用于获取当前时区
// 返回: UTC 时间字符串(如 "2025-11-25 06:00:00"
func ConvertTimeToUTC(ctx http.Context, timeStr string) string {
if timeStr == "" {
return ""
}
// 获取当前请求的时区
timezone := GetCurrentTimezone(ctx)
// 如果已经是 UTC,直接返回
if timezone == carbon.UTC || timezone == "UTC" {
return timeStr
}
// 加载时区
targetLoc, err := time.LoadLocation(timezone)
if err != nil {
// 如果时区无效,假设是 UTC
return timeStr
}
utcLoc, _ := time.LoadLocation("UTC")
// 解析时间字符串(假设是本地时区格式)
// 尝试多种格式
formats := []string{
utils.DateTimeFormat,
utils.DateTimeFormatT,
utils.DateTimeFormatMs,
utils.DateTimeFormatTZ,
time.RFC3339,
}
var t time.Time
var parseErr error
for _, format := range formats {
t, parseErr = time.ParseInLocation(format, timeStr, targetLoc)
if parseErr == nil {
break
}
}
if parseErr != nil {
// 如果所有格式都失败,尝试使用 carbon 解析
dt := carbon.Parse(timeStr)
if dt.IsZero() {
return timeStr
}
// 假设解析的时间是本地时区,转换为 UTC
return dt.SetTimezone(carbon.UTC).ToDateTimeString()
}
// 转换为 UTC 并格式化
return t.In(utcLoc).Format(utils.DateTimeFormat)
}
// GetTimeQueryParam 获取并转换时间查询参数(统一处理时间查询)
// 自动将前端传入的本地时区时间转换为 UTC 时间用于数据库查询
// 支持常见的时间查询参数名称:start_time, end_time, created_at_start, created_at_end, updated_at_start, updated_at_end
func GetTimeQueryParam(ctx http.Context, paramName string) string {
timeStr := ctx.Request().Query(paramName, "")
if timeStr == "" {
return ""
}
return ConvertTimeToUTC(ctx, timeStr)
}
// FormatTimeWithTimezone 使用指定时区格式化 time.Time
func FormatTimeWithTimezone(t time.Time, timezone string) string {
if t.IsZero() {
return ""
}
if timezone == "" {
timezone = "UTC"
}
loc, err := time.LoadLocation(timezone)
if err != nil {
return t.Format("2006-01-02 15:04:05")
}
return t.In(loc).Format("2006-01-02 15:04:05")
}
// FormatCarbonWithTimezone 使用指定时区格式化 Carbon 时间
func FormatCarbonWithTimezone(t *carbon.DateTime, timezone string) string {
if t == nil || t.IsZero() {
return ""
}
return FormatTimeWithTimezone(t.StdTime(), timezone)
}