目录
前言:
周的划分情况:
1.周的起始
2.周的跨年
3.一年有多少周?
Mysql的做法:
每周的第一天:
返回值区间:
如何得出返回值是1:
总结:
其他:
周检索和月检索是不一样的,月检索是超级简单。
但是周检索稍不注意,就会入坑,而且是很久之后才发现的,比如元旦。
在详细接触周检索之前,你需要了解一些关于周的知识。
每周的第一天是从周天算呢还是从周一算呢,这个比较坑的,各个国家有各个国家的国歌的。
如下图,2020年元旦的这一周包含几个周?
它会根据不同地区的习俗习惯包括且不局限于下面的情况:
1.包含一个周:2019.last 2019年的最后周
2.包含一个周:2020.1 2020年的第一个周
3.包含一个周:不按年划分,以周日为起点。
4.包含二个周:不按年划分,以周一为起点
5.包含三个周:既按年又按周一为第一天,分别是2019.last1 2019.last2 2020.1
6.包含三个周: 严格按照周一为第一天,分别是2019.last1 2019.last2 2020.0(因为2020年的第一个周一还没有出现)
....
....
有人会说是52周,这是常识。
有人会说53周,因为52*7=364,还不到365天,即使闰年366天,那么依然应该是53周。
但这都是不对的.我做了个图片。诸多情况,如下所示:
第一种情况:
如果2019.1.1是周一,那么到2019.12.30正好是周日,2019.12.31是新的周一。
为什么?因为365/7=52--1啊。它整好是周的倍数余1天。
那么蓝色代表第1周,橙色是52周,绿色是53周。
一共是53周。
第二种情况:
我们往后推2天,如果2019.1.1是周三,那么应该如图所示。和第一种情况一样。
绿色是53周,橙色是52周。
一共是53周。
第三种情况:
我们往前提一天。如果2019.1.1是周天。那么它和第一种情况正好相反,也肯定是53周。
此时黄色是第一周,橙色是53周。
一共是53周
第四种情况:
和第三种情况差不多,但如果2019年是个闰年,我们就会发现,已经有54周了,比如1916年。
所以,正确答案,应该是绝大部分是53周,如果是闰年且其首日是其所在周的最后一天时将达到54周。
~~~看完科普这么努力,也不给个赞吗?
由于周的划分还是相当的复杂的,mysql这块为了解决这个问题也是耗费了不少策略。
那么我们在mysql的解决上,最应该先关注的是WEEK()函数。
比如:week("2019-03-01") = 8 代表着第八周。
week表达式是这样的:week(date,[mode]);其中,date参数是日期,比如:“2019-01-01”,mode参数代表着如何判断周,分别是0~7mode参数可以省略。
mode代表的意思如下:
Mode | 每周的第一天 | 返回值区间 | 如何得出返回值是1 |
---|---|---|---|
0 | Sunday | 0-53 | 从第一个Sun算 |
1 | Monday | 0-53 | 第一个超过或等于四天的周 |
2 | Sunday | 1-53 | 从第一个Sun算 |
3 | Monday | 1-53 | 第一个超过或等于四天的周 |
4 | Sunday | 0-53 | 第一个超过或等于四天的周 |
5 | Monday | 0-53 | 从第一个MON算 |
6 | Sunday | 1-53 | 第一个超过或等于四天的周 |
7 | Monday | 1-53 | 从第一个MON算 |
这个很好理解。如果我们选择mode的话,在中国,一般会选择1357,而不是0246.
我们上面已经说过了,对于计算机而言,每年的周有53周,偶尔会来个54周。返回值0~53是很正常的。
可为什么会有1~53呢?这不是少一个吗?
那是因为内部的算法,即使在闰年且首日是其所在周的最后一天时,也会将首日或最后一日算到邻年里面,保证其最大值就是53。
不过这样的,就会产生不好的情况,和我们的需求可能不相符合,有时候我们怎能愿意将元旦统计到去年的数据里面呢?
所以这时我们可以过滤掉1~53的了。
那么最终剩下的就是1和5
要以此图为例
从第一个Sun算:
意思是从该年的第一个星期日开始才能算第一周。
那么像上面这张图,1号~5号算是第几周呢?
这还要根据返回值区间来算,如果区间是0~53,那么是第0周。如果是1~53,那么是去年的52~53周。
从第一个MON算:
跟从第一个Sun差不多
意思是从该年的第一个星期一开始才能算第一周。
那么像上面这张图,1号~6号算是第几周呢?
这还要根据返回值区间来算,如果区间是0~53,那么是第0周。如果是1~53,那么是去年的52~53周。
第一个超过或等于四天的周:
一个周有7天,不可能出现跨年时两年拥有本周的天数一致的情况,总有一个多一个少。如果某年拥有的天数超过4或者等于4的话,那么他所拥有的天数就多一些。
如果某年拥有跨年周天数多的话,则这是它的第一周。
如上图,第一行则是第1周,那么第一周的结束还要根据每周的第一天才能知道是截至到星期一还是星期天。
如果某年拥有的跨年周天数少呢?
那么下一周才是它的第一周,那么当前跨年这块是第几周呢?
这依然还要根据返回值区间来算,如果区间是0~53,那么是第0周。如果是1~53,那么是去年的52~53周。
而且第一周的结束也要根据每周的第一天 计算
我们可以做张表,来查看效果。下表是days表.有days和dis两个字段.
其中2019年拥有跨年周的天数较多,我在描述里写了是大,另外2019-01-06是周日,2019-01-07是周一.
其中2021年拥有跨年周的天数较少,我在描述里写了是小,.......
执行sql语句,依次往里面塞入mode分别为0~7
SELECT
days,dis,
WEEK(days,0) 0a,
WEEK(days,1) 1a,
WEEK(days,2) 2a,
WEEK(days,3) 3a,
WEEK(days,4) 4a,
WEEK(days,5) 5a,
WEEK(days,6) 6a,
WEEK(days,7) 7a
FROM days ORDER BY days
结果如下:
大家要重点关注1和5
可以发现区别.
当mode=1且新年跨年周是大周的时候,是1
当mode=5且新年跨年周是大周的时候,是0
我们再翻查mode表格,确实如表格记载.
现在我们有1和5两个选择,那么选择哪个呢?
我倾向于选择5,不必考虑大小周,就按周一来。当然有特殊需要的朋友可以选择其他的。
但是如果你服务端使用java编程的话,那么应该选择3,详情见:Calender关于周需要注意的地方
1.week函数可以省略mode,mode的默认值来自于系统变量default_week_format
而系统变量default_week_format默认是0.
也就是说,如果选择其他的,要么你更改系统变量,要么就别省略mode.
这里建议你永远别省略mode.因为你可能将来会换mysql,那时候忘了修改变量就坑了。
2.YEARWEEK(date,【mode】)这个和week()差不多,它返回的大概是201952这样的。
但是它的mode省略的话,默认值是0,而不是
default_week_format.不过实测它和WEEK返回的结果并不一样
3.DATE_FORMAT(date,format)函数关于周要注意的地方
它只有下面这些东西。其中%X是用来表示年的,和%V是一起使用的。
%U
00
..53
,周日是一周的第一天;WEEK()
模式0%u
00
..53
,周一是一周的第一天;WEEK()
模式1%V
01
..53
,周日是一周的第一天;WEEK()
模式2; 用于%X
%v
01
..53
,周一是一周的第一天;WEEK()
模式3; 用于%x
%X
星期日是第一天,用于 %V
%x
星期一是第一天, 用于 %v
因为没有mode=5,所以我是不乐意用的,有用的同学要注意了。
呼呼呼呼~~写完了。好累哦