214 lines
5.8 KiB
Go
214 lines
5.8 KiB
Go
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)
|
||
}
|