计算当天为星期几

想知道某个日期是星期几时,不一定要打开日历才能找出来。只要学会以下这个方程式,任何日期都可以算出是星期几。

首先,让我们先看看以下这三个表格里面的代码。

| 星期 | 代码 |
| 星期日 | 0 |
| 星期一 | 1 |
| 星期二 | 2 |
| 星期三 | 3 |
| 星期四 | 4 |
| 星期五 | 5 |

| 星期六 | 6 |


| 月份 | 代码 |
| 一月 | 6 |
| 二月 | 2 |
| 三月 | 2 |
| 四月 | 5 |
| 五月 | 0 |
| 六月 | 3 |
| 七月 | 5 |
| 八月 | 1 |
| 九月 | 4 |
| 十月 | 6 |
| 十一月 | 2 |

| 十二月 | 4 |


| 闰年 | 代码 |
| 2000 | 0 |
| 2004 | 5 |
| 2008 | 10 |
| 2012 | 15 |
| 2016 | 20 |
| 2020 | 25 |

| 2024 | 30 |

首先,星期日到星期六分别用0-6来表示,而年份的话以2000年为基年,每加一年则+1(例:2001年的代码为0+1=1),而到了闰年则再+1(例:2004年的代码为0+4+1=5;计算2008年的代码则是用2004的代码5+4+1=10)以此类推。在这里比较困难的只是月份的代码,这里必须背的,将这些代码牢牢地记在脑海里。

第二点要记住的就是,因为一个礼拜有7天,凡是7的倍数都可以被拿掉。例如2008年的代码是10,拿掉7则剩下3;2024年的代码是30,拿掉7x4=28则剩下30-28=2。

▼以下图为例,我们这次要计算2069年7月20号是星期几。

7-20-2069
5 + 6 + 2 = 13
-7
___
6

星期六

为了算出2069年的代码,我们将69拆分成68+1。因为68是闰年,我们除以4可以得出2068是2000年后的第17个闰年。以17乘5我们得出85,再加上1我们可以得出2069年的代码为86。

(简地来说就是69=68+1=(4x17)+1。因为闰年要+1,所以变成[(4+1)x17]+1=86。)

接下来,因为7的倍数可以被拿掉,所以86除以7我们得出余数为2;相同地,20除以7的余数是6。然后根据上面的表格我们知道7月的代码是5。

要计算出某个日期是星期几,我们只需要将年份的代码+月份的代码+日期就可以算出了!首先我们用5+6+2=13,拿掉7后我们最终得出6这个数字。也就是说2069年7月20号是星期六!

看了以上的例子,相信这些简单的加法应该不会难倒你吧!接下来,我们要提醒大家,如果要计算的日期是闰年的1月或2月的话,此时代码就要-1。为了让大家更能了解,就让我们一起来算一算2004年2月18号是星期几吧。


2-18-2004
2 + 4 + 5 = 11

-7
-----
4-1
星期三

以上的算法只适用于2000年到2099年之间的任一日期。如果要算1999年之前或2099年之后的话,只需要再加上以下代码就可以算出正确的答案了。


| 年份 | 代码 |
| 2300到2399 | +1 |
| 2200到2299 | +3 |
| 2100到2199 | +5 |
| 2000到2099 | 0 |
| 1900到1999 | +1 |
| 1800到1899 | +3 |
| 1700到1799 | +5 |
| 1600到1699 | 0 |
| 1500到1599 | +1 |
| 1400到1499 | +3 |
| 1300到1399 | +5 |

| 1200到1299 | 0 |

这些数学小技巧是不是很神奇呢?尤其是最后一个,只需要加减乘除就可以算出某个日期是星期几。学校里完全没有教过这样的方式,也许大家可以拿去和数学老师讨论一下,说不定会被称赞呢。虽然数学还是有很多必须理解的观念,但多了一些小技巧,至少在计算上会快一点,现实生活中偶尔用到也是很实用的。赶快自己动手算一算,然后把这些学校没教的数学分享出去让大家知道吧!

正则表达式总结

正则表达式是匹配模式,要么匹配字符,要么匹配位置

字符匹配

内容包括:
1.两种模糊匹配
2.字符组
3.量词
4.分支结构
5.案例分析

1 两种模糊匹配

1.1 横向模糊匹配

一个正则可匹配的字符串的长度不是固定的,可以是多种情况的
var regex = /ab{2,5}c/g
var string = “abc abbc abbbc abbbbc abbbbbc abbbbbbc”;
console.log( string.match(regex) );
// => [“abbc”, “abbbc”, “abbbbc”, “abbbbbc”]

1.2 纵向模糊匹配

一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种可能。


var regex = /a[123]b/g;
var string = “a0b a1b a2b a3b a4b”;
console.log( string.match(regex) );

// => [“a1b”, “a2b”, “a3b”]

  1. 字符组 []
    2.1 范围表示法
    匹配“a”、“-”、“z”这三者中任意一个字符
    [-az]或[az-]或[a-z]

2.2 排除字符组
字符组的第一位放^(脱字符),表示求反的概念
[^abc],表示是一个除”a”、”b”、”c”之外的任意一个字符

2.3 常见的简写形式
\d 就是[0-9]。表示是一位数字。记忆方式:其英文是digit(数字)。
\D 就是[^0-9]。表示除数字外的任意字符。
\w 就是[0-9a-zA-Z_]。表示数字、大小写字母和下划线。记忆方式:w是word的简写,也称单词字符。
\W 是[^0-9a-zA-Z_]。非单词字符。
\s 是[ \t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。记忆方式:s是space character的首字母。
\S 是[^ \t\v\n\r\f]。 非空白符。
. 就是[^\n\r\u2028\u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。记忆方式:想想省略号…中的每个点,都可以理解成占位符,表示任何类似的东西。

字符组总结:

[\d\D]、[\w\W]、[\s\S]和[^]都可以实现匹配任意字符
  1. 量词

量词也称重复。掌握{m,n}的准确含义后,只需要记住一些简写形式。

3.1 简写形式
{m,} 表示至少出现m次。
{m} 等价于{m,m},表示出现m次。
? 等价于{0,1},表示出现或者不出现。记忆方式:问号的意思表示,有吗?

+        等价于{1,},表示出现至少一次。记忆方式:加号是追加的意思,得先有一个,然后才考虑追加。
*        等价于{0,},表示出现任意次,有可能不出现。记忆方式:看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。

3.2 贪婪匹配和惰性匹配
例子:

贪婪->它会尽可能多的匹配 (只要在能力范围内,越多越好)

var regex = /\d{2,5}/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) );
// => ["123", "1234", "12345", "12345"]

惰性->尽可能少的匹配

var regex = /\d{2,5}?/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) );
// => ["12", "12", "34", "12", "34", "12", "34", "56"]

惰性匹配总结:
    1.通过在量词后面加个问号就能实现惰性匹配,因此所有惰性匹配情形如下:
    {m,n}?
    {m,}?
    ??
    +?
    *?

    2.对惰性匹配的记忆方式是:量词后面加个问号,问一问你知足了吗,你很贪婪吗?
    3.分支也属于惰性
  1. 多选分支

    一个模式可以实现横向和纵向模糊匹配。而多选分支可以支持多个子模式任选其一。
    具体形式如下:(p1|p2|p3),其中p1、p2和p3是子模式,用|(管道符)分隔,表示其中任何之一

    例如要匹配”good”和”nice”可以使用/good|nice/。测试如下:


var regex = /good|nice/g;
var string = "good idea, nice try.";
console.log( string.match(regex) );
// => ["good", "nice"]

但有个事实我们应该注意,比如我用/good|goodbye/,去匹配"goodbye"字符串时,结果是"good":

var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["good"]

而把正则改成/goodbye|good/,结果是:

var regex = /goodbye|good/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["goodbye"]

也就是说,分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。
下面找几个例子演练一下(其中,每个正则并不是只有唯一写法):

  1. 案例分析

    匹配字符,无非就是字符组、量词和分支结构的组合使用罢了。

5.1 匹配16进制颜色值
要求匹配:

#ffbbad

#Fc01DF

#FFF

#ffE

var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
var string = “#ffbbad #Fc01DF #FFF #ffE”;
console.log( string.match(regex) );

// => [“#ffbbad”, “#Fc01DF”, “#FFF”, “#ffE”]

5.2 匹配时间
以24小时制为例。
要求匹配:
23:59

02:07

var regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/;
console.log( regex.test(“23:59”) );
console.log( regex.test(“02:07”) );
// => true

// => true

5.3 匹配日期
比如yyyy-mm-dd格式为例。
要求匹配:

2017-10-13

var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
console.log( regex.test(“2017-06-10”) );

// => true

5.4 window操作系统文件路径
要求匹配:
F:\study\javascript\regex\regular expression.pdf
F:\study\javascript\regex\
F:\study\javascript

F:\

var regex = /^[a-zA-Z]:\([^\<>|’’?\r\n/]+\)*([^\<>|’’?\r\n/]+)?$/;
console.log( regex.test(“F:\study\javascript\regex\regular expression.pdf”) );
console.log( regex.test(“F:\study\javascript\regex\“) );
console.log( regex.test(“F:\study\javascript”) );
console.log( regex.test(“F:\“) );
// => true
// => true
// => true

// => true

5.5 匹配id
要求从

提取出id=”container”

var regex = /id=”[^”]”/;

位置匹配

正则表达式是匹配模式,要么匹配字符,要么匹配位置。请记住这句话。

内容包括:
1.什么是位置?
2.如何匹配位置?
3.位置的特性
4.几个应用实例分析
  1. 什么是位置呢?
    位置是相邻字符之间的位置.
    如:HELLO
    |||||| 这里的一杠所致向的是相邻字符之间的位置.

  2. 如何匹配位置呢?

    在ES5中,共有6个锚字符:
    ^ $ \b \B (?=p) (?!p)

2.1 ^和$
^(脱字符)匹配开头,在多行匹配中匹配行开头。
$(美元符号)匹配结尾,在多行匹配中匹配行结尾。

比如我们把字符串的开头和结尾用”#”替换(位置可以替换成字符的!)

var regex = \^|$\g
var result = “hello”.replace(regex,”#”);
console.log(result);

// => “#hello#”

多行匹配模式时,二者是行的概念,这个需要我们的注意:

var result = “I\nlove\njavascript”.replace(/^|$/gm, ‘#’);
console.log(result);
/*

#I#

#love#

#javascript#

*/

2.2 \b和\B
\b是单词边界,具体就是\w和\W之间的位置,也包括\w和^之间的位置,也包括\w和$之间的位置。

比如一个文件名是"[JS] Lesson_01.mp4"中的\b,如下:

var result = “[JS] Lesson_01.mp4”.replace(/\b/g, ‘#’);
console.log(result);

// => “[#JS#] #Lesson_01#.#mp4#”

\B就是\b的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉\b,剩下的都是\B的。

比如上面的例子,把所有\B替换成"#":

var result = “[JS] Lesson_01.mp4”.replace(/\B/g, ‘#’);
console.log(result);

// => “#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4”

\b和\B总结:
\b -> 不连续单词边界
\B ->

2.3 (?=p)和(?!p)
(?=p),其中p是一个子模式,即p前面的位置。

比如(?=l),表示’l’字符前面的位置,例如:

var result = “hello”.replace(/(?=l)/g, ‘#’);
console.log(result);

// => “he#l#lo”

而(?!p)就是(?=p)的反面意思,比如:

var result = “hello”.replace(/(?!l)/g, ‘#’);
console.log(result);

// => “#h#ell#o#”

(?=p)和(?!p)总结:
-----------------------------------------------------------
|     分类依据     |      (?=p)         |    (?!p)
-----------------------------------------------------------
|     学名         | positive lookahead | negative lookahead
-----------------------------------------------------------
|     中文翻译     |  正向先行断言      |  负向先行断言
-----------------------------------------------------------
|     环视         |  看看右边          |  看看左边
-----------------------------------------------------------
|     (?=p),一般都理解成:要求接下来的字符与p匹配,但不能包括p的那些字符
|     而在本人看来(?=p)就与^一样好理解,就是p前面的那个位置
-----------------------------------------------------------
  1. 位置的特性

    对于位置的理解,我们可以理解成空字符””

    比如”hello”字符串等价于如下的形式:
    “hello” == “” + “h” + “” + “e” + “” + “l” + “” + “l” + “o” + “”;
    也等价于:
    “hello” == “” + “” + “hello”

因此,把/^hello$/写成/^^hello$$$/,是没有任何问题的:

var result = /^^hello$$$/.test(“hello”);
console.log(result);

// => true

甚至可以写成更复杂的:

var result = /(?=he)^^he(?=\w)llo$\b\b$/.test(“hello”);
console.log(result);

// => true

  1. 相关案例

4.1 不匹配任何东西的正则

.^\

因为此正则要求只有一个字符,但该字符后面是开头。

4.2 数字的千位分隔符表示法
比如把”12345678”,变成”12,345,678”。
可见是需要把相应的位置替换成”,”。

4.2.1 弄出最后一个逗号

var result = “12345678”.replace(/(?=\d{3}$)/g, ‘,’)
console.log(result);

// => “12345,678”

4.2.2 弄出所有的逗号

var result = “123456789”.replace(/(?=(\d{3})+$)/g, ‘,’)
console.log(result);

// => “,123,456,789”

因为上面的正则,仅仅表示把从结尾向前数,一但是3的倍数,就把其前面的位置替换成逗号。因此才会出现这个问题。
怎么解决呢?我们要求匹配的到这个位置不能是开头。
我们知道匹配开头可以使用^,但要求这个位置不是开头怎么办?


var string1 = “12345678”,
string2 = “123456789”;
var reg = /(?!^)(?=(\d{3})+$)/g;

var result = string1.replace(reg, ‘,’)
console.log(result);
// => “12,345,678”

result = string2.replace(reg, ‘,’);
console.log(result);

// => “123,456,789”

4.2.4 支持其他形式
如果要把”12345678 123456789”替换成”12,345,678 123,456,789”。

此时我们需要修改正则,把里面的开头^和结尾$,替换成\b:

var string = “12345678 123456789”,
reg = /(?!\b)(?=(\d{3})+\b)/g; 或/\B(?=(\d{3})+\b)/g
var result = string.replace(reg, ‘,’)
console.log(result);

// => “12,345,678 123,456,789”

4.3 验证密码问题
1.同时包含数字和小写字母
2.同时包含数字和大写字母
3.同时包含小写字母和大写字母
4.同时包含数字、小写字母和大写字母

以上的4种情况是或的关系(实际上,可以不用第4条)。

var reg = /((?=.[0-9])(?=.[a-z])|(?=.[0-9])(?=.[A-Z])|(?=.[a-z])(?=.[A-Z]))^[0-9A-Za-z]{6,12}$/;
console.log( reg.test(“1234567”) ); // false 全是数字
console.log( reg.test(“abcdef”) ); // false 全是小写字母
console.log( reg.test(“ABCDEFGH”) ); // false 全是大写字母
console.log( reg.test(“ab23C”) ); // false 不足6位
console.log( reg.test(“ABCDEF234”) ); // true 大写字母和数字

console.log( reg.test(“abcdEF234”) ); // true 三者都有

理解:
上面的正则看起来比较复杂,只要理解了第二步,其余就全部理解了。
/(?=.[0-9])^[0-9A-Za-z]{6,12}$/
对于这个正则,我们只需要弄明白(?=.
[0-9])^即可。
分开来看就是(?=.[0-9])和^。
表示开头前面还有个位置(当然也是开头,即同一个位置,想想之前的空字符类比)。
(?=.
[0-9])表示该位置后面的字符匹配.*[0-9],即,有任何多个任意字符,后面再跟个数字。
翻译成大白话,就是接下来的字符,必须包含个数字。

另外一种解法
“至少包含两种字符”的意思就是说,不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。
那么要求“不能全部都是数字”,怎么做呢?(?!p)出马!
对应的正则是:
var reg = /(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$/;

优化后的正则为:

var reg = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/;
console.log( reg.test(“1234567”) ); // false 全是数字
console.log( reg.test(“abcdef”) ); // false 全是小写字母
console.log( reg.test(“ABCDEFGH”) ); // false 全是大写字母
console.log( reg.test(“ab23C”) ); // false 不足6位
console.log( reg.test(“ABCDEF234”) ); // true 大写字母和数字

console.log( reg.test(“abcdEF234”) ); // true 三者都有

括号的作用

不管哪门语言中都有括号。正则表达式也是一门语言,而括号的存在使这门语言更为强大。
对括号的使用是否得心应手,是衡量对正则的掌握水平的一个侧面标准。
括号的作用,其实三言两语就能说明白,括号提供了分组,便于我们引用它。
引用某个分组,会有两种情形:在JavaScript里引用它,在正则表达式里引用它。
本章内容虽相对简单,但我也要写长点。

内容包括:
1.分组和分支结构
2.捕获分组
3.反向引用
4.非捕获分组
5.相关案例
  1. 分组和分支结

1.1 分组
我们知道/a+/匹配连续出现的“a”,而要匹配连续出现的“ab”时,需要使用/(ab)+/。

其中括号是提供分组功能,使量词+作用于“ab”这个整体,测试如下:

var regex = /(ab)+/g;
var string = “ababa abbb ababab”;
console.log( string.match(regex) );

// => [“abab”, “ab”, “ababab”]

1.2 分支结构
在多选分支结构(p1|p2)中,此处括号的作用也是不言而喻的,提供了子表达式的所有可能。
比如,要匹配如下的字符串:
I love JavaScript
I love Regular Expression

可以使用正则:

var regex = /^I love (JavaScript|Regular Expression)$/;
console.log( regex.test(“I love JavaScript”) );
console.log( regex.test(“I love Regular Expression”) );
// => true

// => true

  1. 引用分组
    这是括号一个重要的作用,有了它,我们就可以进行数据提取,以及更强大的替换操作。
    而要使用它带来的好处,必须配合使用实现环境的API。
    以日期为例。假设格式是yyyy-mm-dd的,我们可以先写一个简单的正则:

var regex = /\d{4}-\d{2}-\d{2}/;

然后再修改成括号版的:


var regex = /(\d{4})-(\d{2})-(\d{2})/;

为什么要使用这个正则呢?

2.1 提取数据

比如提取出年、月、日,可以这么做:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = “2017-06-12”;
console.log( string.match(regex) );

// => [“2017-06-12”, “2017”, “06”, “12”, index: 0, input: “2017-06-12”]

对比:

var regex = /\d{4}-\d{2}-\d{2}/;
var string = “2017-06-12”;
console.log( string.match(regex) );

// => [“2017-06-12”, index: 0, input: “2017-06-12”]

match返回的一个数组,第一个元素是整体匹配结果,然后是各个分组(括号里)匹配的内容,然后是匹配下标,最后是输入的文本。(注意:如果正则是否有修饰符g,match返回的数组格式是不一样的)。

另外也可以使用正则对象的exec方法:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = “2017-06-12”;
console.log( regex.exec(string) );

// => [“2017-06-12”, “2017”, “06”, “12”, index: 0, input: “2017-06-12”]

同时,也可以使用构造函数的全局属性$1至$9来获取:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = “2017-06-12”;

regex.test(string); // 正则操作即可,例如
//regex.exec(string);
//string.match(regex);

console.log(RegExp.$1); // “2017”
console.log(RegExp.$2); // “06”

console.log(RegExp.$3); // “12”

2.2 替换

比如,想把yyyy-mm-dd格式,替换成mm/dd/yyyy怎么做?

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = “2017-06-12”;
var result = string.replace(regex, “$2/$3/$1”);
console.log(result);

// => “06/12/2017”

其中replace中的,第二个参数里用$1、$2、$3指代相应的分组。等价于如下的形式:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = “2017-06-12”;
var result = string.replace(regex, function() {
return RegExp.$2 + “/“ + RegExp.$3 + “/“ + RegExp.$1;
});
console.log(result);

// => “06/12/2017”

也等价于:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = “2017-06-12”;
var result = string.replace(regex, function(match, year, month, day) {
return month + “/“ + day + “/“ + year;
});
console.log(result);

// => “06/12/2017”

  1. 反向引用
    除了使用相应API来引用分组,也可以在正则本身里引用分组。但只能引用之前出现的分组,即反向引用。
    还是以日期为例。
    比如要写一个正则支持匹配如下三种格式:
    2016-06-12
    2016/06/12
    2016.06.12
    最先可能想到的正则是:

var regex = /\d{4}(-|\/|.)\d{2}(-|\/|.)\d{2}/;
var string1 = “2017-06-12”;
var string2 = “2017/06/12”;
var string3 = “2017.06.12”;
var string4 = “2016-06/12”;
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true

console.log( regex.test(string4) ); // true

其中/和.需要转义。虽然匹配了要求的情况,但也匹配”2016-06/12”这样的数据。

假设我们想要求分割符前后一致怎么办?此时需要使用反向引用:

var regex = /\d{4}(-|\/|.)\d{2}\1\d{2}/;
var string1 = “2017-06-12”;
var string2 = “2017/06/12”;
var string3 = “2017.06.12”;
var string4 = “2016-06/12”;
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true

console.log( regex.test(string4) ); // false

注意里面的\1,表示的引用之前的那个分组(-|\/|.)。不管它匹配到什么(比如-),\1都匹配那个同样的具体某个字符。
我们知道了\1的含义后,那么\2和\3的概念也就理解了,即分别指代第二个和第三个分组。
看到这里,此时,恐怕你会有三个问题。