时间日期是经常遇到的数据类型,Qt中时间日期类型的类如下。
时间日期转换为字符串使用QDateTime的toString()函数,分别用不同的格式显示时间、日期、日期时间。toString()的函数原型是:
QString QDateTime::toString(const QString &format) const
它将日期时间数据按照format指定的格式转换为字符串。format是一个字符串,包含一些特定的字符,表示日期或时间的各个部分,下图是用于日期时间显示的常用格式符。
在设置日期时间显示字符串格式时,还可以使用填字符,甚至使用汉字。例如,日期显示格式可以设置为:toString ("yyyy年MM月dd日"),这样得到的字符串格式是“2023年03月15日”。
同样的,也可以将字符串转换为QTime、QDate或QDateTime类型,使用静态函数QDateTime::fromString(),其函数原型为:
QDateTime QDateTime::fromString(const QString &string, const QString &format)
其中,第1个参数string是日期时间字符串形式,第2个参数format是字符串表示的格式。
Qt中有专门用于日期、时间编辑和显示的界面组件,介绍如下。
QDateEdit和QTimeEdit都从QDateTimeEdit继承而来,实现针对日期或时间的特定显示功 能。实际上,QDateEdit和QTimeEdit的显示功能都可以通过QDateTimeEdit实现,只需设置好属性即可。
QDateTimeEdit类的主要属性的介绍如下。
在图中窗体左上方的“日期时间”中,使用QTimeEdit、QDateEdit、QDateTimeEdit组件作为时间、日期、日期时间编辑器;在其下方,放置一个QLineEdit组件用于字符串显示日期时间。界面如下图。
1. 日期时间数据的获取与转换为字符串
“读取当前日期时间”按钮的clicked()信号的槽函数代码如下:
void Widget::on_btnGetTime_clicked()
{
QDateTime curDateTime=QDateTime::currentDateTime();
ui->timeEdit->setTime(curDateTime.time());
ui->dateEdit->setDate(curDateTime.date());
ui->dateTimeEdit->setDateTime(curDateTime);
ui->edtDateTime->setText (curDateTime.toString("yyyy-MM-dd hh:mm:ss"));
}
首先用QDateTime类的静态函数currentDateTime()获取当前日期时间,并赋值给变量curDateTime。
然后用curDateTime变量设置界面上3个日期、时间编辑器的时间或日期值,利用了 QDateTime的time()和date()分别提取时间和日期。
最后,将curDateTime表示的日期时间数据转换为字符串然后在LineEdit编辑框上显示。
2. 字符串转换为日期时间
在程序运行时,手工修改“字符串显示”后面文本框里的日期时间字符串,单击“设置日期时间”按钮,可以将文本框里的字符串转换为QDateTime变量,并设置为组件的日期时间数据,代码如下:
void Widget::on_btnSetTime_clicked()
{
QString str=ui->edtDateTime->text();
str=str.trimmed();
if(!str.isEmpty())
{
QDateTime datetime=QDateTime::fromString(str,"yyyy-MM-dd hh:mm:ss");
ui->timeEdit->setTime(datetime.time());
ui->dateEdit->setDate(datetime.date());
ui->dateTimeEdit->setDateTime(datetime);
}
}
3. QCalendarWidget日历组件
图中窗体右侧是一个QCalendarWidget组件,它以日历的形式显示日期,可以用于日期选择。
QCalendarWidget有一个信号selectionChanged(),在日历上选择的日期变化后会发射此信号,为此信号创建槽函数,编写代码如下:
移动Web页面,受移动网络网速和终端性能影响,我们经常要关注首屏内容展示时间(以下简称首屏时间)这个指标,它衡量着我们的页面是否能在用户耐心消磨完之前展示出来,很大程度影响着用户的使用满意度。
怎么获取首屏时间呢?
我们经常要先问自己:页面是怎么加载数据?
A:加载完静态资源后通过Ajax请求去后台获取数据,数据回来后渲染内容
在每个点打上一个时间戳,首屏时间 = 点8 – 点1
B:使用后台直出,返回的html已经带上内容了
此时首屏时间 = 点4 – 点1。
注:1. 打了这么多个点,是因为当我们收集到首屏时间之后,要去分析到底是哪一段是性能瓶颈,哪一段还有优化空间,所以我们需要收集 点2 – 点1、点3 – 点1 ……这些时间以作分析。
2. 打点1我们一般是在html文件head标签的开头打个时间戳。
3. 在css文件加载前一般没有别的加载处理,所以打点1和打点2一般可以合并。
到此我们就收集到首屏相关各种数据,可以做各种针对性优化。Wait!在你大刀阔斧优化前,你要了解一些细节,它们有利于你做更准确的分析和更细致的优化。
细节1:js后面的点 – js前面的点 ≠ js的加载时间
JsEndTime – JsStartTime = js文件的加载时间,对吗?
不对!明显地,这个等式忽略了js的执行时间。js执行代码是需要花费时间的,特别是做一些复杂的计算或频繁的dom操作,这个执行时间有时会达到几百毫秒。
那么,JsEndTime – JsStartTime = js文件的加载执行时间?
依然不对!因为CSS文件的加载执行带来了干扰。觉得很奇怪对吧,别急,我们来做个试验:我们找一个demo页面,在chrome里面打开,然后启动控制台,模拟低网速,让文件加载时间比较久:
先在正常情况下收集 JsEndTime – JsStartTime 的时间,然后使用fiddler阻塞某一条css请求几秒钟:
然后再恢复请求,拿到此时的 JsEndTime – JsStartTime 结果,会发现第一次的时间是几百毫秒将近1s,而第二次的时间低于100ms甚至接近为0(我的示例,时间视读者具体的js文件决定),两者的差距非常明显。
这是什么原理?这就是我们常说的“加载是并行的,执行是串行的”的结果。html开始加载的时候,浏览器会将页面外联的css文件和js文件并行加载,如果一个文件还没回来,它后面的代码是不会执行的。刚刚我们的demo,我们阻塞了css文件几秒,此时js文件因为并行已经加载回来,但由于css文件阻塞住,所以后面 JsStartTime 的赋值语句是不执行的!当我们放开阻塞,此时才会运行到 JsStartTime 的赋值、js文件的解析、JsEndTime的赋值,由于大头时间加载早已完成,所以 JsEndTime 和 JsStartTime 的差值非常小。
知道这个有何用?
1、别再把 JsEndTime – JsStartTime 的结果成为js文件的加载执行时间(除非你没有外联css文件),不然会被内行人取笑滴。
2、Css文件的阻塞会影响后面js代码的执行,自然也包括html代码的执行,即是说此时你的页面就是空白的。所以css文件尽量内联,你可以让构建工具帮你忙。
3、如果真想要知道js文件的加载时间,最正确的姿势是使用 Resource Timing API,不过这个API移动端只能在Android4.4及以上的版本拿到数据,也就在业务PV大的场景才够我们做分析用。
当然,那两个打点留着还是可以做分析用的。
细节2:html里面外联的js文件,前一个文件的加载会阻塞下一个文件的执行;而如果a.js负责渲染并会动态拉取js、拉取cgi并做渲染,会发现它后面的js文件再怎么阻塞也不会影响到它的处理
前半部分的结论在细节1里面已经证明,因为浏览器的执行是串行的。这说明,我们负责渲染内容的js代码要等到它前面所有的js文件加载执行完才会执行,即使那些代码跟渲染无关的代码如数据上报:
而后半部分的结论很好验证,我们在负责渲染的js文件后面外联一个别的js文件并把它阻塞住,你会发现渲染相关的js不管是动态拉取新的js文件、拉取渲染相关内容都一切正常,页面内容顺利渲染出来,它们的执行并不需要等被阻塞的这个文件。
知道这个有何用?
1、无关紧要”的js不要放在负责渲染的js前面,这里的“无关紧要”是指和首屏渲染无关,如数据上报组件。我们可以选择将要上报的数据临时存起来,先继续执行渲染的js,等负责渲染的js执行完再加载上报组件再上报。甚至连zepto之类的库我们也可以放后面,把渲染相关的代码抽离出来并用原生js书写,放到最前面。
2、可以看到,动态加载的js的执行是不会受到html后面外联的js的阻塞的影响,即是说,它的执行和后面js的执行顺序是不确定的。因此我们要小心处理好文件的依赖关系。当然还可以采用最不容易出错的方法:负责动态加载js的文件是html里面外联的最后一个文件。
细节3:如果html的返回头包含chunk,则它是边返回边解析的,不然就是一次性返回再解析。这个是在服务器配置的
打点1一般写在html里head标签的最前面,时常有朋友拿直出时的 点4 – 点1 的时间和非直出时 点8 – 点1 的时候做对比,来说明直出优化了多少多少毫秒,我倒觉得不一定。要知道直出的情况html文件包含渲染后的内容和dom节点,文件大小一般比非直出大,有时甚至大个几十K都有,那我觉得要说明直出优化了多少就要把html的加载时间考虑进去了。那上面的计算方法是否考虑上html的加载时间?
那就要看html文件的返回头是否包含chunk:
如果包含这个返回头,那html文件是边返回边解析的,此时上面的计算方法是合理的。如果不包含这个头,则html文件是整一个返回来后才开始解析,此时上面的计算方法就少算了html的加载时间,也就不够精准。这个返回头是由后台控制的。
知道这个有何用?
1、如果我们想说明直出的优化程度,最好先瞧瞧你的html返回头。如果不包含chunk返回头,考虑拿HTML5 performance里面的 navigationStart 作为打点1(这个API也是Android4.4及以上才支持),要不就要评估文件大小变化做点修正了。
2、对于没有启用chunk的html,建议不要inline太多跟渲染首屏内容无关的js在里面,这样会影响渲染时间
细节4:写在html里面的script节点的加载和解析会影响 domContentLoaded 事件的触发时间
我们有时会用 domContentLoaded 事件代替 onload 事件,在页面准备好的时候做一些处理。然而要知道domContentLoaded里面的dom不止包含我们常说的普通dom节点,还包括script节点。
试验一下,我们将页面里面外联的一个js文件阻塞住一段时间再放开,我们看下chrome控制台:
很明显,js文件的加载时间会影响这个事件的触发事件。那js代码的解析时间会不会影响?我们在最后一个外联JS文件后面打了一个点,它的时间是:
所以js文件加载执行会影响domContentLoaded事件的执行时机。
知道这个有何用
1、如果我们打算在domContentLoaded、onLoad 事件里面做一些特殊处理且这些处理比较重要(如跟渲染有关),那我们最好就不要在html里面直接外联一些跟渲染无关的js文件,可以考虑改用动态加载。
总结
研究首屏时间和资源加载是一件挺有意思的事情,大家利用好chrome控制台(特别是里面的network标签)以及fiddler可以挖掘出很多有趣的小细节小结论。别以为这是在没事找事,理解好这些对大家做首屏性能优化、定位因为js文件执行顺序错乱导致报错等场景是非常有好处的。
文章转载自 AlloyTeam:http://www.alloyteam.com/2016/01/points-about-resource-loading/
更多技术干货请关注听云博客 blog.tingyun.com
近在编程中遇到了时间与时区相关的问题,整理在这里
我的程序是一个在hadoop上运行的分布式程序,从mysql数据库中取数据,经过处理之后输出
一. 基本概念
时区 :time zone 1884年国际经线会议规定,全球按经度分为24个时区,每区各占经度15°。
以本初子午线为中央经线的时区为零时区,由零时区向东、西各分12区,东、西12区都是半时区,共同使用180°经线的地方时。
CST :China Standard Time UTC+8:00 中国标准时间(北京时间),在东八区
UTC :Universal Time Coordinated,世界协调时间,又称世界标准时间、世界统一时间。UTC 提供了一种与时区无关(或非特定于时区)的时间。
世界上的所有时区都可以表示为 UTC 加上或减去一个偏移量。
因此,UTC是0时区的时间,如北京为早上八点(东八区),UTC时间就为零点,时间比北京时晚八小时
GMT :Greenwich Mean Time格林威治标准时间,指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。
Unix timestamp :Unix时间戳,或称Unix时间(Unix time)、POSIX时间(POSIX time),是一种时间表示方式,
定义为从格林威治时间(UTC/GMT的午夜)1970年01月01日00时00分00秒起至现在的总秒数。
可以这么说:
UTC和GMT几乎是同一概念,两者的区别是GMT是一个天文上的概念,UTC是基于原子钟。
GMT=UTC
GMT + 8 = UTC + 8 = CST
UTC+时间差=本地时间 (时间差东为正,西为负,东八区记为 +0800)
二. 从数据库取数据的过程
mysql>select auction_id,startsfrom auctions where auction_id=88888;
±------------±--------------------+
| auction_id | starts |
±------------±--------------------+
| 88888 | 2011-10-24 20:32:58 |
±------------±--------------------+
1 rowin set (0.00 sec)
mysql> show columnsfrom auctions;
±-------------------------------±--------------±-----±----±--------±------+
| Field | Type |Null | Key| Default | Extra |
±-------------------------------±--------------±-----±----±--------±------+
|starts | datetime | YES | MUL |NULL | |
可见:数据库的时间字段starts存的是datetime类型,它是一个和时区相关的string(显然:string都是和时区相关的)
而且数据库是按照CST时区存的时间
程序中从数据库取数据用的sql语句:
mysql>select auction_id,DATE_FORMAT(starts,’%Y%m%d%H%i%S’)from auctions where auction_id=88888;
±------------±-----------------------------------+
| auction_id | DATE_FORMAT(starts,’%Y%m%d%H%i%S’) |
±------------±-----------------------------------+
| 88888 | 20111024203258 |
±------------±-----------------------------------+
1 rowin set (0.00 sec)
这里只是简单的用DATE_FORMAT函数把datetime类型的starts字段转换为我们需要的格式 %Y%m%d%H%i%S 而已
三、java代码
看这样一段转换时间的java代码:
// 将字符串时间转化为秒数(yyyyMMddHHmmss)
staticpublic long getUnixTimestamp(String srcTime)
{
SimpleDateFormat sdf =new SimpleDateFormat(“yyyyMMddHHmmss”);
Date result_date;
longresult_time = 0;
try{
result_date = sdf.parse(srcTime);
//返回的是毫秒数故除以1000
result_time = result_date.getTime()/1000;
}catch (Exception e) {
//出现异常时间赋值为20000101000000
result_time =946684800;
}
returnresult_time;
}
计算结果:
getUnixTimestamp(“20111204212224”) =1323004944
说明:java.util.Date中的getTime函数定义如下:
java.util.Date代表一个时间点,其值为距公元1970年1月1日 00:00:00的毫秒数。所以它是没有时区和Locale概念的。
public long getTime() 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数
java中通过如下形式取得当前时间点:
Date now =new Date(); //这个时间点与本地系统的时区无关
而正因为其与时区的无关性,才使得我们的存储数据(时间)是一致的(时区一致性)。
一般的我们将now存储于数据库中,当我们需要展现数据时,将now格式化成想要的格式,如:2011-12-04 21:22:24
而这个功能一般交由java.text.DateFormat来实现。例如:
SimpleDateFormat sdf =new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
String snow = sdf.format(now);
我们发现snow是带时间(如2011-12-04 21:22:24)的字符串,那么 2011-12-04 21:22:24 这个时间是哪个时区的时间呢?
默认情况下,SimpleDateFormat 取得本地系统的时区(我的时区为GMT+8北京),然后按照 pattern(“yyyy-MM-dd HH:mm:ss”)格式化now,
此时输出的就是 GMT+8 区的时间了。如果想支持国际化时间,则先指定时区,然后再格式化date数据。例如:
SimpleDateFormat sdf =new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
sdf.setTimeZone(TimeZone.getTimeZone(“GMT+8”));
String snow = sdf.format(now);// snow = 2011-12-04 21:22:24
sdf.setTimeZone(TimeZone.getTimeZone(“GMT+7”));
String snow2 = sdf.format(now);// snow2 = 2011-12-04 20:22:24 (可见:东八区比东七区早一个小时)
另外,你可以通过如下代码修改本地时区信息:
TimeZone.setDefault(TimeZone.getTimeZone(“GMT+8”));
在windows操作系统中,是通过桌面右下角,也可以指定操作系统的时区。
在linux系统中,通过如下命令可以得到当前时区
[admin@localhost]$ date -R
Sun,04 Dec 201122:49:00+0800
四、结论:
getTime()返回的已经是一个UTC的unix timestamp秒数了,与时区无关;而转换为字符串后,就和时区相关了
对于这个秒数,不同时区的人,按照自己所在的时区去解析,就可以得到正确的时间了
[admin@localhost]$ date -d@1323004944
2011年12月 04日 星期日21:22:24CST
[admin@localhost]$ date -d@1323004944 -u
2011年12月 04日 星期日13:22:24UTC
对于涉及到时间转换的程序来说,如果代码里面没有强行指定时区,那就会依赖于操作系统的时区。
特别是对于分布式程序,如果不同机器上系统时区不一样,那就会出现不一致的数据了!
五、对unix timestamp和时区概念的曲解和误用
由于历史原因,发现程序中有这样一段代码:
// 将字符串时间转化为秒数(yyyyMMddHHmmss),有8个小时的时差
staticpublic long getLongTime(String srcTime)
{
SimpleDateFormat sdf =new SimpleDateFormat(“yyyyMMddHHmmss”);
Date result_date;
longresult_time = 0;
try{
result_date = sdf.parse(srcTime);
//返回的是毫秒数故除以1000
result_time = result_date.getTime()/1000+ 8 * 3600; // 这里加了八个小时
}catch (Exception e) {
//出现异常时间赋值为20000101000000
result_time =946684800;
}
returnresult_time;
}
计算结果:
getUnixTimestamp(“20111204212224”) =1323033744
显然,这个时间比上面通过 getUnixTimestamp(“20111204212224”) = 1323004944 得到的时间多了8个小时
1323033744 - 1323004944 = 28800 = 8 * 3600 = 8h
如果用户将得到的 1323033744 按照自己所在的时区解析后得到的结果是:
[admin@localhost]$ date -d@1323033744
2011年12月 05日 星期一05:22:24CST
得到了一个完全错误的结果!
但如果用户将这个 1323033744 按照UTC时区来解析后得到的结果是:
[admin@localhost]$ date -d@1323033744 -u
2011年12月 04日 星期日21:22:24UTC
为了方便对比,把 1323004944 的解析结果也拿来对比
[admin@localhost]$ date -d@1323004944
2011年12月 04日 星期日21:22:24CST
[admin@localhost]$ date -d@1323004944 -u
2011年12月 04日 星期日13:22:24UTC
可以看到,这个代码中得到的秒数时间是比UTC的unix timestamp秒数多了八个小时
这个时间 1323033744 可以理解为北京时区得到的秒数,但是不是unix timstamp时间!
unix timestamp秒数是与时区无关的,不管是在哪个时区得到的unix timestamp都是一样的
我们可以验证一下,用北京时间“20111204212224”减去“19700000000000”得到的秒数,就是 1323033744
SimpleDateFormat df =new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
java.util.Date end = df.parse(“2011-12-04 21:22:24”);
java.util.Date start = df.parse(“1970-01-01 00:00:00”);
longdelta = (end.getTime() - start.getTime())/1000;
System.out.println(“delta=”+ delta); // delta=1323033744
或者用shell命令来求时间差
[admin@localhost]$ date -d"2011-12-04 21:22:24" +%s
1323004944
[admin@localhost]$ date -d"1970-01-01 0:0:0" +%s
-28800
[admin@localhost]$ date -d"2011-12-04 21:22:24" +%s -u
1323033744
[admin@localhost]$ date -d"1970-01-01 0:0:0" +%s -u
0
1323004944 + 28800 = 1323033744
对于东八区的人来说,1323033744 这个时间按照UTC时间可以解析正确。不能按照自己所在的时区去解析,不然就是错的
但是如果是东七区的人呢?需要按照UTC时间解析后,自己去减1个小时的时差,so ugly!
所以,用户在解析1323033744 这个数据的时候:
(1) 按照UTC时间来解析得到北京时间,然后根据时间差换算成自己所在时区的时间
(当然,一般都是在北京时区了,所以不用换算,按UTC时间来解析就能得到正确的时间)
(2) 将这个时间减去8小时得到unix timestamp,然后按照自己所在的时区去解析就可以了
总结:这段代码是对unix timestamp和时区的曲解和误用。
六、从数据库获取unix timestamp时间
*请认真填写需求信息,我们会在24小时内与您取得联系。