日期居然用字符串保存我笑了

微信公众号「后端进阶」,专注后端技术分享:Java、Golang、WEB框架、分布式中间件、服务治理等等。 老司机倾囊相授,带你一路进阶,来不及解释了快上车!我发现数据库有些日期居然用字符串保存?于是跟几个小伙伴讨论了关于数据库的日期应该要怎么保存的问题,其实我一直都建议直接用数值保存时间戳,为什么我要这么建议呢? 以下,我会从时区的概念来跟你们解释一下,为什么用数值保存时间戳是最好的方案,同时也为了分享出来,让更多开发小伙伴留意这些细节性的东西。 相信时区对于很多人来说的很熟悉,因为地球是圆的,在地球上不同角落看到的太阳上升的角度都是不同的,即每个人对于时间的显示都是不一样的, 举个例子: 此时处于东 8 区的我们北京时间是 10 点,那么处于东 1 区的时间就是 3 点,但是他们的时间是等价的: "2019-06-20 10:00 +8:00" = "2019-06-20 3:00 +1:00"所以说,对于不同时区的人来说,显示的时间是不一样的,那么此时你是如何将将时间保存到数据中的呢? 我姑且假设你用的是 new Date() 方法来保存当时日期,但据我所知道的,数据库的 DateTime 类型是没有时区信息的,如果你此时用 DateTime 格式保存日期,就会丢失时区信息,如果你的服务器更该地址,从数据库读出来的日期数据就是错误的! 可能你会说,那我用 timeStamp 类型保存总不会丢失时区信息了吧?确实没丢失,没毛病。但是据我所知道的,timeStamp 保存的时间最长不能超过 2037 年,而且你要考虑每个数据的 timeStamp 类型都有可能不一样。 至于用字符串来存储时间,就更加不推荐了,姑且不从时区来说,你比较日期大小也是个问题,我举个例子: to_char(SYSDATE, 'yyyy-MM-dd') > START_TIME要比较一个时间大小,我需要这么做,还需要将系统时间转成字符串来给你对比,而且在转换成字符串比较时,数据库内部也会将其转换成时间来比较,你觉得这种查询条件会好到哪里去? 我们也知道在 JDK8 中新的时间 API LocalDateTime 中,有着丰富的时区转换的方法可用,但即便你说你精通 LocalDateTime 的各种花式用法,你也不得不面对繁杂的转换。 所以,我们需要一个拥有「绝对是时间」,来帮助我们记录日期,帮我们节省下转换的时间,这个「绝对时间」就是时间戳,时间戳的定义是从一个基准时间开始算起,这个基准时间是「1970-1-1 00:00:00 +0:00」,从这个时间开始,用整数表示,以秒计时,随着时间的流逝这个时间整数不断增加。这样一来,我只需要一个数值,就可以完美地表示时间了,而且这个数值是一个绝对数值,即无论的身处地球的任何角落,这个表示时间的时间戳,都是一样的,生成的数值都是一样的,并且没有时区的概念,所以在系统的中时间的传输中,都不需要进行额外的转换了,只有在显示给用户的时候,才转换为字符串格式的本地时间。 而且很重要的一点就是,在现有的编程语言中,都提供了方法来获取时间戳,这对于我们不同语言的项目交互来说,不要太方便!所以在这里我强烈建议前后端关于时间的交互,都用时间戳来交互。 这时,可能有同学又来杠一波,你用一个出数值来表示时间,我查数据库时,以我的眼力和口算,根本不知道时间是多少,我觉得这个根本不需要担心啊,你查数据库无非是查看需要的数据而已,你在 sql 里面对时间戳个时间转换函数就好了,比如: from_unixtime(1561053690000)如果你还要继续杠,说我就是要在数据库表中看到时间,我觉得如果你要这样,为什么还需要前端,直接拿数据库当前端展示就好了。 我总结一下数据库用数值保存时间戳的诸多好处: 在数据库中日期比较不要太方便,小学一年级就会的数学题,而且性能好;数值对于任何系统交互来说都不存在障碍;基于绝对时间的数值存储,不存在时区问题;在交互过程中,摒弃没必要的重重转换,一个数字走天下,用户需要显示,前端只需要拿到时间戳显示正确的本地时间;解决了由于各个数据库对于时间实现的不一样导致的问题,比如说 Mysql 的时间函数跟 Oracle 会有一些差别,假如你现在的 sql 有时间函数,换了数据库很可能就会出错。 ...

June 24, 2019 · 1 min · jiezi

JavaScript-系列JavaScript一些奇淫技巧的实现方法一简短的sleep函数获取时间戳

一、前言有些东西很好用,但是你未必知道;有些东西你可能用过,但是你未必知道原理。实现一个目的有多种途径,俗话说,条条大路通罗马。发散一下大家的思维以及拓展一下知识面。 二、实现一个简短的sleep函数sleep函数主要用来做延迟执行的,很多编程语言都有sleep函数,但是javascript没有这个函数,我们实现一下: 1、简单版本function sleep(sleepTime){ for(var start = +new Date;+new Date - start<sleepTime;){}}var t1 = +new Date();sleep(3000);var t2 = +new Date();console.log(t2-t1);优点:简单粗暴,通俗易懂。 缺点:确实sleep了,但是卡死了,cpu会飙升,精确度不准 2、promise版本// promise版本function sleep(sleepTime){ return new Promise(resolve => setTimeout(resolve,sleepTime));}var t1 = +new Date();sleep(3000).then(()=>{ var t2 = +new Date(); console.log(t2-t1);})优点:实际上用了setTimeout,没有形成进程阻塞,不会造成性能和负载问题。 缺点:虽然解决了回调函数的嵌套,但是还是不美观,而且异步不彻底,过程中停止执行。 3、generator版本// generotor版本function sleep(sleepTime){ return function(cb){ setTimeout(cb.bind(this), sleepTime); }}function* genSleep(){ var t1 = +new Date(); yield sleep(3000); var t2 = +new Date(); console.log(t2-t1);}async(genSleep);function async(gen){ const iter = gen(); function nextStep(it){ if(it.done) return ; if (typeof it.value === "function") { it.value(function(ret) { nextStep(iter.next(ret)) }) } else { nextStep(iter.next(it.value)); } } nextStep(iter.next());}优点:跟promise一样优点,代码变得更简单干净。 ...

May 31, 2019 · 2 min · jiezi

Golang 日期/时间包的使用

golang 的日期时间包:time 的使用方式。time package 包含了 time.Time 时间对象 及 构建此时间对象的一些方法(time.Unix(), time.Parse())golang 可精确到 nanosecond,故相应的函数返回值或参数都已纳秒为单位,我们可以借助time.ParseDuration(durationString string)友好的生成纳秒度量的时间跨度值golang 的时间格式串Layout固定为 2006-01-02 15:04:05golang 默认使用 Local 即本地时区,可以通过 time.LoadLocation(zoneName string) (*Location, error)来设定时区时区构建/格式化模式串// 构建时区var timeLocation *time.LocationtimeLocation, _ = time.LoadLocation("") //UTCtimeLocation, _ = time.LoadLocation(“UTC”) //UTCtimeLocation, _ = time.LoadLocation(“Local”) //LocaltimeLocation, _ = time.LoadLocation(“Asia/Shanghai”) //使用时区码//golang的时间格式化patternvar timeLayout = “2006-01-02 15:04:05"当前时间对象// 获取当前时间对象var timer time.Timetimer = time.Now()// 为时间设定时区 可以通过 timer.Local()/timer.UTC() 快速设定时区timer.In(timeLocation)获取秒级时间戳/纳秒级时间戳// 获取当前秒级时间戳var curTimestamp int64curTimestamp = timer.Unix()println(“current timestamp:” + strconv.FormatInt(curTimestamp, 10))// 获取当前纳秒及时间戳 1秒=1000毫秒=1000,000微妙=1000,000,000纳秒var curNanoTimestamp int64curNanoTimestamp = timer.UnixNano()println(“current nano timestamp:” + strconv.FormatInt(curNanoTimestamp, 10))获取本地时间的时区/CST标准时间/自定义格式// 获取本地时间的时区/快速获取时区时间/自定义格式timeZone, _ := timer.Zone()fmt.Printf(“time zone: %s\n”, timeZone)fmt.Printf(“time location: %s\n”, timer.Location())fmt.Printf(“time in local zone: %s\n”, timer.Local().String())fmt.Printf(“time in utc zone: %s\n”, timer.UTC().String())fmt.Printf(“time: %s\n”, timer.String())fmt.Printf(“time formatted: %s\n”, timer.Format(“2006-01-02 15:04:05”))获取当前的年/月/日 时:分:秒 纳秒// 获取当前的年/月/日 时:分:秒 纳秒fmt.Printf(“current year: %d\n”, timer.Year())fmt.Printf(“current month: %d %s\n”, timer.Month(), timer.Month()) //返回的Month对象fmt.Printf(“current day: %d\n”, timer.Day())fmt.Printf(“current hour: %d\n”, timer.Hour())fmt.Printf(“current minute: %d\n”, timer.Minute())fmt.Printf(“current second: %d\n”, timer.Second())fmt.Printf(“current nanosecond: %d\n”, timer.Nanosecond())获取当前时间/日期// 获取当前时间/日期curHour, curMinute, curSecond := timer.Clock()fmt.Printf(“current clock: %d:%02d:%02d\n”, curHour, curMinute, curSecond)curYear, curMonth, curDay := timer.Date()fmt.Printf(“current date: %d-%02d-%02d\n”, curYear, curMonth, curDay)编辑时间/求两个日期的时间差time.ParseDuration(durationString string)可以方便我们使用语义构建时间跨度值,数值单位为纳秒,比如:timeDuration, _ := time.ParseDuration(“24h”)timeDuration, _ := time.ParseDuration(“12m”)timeDuration, _ := time.ParseDuration(“6s”)timeDuration, _ := time.ParseDuration(“1ms”)timeDuration, _ := time.ParseDuration(“1us”)timeDuration, _ := time.ParseDuration(“1ns”)// 已当前时间为基增长年/月/日后的时间对象timerAdded := timer.AddDate(1, 2, 3)curYear, curMonth, curDay = timerAdded.Date()fmt.Printf(“current date: %d-%02d-%02d\n”, curYear, curMonth, curDay)// 以当前时间为基增长N纳秒后的时间对象 比如增长了一天timeDuration, _ := time.ParseDuration(“24h”)timerAdded = timer.Add(timeDuration)// 计算两个时间的差值 返回的是纳秒 按需求自行计算其他单位// Duration is type of int64 and nanosecondstimeDuration = timerAdded.Sub(timer)fmt.Printf(“days duration between %s~%s: %d\n”, timerAdded.Format(timeLayout), timer.Format(timeLayout), timeDuration/1000/1000/1000/24/60/60)使用 时间字符串 / Unix Timestamp 构建时间对象// 使用时间串获取时间对象timer, _ = time.Parse(timeLayout, “2018-08-08 08:08:08”)// 使用时间串获取时间对象 并设定时区timer, _ = time.ParseInLocation(timeLayout, “2018-08-08 08:08:08”, timeLocation)// 使用Unix时间戳构建时间对象timer = time.Unix(1552368806, 0) //2019-03-12 13:33:26的Unix时间戳fmt.Println(timer.Format(timeLayout))获取当前时间是本年第几天 本周第几天注意周日 的 Weekday编号为 0// 获取当前时间是本年第几天 本周第几天fmt.Printf(“year day: %d, week day: %d\n”, timer.YearDay(), timer.Weekday())使用表征字符串转换时间跨度// 使用表征字符串转换时间跨度timeDuration, _ = time.ParseDuration(“300s”)fmt.Printf(“nanosecond: %d\n”, timeDuration)timeDuration, _ = time.ParseDuration(“300us”)fmt.Printf(“nanosecond: %d\n”, timeDuration) ...

March 12, 2019 · 2 min · jiezi