| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- using System;
- using System.Linq;
- using Fort23.UTool;
- using UnityEngine;
- namespace Fort23.Core
- {
- public static class TimeHelper
- {
- public static int refreshHour = 0;
- public static readonly long epoch = 0;
- private static readonly DateTime ServerStartTime = new DateTime(2025, 4, 1, 0, 0, 0, DateTimeKind.Utc);
- public static DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
- public const long OneDay = 86400000;
- public const long Hour = 3600000;
- public const long Minute = 60000;
- private static long _clientFrame;
- public static long clientFrame
- {
- set => _clientFrame = value;
- }
- // 最近一次校准的网络时间戳毫秒
- private static long _startNetworkMs = 0;
- // 最近一次校准时的 Unity 时间
- private static float _lastSyncUnityTime = 0f;
- // 游戏首次启动时间
- private static float _gameStartTime;
- private const int SyncInterval = 300;
- /// <summary>
- /// 是否获取了网络时间
- /// </summary>
- public static bool IsNetworkTimeReady { get; private set; } = false;
- /// <summary>
- /// 必须在游戏启动时调用(异步)
- /// </summary>
- public static async CTask InitNetworkTime()
- {
- // 启动时的 Unity 时间
- _gameStartTime = Time.unscaledTime;
- // 首次同步
- await SyncWithNetwork();
- //每隔五分钟重新同步一次时间
- TimerComponent.Instance.AddTimer(SyncInterval * 1000, null, Int32.MaxValue, () => { SyncWithNetwork(); });
- }
- // 核心:与百度时间同步
- private static async CTask SyncWithNetwork()
- {
- var networkTime = await NetworkTime.GetNetworkTimeAsync();
- if (networkTime.HasValue)
- {
- long networkMs = (long)(networkTime.Value - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))
- .TotalMilliseconds;
- if (!IsNetworkTimeReady)
- {
- // 首次同步
- _startNetworkMs = networkMs;
- _lastSyncUnityTime = Time.unscaledTime;
- IsNetworkTimeReady = true;
- LogTool.Log($"首次网络时间同步成功: {networkTime.Value:yyyy-MM-dd HH:mm:ss} UTC");
- }
- else
- {
- // 定期校准:计算漂移并修正
- float elapsedUnity = Time.unscaledTime - _lastSyncUnityTime;
- long expectedMs = _startNetworkMs + (long)(elapsedUnity * 1000);
- long driftMs = networkMs - expectedMs;
- if (Mathf.Abs((float)driftMs) > 5000) // 漂移 > 5 秒才修正(避免微小误差)
- {
- _startNetworkMs = networkMs;
- _lastSyncUnityTime = Time.unscaledTime;
- LogTool.Log($"时间校准: 漂移 {driftMs}ms,已修正");
- }
- else
- {
- LogTool.Log($"时间校准: 漂移 {driftMs}ms,正常");
- }
- }
- }
- else
- {
- LogTool.Warning("网络时间校准失败,保持当前时间");
- }
- }
- /// <summary>
- /// 客户端非暂停状态经历的帧数对应的毫秒
- /// </summary>
- /// <returns></returns>
- public static long ClientFrame()
- {
- return _clientFrame;
- }
- /// <summary>
- /// 获取安全的当前时间戳(毫秒)
- /// </summary>
- private static long GetSafeNowMs()
- {
- if (!IsNetworkTimeReady || _startNetworkMs == 0)
- return (long)(DateTime.UtcNow.Ticks / 10000); // 回退
- // Unity运行毫秒(完全不受系统时间影响)
- float elapsedUnity = Time.unscaledTime - _gameStartTime;
- long elapsedMs = (long)(elapsedUnity * 1000);
- return _startNetworkMs + elapsedMs;
- }
- public static long GetServerStartTime()
- {
- return ((ServerStartTime.Ticks - epoch) / 10000) + (8 * 60 * 60 * 1000);
- }
- public static long ClientNowMicroseconds()
- {
- return GetSafeNowMs() * 1000;
- }
- public static long ClientNow()
- {
- return GetSafeNowMs() + (8 * 60 * 60 * 1000); // 东八区
- }
- public static long ClientNowSeconds()
- {
- return ClientNow() / 1000;
- }
- public static DateTime DateTimeNow()
- {
- return DateTimeOffset.FromUnixTimeMilliseconds(ClientNow()).UtcDateTime;
- }
- public static long ServerNow()
- {
- return ClientNow();
- }
- public static long ServerNowSeconds()
- {
- return ClientNowSeconds();
- }
- /// <summary>
- /// 客户端时间(年月日)
- /// </summary>
- /// <param name="timeStamp">当前时间</param>
- /// <returns></returns>
- public static int ClientNowInt(long timeStamp)
- {
- TimeSpan toNow = new TimeSpan(timeStamp * 10000);
- DateTime dateTime = dtStart.Add(toNow);
- int year = dateTime.Year;
- int month = dateTime.Month;
- int day = dateTime.Day;
- int date;
- string smouth = "";
- if (month >= 10)
- {
- smouth = month.ToString();
- }
- else
- {
- smouth = $"0{month}";
- }
- string sday = "";
- if (day >= 10)
- {
- sday = day.ToString();
- }
- else
- {
- sday = $"0{day}";
- }
- if (int.TryParse($"{year}{smouth}{sday}", out date))
- {
- return date;
- }
- return 20201010;
- }
- /// <summary>
- /// 客户端时间(月日时分秒)
- /// </summary>
- /// <param name="timeStamp">当前时间(毫秒)</param>
- /// <returns></returns>
- public static string ToString1(long timeStamp)
- {
- TimeSpan toNow = new TimeSpan(timeStamp * 10000);
- DateTime dateTime = dtStart.Add(toNow);
- string month = String.Empty;
- string day = String.Empty;
- string hour = String.Empty;
- string minute = String.Empty;
- string second = String.Empty;
- if (dateTime.Month >= 10)
- {
- month = dateTime.Month.ToString();
- }
- else
- {
- month = $"0{dateTime.Month}";
- }
- if (dateTime.Day >= 10)
- {
- day = dateTime.Day.ToString();
- }
- else
- {
- day = $"0{dateTime.Day}";
- }
- if (dateTime.Hour >= 10)
- {
- hour = dateTime.Hour.ToString();
- }
- else
- {
- hour = $"0{dateTime.Hour}";
- }
- if (dateTime.Minute >= 10)
- {
- minute = dateTime.Minute.ToString();
- }
- else
- {
- minute = $"0{dateTime.Minute}";
- }
- if (dateTime.Second >= 10)
- {
- second = dateTime.Second.ToString();
- }
- else
- {
- second = $"0{dateTime.Second}";
- }
- string result = $"{month}月{day}日{hour}:{minute}:{second}";
- return result;
- }
- /// <summary>
- /// 判断法定工作日和节假日
- /// </summary>
- /// <param name="dt">时间</param>
- /// <returns>0上班,1不需要上班的周末(也包括节假日),2法定节假日</returns>
- public static int IsHolidayOrWeekend(DateTime dt)
- {
- //特殊的周末(周末却上班)
- string[] specalWeek2022 = { };
- string[] specalWeek2023 = { "0128", "0129", "0214", "0429", "0430", "0625", "1007", "1008" };
- //法定假日
- string[] holiday2022 = { "1231" };
- string[] holiday2023 =
- {
- "0101", "0102", "0121", "0122", "0123", "0124", "0125", "0126", "0127", "0405", "0501", "0502", "0503",
- "0504", "0505", "0622", "0623",
- "0929", "1002", "1003", "1004", "1005", "1006"
- };
- bool isHoildayOrWeek = false;
- //取年
- string weekYear = dt.Year.ToString();
- //取月日
- string[] weekDate = { dt.ToString("MMdd") };
- //判断周末和周五
- if ((int)dt.DayOfWeek == 0 || (int)dt.DayOfWeek == 6 || (int)dt.DayOfWeek == 5)
- {
- //周末是否需要上班
- switch (weekYear)
- {
- case "2022":
- isHoildayOrWeek = specalWeek2022.Intersect(weekDate).Count() == 0;
- break;
- case "2023":
- isHoildayOrWeek = specalWeek2023.Intersect(weekDate).Count() == 0;
- break;
- }
- }
- if (isHoildayOrWeek)
- {
- return 1;
- }
- //判断法定节假日
- switch (weekYear)
- {
- case "2022":
- isHoildayOrWeek = holiday2022.Intersect(weekDate).Count() > 0;
- break;
- case "2023":
- isHoildayOrWeek = holiday2023.Intersect(weekDate).Count() > 0;
- break;
- }
- if (isHoildayOrWeek)
- {
- return 2;
- }
- return 0;
- }
- public static long GetBaseRefreshTime(long baseTime, int day = 1)
- {
- DateTime dateTime = DateTimeOffset.FromUnixTimeMilliseconds(baseTime).DateTime;
- int hour = dateTime.Hour;
- DateTime refreshDateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, refreshHour, 0, 0,
- DateTimeKind.Utc);
- if (hour < refreshHour)
- {
- day -= 1;
- }
- refreshDateTime = refreshDateTime.AddDays(day);
- long refreshTime = new DateTimeOffset(refreshDateTime).ToUnixTimeMilliseconds();
- return refreshTime;
- }
- }
- }
|