代码改变世界

深入JavaScript与.NET Framework中的日期时间(2):JavaScript中的Date类型(上)

2007-06-06 14:02 Jeffrey Zhao 阅读(...) 评论(...) 编辑 收藏

概念

在JavaScript中处理时间使用的就是Date原生类型了,这也是在使用JavaScript进行开发时最常用的对象之一。JavaScript中Date类型的功能,与一些成熟编程环境的日期时间对象相比的确功能有些欠缺,但是已经足够我们在一般情况下的使用了。如果需要有什么特殊的要求,可以使用JavaScript的动态性扩展Date类型的功能,正如Microsoft AJAX Library那样。

既然我们要“深入”JavaScript的Date类型,那么我们先来搞清楚它到底是什么。首先,这是一个数据类型,它的实例是一个数据对象——这个从理解上应该没有问题。其次,它的每个实例其实维护的都仅仅是一个数字,这点可能需要详细解释一下。这个数字的含义是相对于UTC时间1970年1月1日0时整的时间偏移量,单位是毫秒,在JavaScript中每个Date类型实例都是使用这种方式来记录一个时间。理论上说,这是一个“ECMAScript number”类型的数字,表示的范围是从-9,007,199,254,740,991到9,007,199,254,740,991的整数,这使得每个Date类型的实例能表示时间范围是相对于UTC时间1970年1月1日0时整,正负各285,616年。不过实际上Date类型能够表示的时间是相对正负各100,000,000天,略小于285,616年,不过也足够我们使用了。

这段理解根据ECMAScript Language Specification所得,至于每个脚本引擎的实现方式并不在我们关心的范围之内。请注意上面的说法是:每个实例维护的“仅仅是”一个数字,这代表在JavaScript中,两个Date对象的这个数字相同,即代表这两个对象表示的时间相同,这一点并不像.NET Framework中每个DateTime对象还维护着自身的类型信息(表示UTC时间,表示本地时间亦或都不表示,这点在今后的文章中我会详细描述)一样。说的再简单一些,JavaScript中的Date类型表示(记录?维护?)的永远是UTC时间。 

根据Spec的说法,这个“数字”被称为time value。

 

Date函数

这里说的Date函数并非是指构造Date类型对象所用的构造函数,而仅仅是把Date作为一个普通函数所使用。它只有一种调用方式:

  • Date ( [ year [, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] ] ] )

例如:

var d = Date(2000, 1, 1);

有意思的是,Spec对于这个调用的描述非常有趣:“All of the arguments are optional; any arguments supplied are accepted but are completely ignored.”,也就是说,无论您传入任何的参数都会被忽略。这个调用返回的时一个字符串,用于表示当前时间。按照Spec的定义,它与下面的代码所得结果相同:

new Date().toString()

可惜在IE中,Date()返回的结果是“Mon Jun 04 11:12:22 2007”,而new Date().toString()返回的结果是“Mon Jun 4 11:12:22 UTC+0800 2007”。在FireFox中,两者都返回“Mon Jun 04 2007 11:12:22 GMT+0800”。在这点上,FireFox更符合标准。

不过,在实际开发时中几乎不会使用这种方法。

 

Date对象的构造

构造一个Date对象有以下三种方式: 

  1. new Date()
  2. new Date(value)
  3. new Date (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )

例如:

var d1 = new Date() // 构造一个表示当前时间的对象
var d2 = new Date(0); // 构造一个表示UTC时间1970年1月1日0时整的对象
var d3 = new Date(2007, 5, 6); // 构造一个表示本地时间2007年6月6日0时整的对象

经过上面的代码之后,d1表示的是构造那一刻的时间。请注意它并不区分本地时间还是UTC时间,时间只有一个,UTC时间也好本地时间也罢,这只是同一个时间的不同表现形式而已。d2对象在构造时只接受了一个参数,因此这个参数将会被作为相对于UTC时间1970年1月1日0时整的偏移毫秒数,也就是time value。如果使用了多个参数来构造Date对象,则参数会被依次认作为年、月、日,直到最后个参数表示毫秒。除了年和月之外,所有的参数都是可选的,不提供的参数使用0作为其默认值。关于这个构造方式有两点是需要注意的:

  1. “月份”从0开始计算:在JavaScript中,Date类型对象的Month值是从0开始计算的,也就是说使用0到11来表示1月到12月。因此上例的代码中构造的时6月4日而不是5月4日。这一点常常被人忽视,甚至会被认作是JavaScript的bug,其实这一点在Spec上写的清清楚楚。
  2. 参数表示的是本地时间信息:上例的代码所构造出的时间对象表示2007年6月6日0时整,请注意这是站在“本地时间”的角度来考虑的。例如,我在时区设置为UTC +8的系统上调用new Date(2007, 5, 6)构造出的其实是UTC时间2007年6月5日16点整。

我们通过new关键字并指定年、月、日等信息构造出的Date对象是基于本地时间的,那么我们该如何构造出一个有特定年、月、日等UTC时间信息的对象呢?我们只能间接地这么做,因为JavaScript只提供了一个Date.UTC方法,它的方法签名如下:

  • Date.UTC (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )

这个方法的作用是根据指定的年、月、日等UTC时间信息,返回一个time value值,通过这个值,我们就可以使用上面的第二种方法来构造一个Date对象了,如下:

var d = new Date(Date.UTC(2007, 5, 6)); // 构造一个表示UTC时间2007年6月6日0时整的对象

一般来说,我们最常用的构造Date对象的方法就是上述这些。