Files
5.24BuYu/Assets/Framework/Tool/BeijingTimeManager.cs
T
2026-07-27 16:59:22 +08:00

195 lines
7.4 KiB
C#
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.
using System;
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using Newtonsoft.Json.Linq;
/// <summary>
/// 北京时间管理器 - 同步时记录服务器时间与本地系统时间的差值,获取时返回本地时间+差值
/// </summary>
public class BeijingTimeManager : MonoSingleton<BeijingTimeManager>
{
[Header("时间状态")]
[SerializeField] private bool _isTimeSynced = false;
[SerializeField] private TimeSpan _serverTimeOffset; // 服务器时间 - 本地系统时间
/// <summary>
/// 获取当前北京时间
/// </summary>
public DateTime CurrentBeijingTime
{
get
{
if (_isTimeSynced)
{
return DateTime.Now + _serverTimeOffset;
}
return DateTime.Now; // 未同步时返回本地时间
}
}
/// <summary>
/// 获取时间戳(秒)- 1970-01-01 08:00:00 到现在的秒数
/// </summary>
public long CurrentTimestamp
{
get
{
DateTime beijingTime = CurrentBeijingTime;
DateTime epoch = new DateTime(1970, 1, 1, 8, 0, 0); // 北京时间起点
return (long)(beijingTime - epoch).TotalSeconds;
}
}
/// <summary>
/// 获取时间戳(毫秒)
/// </summary>
public long CurrentTimestampMs
{
get
{
DateTime beijingTime = CurrentBeijingTime;
DateTime epoch = new DateTime(1970, 1, 1, 8, 0, 0); // 北京时间起点
return (long)(beijingTime - epoch).TotalMilliseconds;
}
}
public bool IsTimeSynced => _isTimeSynced;
public event Action<DateTime> OnTimeSynced;
private void Start()
{
SyncBeijingTime();
}
/// <summary>
/// 同步北京时间
/// </summary>
public void SyncBeijingTime()
{
StartCoroutine(FetchBeijingTime());
}
private IEnumerator FetchBeijingTime()
{
string[] timeServers = new string[]
{
"http://47.109.132.12/api/Login/GetDateTime",
"https://timeapi.io/api/Time/current/zone?timeZone=Asia/Shanghai",
"http://worldtimeapi.org/api/timezone/Asia/Shanghai",
"https://quan.suning.com/getSysTime.do"
};
foreach (string url in timeServers)
{
// 记录请求开始时间,用于 RTT 补偿
DateTime requestStartTime = DateTime.Now;
using (UnityWebRequest request = UnityWebRequest.Get(url + "?_=" + DateTime.Now.Ticks))
{
request.timeout = 5;
yield return request.SendWebRequest();
// 收到响应的本地时间
DateTime requestEndTime = DateTime.Now;
if (request.result == UnityWebRequest.Result.Success)
{
string jsonText = request.downloadHandler.text;
Debug.Log($"返回原始数据:{jsonText}");
if (!jsonText.TrimStart().StartsWith("{"))
{
Debug.LogWarning("返回的不是 JSON,跳过该源");
continue;
}
try
{
JObject json = JObject.Parse(jsonText);
DateTime? serverTime = null;
if (url.Contains("47.109.132.12"))
{
// 返回格式:{"data":{"dateTime":"2026-07-26 20:02:48","milliSeconds":330,...},"code":0,"info":null}
// dateTime 只精确到秒,需要补 milliSeconds 字段
string timeStr = json["data"]?["dateTime"]?.ToString();
int ms = json["data"]?["milliSeconds"]?.Value<int>() ?? 0;
Debug.Log($"47.109.132.12 提取到的时间字符串:{timeStr} 毫秒:{ms}");
if (!string.IsNullOrEmpty(timeStr) &&
DateTime.TryParseExact(timeStr, "yyyy-MM-dd HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime t))
{
serverTime = t.AddMilliseconds(ms);
}
else
{
Debug.LogWarning($"无法解析时间字符串:{timeStr}");
}
}
else if (url.Contains("timeapi.io"))
{
string timeStr = json["dateTime"]?.ToString();
Debug.Log(timeStr);
if (!string.IsNullOrEmpty(timeStr))
{
serverTime = DateTime.Parse(timeStr);
}
}
else if (url.Contains("worldtimeapi"))
{
string timeStr = json["datetime"]?.ToString();
Debug.Log(timeStr);
if (!string.IsNullOrEmpty(timeStr))
{
serverTime = DateTimeOffset.Parse(timeStr)
.ToOffset(TimeSpan.FromHours(8))
.DateTime;
}
}
else if (url.Contains("suning"))
{
string timeStr = json["sysTime2"]?.ToString();
Debug.Log(timeStr);
if (!string.IsNullOrEmpty(timeStr))
{
serverTime = DateTime.ParseExact(timeStr, "yyyy-MM-dd HH:mm:ss", null);
}
}
if (serverTime.HasValue)
{
// RTT 补偿:服务器生成响应到客户端接收耗时约为 RTT/2(假设网络对称)
// 补偿后服务器时间 = 服务器响应时间 + RTT/2
TimeSpan rtt = requestEndTime - requestStartTime;
DateTime compensatedServerTime = serverTime.Value + TimeSpan.FromTicks(rtt.Ticks / 2);
_serverTimeOffset = compensatedServerTime - requestEndTime;
_isTimeSynced = true;
Debug.Log($"北京时间同步成功:{CurrentBeijingTime:yyyy-MM-dd HH:mm:ss.fff},偏移量:{_serverTimeOffset.TotalSeconds:F3}s,RTT:{rtt.TotalSeconds:F3}s");
OnTimeSynced?.Invoke(CurrentBeijingTime);
yield break;
}
}
catch (Exception e)
{
Debug.LogWarning($"解析失败:{e.Message}");
continue;
}
}
}
}
Debug.LogError("所有时间服务器都失败了,使用本地时间");
_serverTimeOffset = TimeSpan.Zero;
_isTimeSynced = false;
}
/// <summary>
/// 获取格式化的北京时间
/// </summary>
public string GetFormattedTime(string format = "yyyy-MM-dd HH:mm:ss")
{
return CurrentBeijingTime.ToString(format);
}
}