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);
}
}