给出时间戳,根据指定时区,获取对应的时间字符串;
给出字符串,根据指定时区,获取对应的时间戳;
以上两种都需要展示出:时间字符串 + 时区,如:2019-12-02 02:00:01 +0800
这里需要的时间格式字符串指的是:XXXX-XX-XX XX:XX:XX;
指定时区为两种:一种是直接指定一个时区字符串,如:-08:00、+00:00,另一种是指定本地时区;
由于
Date
构造函数对参数格式的要求,以及产品需求对数值展示时的格式要求,需将所计算的数值再次进行格式化处理;
由于需要展示时区,以及获取本地时区与0时区的差用来做时区对应时间戳和事件字符串的换算,需要对本地时区与本地时区0时区差值进行计算。
本地时区需要动态计算,而计算本地时区需要依靠原生方法,需要对原生方法生成时区相关的各种api进行了解和踩坑。
字符串转时间戳
*
@date
{
str
} 时间字符串 - '2015-01-01'、'2015-01-01 12'、 '2015-01-01 12:11'、'2015-01-01 12:12:12'格式
*
@timeZone
{
str
} 时区字符串 - '+04:00'、'+00:00'格式
*
@returns
{
number
} 按时区转换的时间戳
getAnyTimespan
(
date, timeZone
) {
if
(!
/^\d{5,}$/
.
test
(date)) {
date =
String
(date)
.
replace
(
/^([0-9]{4})$/
,
'$1-01-01 00:00:00'
)
.
replace
(
/^([0-9]{4}-[0-9]{2})$/
,
'$1-01 00:00:00'
)
.
replace
(
/^([0-9]{4}-[0-9]{2}-[0-9]{2})$/
,
'$1 00:00:00'
)
.
replace
(
/^([0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{2})$/
,
'$1:00:00'
)
date =
new
Date
(date).
getTime
()
return
date -
new
Date
(date).
getTimezoneOffset
() *
60
*
1000
-
this
.
getOffsetMinute
(timeZone) *
60
*
1000
;
return
date;
getOffsetMinute
(
timeZone
) {
if
(!
/^[+-][0-9]{2}:((0[0-9])|([1-5][0-9]))$/
.
test
(timeZone)) {
return
0
;
let
time = timeZone.
split
(
':'
);
if
(timeZone[
0
] ===
'-'
) {
return
parseInt
(time[
0
]) *
60
-
parseInt
(time[
1
].
slice
(
0
,
2
));
}
else
{
return
parseInt
(time[
0
]) *
60
+
parseInt
(time[
1
].
slice
(
0
,
2
));
getAnyTimespan
输入字符串,返回时间戳,步骤如下:
判断是否是时间戳格式,已经是时间戳格式则自动原封不动返回传入时间戳;
若传入参数不是时间戳,则视为时间字符串处理,步骤如下:
将字符串处理为本地时间的时间戳(js没有提供直接将字符串转为指定时区时间戳的方法,由于每个月天数不一样,也较难直接通过字符串计算时间戳);
格式化为字符串格式,防止报错阻断代码执行;
利用repalce进行正则判断,补0为
Date
构造函数标准的参数格式;
关于不标准的参数格式:
'2019'
、
'2019-01'
、
'2019-01-02'
仅有年月日的时间格式,会被按0时区转换为时间戳,而其他格式会按本地时区转为时间戳,并且其他有时分秒的格式无法直接获得0时区时间戳,所以必须转为
'2019-01-02 00:00:00'
,才可以获得统一的值;
'2019-01-02 00'
作为参数是非法时间格式;
综上所述,将利用repalce统一补0;
时间戳转字符串
由于无法直接根据时间戳获取目标时区的时间字符串,每个月的天数不一样,直接计算出对应日期较为困难。我的思路是:根据本地时区和目标时区的时间差,计算出本地时区在目标时区当前时间的时间戳,进而通过new Date(默认转本地时间)获得目标时区当前时间。获取本地时间戳以获取所对应的年月日时分秒,并进行补0处理合成字符串。
*
@date
{
number
} 时间戳 - 1564704000000格式
*
@timeZone
{
str
} 时区字符串 - '+04:00'、'+00:00'格式
*
@returns
{
str
} - '2015-01-01 12:12:12'格式
getAnyTimeString
(
date, timeZone
) {
let
sameTimelocalTimeStamp = date -
0
+
new
Date
(date).
getTimezoneOffset
() *
60
*
1000
+
this
.
getOffsetMinute
(timeZone) *
60
*
1000
;
let
targetTime =
new
Date
(sameTimelocalTimeStamp)
let
targetTimeString =
String
(targetTime.
getFullYear
()) +
'-'
+
String
(targetTime.
getMonth
() +
1
).
replace
(
/^(\d)$/
,
'0$1'
) +
'-'
+
String
(targetTime.
getDate
()).
replace
(
/^(\d)$/
,
'0$1'
) +
' '
+
String
(targetTime.
getHours
()).
replace
(
/^(\d)$/
,
'0$1'
) +
':'
+
String
(targetTime.
getMinutes
()).
replace
(
/^(\d)$/
,
'0$1'
) +
':'
+
String
(targetTime.
getSeconds
()).
replace
(
/^(\d)$/
,
'0$1'
)
return
targetTimeString;
时区的计算
由于时区需要被单独展示出来,所以这里提供了时区的计算方法。代码中case1、case2都是给出指定的时区字符串,这里不做分析。仅在获取默认值本地时区时,需要注意一些细节。
timeZone() {
switch (this.timeZoneType) {
case 1:
return '+00:00';
case 2:
return this.activeTimeZone;
default:
let timegapHour = String(0 - Math.floor(new Date(date).getTimezoneOffset()/60))
.replace(/^([+-]?)(\d)$/, '$1' + 0 + '$2')
.replace(/^(\d+)$/, '+' + '$1');
let timegapMinute = String(Math.abs(new Date(date).getTimezoneOffset()%60))
.replace(/^(\d)$/, 0 + '$1');
return `${timegapHour}:${timegapMinute}`;
通过new Date(date).getTimezoneOffset()
获取时区差值时,仍需要注意传入要转换的时间。同一地区,展示的时间不同时,若时间的夏令时不同,则时区不同。
对于算出的小时数值进行补0补+号处理;
本次功能的实现,在格式化部分,均使用正则方法对字符串和数字格式进行处理。在时区对应值的计算上,均使用0时区作为媒介,获取时间差进行计算。需要注意的是夏令时时区与时区差的获取,必须传入具体时间作为参数,才可以避免由于夏令时产生的1小时偏差的影响。若一个时间控件使用此类多时区转换的功能,当用户选择一个夏令时和非夏令时时,若对应的是本地时区,则时区具体数值将随着是否夏令时而变化。
欢迎大家针对我的代码进行交流学习,提出更好的优化建议。