zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Unity 之 切换语言导致报错:System.FormatException:String was not recognized as a valid DateTime.

语言 报错 string not Unity 切换 system 导致
2023-09-11 14:20:51 时间

Unity 之 切换语言导致报错:System.FormatException:String was not recognized as a valid DateTime.

一,发生问题

1.1 问题背景

最近在开发多语言本地化时(简体-繁体来回切换),发生了这个错误:System.FormatException: String was not recognized as a valid DateTime. --> 系统格式异常:字符串未被识别为有效的日期时间。

看起来是逻辑上错误传了一个错误的字符串给DateTime.Parse(“”)方法作为参数了。那么奇怪的是在编辑器下没有发生错误,在真机不切换语言的情况也没有发生错误,只有在IOS切换简繁体系统语言时会发生这个错误

于是猜测在不同语言环境下DateTime.Now.ToString()得到的结果是不同的,所以就有了2.1的测试用例。

1.2 报错日志

真机切换语言捕获的错误日志:

System.AggregateException: One or more errors occurred. ---> System.FormatException: String was not recognized as a valid DateTime.
  at System.DateTimeParse.Parse (System.String s, System.Globalization.DateTimeFormatInfo dtfi, System.Globalization.DateTimeStyles styles) [0x00000] in <00000000000000000000000000000000>:0 
  at WZC.ActivityManager+<>c__DisplayClass45_0.<GetActivitySwitchConfig>b__0 (WZC.ActivitySwitchConfig e) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ContextCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ContextCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.Tasks.AwaitTaskContinuation.RunCallback (System.Threading.ContextCallback callback, System.Object state, System.Threading.Tasks.Task& currentTask) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.Tasks.Task`1[TResult].TrySetResult (TResult result) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[TResult].Create () [0x00000] in <00000000000000000000000000000000>:0 
  at WZC.ApiHelper..cctor () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ContextCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ContextCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
   --- End of inner exception stack trace ---

二,定位问题

2.1 测试用例

按照游戏逻辑还原了DateTime的使用情况,为了方便观察日志创建了Scroll View组件,里面放一个很长的Text,测试代码和场景搭建效果如下:

using System;
using UnityEngine;
using UnityEngine.UI;

public class ConverTimeError : MonoBehaviour
{
    public Text context;
    public Button cunShiJian;
    public Button quShiJian;
    
    void Start()
    {
        var strRus = PlayerPrefs.GetString("Hall:RegionGetDate");
        var strRusDate = DateTime.Parse(strRus);
        context.text += "转换结果:+++" +  strRusDate.Year + "/" + strRusDate.Month+ "/" + strRusDate.Day + ";\n";
        
        cunShiJian.onClick.AddListener(()=>{
            context.text += "存时间 :---" + DateTime.Now + ";\n";
            PlayerPrefs.SetString("Hall:RegionGetDate", DateTime.Now.ToString());
            context.text += "存储结果:---" +  PlayerPrefs.GetString("Hall:RegionGetDate") + ";\n";
        });
        quShiJian.onClick.AddListener(()=>{
            
            context.text += "取时间 :+++" + DateTime.Now + ";\n";
            var regionGetDateStr = PlayerPrefs.GetString("Hall:RegionGetDate");
            context.text += "取到结果:+++" +  regionGetDateStr + ";\n";
            var regionGetDate = DateTime.Parse(regionGetDateStr);
            context.text += "取到转换结果:+++" +  regionGetDate.Year + "/" + regionGetDate.Month+ "/" + regionGetDate.Day + ";\n";
        });
    }
}

使用此测试用例在macOS和IOS上都进行了切换语言的测试,结果都没有任何异常,发现DateTime.Now.ToString()的结果都是:月/日/年 时:分:秒 这种格式。


2.2 找到问题

测试用例没发现问题,那不得不在原工程中补日志了,几经周折最后确定了有一个存时间的字符串格式为日/月/年 时:分:秒

于是在编辑器中模拟转换了年/月/日月/日/年日/月/年 三种格式的时间转换:

Debug.Log("年/月/日: " + DateTime.Parse("2022/7/25 13:11:00"));
Debug.Log("月/日/年: " + DateTime.Parse("7/25/2022 13:11:00"));
// 有问题的时间格式转换
Debug.Log("日/月/年: " + DateTime.Parse("25/7/2022 13:11:00")); 

结果表明:日/月/年这种格式的字符串确实不能使用DateTime.Parse()方法进行转换。

那么新的问题就来了,在时间存储时使用的是:DateTime.Now.ToString(),而在2.1的测试用例中我们有确认了DateTime.Now.ToString()的结果都是:月/日/年 时:分:秒 这种格式。

同时又可以确定的是游戏中存储时间使用的都是DateTime.Now.ToString()这种形式,这就很让人费解游戏逻辑中日/月/年格式的字符串是怎么出来的呢?


三,解决问题

3.1 尝试解决

刚开始是使用了

DateTime.ParseExact(DateTime.Now.ToString(), "yyyy/dd/MM", System.Globalization.CultureInfo.InvariantCulture);

这个方法来进行格式转换,因为这是指定格式的转换,所以根据上面的测试结果也是不行的。

3.2 解决问题

将存储的代码固定格式为:

DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss")

好在工程里面只有几处DateTime.Now的赋值,修改起来还是很方便的。最后也测试通过了。

遇到了这么奇怪的问题,记录一下,供遇到相似问题童鞋参考一下。