mysql 中 时间和日期函数

转自:http://www.cnblogs.com/redfox241/archive/2009/07/23/1529092.html

一、MySQL 获得当前日期时间 函数

1.1 获得当前日期+时间(date + time)函数:now()

mysql> select now();

+——————-+
| now() |
+——————-+
| 2008-08-08 22:20:46 |
+——————-+

除了 now() 函数能获得当前的日期时间外,MySQL 中还有下面的函数:

current_timestamp()
,
current_timestamp
,localtime()
,localtime
,localtimestamp
(v4.0.6)
,localtimestamp() (v4.0.6)

这些日期时间函数,都等同于 now()。鉴于 now() 函数简短易记,建议总是使用 now() 来替代上面列出的函数。

1.2 获得当前日期+时间(date + time)函数:sysdate()

sysdate() 日期时间函数跟 now() 类似,不同之处在于:now() 在执行开始时值就得到了, sysdate() 在函数执行时动态得到值。看下面的例子就明白了:

mysql> select now(), sleep(3), now();

+——————-+———-+———————+
| now() | sleep(3) | now() |
+——————-+———-+———————+
| 2008-08-08 22:28:21 | 0 | 2008-08-08 22:28:21 |
+——————-+———-+———————+

mysql
> select sysdate(), sleep(3), sysdate();

+——————-+———-+———————+
| sysdate() | sleep(3) | sysdate() |
+——————-+———-+———————+
| 2008-08-08 22:28:41 | 0 | 2008-08-08 22:28:44 |
+——————-+———-+———————+

可以看到,虽然中途 sleep
3 秒,但 now() 函数两次的时间值是相同的; sysdate() 函数两次得到的时间值相差 3 秒。MySQL Manual 中是这样描述 sysdate() 的:Return the time at which the function executes。

sysdate() 日期时间函数,一般情况下很少用到。

2. 获得当前日期(date)函数:curdate()

mysql> select curdate();

+———-+
| curdate() |
+———-+
| 2008-08-08 |
+———-+

其中,下面的两个日期函数等同于 curdate():

current_date()
,
current_date

3. 获得当前时间(time)函数:curtime()

mysql> select curtime();

+———+
| curtime() |
+———+
| 22:41:30 |
+———+

其中,下面的两个时间函数等同于 curtime():

current_time()
,
current_time

4. 获得当前 UTC 日期时间函数:utc_date(), utc_time(), utc_timestamp()

mysql> select utc_timestamp(), utc_date(), utc_time(), now()

+——————-+————+————+———————+
| utc_timestamp() | utc_date() | utc_time() | now() |
+——————-+————+————+———————+
| 2008-08-08 14:47:11 | 2008-08-08 | 14:47:11 | 2008-08-08 22:47:11 |
+——————-+————+————+———————+

因为我国位于东八时区,所以本地时间
= UTC 时间 + 8 小时。UTC 时间在业务涉及多个国家和地区的时候,非常有用。
二、MySQL 日期时间 Extract(选取) 函数。

1. 选取日期时间的各个部分:日期、时间、年、季度、月、日、小时、分钟、秒、微秒

set @dt = 2008-09-10 07:15:30.123456;

select date(@dt); 2008-09-10
select time(@dt); 07:15:30.123456
select year(@dt); 2008
select quarter(@dt); 3
select month(@dt); 9
select week(@dt); 36
select day(@dt); 10
select hour(@dt); 7
select minute(@dt); 15
select second(@dt); 30
select microsecond(@dt); 123456

2. MySQL Extract() 函数,可以上面实现类似的功能:

set @dt = 2008-09-10 07:15:30.123456;

select extract(year from @dt); 2008
select extract(quarter from @dt); 3
select extract(month from @dt); 9
select extract(week from @dt); 36
select extract(day from @dt); 10
select extract(hour from @dt); 7
select extract(minute from @dt); 15
select extract(second from @dt); 30
select extract(microsecond from @dt); 123456

select extract(year_month from @dt); 200809
select extract(day_hour from @dt); 1007
select extract(day_minute from @dt); 100715
select extract(day_second from @dt); 10071530
select extract(day_microsecond from @dt); 10071530123456
select extract(hour_minute from @dt); 715
select extract(hour_second from @dt); 71530
select extract(hour_microsecond from @dt); 71530123456
select extract(minute_second from @dt); 1530
select extract(minute_microsecond from @dt); 1530123456
select extract(second_microsecond from @dt); 30123456

MySQL Extract() 函数除了没有date(),time() 的功能外,其他功能一应具全。并且还具有选取‘day_microsecond’ 等功能。注意这里不是只选取
day 和 microsecond,而是从日期的 day 部分一直选取到 microsecond 部分。够强悍的吧!

MySQL Extract() 函数唯一不好的地方在于:你需要多敲几次键盘。

3. MySQL dayof… 函数:dayofweek(), dayofmonth(), dayofyear()

分别返回日期参数,在一周、一月、一年中的位置。

set @dt = 2008-08-08;

select dayofweek(@dt); 6
select dayofmonth(@dt); 8
select dayofyear(@dt); 221

日期 ‘
2008-08-08′ 是一周中的第 6 天(1 = Sunday, 2 = Monday, …, 7 = Saturday);一月中的第 8 天;一年中的第 221 天。

4. MySQL week… 函数:week(), weekofyear(), dayofweek(), weekday(), yearweek()

set @dt = 2008-08-08;

select week(@dt); 31
select week(@dt,3); 32
select weekofyear(@dt); 32

select dayofweek(@dt); 6
select weekday(@dt); 4

select yearweek(@dt); 200831

MySQL week() 函数,可以有两个参数,具体可看手册。 weekofyear() 和 week() 一样,都是计算“某天”是位于一年中的第几周。 weekofyear(
@dt) 等价于 week(@dt,3)。

MySQL weekday() 函数和 dayofweek() 类似,都是返回“某天”在一周中的位置。不同点在于参考的标准, weekday:(0 = Monday, 1 = Tuesday, …, 6 = Sunday); dayofweek:(1 = Sunday, 2 = Monday, …, 7 = Saturday)

MySQL yearweek() 函数,返回 year(2008) + week 位置(31)。

5. MySQL 返回星期和月份名称函数:dayname(), monthname()

set @dt = 2008-08-08;

select dayname(@dt); Friday
select monthname(@dt); August

思考,如何返回中文的名称呢?

6. MySQL last_day() 函数:返回月份中的最后一天。

select last_day(2008-02-01); 2008-02-29
select last_day(2008-08-08); 2008-08-31

MySQL last_day() 函数非常有用,比如我想得到当前月份中有多少天,可以这样来计算:

mysql> select now(), day(last_day(now())) as days;

+——————-+——+
| now() | days |
+——————-+——+
| 2008-08-09 11:45:45 | 31 |
+——————-+——+

三、MySQL 日期时间计算函数

1. MySQL 为日期增加一个时间间隔:date_add()

set @dt = now();

select date_add(@dt, interval 1 day); add 1 day
select date_add(@dt, interval 1 hour); add 1 hour
select date_add(@dt, interval 1 minute);
select date_add(@dt, interval 1 second);
select date_add(@dt, interval 1 microsecond);
select date_add(@dt, interval 1 week);
select date_add(@dt, interval 1 month);
select date_add(@dt, interval 1 quarter);
select date_add(@dt, interval 1 year);

select date_add(@dt, interval -1 day); sub 1 day

MySQL adddate(), addtime()函数,可以用 date_add() 来替代。下面是 date_add() 实现 addtime() 功能示例:

mysql> set @dt = 2008-08-09 12:12:33;

mysql>
mysql
> select date_add(@dt, interval 01:15:30 hour_second);

+———————————————-+
| date_add(@dt, interval 01:15:30 hour_second) |
+———————————————-+
| 2008-08-09 13:28:03 |
+———————————————-+

mysql
> select date_add(@dt, interval 1 01:15:30 day_second);

+———————————————–+
| date_add(@dt, interval 1 01:15:30 day_second) |
+———————————————–+
| 2008-08-10 13:28:03 |
+———————————————–+

date_add() 函数,分别为
@dt 增加了“1小时 15分 30秒” 和 “1天 1小时 15分 30秒”。建议:总是使用 date_add() 日期时间函数来替代 adddate(), addtime()。

2. MySQL 为日期减去一个时间间隔:date_sub()

mysql> select date_sub(1998-01-01 00:00:00, interval 1 1:1:1 day_second);

+————————————————————–+
| date_sub(1998-01-01 00:00:00, interval 1 1:1:1 day_second) |
+————————————————————–+
| 1997-12-30 22:58:59 |
+————————————————————–+

MySQL date_sub() 日期时间函数 和 date_add() 用法一致,不再赘述。另外,MySQL 中还有两个函数 subdate(), subtime(),建议,用 date_sub() 来替代。

3. MySQL 另类日期函数:period_add(P,N), period_diff(P1,P2)

函数参数“P” 的格式为“YYYYMM” 或者 “YYMM”,第二个参数“N” 表示增加或减去 N month(月)。

MySQL period_add(P,N):日期加/减去N月。

mysql> select period_add(200808,2), period_add(20080808,-2)

+——————–+————————-+
| period_add(200808,2) | period_add(20080808,-2) |
+——————–+————————-+
| 200810 | 20080806 |
+——————–+————————-+

MySQL period_diff(P1,P2):日期 P1
-P2,返回 N 个月。

mysql> select period_diff(200808, 200801);

+—————————+
| period_diff(200808, 200801) |
+—————————+
| 7 |
+—————————+

在 MySQL 中,这两个日期函数,一般情况下很少用到。

4. MySQL 日期、时间相减函数:datediff(date1,date2), timediff(time1,time2)

MySQL datediff(date1,date2):两个日期相减 date1 - date2,返回天数。

select datediff(2008-08-08, 2008-08-01); 7
select datediff(2008-08-01, 2008-08-08); -7

MySQL timediff(time1,time2):两个日期相减 time1
- time2,返回 time 差值。

select timediff(2008-08-08 08:08:08, 2008-08-08 00:00:00); 08:08:08
select timediff(08:08:08, 00:00:00); 08:08:08

注意:timediff(time1,time2) 函数的两个参数类型必须相同。
四、MySQL 日期转换函数、时间转换函数

1. MySQL (时间、秒)转换函数:time_to_sec(time), sec_to_time(seconds)

select time_to_sec(01:00:05); 3605
select sec_to_time(3605); ’01:00:05′

2. MySQL (日期、天数)转换函数:to_days(date), from_days(days)

select to_days(0000-00-00); 0
select to_days(2008-08-08); 733627

select from_days(0); ’0000-00-00′
select from_days(733627); ’2008-08-08′

3. MySQL Str to Date (字符串转换为日期)函数:str_to_date(str, format)

select str_to_date(08/09/2008, %m/%d/%Y); 2008-08-09
select str_to_date(08/09/08 , %m/%d/%y); 2008-08-09
select str_to_date(08.09.2008, %m.%d.%Y); 2008-08-09
select str_to_date(08:09:30, %h:%i:%s); 08:09:30
select str_to_date(08.09.2008 08:09:30, %m.%d.%Y %h:%i:%s); 2008-08-09 08:09:30

可以看到,str_to_date(
str,format) 转换函数,可以把一些杂乱无章的字符串转换为日期格式。另外,它也可以转换为时间。“format” 可以参看 MySQL 手册。

4. MySQL Date/Time to Str(日期/时间转换为字符串)函数:date_format(date,format), time_format(time,format)

mysql> select date_format(2008-08-08 22:23:00, %W %M %Y);

+———————————————-+
| date_format(2008-08-08 22:23:00, %W %M %Y) |
+———————————————-+
| Friday August 2008 |
+———————————————-+

mysql
> select date_format(2008-08-08 22:23:01, %Y%m%d%H%i%s);

+————————————————–+
| date_format(2008-08-08 22:23:01, %Y%m%d%H%i%s) |
+————————————————–+
| 20080808222301 |
+————————————————–+

mysql
> select time_format(22:23:01, %H.%i.%s);

+———————————–+
| time_format(22:23:01, %H.%i.%s) |
+———————————–+
| 22.23.01 |
+———————————–+

MySQL 日期、时间转换函数:date_format(date,format), time_format(time,format) 能够把一个日期
/时间转换成各种各样的字符串格式。它是 str_to_date(str,format) 函数的 一个逆转换。

5. MySQL 获得国家地区时间格式函数:get_format()

MySQL get_format() 语法:

get_format(date|time|datetime, eur|usa|jis|iso|internal

MySQL get_format() 用法的全部示例:

select get_format(date,usa)          ; ‘%m.%d.%Y’
select get_format(date,jis)          ; ‘%Y-%m-%d’
select get_format(date,iso)          ; ‘%Y-%m-%d’
select get_format(date,eur)          ; ‘%d.%m.%Y’
select get_format(date,internal)     ; ‘%Y%m%d’
select get_format(datetime,usa)      ; ‘%Y-%m-%d %H.%i.%s’
select get_format(datetime,jis)      ; ‘%Y-%m-%d %H:%i:%s’
select get_format(datetime,iso)      ; ‘%Y-%m-%d %H:%i:%s’
select get_format(datetime,eur)      ; ‘%Y-%m-%d %H.%i.%s’
select get_format(datetime,internal) ; ‘%Y%m%d%H%i%s’
select get_format(time,usa)          ; ‘%h:%i:%s %p’
select get_format(time,jis)          ; ‘%H:%i:%s’
select get_format(time,iso)          ; ‘%H:%i:%s’
select get_format(time,eur)          ; ‘%H.%i.%s’
select get_format(time,internal)     ; ‘%H%i%s’

MySQL get_format() 函数在实际中用到机会的比较少。

6. MySQL 拼凑日期、时间函数:makdedate(year,dayofyear), maketime(hour,minute,second)

select makedate(2001,31); ’2001-01-31′
select makedate(2001,32); ’2001-02-01′

select maketime(12,15,30); ’12:15:30′

五、MySQL 时间戳(
Timestamp)函数

1. MySQL 获得当前时间戳函数:current_timestamp, current_timestamp()

mysql> select current_timestamp, current_timestamp();

+——————-+———————+
| current_timestamp | current_timestamp() |
+——————-+———————+
| 2008-08-09 23:22:24 | 2008-08-09 23:22:24 |
+——————-+———————+

2. MySQL (Unix 时间戳、日期)转换函数:

unix_timestamp(),
unix_timestamp(date),
from_unixtime(unix_timestamp),
from_unixtime(unix_timestamp,format)

下面是示例:

select unix_timestamp(); 1218290027
select unix_timestamp(2008-08-08); 1218124800
select unix_timestamp(2008-08-08 12:30:00); 1218169800

select from_unixtime(1218290027); ’2008-08-09 21:53:47′
select from_unixtime(1218124800); ’2008-08-08 00:00:00′
select from_unixtime(1218169800); ’2008-08-08 12:30:00′

select from_unixtime(1218169800, %Y %D %M %h:%i:%s %x); ’2008 8th August 12:30:00 2008′

3. MySQL 时间戳(timestamp)转换、增、减函数:

timestamp(date) date to timestamp
timestamp(dt,time) dt + time
timestampadd(unit,interval,datetime_expr)
timestampdiff(unit,datetime_expr1,datetime_expr2)

请看示例部分:

select timestamp(2008-08-08); 2008-08-08 00:00:00
select timestamp(2008-08-08 08:00:00, 01:01:01); 2008-08-08 09:01:01
select timestamp(2008-08-08 08:00:00, 10 01:01:01); 2008-08-18 09:01:01

select timestampadd(day, 1, 2008-08-08 08:00:00); 2008-08-09 08:00:00
select date_add(2008-08-08 08:00:00, interval 1 day); 2008-08-09 08:00:00

MySQL timestampadd() 函数类似于 date_add()。

select timestampdiff(year,2002-05-01,2001-01-01); -1
select timestampdiff(day ,2002-05-01,2001-01-01); -485
select timestampdiff(hour,2008-08-08 12:00:00,2008-08-08 00:00:00); -12

select datediff(2008-08-08 12:00:00, 2008-08-01 00:00:00); 7

MySQL timestampdiff() 函数就比
datediff() 功能强多了,datediff() 只能计算两个日期(date)之间相差的天数。
六、MySQL 时区(timezone)转换函数

convert_tz(dt,from_tz,to_tz)

select convert_tz(2008-08-08 12:00:00, +08:00, +00:00); 2008-08-08 04:00:00

时区转换也可以通过 date_add, date_sub, timestampadd 来实现。

select date_add(2008-08-08 12:00:00, interval -8 hour); 2008-08-08 04:00:00
select date_sub(2008-08-08 12:00:00, interval 8 hour); 2008-08-08 04:00:00
select timestampadd(hour, -8, 2008-08-08 12:00:00); 2008-08-08 04:00:00

发表在 数据库 | 标签为 | 留下评论

HTML5设计原理[转]

转自:http://www.cn-cuckoo.com/2010/10/21/the-design-of-html5-2151.html

Jeremy Keith在 Fronteers 2010 上的主题演讲

今天我想跟大家谈一谈HTML5的设计。主要分两个方面:一方面,当然了,就是HTML5。我可以站在这儿只讲HTML5,但我并不打算这样做,因为如果你想了解HTML5的话,你可以Google,可以看书,甚至可以看规范。

实际上,确实有人会谈到规范的内容。史蒂夫·福克纳(Steve Faulkner)会讲HTML5与可访问性。而保罗·艾里什(Paul Irish)则会讲HTML5提供的各种API。因此,我今天站在这里,不会光讲一讲HTML5就算完事了。

说老实话,在正式开始之前,我想先交待清楚我所说的HTML5到底是什么意思。这话听起来有点搞笑:这会子你一直在说HTML5,难道我们还不知道什么是HTML5吗?大家知道,有一个规范,它的名字叫HTML5。我所说的HTML5,指的就是这个规范。但问题是,有些人所说的HTML5,指的不仅仅是这个规范,还有别的意思。比如说,用HTML5来代指CSS3就是一种常见的叫法。我可不是这样的。我所说的HTML5,不包含CSS3,就是HTML5。

类似的术语问题以前也有过。Ajax本来是一种含义明确的技术,但过了不久,它的含义就变成了“用JavaScript来做一切好玩的东西”。这就是Ajax,对不对?今天,HTML5也面临同样的问题,它本来指的是一个特定的规范,但如今含义却成了“在Web上做一切好玩的事。”我说的不是这种HTML5,不是这种涵盖了最近刚刚出现的各种新东东的HTML5。我说的仅仅是规范本身:HTML5。

刚才已经说了,我今天想要讲的内容不多,也没有打算介绍HTML5都包含什么。今天我要讲的是它的另一方面,即HTML5的设计。换句话说,我要讲的不是规范里都包含什么,而是规范里为什么会包含它们,以及在设计这个规范的时候,设计者们是怎么看待这些东西的。

设计原理

设计原理本质上是一种信念、一种想法、一个概念,是你行动的支柱。不管你是制定规范,还是制造一种有形的物品,或者编写软件,甚至发明编程语言。你都能找到背后的一个或者多个设计原理,多人协作的任何成果都是例证。不仅仅Web开发领域是这样。纵观人类历史,像国家和社会这样大规模的构建活动背后,同样也有设计原理。

就拿美国为例吧,美国的设计原理都写在了《独立宣言》中了。

我们认为这些真理是不言而喻的,人人生而平等,造物主赋予了每个人不可剥夺的权利,包括生存、自由和追求幸福。

这里有一句口号:生存、自由和追求幸福。这是被写进宪法中的核心理念,它关系到我们所有人的一切,也就是我们构建自己社会的原则。

还有一个例子,就是卡尔·马克思(Karl Marx),他的著作在20世纪曾被奉为建设社会主义的圭臬。其基本思想大致可以归结为下面这条设计原理:

各尽所能,各取所需。

这其实就是一种经济体系背后的设计原理。

还有一个例子,比前面两个的历史更久远一些,不过大同小异:

人人为我,我为人人。

这个极为简单的设计原理,是两千年前的拿撒勒犹太人耶稣基督提出来的。而这条原则成为了后来许多宗教的核心教义。原理与实践有时候并不是同步的。

下面是小说中的一个例子。英国小说家乔治·奥威尔(George Orwell)笔下的《动物庄园》,就是在一条设计原理的基础上构建起来的虚拟社会。这条设计原理是:

四条腿的都是好人,两条腿的都是坏蛋!

《动物庄园》中有意思的是,随着社会的变迁——变得越来越坏,这条设计原理也跟着发生了改变,变成了“四条腿的都是好人,两条腿的就更好了。”最关键的是,即使是在虚构的作品里,设计原理都是存在的。

还有一套虚构的作品是以三条设计原理为基础构建起来的,那就是美国著名小说家艾萨克·阿西莫夫(Issac Asimov)的机器人经典系列。阿西莫夫发明了机器人学这个术语,并提出了机器人学三大法则,然后在这三个简单的设计原理基础上创作了一系列经典作品——大约有50本书。无论作品的情节如何变化,实际上都是从不同的角度来阐释这三大设计原理。我想,在座各位对机器人三大法则都不应该陌生。

机器人不得伤害人类,或袖手旁观人类受伤害。
机器人必须服从人类命令,除非命令违反第一法则。
机器人必须自卫,只要不违背第一和第二法则。

这些恐怕是第一次出现在小说中的针对软件的设计原理了。虽然基于这三个设计原理的软件运行在虚构的机器人的“正电子脑”中,但我想这应该是软件设计原理的事实开端。从此以后,我们才看到大量优秀软件背后的设计原理。

蒂姆·伯纳斯-李(Tim Berners-Lee),Web的发明者,在W3C的网站上发表过一份文档,其中有一个URL给出了他自己的一套设计原理。这些设计原理并不那么容易理解,不仅多,而且随着时时间推移,他还会不断补充、修改和删除。不过我还是觉得把自己认同的设计原理写出来放在某个地方真是个不错的主意。

实际上,CSS的发明人之一伯特·波斯(Bert Bos),也在W3C的网站上放着一份文档,其中讲的都是基本的设计原理,比如怎样设计并构建一种格式,无论是CSS还是其他格式。推荐大家看一看。

只要你在W3C的站点中随便找一找,就可以发现非常多的这种设计原理,包括蒂姆·伯纳斯-李个人的。当然,你还会看到他从软件工程学校里借用的一些口号:分权(decentalisation)、容忍(tolerance)、简易(simplicity)、模块化(modularity)。这些都是在他发明新格式的时候,头脑中无时无刻不在想的那些关键词。

在座各位对蒂姆·伯纳斯-李的贡献都是非常熟悉的,因为大家每天都在用。他发明了Web,与罗伯特·卡里奥(Robert Cailliau)共同发明了Web,而且在发明Web的同时,也发明了我们每天都在Web上使用的语言。当然,这门语言就是HTML:超文本标记语言。

HTML

HTML最早是从2.0版开始的。从来就没有1.0版。如果有人告诉你说,他最早是从HTML 1.0开始使用HTML的,那他绝对是在忽悠你。从前确实有一个名叫HTML Tags的文档,其中的部分标签一直用到现在,但那个文档并非官方的规范。

使用标签、尖括号、p或h1,等等,并不是蒂姆·伯纳斯-李首创的想法。当时的SGML里就有了这些概念,而且当时的CERN(Conseil Europeen pour la Recherche Nucleaire,欧洲核子研究委员会)也在使用SGML的一个特定的版本。也就是说,即便在那个时代,他也没有白手起家;这一点在HTML后来的发展过程中也体现了出来:继往开来、承前启后,而不是另立门户、从头开始。

换句话说,这篇名为HTML Tags的文档可以算作HTML的第一个版本,但它却不是一个正式的版本。第一个正式版本,HTML 2.0,也不是出自W3C之手。HTML 2.0是由IETF,因特网工程任务组(Internet Engineering Task Force)制定的。在W3C成立之前,IETF已经发布了不少标准。但从第三个版本开始往后,W3C,万维网联盟(World Wide Web Consortium)开始接手,并负责后续版本的制定工作。

20世纪九十年代HTML有过几次快速的发展。众所周知,在那个时代要想构建网站,可是一项十分复杂的工程。浏览器大战曾令人头疼不已。市场竞争的结果就是各家浏览器里都塞满了各种专有的特性,都试图在专有特性上胜人一筹。当时的混乱程度不堪回首,HTML到底还重不重要,或者它作为Web格式的前景如何,谁都说不清楚。

从1997年到1999年,HTML的版本从3.2到4.0到4.01,经历了非常快的发展。问题是到了4.01的时候,W3C的认识发生了倒退,他们说“好了,这个版本就这样了,HTML也就这样了;HTML 4.01是HTML的最后一个版本了,我们用不着HTML工作组了。”

W3C并没有停止开发这门语言,只不过他们对HTML不再感兴趣了。在HTML 4.01之后,他们提出了XHTML 1.0。虽然听起来完全不同,但XHTML 1.0与HTML 4.01其实是一样的。我的意思是说,从字面上看这两个规范的内容是一样的,词汇表是一样的,所有的元素是一样,所有的属性也都是一样的。唯一一点不同之处,就是XHTML 1.0要求使用XML语法。也就是说,所有属性都必须使用小写字母,所有元素也必须使用小写字母,所有属性值都必须加引号,你还得记着使用结束标签,记着对img和br要使用自结束标签。

从规范本身的内容来看,实际上是相同的,没有什么不同。不同之处就是编码风格,因为对浏览器来说,读取符合HTML 4.01、HTML 3.2,或者XHTML 1.0规范的网页都没有问题,对浏览器来说这些网页都是一样的,都会生成相同的DOM树。只不过人们会比较喜欢XHTML 1.0,因为不少人认同它比较严格的编码风格。

到了2000年,Web标准项目(Web Standards Project)的活动开展得如火如荼,开发人员对浏览器里包含的那些乱七八糟的专有特性已经忍无可忍了。大家都很生气,就骂那些浏览器厂商“遵守个规范就他妈的真有那么难吗?”当时CSS有了长足的发展,而且与XHTML 1.0结合得也很紧密,CSS加XHTML 1.0基本上就可以算是“最佳实践”了。虽然在我看来HTML 4.01与XHTML 1.0没有本质上的不同,但大家都接受了。专业的开发人员能做到元素全部小写,属性全部小写,属性值也全部加引号:由于专业人员起到了模范带头作用,越来越多的人也都开始支持这种语法。

我就是一个例子!过去的10年,我一直都使用XHTML 1.0文档类型,原因是这样一来验证器就能给我帮上很大的忙,对不对?只要我写的是XHTML 1.0,然后用验证器测试,它就能告诉我是不是忘了给属性值加引号,是不是没有结束某个标签,等等等等。而如果我写的是HTML 4.01,同样的问题就变成了有效的了,验证器就不一定会提醒我了。

这就是我一直使用XHTML 1.0的原因。我估计很多人都……使用XHTML 1.0的朋友,请把手举起来。好的。HTML 4.01呢?人少多了。一直没有举手的呢,大声点,你们用什么?HTML5,也很好!更早的呢,还有人使用更早的文档类型吗?没有了?

10年来我一直使用XHTML 1.0,就是因为验证器能够真正帮到我。有人用XHTML 1.1吗?你知道有人用吗?请举手,别放下。有人把网页标记为XML文档吗?有吗?那你们使用的就不是XHTML 1.1。

这就是个大问题。XHTML 1.0之后是XHTML 1.1,只是小数点后面的数字加了一个1,而且从词汇表的角度看,规范本身没有什么新东西,元素也都相同,属性也都相同。但对XHTML 1.1来说,唯一的变化是你必须把自己的文档标记为XML文档。在使用XHTML 1.0的时候,还可以把文档标记为HTML,而我们也正是这样做的,否则把文档标记为XML没准真会把人逼疯的。

为什么这么说呢?首先,把文档标记为XML后,Internet Explorer不能处理。当然,IE9是可以处理了。恐怕有人会讲“真是太可爱了”,他们到现在居然都没有忘了这件事。这艘船终于靠岸了!不过那时候,作为全球领先的浏览器,IE无法处理接收到的XML文档类型的文档,而规范又要求你以XML文档类型来发送文档,这不把人逼疯才怪呢。

所以说XHTML 1.1有点脱离现实,而你不想把文档以XML格式发送给那些能够理解XML的浏览器,则是因为XML的错误处理模型。XML的语法,无论是属性小写,元素小写,还是始终要给属性值加引号,这些都没有问题,都很好,事实上我也喜欢这样做,但XML的错误处理模型却是这样的:解析器如果遇到错误,停止解析。规范里就是这么写的。如果你把XHTML 1.1标记为XML文档类型,假设你用Firefox打开这个文档,而文档中有一个和号(&)没有正确编码,就算整个页面中就这一处错误,你看到的也将是黄屏,浏览器死掉了。Firefox会说:“没戏了,页面中有一个错误,你看不到这个网页了。”根据XML规范,这样处理是正确的,对Firefox而言,遇到错误就停止解析,并且不呈现其他任何内容是严格按照XML规范做的。因为它不是HTML,HTML根本就没有错误处理模型,但根据XML规范,这样做没错。

这就是为什么你不会把文档标记为XML的另一个原因。接下来,新的版本是XHTML 2,大家注意后面没有日期,因为这个规范并没有完成。

现在就说说XHTML 2,我很愿意把问题说清楚,XHTML 2实际上真是一个非常非常好的规范,确实非常好……从理论的角度来说。我的意思是说,制定这个规范的人都是非常非常有头脑的。直说吧,领导制定这个规范的家伙是斯蒂芬·彭伯顿(Stephen Pemberton),他应该是本地人,是一个聪明过人的家伙。规范本身也很了不起,如果所有人都同意使用的话,也一定是一个非常好的格式。只不过,还不够实际。

首先,XHTML 2仍然使用XML错误处理模型,你必须保证以XML文档类型发送文档;这一点不言自明:没人愿意这样做。其次,XHTML 2有意不再向后兼容已有的HTML的各个版本。他们甚至曾经讨论过废除img元素,这对每天都在做Web开发的人来说确实有点疯了的味道。但我们知道,他们之所以这样做,理论上确实有充足的理由——使用object元素可能会更好。

因此,无论XHTML 2在理论上是多么完美的一种格式,但却从未有机会付诸实践。而之所以难以将其付诸实践,就是因为像你我这样的开发人员永远不会支持它,它不向后兼容。同样,浏览器厂商也不会,浏览器厂商必须要保证向后兼容。

为什么XHTML 1.1没有像XML那样得到真正广泛地应用,为什么XHTML 2从未落到实处?因为它违反了一条设计原理,这条设计原理就是著名的伯斯塔尔法则(Postel’s Law)。大家都知道:

发送时要保守;接收时要开放。

没错,接收的时候要开放,而这也正是Web得以构建的基础。开发浏览器的人必须敞开胸怀,接收所有发送给浏览器的东西,因为它们过去一直都在接收那些不够标准的东西,对不对?Web上的很多文档都不规范,但那正是Web发展的动力。从某种角度讲,Web走的正是一条混沌发展之路,虽然混沌,但却非常美丽诱人。在Web上,格式不规范的文档随处可见,但那又怎样呢?如果所有人都能够写出精准的XML,所有文档的格式都十分正确,那当然好了。可是,那不现实。现实是伯斯塔尔法则。

作为专业人士,在发送文档的时候,我们会尽量保守一些,尽量采用最佳实践,尽量确保文档格式良好。但从浏览器的角度说,它们必须以开放的姿态去接收任何文档。

有人可能会说XML有错误处理模型,XHTML 1.1和XHTML 2都使用该模型,但那个错误处理模型太苛刻了。它绝对不符合接收时开放这个法则,遇到一个错误就停止解析怎么能叫开放呢?我们只能说它与健壮性法则(也就是伯斯塔尔法则)是对立的。

HTML5

之后,就到了HTML5,但HTML5并不是由W3C直接制定的。故事的经过是这样的,到20世纪末的时候,还没有HTML工作组,W3C内部的一些人就开始琢磨了,“HTML也许还可以更长寿一点,只要我们对它稍加扩展就行了。只要把我们放在XHTML上的时间和精力拿出一部分来,就可以提升一下HTML中的表单,可以让HTML更接近编程语言,就可以让它更上一层楼。”

于是,在2004年W3C成员内部的一次研讨会上,当时Opera公司的代表伊恩·希克森(Ian Hickson)提出了一个扩展和改进HTML的建议。他建议新任务组可以跟XHTML 2并行,但是在已有HTML的基础上开展工作,目标是对HTML进行扩展。W3C投票表决的结果是——“反对”,因为HTML已经死了,XHTML 2才是未来的方向。然后,Opera、Apple等浏览器厂商,以及其他一些成员说:“那好吧,不指望他们了,我们自已一样可以做这件事,我们脱离W3C。”他们成立了Web Hypertext Applications Technology Working Group(Web超文本应用技术工作组,WHATWG)——可巧的是,他们自称工作组,而不是特别小组(task force),这就为HTML5将来的命运埋下了伏笔。

WHATWG决定完全脱离W3C,在HTML的基础上开展工作,向其中添加一些新东西。这个工作组的成员里有浏览器厂商,因此他们不仅可以说加就加,而且还能够一一实现。结果,大家不断提出一些好点子,并且逐一做到了浏览器中。

WHATWG的工作效率很高,不久就初见成效。在此期间,W3C的XHTML 2没有什么实质性的进展。特别是,如果从实现的角度来说,用原地踏步形容似乎也不为过。

结果,一件有意思的事情发生了。那是在2006年,蒂姆·伯纳斯-李写了一篇博客,说:“你们知道吗?我们错了。我们错在企图一夜之间就让Web跨入XML时代,我们的想法太不切实际了,是的,也许我们应该重新组建HTML工作组了。”善哉斯言,后来的故事情节果真就是这样发展的。W3C在2007年组建了HTML5工作组。这个工作组面临的第一个问题,毫无疑问就是“我们是从头开始做起呢,还是在2004年成立的那个叫WHATWG的工作组既有成果的基础上开始工作呢?”答案是显而易见的,他们当然希望从已经取得的成果着手,以之为基础展开工作。于是他们又投了一次票,同意“在WHATWG工作成果的基础上继续开展工作”。好了,这下他们要跟WHATWG并肩战斗了。

第二个问题就是如何理顺两个工作组之间的关系。W3C这个工作组的编辑应该由谁担任?是不是还让WHATWG的编辑,也就是现在Google的伊恩·希克森来兼任?于是他们又投了一次票,赞成“让伊恩·希克森担任W3C HTML5规范的编辑,同时兼任WHATWG的编辑,更有助于新工作组开展工作。”

这就是他们投票的结果,也就是我们今天看到的局面:一种格式,两个版本。WHATWG的网站上有这个规范,而W3C的站点上同样也有一份。

如果你不了解内情,很可能会产生这样的疑问:“哪个版本才是真正的规范?”当然,这两个版本内容是一样的……基本上相同。实际上,这两个版本将来还会分道扬镳。现在已经有了分道扬镳的迹象了。我的意思是说,W3C最终要制定一个具体的规范,这个规范会成为一个工作草案,定格在某个历史时刻。

而WHATWG呢,他们还在不断地迭代。即使目前我们说的HTML5,也不能完全涵盖WHATWG正在从事的工作。最准确的理解是他们正在开发一项简单的HTML或Web技术,因为这才是他们工作的核心目标。然而,同时存在两个这样的工作组,这两个工作组同时开发一个基本相同的规范,这无论如何也容易让人产生误解。误解就可能造成麻烦。

其实这两个工作组背后各自有各自的流程,因为它们的理念完全不同。在WHATWG,可以说是一种独裁的工作机制。我刚才说了,伊恩·希克森是编辑。他会听取各方意见,在所有成员各抒己见,充分陈述自己的观点之后,他批准自己认为正确的意见。

W3C则截然相反,可以说是一种民主的工作机制。所有成员都可以发表意见,而且每个人都有投票表决的权利。这个流程的关键在于投票表决。从表面上看,WHATWG的工作机制让人不好接受。岂止是不好接受,简直是历史的倒退。相信谁都会认为“运作任何项目都不能采取这种方式!”

W3C的工作机制听起来让人很舒服。至少体现了人人平等嘛。但在实践中,WHATWG的工作机制运行得非常非常好。我认为之所以会这样,主要归功于伊恩·希克森。他的的确确是一个非常称职的编辑。他在听取各方意见时,始终可以做到丝毫不带个人感情色彩。

从原理上讲,W3C的工作机制很公平,而实际上却非常容易在某些流程或环节上卡壳,造成工作停滞不前,一件事情要达成决议往往需要花费很长时间。那到底哪种工作机制最好呢?我认为,最好的工作机制是将二者结合起来。而事实也是两个规范制定主体在共同制定一份相同的规范,我想,这倒是非常有利于两种工作机制相互取长补短。

两个工作组之所以能够同心同德,主要原因是HTML5的设计思想。因为他们从一开始就确定了设计HTML5所要坚持的原则。结果,我们不仅看到了一份规范,也就是W3C站点上公布的那份文档,即HTML5语言规范,还在W3C站点上看到了另一份文档,也就是HTML设计原理。而这份文档的一位编辑今天也来到了我们大会的现场,她就是安妮·奇泰丝(Anne Van Kesteren)。如果大家对这份文档有问题,可以请教安妮。

这份文档非常好,真的非常出色。这份文档,可以说见证了W3C与WHATWG同心协力共谋发展的历程。难道你们不觉得他们像是一对欢喜冤家吗?那他们还怎么同心同德呢?这份文档忠实地记录了他们一道做了什么,他们共同拥护什么。

接下来,我想要讲的就是这份文档。因为,既然他们能就这份文档达成共识,那么我相信,HTML5必将是一个伟大的规范,而他们已经认可这就是他们的共同行动纲领。为此,你才会看到诸如兼容性、实用性、互用性之类的概念。即便W3C与WHATWG之间再有多大的分歧——确实相当多——至少他们还有这份文档中记录的共识。这一点才是至关重要的。正因为他们有了共识,才有了这份基于共识描述设计原理的文档。

避免不必要的复杂性

下面我就给大家介绍一些这份文档中记载的设计原理。第一个,非常简单:避免不必要的复杂性。好像很简单吧。我用一个例子来说明。

假设我使用HTML 4.01规范,我打开文档,输入doctype。这里有人记得HTML 4.01的doctype吗?好,没有,我猜没有。除非……我的意思是说,你是傻冒。现场恐怕真有人背过,这就是HTML 4.01的doctype:

1 <!DOCTYPE html PUBLIC "-//W3C/DTD HTML 4.01//EN"

我不记这个两行代码,不然还要记事本、要Google、要模板有什么用呢?

要是我使用XHTML 1.0呢,这个规范我都已经用了10年了。有谁记得住这个doctype吗?没错,它的长度跟HTML 4.01的差不太多:

1 <!DOCTYPE html PUBLIC "-//W3C/DTD XHTML 1.0 Strict//EN"

是不是,基本上相同。它要告诉浏览器的是:这个文档是XHTML 1.0的文档。那么在HTML 5中,省掉不必要的复杂性,doctype就简化成了:

1 <!DOCTYPE html>

仅此而已。好了,就连我也能过目不忘了。我用不着把这几个字符记在记事本里了。我得说,在我第一次看到这个doctype的时候——我当然以为这是一个HTML文档的doctype——被它吓了一跳:“是不是还少一个数字5啊?”我心里想:“这个doctype想告诉浏览器什么呢?就说这个文档是HTML吗?难道这是有史以来唯一一个HTML版本吗,这件事我得首先搞清楚,HTML今后永远不会再有新版本了吗?”好一副唯我独尊的架式!我错了,因为这个doctype并没有这个意思。为此,必须先搞清楚为什么文档一开头就要写doctype。它不是写给浏览器看的。Doctype是写给验证器看的。也就是说,我之所以要在文档一开头写那行XHTML 1.0的doctype,是为了告诉验证器,让验证器按照该doctype来验证我的文档。

浏览器反倒无所谓了。假设我写的是HTML 3.2文档,文档开头写的是HTML 3.2的doctype。而在文档中某个地方,我使用了HTML 4.01中才出现的一个元素。浏览器会怎么处理这种情况?它会因为这个元素出现在比doctype声明的HTML版本更晚的规范中,就不解释呈现该元素吗?不会,当然不会!它照样会解释呈现该元素,别忘了伯斯塔尔法则,别忘了健壮性。浏览器在接收的时候必须要开放。因此,它不会检查任何格式类型,而验证器会,验证器才关心格式类型。这才是存在doctype的真正原因。

而按照HTML5的另一个设计原理,它必须向前向后兼容,兼容未来的HTML版本——不管是HTML6、HTML7,还是其他什么——都要与当前的HTML版本,HTML5,兼容。因此,把一个版本号放在doctype里面没有多大的意义,即使对验器证也一样。

刚才,我说doctype不是为浏览器写的,这样说大多数情况下没有问题。在有一种情况下,你使用的doctype会影响到浏览器,相信在座诸位也都知道。但在这种情况下,Doctype并非真正用得其所,而只是为了达到某种特殊的目的才使用doctype。当初微软在引入CSS的时候,走在了标准的前头,他们率先在浏览器中支持CSS,也推出了自己的盒模型——后来标准发布了,但标准中使用了不一样的盒模型。他们怎么办?他们想支持标准,但也想向后兼容自己过去推出的编码方式。他们怎么知道网页作者想使用标准,还是想使用他们过去的方式?

于是,他们想出了一个非常巧妙的主意。那就是利用doctype,利用有效的doctype来触发标准模式,而不是兼容模型(quiks mode)。这个主意非常巧妙。我们今天也都是这样在做,在我们向文档中加入doctype时,就相当于声明了“我想使用标准模式”,但这并不是发明doctype的本意。这只是为了达到特殊的目的在利用doctype。

下面我出一道有奖抢答题,听好:“一分钟后开始,如果你手快的话,第一个在文档前面写完doctype html,然后我用Internet Explorer打开你的文档,会触发它的标准模式,还是会触发它的兼容模式?”

答案是,这是在Internet Explorer中触发标准模式的最少字符数目。我认为这也说明了HTML5规范的本质:它不追求理论上的完美。HTML5所体现的不是“噢,给作者一个简短好记的doctype不好吗?”,没错,简短好记是很好,但如果这个好记的doctype无法适应现有的浏览器,还不如把它忘了更好。因此,这个平衡把握得非常好,不仅理论上看是个好主意——简短好记的doctype,而且实践中同样也是个好主意——仍然可以触发标准模式。应该说,Doctype是一个非常典型的例子。

还有一个例子,同样可以说明规范是如何省略不必要的复杂性,避免不必要的复杂性的。如果前面的文档使用的是HTML 4.01,假设我要指定文档的字符编码。理想的方式,是通过服务器在头部信息中发送字符编码,不过也可以在文档这个级别上指定:

1 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

同样,我也不会把这行代码背下来。我还想省下自己的脑细胞去记点别的更有价值的东西呢。不过,如果我想指定文档使用UTF-8编码,只能添加这行代码。这是在HTML 4.01中需要这样做。要是你在XHTML 1.0指定同样的编码,就得多敲一下键盘,因为你还得声明meta元素位于一个开始的XML标签中。

1 <?xml version="1.0" encoding="UTF-8" ?>
2 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

在HTML5中,你要敲的字符只有:

1 <meta charset="utf-8">

简短好记。我能背下来。

同样,这样写也是有效的。它不仅适用于最新版本的浏览器,只要是今天还有人在用的浏览器都同样有效。为什么?因为在我们把这些meta元素输入浏览器时,浏览器会这样解释它:“元数据(meta)点点点点点,字符集(charset)utf-8。”这就是浏览器在解释那行字符串时真正看到的内容。它必须看到这些内容,根据就是伯斯塔尔法则,对不对?

我多次提到健壮性原理,但总有人不理解。我们换一种说法,浏览器会想“好,我觉得作者是想要指定一个字符集……看,没错,utf-8。”这些都是规范里明文规定的。如今,不仅那个斜杠可以省了,而且总共只要写meta charset=”utf-8″就行了。

关于省略不必要的复杂性,或者说避免不必要的复杂性的例子还有不少。但关键是既能避免不必要的复杂性,还不会妨碍在现有浏览器中使用。比如说,在HTML5中,如果我使用link元素链接到一个样式表,我说了rel=”stylesheet”,然后再说type=”text/css”,那就是重复自己了。对浏览器而言,我就是在重复自己。浏览器用不着同时看到这两个属性。浏览器只要看到rel=”stylesheet”就够了,因为它可以猜出来你要链接的是一个CSS样式表。所以就不用再指定type属性了。你不是已经说了这是一个样式表了嘛;不用再说第二次了。当然,愿意的话,你可以再说;如果你想包含type属性,请便。

同样地,如果你使用了script元素,你说type=”text/javascript”,浏览器差不多就知道是怎么回事了。对Web开发而言,你还使用其他的脚本语言吗?如果你真想用其他脚本语言,没人会阻拦你。但我要奉劝你一句,任何浏览器都不会支持你。

愿意的话,你可以添加一个type属性。不过,也可以什么都不写,浏览器自然会假设你在使用JavaScript。避免-不必要的-复杂性。

支持已有的内容

支持已有的内容。这一点非常重要,因为很多人都认为HTML5很新,很闪亮;它应该代表着未来发展的方向,应该把Web推向一个新的发展阶段。这就是HTML5,对吗?显然,我们都会考虑让Web的未来发展得更好,但他们则必须考虑过去。别忘了W3C这个工作组中有很多人代表的是浏览器厂商,他们肯定是要考虑支持已有内容的。只要你想构建一款浏览器,就必须记住这个原则:必须支持已有的内容。

下面我们就来看一个HTML5支持已有内容的例子。

这个例子展示了编写同样内容的四种不同方式。上面是一个img元素,下面是带一个属性的段落元素。四种写法唯一的不同点就是语法。把其中任何一段代码交给浏览器,浏览器都会生成相同的DOM树,没有任何问题。从浏览器的角度看,这四种写法没有区别。因而在HTML5中,你可以随意使用下列任何语法。

01 <img src="foo" alt="bar" />
02 <p class="foo">Hello world</p>
03
04 <img src="foo" alt="bar">
05 <p class="foo">Hello world
06
07 <IMG SRC="foo" ALT="bar">
08 <P CLASS="foo">Hello world</P>