diff --git a/Documentation~/zh/changelog.md b/Documentation~/zh/changelog.md index 4634ea0d..51776770 100644 --- a/Documentation~/zh/changelog.md +++ b/Documentation~/zh/changelog.md @@ -80,6 +80,7 @@ slug: /changelog ## master +* (2025.10.30) 增加`Chart`的`useUtc`参数设置显示时间是否用UTC时间 * (2025.10.30) 优化`Candlestick`对时间轴的支持 * (2025.10.30) 增加`Scatter`的`ignore`支持设置忽略数据 * (2025.10.24) 优化`Sankey`的线条绘制排序 diff --git a/Editor/Charts/BaseChartEditor.cs b/Editor/Charts/BaseChartEditor.cs index 665e78f7..739903b1 100644 --- a/Editor/Charts/BaseChartEditor.cs +++ b/Editor/Charts/BaseChartEditor.cs @@ -26,6 +26,7 @@ namespace XCharts.Editor protected SerializedProperty m_Settings; protected SerializedProperty m_Theme; protected SerializedProperty m_ChartName; + protected SerializedProperty m_UseUtc; protected SerializedProperty m_DebugInfo; protected SerializedProperty m_RaycastTarget; @@ -49,6 +50,7 @@ namespace XCharts.Editor m_Script = serializedObject.FindProperty("m_Script"); m_EnableTextMeshPro = serializedObject.FindProperty("m_EnableTextMeshPro"); m_ChartName = serializedObject.FindProperty("m_ChartName"); + m_UseUtc = serializedObject.FindProperty("m_UseUtc"); m_Theme = serializedObject.FindProperty("m_Theme"); m_Settings = serializedObject.FindProperty("m_Settings"); m_DebugInfo = serializedObject.FindProperty("m_DebugInfo"); @@ -124,6 +126,7 @@ namespace XCharts.Editor { EditorGUILayout.PropertyField(m_Script); EditorGUILayout.PropertyField(m_ChartName); + EditorGUILayout.PropertyField(m_UseUtc); EditorGUILayout.PropertyField(m_RaycastTarget); if (XChartsMgr.IsRepeatChartName(m_Chart, m_ChartName.stringValue)) { diff --git a/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs b/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs index a96a7b5c..03f30f62 100644 --- a/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs +++ b/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs @@ -85,7 +85,7 @@ namespace XCharts.Runtime float scaleAngle = AxisHelper.GetScaleWidth(axis, total, i + 1, null); bool inside = axis.axisLabel.inside; var labelName = AxisHelper.GetLabelName(axis, total, i, axis.context.minValue, axis.context.maxValue, - null, isPercentStack); + null, isPercentStack, chart.useUtc); var label = ChartHelper.AddAxisLabelObject(splitNumber, i, objName + i, axisObj.transform, new Vector2(scaleAngle, txtHig), axis, chart.theme.axis, labelName, Color.clear); diff --git a/Runtime/Component/Axis/AxisHandler.cs b/Runtime/Component/Axis/AxisHandler.cs index d1d4432c..2db074b4 100644 --- a/Runtime/Component/Axis/AxisHandler.cs +++ b/Runtime/Component/Axis/AxisHandler.cs @@ -290,7 +290,7 @@ namespace XCharts if (context.labelObjectList[i] != null) { var index = i < showData.Count ? showData[i].index : i; - var text = AxisHelper.GetLabelName(axis, coordinateWidth, index, destMinValue, destMaxValue, dataZoom, forcePercent, i); + var text = AxisHelper.GetLabelName(axis, coordinateWidth, index, destMinValue, destMaxValue, dataZoom, forcePercent, chart.useUtc, i); context.labelObjectList[i].SetText(text); } } @@ -322,7 +322,7 @@ namespace XCharts { if (context.labelObjectList[i] != null) { - var text = AxisHelper.GetLabelName(axis, coordinateWidth, i, destMinValue, destMaxValue, dataZoom, forcePercent); + var text = AxisHelper.GetLabelName(axis, coordinateWidth, i, destMinValue, destMaxValue, dataZoom, forcePercent, chart.useUtc); context.labelObjectList[i].SetText(text); } } @@ -364,7 +364,7 @@ namespace XCharts { var lastCount = axis.context.labelValueList.Count; axis.context.tickValue = DateTimeUtil.UpdateTimeAxisDateTimeList(axis.context.labelValueList, - axis.context.minValue, axis.context.maxValue, axis.splitNumber, axis.ceilRate); + axis.context.minValue, axis.context.maxValue, axis.splitNumber, axis.ceilRate, !chart.useUtc); if (axis.context.labelValueList.Count != lastCount) axis.SetAllDirty(); @@ -454,7 +454,7 @@ namespace XCharts label.SetTextActive(false); return; } - if(content == null) + if (content == null) { content = label.text.GetText(); } @@ -465,12 +465,12 @@ namespace XCharts if (i == 0) { var dist = GetLabelPosition(0, 1).x - pos.x; - label.SetTextActive(axis.IsNeedShowLabel(i,0,content) && dist > label.text.GetPreferredWidth()); + label.SetTextActive(axis.IsNeedShowLabel(i, 0, content) && dist > label.text.GetPreferredWidth()); } else if (i == axis.context.labelValueList.Count - 1) { var dist = pos.x - GetLabelPosition(0, i - 1).x; - label.SetTextActive(axis.IsNeedShowLabel(i,0,content) && dist > label.text.GetPreferredWidth()); + label.SetTextActive(axis.IsNeedShowLabel(i, 0, content) && dist > label.text.GetPreferredWidth()); } } else @@ -478,12 +478,12 @@ namespace XCharts if (i == 0) { var dist = GetLabelPosition(0, 1).y - pos.y; - label.SetTextActive(axis.IsNeedShowLabel(i,0,content) && dist > label.text.GetPreferredHeight()); + label.SetTextActive(axis.IsNeedShowLabel(i, 0, content) && dist > label.text.GetPreferredHeight()); } else if (i == axis.context.labelValueList.Count - 1) { var dist = pos.y - GetLabelPosition(0, i - 1).y; - label.SetTextActive(axis.IsNeedShowLabel(i,0,content) && dist > label.text.GetPreferredHeight()); + label.SetTextActive(axis.IsNeedShowLabel(i, 0, content) && dist > label.text.GetPreferredHeight()); } } } @@ -552,7 +552,7 @@ namespace XCharts var labelName = AxisHelper.GetLabelName(axis, axisLength, sortIndex, axis.context.destMinValue, axis.context.destMaxValue, - dataZoom, isPercentStack, i); + dataZoom, isPercentStack, chart.useUtc, i); var label = ChartHelper.AddAxisLabelObject(splitNumber, i, ChartCached.GetAxisLabelName(i), @@ -671,7 +671,7 @@ namespace XCharts var labelName = AxisHelper.GetLabelName(axis, axisLength, sortIndex, axis.context.destMinValue, axis.context.destMaxValue, - dataZoom, isPercentStack, i); + dataZoom, isPercentStack, chart.useUtc, i); var label = ChartHelper.AddAxisLabelObject(splitNumber, i, ChartCached.GetAxisLabelName(i), diff --git a/Runtime/Component/Axis/AxisHelper.cs b/Runtime/Component/Axis/AxisHelper.cs index b1883479..955e5c87 100644 --- a/Runtime/Component/Axis/AxisHelper.cs +++ b/Runtime/Component/Axis/AxisHelper.cs @@ -116,7 +116,7 @@ namespace XCharts.Runtime /// /// public static string GetLabelName(Axis axis, float coordinateWidth, int index, double minValue, double maxValue, - DataZoom dataZoom, bool forcePercent, int sortIndex = -1) + DataZoom dataZoom, bool forcePercent, bool useUtc, int sortIndex = -1) { int split = GetSplitNumber(axis, coordinateWidth, dataZoom); if (sortIndex == -1) sortIndex = index; @@ -161,7 +161,7 @@ namespace XCharts.Runtime return string.Empty; var value = axis.GetLabelValue(index); - return axis.axisLabel.GetFormatterDateTime(sortIndex, axis.context.labelValueList.Count, value, minValue, maxValue); + return axis.axisLabel.GetFormatterDateTime(sortIndex, axis.context.labelValueList.Count, value, minValue, maxValue, !useUtc); } var showData = axis.GetDataList(dataZoom); int dataCount = showData.Count; diff --git a/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs b/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs index 036665e7..66e3ce0f 100644 --- a/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs +++ b/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs @@ -114,7 +114,7 @@ namespace XCharts.Runtime var inside = axis.axisLabel.inside; var isPercentStack = SeriesHelper.IsPercentStack(chart.series); var labelName = AxisHelper.GetLabelName(axis, radius, i, axis.context.minValue, axis.context.maxValue, - null, isPercentStack); + null, isPercentStack, chart.useUtc); var label = ChartHelper.AddAxisLabelObject(splitNumber, i, objName + i, axisObj.transform, new Vector2(labelWidth, txtHig), axis, chart.theme.axis, labelName, Color.clear); diff --git a/Runtime/Component/Label/LabelStyle.cs b/Runtime/Component/Label/LabelStyle.cs index 07a52f5f..563ad1ac 100644 --- a/Runtime/Component/Label/LabelStyle.cs +++ b/Runtime/Component/Label/LabelStyle.cs @@ -485,10 +485,10 @@ namespace XCharts.Runtime private static bool isDateFormatter = false; private static string newFormatter = null; - public string GetFormatterDateTime(int labelIndex, int totalIndex, double value, double minValue, double maxValue) + public string GetFormatterDateTime(int labelIndex, int totalIndex, double value, double minValue, double maxValue, bool local) { var timestamp = value; - var dateTime = DateTimeUtil.GetDateTime(timestamp); + var dateTime = DateTimeUtil.GetDateTime(timestamp, local); var dateString = string.Empty; if (string.IsNullOrEmpty(numericFormatter) || numericFormatter.Equals("f2")) { @@ -501,7 +501,7 @@ namespace XCharts.Runtime if (DateTimeUtil.IsDateOrTimeRegex(numericFormatter, ref isDateFormatter, ref newFormatter)) { if (isDateFormatter) - dateString = ChartCached.NumberToDateStr(timestamp, newFormatter); + dateString = ChartCached.NumberToDateStr(timestamp, newFormatter, local); else dateString = ChartCached.NumberToTimeStr(timestamp, newFormatter); } diff --git a/Runtime/Component/Tooltip/TooltipHandler.cs b/Runtime/Component/Tooltip/TooltipHandler.cs index 3ff95e7a..1599bbfe 100644 --- a/Runtime/Component/Tooltip/TooltipHandler.cs +++ b/Runtime/Component/Tooltip/TooltipHandler.cs @@ -268,7 +268,7 @@ namespace XCharts.Runtime } else if (axis.IsTime()) { - label.SetText(axis.indicatorLabel.GetFormatterDateTime(0, 0, axis.context.pointerValue, axis.context.minValue, axis.context.maxValue)); + label.SetText(axis.indicatorLabel.GetFormatterDateTime(0, 0, axis.context.pointerValue, axis.context.minValue, axis.context.maxValue, !chart.useUtc)); } else { @@ -356,7 +356,7 @@ namespace XCharts.Runtime if (isTriggerAxis) { var index = serie.context.dataZoomStartIndex + (int)yAxis.context.pointerValue; - if(serie.useSortData) index = yAxis.context.sortedDataIndices[index]; + if (serie.useSortData) index = yAxis.context.sortedDataIndices[index]; serie.context.pointerEnter = true; serie.context.pointerAxisDataIndexs.Add(index); serie.context.pointerItemDataIndex = index; @@ -376,7 +376,7 @@ namespace XCharts.Runtime if (isTriggerAxis) { var index = serie.context.dataZoomStartIndex + (int)xAxis.context.pointerValue; - if(serie.useSortData) index = xAxis.context.sortedDataIndices[index]; + if (serie.useSortData) index = xAxis.context.sortedDataIndices[index]; if (chart.isTriggerOnClick) { if (serie.insertDataToHead) @@ -616,7 +616,7 @@ namespace XCharts.Runtime var serieData = serie.GetSerieData(serie.context.pointerItemDataIndex); if (serieData != null) { - tooltip.context.data.title = DateTimeUtil.GetDefaultDateTimeString((int)serieData.GetData(0), axisRange); + tooltip.context.data.title = DateTimeUtil.GetDefaultDateTimeString((int)serieData.GetData(0), axisRange, !chart.useUtc); } } serie.handler.UpdateTooltipSerieParams(dataIndex, showCategory, category, diff --git a/Runtime/Internal/BaseChart.API.cs b/Runtime/Internal/BaseChart.API.cs index bcd8e143..41aacb9b 100644 --- a/Runtime/Internal/BaseChart.API.cs +++ b/Runtime/Internal/BaseChart.API.cs @@ -31,6 +31,11 @@ namespace XCharts.Runtime } } /// + /// Whether to use UTC time for the chart. + /// ||图表的时间是否都显示为UTC时间。 + /// + public bool useUtc { get { return m_UseUtc; } set { m_UseUtc = value; } } + /// /// The theme. /// || public ThemeStyle theme { get { return m_Theme; } set { m_Theme = value; } } diff --git a/Runtime/Internal/BaseChart.cs b/Runtime/Internal/BaseChart.cs index 9b325278..b3536b48 100644 --- a/Runtime/Internal/BaseChart.cs +++ b/Runtime/Internal/BaseChart.cs @@ -16,6 +16,7 @@ namespace XCharts.Runtime public partial class BaseChart : BaseGraph, ISerializationCallbackReceiver { [SerializeField] protected string m_ChartName; + [SerializeField] protected bool m_UseUtc = true; [SerializeField] protected ThemeStyle m_Theme = new ThemeStyle(); [SerializeField] protected Settings m_Settings; [SerializeField] protected DebugInfo m_DebugInfo = new DebugInfo(); diff --git a/Runtime/Internal/Utilities/ChartCached.cs b/Runtime/Internal/Utilities/ChartCached.cs index aa2e6e97..e80d68f6 100644 --- a/Runtime/Internal/Utilities/ChartCached.cs +++ b/Runtime/Internal/Utilities/ChartCached.cs @@ -100,9 +100,9 @@ namespace XCharts.Runtime return NumberToStr(value, numericFormatter); } - public static string NumberToDateStr(double timestamp, string formatter) + public static string NumberToDateStr(double timestamp, string formatter, bool local = false) { - var dt = NumberToDateTime(timestamp); + var dt = NumberToDateTime(timestamp, local); try { return dt.ToString(formatter, ci); @@ -132,11 +132,11 @@ namespace XCharts.Runtime } } - public static DateTime NumberToDateTime(double timestamp) + public static DateTime NumberToDateTime(double timestamp, bool local = false) { if (!s_TimestampToDateTimeDict.ContainsKey(timestamp)) { - s_TimestampToDateTimeDict[timestamp] = DateTimeUtil.GetDateTime(timestamp); + s_TimestampToDateTimeDict[timestamp] = DateTimeUtil.GetDateTime(timestamp, local); } return s_TimestampToDateTimeDict[timestamp]; } diff --git a/Runtime/Utilities/DateTimeUtil.cs b/Runtime/Utilities/DateTimeUtil.cs index 33936825..07486eaa 100644 --- a/Runtime/Utilities/DateTimeUtil.cs +++ b/Runtime/Utilities/DateTimeUtil.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; -using UnityEngine; namespace XCharts.Runtime { @@ -145,7 +144,7 @@ namespace XCharts.Runtime /// /// /// - internal static float UpdateTimeAxisDateTimeList(List list, double minTimestamp, double maxTimestamp, int splitNumber, double ceilRate) + internal static float UpdateTimeAxisDateTimeList(List list, double minTimestamp, double maxTimestamp, int splitNumber, double ceilRate, bool local) { var range = maxTimestamp - minTimestamp; if (range <= 0) @@ -153,8 +152,8 @@ namespace XCharts.Runtime list.Clear(); return 0; } - var dtMin = GetDateTime(minTimestamp); - var dtMax = GetDateTime(maxTimestamp); + var dtMin = GetDateTime(minTimestamp, local); + var dtMax = GetDateTime(maxTimestamp, local); int tick; if (ceilRate != 0) { @@ -179,7 +178,7 @@ namespace XCharts.Runtime if (range >= ONE_YEAR * MIN_TIME_SPLIT_NUMBER) { var num = splitNumber <= 0 ? GetSplitNumber(range, ONE_YEAR) : (int)Math.Max(range / (splitNumber * ONE_YEAR), 1); - var dtStart = GetDateTime(GetFirstMaxValue(list, minTimestamp)); + var dtStart = GetDateTime(GetFirstMaxValue(list, minTimestamp), local); dtStart = new DateTime(dtStart.Year, dtStart.Month, 1); while (dtStart > dtMin) { @@ -193,14 +192,14 @@ namespace XCharts.Runtime list.Clear(); while (dtStart.Ticks < dtMax.Ticks) { - list.Add(DateTimeUtil.GetTimestamp(dtStart)); + list.Add(DateTimeUtil.GetTimestamp(dtStart, local)); dtStart = dtStart.AddYears(num); } } else if (range >= ONE_MONTH * MIN_TIME_SPLIT_NUMBER) { var num = splitNumber <= 0 ? GetSplitNumber(range, ONE_MONTH) : (int)Math.Max(range / (splitNumber * ONE_MONTH), 1); - var dtStart = GetDateTime(GetFirstMaxValue(list, minTimestamp)); + var dtStart = GetDateTime(GetFirstMaxValue(list, minTimestamp), local); dtStart = new DateTime(dtStart.Year, dtStart.Month, 1); while (dtStart > dtMin) { @@ -214,7 +213,7 @@ namespace XCharts.Runtime list.Clear(); while (dtStart.Ticks < dtMax.Ticks) { - list.Add(DateTimeUtil.GetTimestamp(dtStart)); + list.Add(DateTimeUtil.GetTimestamp(dtStart, local)); dtStart = dtStart.AddMonths(num); } }