需求与场景分析
在游戏前端开发中,我经常遇到把时间格式化成多种格式在不同UI上显示的需求。又或者是不同功能需要显示不同格式的时间。最多的就是对服务器返回的一个剩余时间秒数进行倒计时的显示,例如活动剩余时间、某些操作的CD时间等。
下面列举一些常要用到的显示格式:
- 12:05:03
- 12小时05分钟03秒
- 12时05分03秒
- 剩余0天12时5分5秒
- 50分05秒 (当小时小于0时不显示)
就先列举5个,在同一个游戏项目中可能在不同场景,不同界面就需要显示不一样的时间格式,
我们该如何处理这种情况呢?
我常常看到一些处理方式是,在一个工具类里定义多个格式化函数,需要哪个格式就调用哪个函数。
例如:
DateTimeUtils.FormatTime1
DateTimeUtils.FormatTime2
DateTimeUtils.FormatTime3
这种方式虽然可以解决问题,但是会发觉后面格式化函数越来越多,里面有大量冗余的代码。
所以我就想可不可以用一个方法就能支持多种格式的呢?
通过分析时间格式化的需求,其实不同也就是非数字部分的内容,所以我就想着可以通过正则和字符串分割的方式写了一个方法能满足大多数的格式化需求.
例如:
- 传入 DateTimeUtils.Format(“剩余dd天hh时m分s秒”,sec) 返回 剩余0天12时05分03秒
- 传入 DateTimeUtils.Format(“hh:mm:ss”,sec) 返回 12:05:03
代码实现
定义支持的格式,‘日时分秒’分别对应=>dhms
local fmtSecInfo = {}
fmtSecInfo["d"] = 86400 -- 1天的秒数
fmtSecInfo["h"] = 3600 --1小时的秒数
fmtSecInfo["m"] = 60 --一分钟的秒数
fmtSecInfo["s"] = 0
分析传入的格式
local sec = 0 + 3600*3 + 60*3 + 26 --0天3小时3分26秒
local fmt = "剩余dd天hh时mm分s秒"
local startIdx = 1
local parttern = "%l+" --正则匹配1个或多个小写字母
local i,j = string.find( fmt,parttern,startIdx)
local fmtChar = string.match(fmt,parttern,startIdx) --从左到右得到第一个格式字符
截取格式前面的字符
local retFmt = string.sub(fmt,1,i-1)
到这里完成了第一个格式的提取工作,接下来就是根据格式字符串转换秒数到具体的时间格式,然后执行循环处理即可。
local useShort = true --是否为0的不显示 例如 剩余dd天hh时m分s秒 当天数为0时 显示成 剩余hh时m分s秒
while j and fmtChar do
print("while find i=",i,"j=",j,"fmtChar=",fmtChar)
startIdx = j + 1
local k,m = string.find( fmt,parttern,startIdx)
local nextStart = k and k - 1 or #fmt
local f = string.sub(fmtChar,1,1)
local fmtSec = fmtSecInfo[f] --获取该格式的秒数 例如 h=3600 一小时的描述
if not fmtSec then
return "时间格式="..fmt.." 无效"
end
local val
if fmtSec > 0 then
val = math.floor(sec/fmtSec) --计算具体数值
sec = sec%fmtSec
else
val = sec
end
if val > 0 or not useShort then
local valStr = tostring(val)
if #fmtChar==2 and val < 10 then --传入的格式是2个字符的 显示的数字不足10也是2位数
valStr = "0"..val
end
--截取拼接非格式部分的内容
local tempFmtStr = string.sub(fmt,i,nextStart)
retFmt = retFmt..string.gsub(tempFmtStr,parttern,valStr)
end
fmtChar = string.match(fmt,parttern,startIdx)
i,j= k,m
end
至此完成了格式化的定义,封装成函数的完整代码
--通用的格式化时间显示
---@param sec integer 剩余秒数
---@param fmt string 时间显示格式,日时分秒 -> dhms (如果是双字母表示小于10的显示成01,例如:hh 1小时 就显示成 01小时 ) 例如: 剩余dd天hh时m分s秒,hh:mm:ss
---@param useShort boolean 是否为0的不显示 例如 剩余dd天hh时m分s秒 当天数为0时 显示成 剩余hh时m分s秒
---@examples 调用样例: DateTimeUtils.Format(sec,"hh:mm:ss") 返回 06:05:01 ;
function DateTimeUtils.Format(sec,fmt,useShort)
fmt = string.lower(fmt)
local fmtSecInfo = {}
fmtSecInfo["d"] = 86400 -- 1天的秒数
fmtSecInfo["h"] = 3600 --1小时的秒数
fmtSecInfo["m"] = 60 --一分钟的秒数
fmtSecInfo["s"] = 0
local startIdx = 1
local parttern = "%l+"
local i,j = string.find( fmt,parttern,startIdx)
local fmtChar = string.match(fmt,parttern,startIdx)
local retFmt = string.sub(fmt,1,i-1)
while j and fmtChar do
print("while find i=",i,"j=",j,"fmtChar=",fmtChar)
startIdx = j + 1
local k,m = string.find( fmt,parttern,startIdx)
local nextStart = k and k - 1 or #fmt
local f = string.sub(fmtChar,1,1)
local fmtSec = fmtSecInfo[f]
if not fmtSec then
return "时间格式="..fmt.." 无效"
end
local val
if fmtSec > 0 then
val = math.floor(sec/fmtSec)
sec = sec%fmtSec
else
val = sec
end
if val > 0 or not useShort then
local valStr = tostring(val)
if #fmtChar==2 and val < 10 then
valStr = "0"..val
end
local tempFmtStr = string.sub(fmt,i,nextStart)
retFmt = retFmt..string.gsub(tempFmtStr,parttern,valStr)
end
fmtChar = string.match(fmt,parttern,startIdx)
i,j= k,m
end
return retFmt
end
测试几个格式看看结果:
看看输出的结果:
完成!如果喜欢欢迎收藏转发,如果有错误可以指出。谢谢!