今天接到了一个需求,在某个页面内用户选择一个DateTime后,服务端保存这个DateTime,当用户再次进入这个页面,直接显示之前选择的DateTime(如果服务端没有返回,则直接显示当前DateTime)。 这个需求听起来并没有什么难度,于是就风风火火开始coding……(此处省略800字,请自行脑补血腥的coding场面)。
DateTimePicker使用原生控件以节约性能,并且可以根据不同的系统有不同的本地化样式,反正就看浏览器是怎么处理的了。
<input name="datetime" id="J_datetime_picker_1" type="datetime" />
<input name="datetime" id="J_datetime_picker_2" type="datetime-local" />
根据w3school,DateTimePicker有datetime
和datetime-local
两种type,但根据我的试验,前者只能展现出一个text
类型的输入框,Don't konw why 。。。
之后要做的就很简单了,当用户选择了一个DateTime之后,获取它并展示它并把它存起来。然后我就这么做了……
const dateTimePicker = document.querySelector('#J_datetime_picker');
const dateTime = dateTimePicker.value;
// dateTime <==> '2017-02-22T18:40'
const dateTimeString = dateTime.replace('-', '年')
.replace('-', '月')
.replace('T', '日 ');
// dateTimeString <==> '2017年02月22日 18:40'
// dateTime用来存,dateTimeString用来显示
看到这里就不免感觉有些奇怪了,平时我们看到的时间对象并不长这样啊?!!!
new Date();
// Wed Feb 22 2017 18:40:00 GMT+0800 (CST)
new Date('1487760000000');
// Invalid Date
Date.now();
// 1487760000000
无论如何也没能通过Date对象构造出一个带'T'的时间字符串…… 于是老老实实查阅资料~
RFC2822 标准日期字符串
YYYY/MM/DD HH:MM:SS ± timezon(时区用4位数字表示) // 如: 1992/02/12 12:23:22+0800
ISO 8601 标准日期字符串
YYYY-MM-DDThh:mm:ss ± timezone(时区用HH:MM表示)
1997-07-16T08:20:30Z // “Z”表示UTC标准时区,即"00:00",所以这里表示零时区的
1997年7月16日08时20分30秒
//转换成位于东八区的北京时间则为1997年7月17日16时20分30秒
1997-07-16T19:20:30+01:00 // 表示东一区的1997年7月16日19时20秒30分,转换成UTC标准时间的话是1997-07-16T18:20:30Z
由于业务的需求,程序中存储的是时间Date对象,而不是和服务端交互用的字符串,然后就发现了一个新的问题。
// 之前存在服务端的DateTime字符串为'2017-02-22T18:40'
new Date('2017-02-22T18:40');
// Thu Feb 23 2017 02:40:00 GMT+0800 (CST)
// 改用RFC2822标准日期字符串
new Date('2017/02/22 18:40');
// Wed Feb 22 2017 18:40:00 GMT+0800 (CST)
可以看到,在未指定时区的前提下,RFC2822返回结果是以当前时区的零点为准,而ISO8601返回结果则会以UTC时间为标准进行解析。
于是,对于从DateTimePicker中取出的时间串,在转换成Date对象前需要对应当前时区进行转换。
// 从DateTimePicker中取出的时间字符串为'2017-02-22T18:40'
const inputValue = '2017-02-22T18:40';
// 转换成正确的Date对象
const dateTime = new Date(inputValue + '+08:00');
而DateTimePicker绑定值进行回显时也需要注意:
<!-- 这种写法是不被支持的,虽然value是一个标准的ISO8601日期字符串 -->
<input type="datetime-local" name="datetime" value="2017-02-22T18:40+08:00" />
<!-- 这种写法是正确的,会直接按照绑定的时间显示,不考虑时区 -->
<input type="datetime-local" name="datetime" value="2017-02-22T18:40" />
郁闷极了,同是表示日期时间的对象(或对象字符串),竟然有如此多种表达形式,并且在各种形式上也没有达成统一。
除此之外,Date对象的一些方法也是匪夷所思:
方法名 | 作用 | 2017年2月22日 18:40(星期三) |
---|---|---|
getFullYear | 从 Date 对象以四位数字返回年份 | 2017 |
getMonth | 从 Date 对象返回月份 (0 ~ 11) | 1 |
getDate | 从 Date 对象返回一个月中的某一天 (1 ~ 31) | 22 |
getHours | 返回 Date 对象的小时 (0 ~ 23) | 18 |
getMinutes | 返回 Date 对象的分钟 (0 ~ 59) | 40 |
getSeconds | 返回 Date 对象的秒数 (0 ~ 59) | 0 |
可以看到,年月日时分秒中,只有月份是不正常的,我们在处理Date对象展示年月日信息时还需要对月份进行单独处理。
const dateObj = new Date('2017/02/22 18:40');
const year = dateObj.getFullYear();
const month = dateObj.getMonth() + 1;
const date = dateObj.getDate();
const hour = dateObj.getHours();
const minute = dateObj.getMinutes();
const tmp = {
year: year.toString(),
month: month < 10 ? '0' + month : month.toString(),
date: date < 10 ? '0' + date : date.toString(),
hour: hour < 10 ? '0' + hour : hour.toString(),
minute: minute < 10 ? '0' + minute : minute.toString()
}
const {year, month, date, hour, minute} = tmp;
return `${year}年${month}月${date}日 ${hour}:${minute}`;
// 2017年02月22日 18:40
作为一个小前端,心有余而力不足,只能盼望着早日统一标准,填坑愉快吧~