195 lines
7.4 KiB
C#
195 lines
7.4 KiB
C#
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);
|
||
}
|
||
} |