Fork me on GitHub

随笔分类 - other

关于时间的梳理

[TOC] # 1. 明确几个概念 - UTC: Universal Time Coordinated 协调世界时,又称世界标准时间,比GMT更精确 - GMT: Greenwich Mean Time 格林尼治平均时 - 时间戳 int类型,将GMT/UTC的1970年01月01日00时00分00秒作为起始值进行计算,得到的总秒数就是这个Unix时间戳,不分地区。 UTC和GMT都与英国伦敦的本地时相同,所以程序中UTC与GMT没什么不同。但是实际上,GMT 是一个时区,而 UTC 是一个时间标准。 # 2. ISO_8601日期格式标准 ISO 8601的标准格式是:YYYY-MM-DDTHH:mm:ss.sssZ,分别表示: - YYYY:年份,0000 ~ 9999 - MM:月份,01 ~ 12 - DD:日,01 ~ 31 - T:分隔日期和时间 - HH:小时,00 ~ 24 - mm:分钟,00 ~ 59 - ss:秒,00 ~ 59 - .sss:毫秒 - Z:时区,可以是:Z(UFC)、+HH:mm、-HH:mm T T 也可以用空格表示,但是这两种表示有点不一样,T 其实表示 UTC,而空格会被认为是本地时区(**前提是不通过 Z 指定时区**)。 Z Z 用来表示传入时间的时区(zone),不指定并且没有使用 T 分隔而是使用空格分隔时,就按本地时区处理,比如下面的例子: # 3. JavaScript中的时间 Data()构造时间的方法有下面几种: - new Date(); // 当前时间 - new Date(value); // 自 1970-01-01 00:00:00 UTC 经过的毫秒数 - new Date(dateString); // 时间字符串 - new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]); ``` new Date(); new Date(1000 * 1) new Date('Thu Jan 01 1970 00:00:00 GMT+0800 (CST)') new Date(1970, 0, 1, 0, 0, 0) ``` Javascript则精确到毫秒数。用dateObject.getTime()来获得当前时间戳,用Date.parse(datestring)来生成一个时间戳。 使用谷歌浏览器的控台,探究一下 在浏览器中直接new一个date对象,因为我们处于UTC+0800的时区,所以控制台给我们打印出来的时间是GMT+0800的时间 ``` new Date() Thu Feb 08 2018 17:25:13 GMT+0800 (中国标准时间) ``` 通过日期字符串new一个Date对象,输入的时间字符串是2018-02-08 00:00:00,没有带ISO标准的“T”字母,因此浏览器认为我们想输入的是当地时间 ``` new Date("2018-02-08 00:00:00") Thu Feb 08 2018 00:00:00 GMT+0800 (中国标准时间) ``` 2018-02-08T00:00:00+0800 代表UTC+0800时区2月8日0时0分,2018-02-08T00:00:00-0800 代表UTC-0800时区2月8日0时0分,在控制台中显示分别如下 ``` new Date("2018-02-08T00:00:00+0800") Thu Feb 08 2018 00:00:00 GMT+0800 (中国标准时间) new Date("2018-02-08T00:00:00-0800") Thu Feb 08 2018 16:00:00 GMT+0800 (中国标准时间) ``` # 4. MySql中的时间 MySQL 中和时间相关的数据类型主要包括:YEAR、TIME、DATE、DATETIME、TIMESTAMP。 DATE、YEAR、TIME 比较简单,大概总结如下: 名称 | 占用字节 | 取值 -|-|- DATE| 3 字节| 1000-01-01 ~ 9999-12-31 YEAR| 1 字节| 1901 ~ 2155 TIME| 3 字节| -838:59:59 ~ 838:59:59 注:TIME 的小时范围可以这么大(超过 24 小时),是因为它还可以用来表示两个时间点之差。 DATEIME vs TIMESTAMP 我们主要来说明下 DATETIME 和 TIMESTAMP,可以做下面的总结: 名称| 占用字节| 取值| 受 time_zone 设置影响 -|-|-|- DATETIME| 8 字节| 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59| 否 TIMESTAMP| 4 字节| 1970-01-01 00:00:00 ~ 2038-01-19 03:14:07| 是 第一个区别是占用字节不同,导致能表示的时间范围也不一样。 第二个区别是 DATETIME 是“常量”,保存时就是保存时的值,检索时是一样的值,不会改变;而 TIMESTAMP 则是“变量”,保存时数据库服务器将其从time_zone 时区转换为 UTC 时间后保存,检索时将其转换从 UTC 时间转换为 time_zone 时区时间后返回。 创建数据表: ``` CREATE TABLE `tests` ( `id` INTEGER NOT NULL auto_increment , `datetime` DATETIME, `timestamp` TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB; ``` 连接到数据库服务器后,可以执行 SHOW VARIABLES LIKE '%time_zone%' 查看当前时区设置。类似下面这样的结果: Variable_name| Value -|- system_time_zone| CST time_zone| SYSTEM 说明我目前时区是 CST(China Standard Time),也就是东八区。 INSERT INTO `tests` (`id`, `datetime`, `timestamp`) VALUES (DEFAULT, '2000-01-01 00:00:00', '2000-01-01 00:00:00'); 检索 id| datetime| timestamp -|-|- 1| 2000-01-01 00:00:00| 2000-01-01 00:00:00 如果我们先将 time_zone 设置为一个不同的值后再进行检索就会发现不同的结果: SET time_zone = '+00:00'; 再次检索 id| datetime| timestamp -|-|- 1| 2000-01-01 00:00:00| 1999-12-31 16:00:00 # 5. Vue(ElementUI) + Laravel 的前后端数据的处理 ElementUI前端初始化数据的时候, 参数| 说明| 类型| 可选值| 默认值 -|-|-|-|- default-value| 可选,选择器打开时默认显示的时间| Date| 可被new Date()解析| — default-time| 范围选择时选中日期的默认具体时刻| string[]| 数组,长度为 2,每项值为字符串,形如12:00:00,第一项指定开始日期的时刻,第二项指定结束日期的时刻,不指定会使用时刻 00:00:00| — value-format| 可选,绑定值的格式。不指定则绑定值为 Date 对象| string| [见日期格式](http://element-cn.eleme.io/#/zh-CN/component/date-picker#ri-qi-ge-shi)| — 采用默认格式,参照官方文档 ``` <script> export default { data() { return { pickerOptions2: { shortcuts: [{ text: '最近一周', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); picker.$emit('pick', [start, end]); } }] }, value3: [new Date(2000, 10, 10, 10, 10), new Date(2000, 10, 11, 10, 10)], value4: '' }; } }; </script> ``` 传递给时间控件的值,可以通过new Date()初始化, 通过time.getTime()获取时间戳,进行时间的计算, 再通过time.setTime()转换成new Date()类型。 这里需要注意的是,time.getTime()获取的是毫秒值。 如果前端不加任何其它处理,我们后端获取的值是UTC时间格式。 ``` dd($request->input('t_s'),date('Y-m-d H:i:s', strtotime($request->input('t_s')))); ``` ``` "2018-02-08T02:30:32.743Z" "2018-02-08 10:30:32" ``` 通过转换可以得到mysql数据库中(DATEIME或TIMESTAMP格式),然后进行存储。 # 6. php(Laravel)中时区问题 1. 配置Laravel时区 我们在config/app.php中配置好找到timezone, 修改成如下时区即可,如果时间正确,就可以不用管第二步 'timezone' => 'PRC', 2. 修改php.ini中的时区 找到PHP配置文件(php.ini,根据自己情况查找) date.timezone = PRC 3. 不使用框架,没有提供配置时区功能 Laravel提供给我们设置时区的配置,如果你没有使用框架,我们可以在系统初始化的时候设置。 ``` ini_set('date.timezone','PRC'); //或 date_default_timezone_set("PRC"); ``` 这样就可以解决时区问题 # 参考资料 [关于“时间”的一次探索](https://segmentfault.com/a/1190000004292140 )

算法总结

## 1.插入排序 插入排序法的基本思路:同样以案例来说明,还是以$arr = array(2,6,3,9),由大到小排序。 实现原理:插入排序的思想有点像打扑克抓牌的时候,我们插入扑克牌的做法。想象一下,抓牌时,我们都是把抓到的牌按顺序放在手中。因此每抓一张新牌,我们都将其插入到已有的排好序的手牌当中,注意体会刚才的那句话。也就是说,插入排序的思想是,将新来的元素按顺序放入一个已有的有序序列当中。 代码规律分析: array(23,34,12,56,43,98,89) 注意下面括号中元素插入的位置,和比较的方法 [23,(34)] ,12,56,43,98,89 第一次大循环:$[1]与$[0]比; [(12),23,34] ,56,43,98,89 第二次大循环:$[2]与$[1]比,$[1]与$[0]比; [12,23,34,(56)] ,43,98,89 第三次大循环:$[3]与$[2]比,$[2]与$[1]比,$[1]与$[0]比; [12,23,34,(43),56] ,98,89 ... [12,23,34,43,56,(98)] ,89 ... [12,23,34,43,56,(89),98] ... ``` <?php $arr = Array(23,34,12,56,43,98,89); public function charu(&$arr){ //第一层,从第二个元素开始,一共进行n-1次循环 for($i=1;$i<count($arr);$i++){ //第二层,将第i个元素插入到左侧相应的位置 for($j=$i;$j>0;$j--){ //从第i个元素开始,与左侧元素依次比较,最小元素不断左移,直到结束 if($arr[$j]<$arr[$j-1]){ //满足条件,两个元素交换位置 $insertVal = $arr[$j]; $arr[$j] = $arr[$j-1]; $arr[$j-1] = $insertVal; } } } return $arr; } ``` ## 2.选择排序 在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。 代码规律分析: array(23,34,12,56,43,98,89) (12) ,34,[23],56,43,98,89 第一次大循环:选出12放在第一位置;将23放在12原位置 12,([23]) ,34,56,43,98,89 第二次大循环:选出23放在第二位置;恰好自身也在此位置 12,23,([34]) ,56,43,98,89 第三次大循环:选出34放在第二位置;恰好自身也在此位置 12,23,34,(43), [56],98,89 第四次大循环:选出43放在第二位置;将56放在43原位置 12,23,34,43,([56]) ,98,89 ... 12,23,34,43,56,(89) ,[98] ... 12,23,34,43,56,89,([98]) ... ``` <?php $arr = Array(23,34,12,56,43,98,89); public function xuanze($arr){ //定义进行交换的变量 $temp=0; for($i=0;$i<count($arr)-1;$i++){ //假设$i就是最小值 $valmin=$arr[$i]; //记录最小值的下标 $minkey=$i; for($j=$i+1;$j<count($arr);$j++){ //最小值大于后面的数就进行交换,并且记录下相应的下标 if($valmin>$arr[$j]){ $valmin=$arr[$j]; $minkey=$j; } } //将第i个元素和最小元素进行位置交换 $temp=$arr[$i]; $arr[$i]=$arr[$minkey]; $arr[$minkey]=$temp; } return $arr; } ``` ## 3.冒泡排序 冒泡排序的思想很简单,就是以此比较相邻的元素大小,将小的前移,大的后移,就像水中的气泡一样,最小的元素经过几次移动,会最终浮到水面上。 代码规律分析: array(23,34,12,56,43,98,89) 23,34,12,56,43,[89,98] 第一次小循环:89和98比较,交换位置 23,34,12,56,[43,89],98 第二次小循环:43和89比较,不用换位置 23,34,12,[43,56],89,98 第三次小循环:56和43比较,交换位置 23,34,[12,43],56,89,98 ... 23,[12,34],43,56,89,98 ... [12,23],34,43,56,89,98 ... 至此第一次大循环结束,做小的元素已经移到做左侧,按照同样原理进行第二次大循环.就像上面的列子,不用完整的循环n-1次,所以我们可以引入flag标记,进行优化. ``` <?php $arr = Array(23,34,12,56,43,98,89); public function maopao($arr) { $count = count($arr); //第一层循环开始 for($i=1; $i<$count; $i++) { //本趟排序开始前,交换标志应为假 $flag = false; //第二层循环,使右侧最小的元素移到最左侧 for($j=$count-1;$j>=$i;$j--) { if($arr[$j]<$arr[$j-1])//交换记录 {//如果是从大到小的话,只要在这里的判断改成if($arr[$j]>$arr[$j-1])就可以了 $x=$arr[$j]; $arr[$j]=$arr[$j-1]; $arr[$j-1]=$x; //发生了交换,故将交换标志置为真 $flag = true; } } //本趟排序未发生交换,提前终止算法 if(! $flag) return $arr; } } ``` ## 4.快速排序 所谓快速排序,就是在$arr数组中任意取一个值作为中间值,然后将这个值与数组内其他所有元素相比较,比这个值小的放到左边,比这个值大的放到这个值的右边, 此时完成一趟排序.以此类推,再将这个值左边的数进行上述排序,同样对右边的数进行上述排序.直到将所有的值都比较完. 首先使用快速排序的理念就需要使用递归,说到递归,就是用函数自己调用自己,逐层深入,最后将拿到的值返回的思想. 下面我们来看,首先我们需要自定义一个函数quick(),让此函数自己调用自己. 这里中间值我们用$arr[0],这个是随意取的,不是必须的, 为了让大家思路清晰,我们先定义两个空数组,就相当于两个杯子, 让$arr[0]与数组内所有的值进行比较,比$arr[0]小的值放到第一个杯子中,比$arr[0]大的放到第二个杯子中,在程序中分别为$left和$right, 我们判断,如果给定的数组元素只有一个或为空,此数组将被quick函数返回,不再执行下面的内容, 接下来我们对数组进行遍历,并用遍历出来的值分别与$arr[0]进行比较,比$arr[0]小的值存入$left数组,比$arr[0]大的的值存入$right数组,此时完成一趟排序, 接下来再对$left数组和$right数组进行同样的操作,也就是调用函数自己,将$left数组和$right数组传入,直到$left数组或$right数组的数组元素为一个时,不再调用自己,直接返回, 最后将左边的数组,$arr[0],$right数组合并,即可得到最终的排序结果. ``` <?php $arr = Array(23,34,12,56,43,98,89); public function kuaisu($arr){ $left = array(); $right = array(); if(count($arr)<=1){ return $arr; } for($i=1;$i<count($arr);$i++){ if($arr[0]>$arr[$i]){ $left[] = $arr[$i]; }else{ $right[] = $arr[$i]; } } $left = kuaisu($left); $right = kuaisu($right); return array_merge($left,array($arr[0]),$right); } ?> ```