- 浏览: 24797 次
- 性别:
- 来自: 深圳
文章分类
最新评论
关于时间,在JavaDoc中谈论比较多文字的是UTC、UT、GMT、TimeZone等
下面是科学的对它们的简单解释。
UTC:科学纪年,时间采自原子时钟。在每过一两年会有一个跳秒,在某个跳点,一分钟有61秒
UT: GMT格林威治时间的科学学名,取自天文学观测。GMT 是标准的“民间”名称;UT 是相同标准的“科学”名称
但java中, GMT的意思是有所不同的。 文中会介绍它们的意义。
UTC
如果我们要回答 "when" 而不是 “30秒之后”, 我们需要一种方法能够表示任何一个时间点。 我们可以通过使用时间刻度 -- 就像尺子 -- 达到我们的目的。在一把尺子的范围内, 它有零刻度, 正刻度(可能也有负刻度)。对于时间, 我们可以使用相同的方法。 我们任意定义一个时间为 “0”。 通过这个零点, 我们可以表示将来的任意时间点, 当然也可以表示过去的时间点。 这就是 UTC (Coordinated Universal Time)所以做的功能。 通过它, 我们能给每一秒命名。例如 "2010-1-13 16:34:58.000 UTC". UTC总是增长的, 它从不会返回。 有时候,一分钟并不总是60秒, 可能为59秒或者61秒。 我们把这些秒叫做跳秒, 他们用于
纠正时间。
通过操作系统表示时间
在IT系统里, 有时间, 我们需要准确的表示一个时间点。 比如
文件上次更改的时间
启动一个设备
什么时候发送的邮件
这些时间点, 都有一个事件的特征。 如果两个时间是在同一时刻发生的。 不管一个是发生在上海, 另一个发生在伦敦。 它们都是发生在同一时刻的。与时区没有关系。 我们经常使用操作系统时间去表示。操作系统的时间戳是一个从1970-1-1 UTC 零点到现在的整型数字, Java中的java.util.Date 类有效的封装了这个时间戳。它跟时区是没有关系的。
注意:Date 的 toString 方法会用JVM所在系统的时区把时间打印出来。
时区 -- Time Zones
您肯定听说过时区, 但您真正理解时区的意义吗? 把他们理解为一种测量时间的单位。就像测量长度的单位。 1米的长度是1000毫米。不管你说成1米或者1000毫米,长度是不会变化的。对于时间也是一样。 一个确切的时间点不会因为在不用的时区下而变化。只是表达不同而已。所有时区的母亲是 Greenwich Mean Time (GMT). GMT是UTC使用的时区。 所有时区的偏移量, 都是通过与GMT的偏移来表示的。 往东的时区有正的偏移量, 那么往西的话就是负的偏移量了。同样我们还可以跟长度比较。 米是所有长度单位的母亲。
1mm = 1/1000m, 1km=1000m.
转为时区的话, 也是同样的道理:
18:00GM = 18:00+00:00 = 19:00+01:00 = 17:00-01:00
Java 中java.util.TimeZone能够表示两个内容。
A time zone
A time zone database of a location
时区不是通过天文学家划分的, 而是政治家。这些划分会变化。 这就是为什么许多城市保留一个历史时区数据库。 许多国家在一年中会跟改时区两次: 夏令时(daylight savings time) 只是一个time zone. 许多关于时间的应用中, 通过TimeZone.getTimeZone("Europe/Paris")使用a location database。 这样可以解决
夏令时中的很多问题。 当然了, 应用也可以简单的使用确切地时区去获得TimeZone: TimeZone.getTimeZone("GMT+04:30").
在java.util.Calendar和java.text.DateFormat正确的使用 time zone是对应用中时区的安全问题至关重要的。应用的时间国际化, 不仅仅是支持不同的LocaleS. 同样需要支持不同的TimeZoneS. 所有, 应用中Locale依赖的, 同样也是TimeZone依赖的。需要注意的一个地方是TimeZone.getTimeZone()不会因为不认识传入的时区名字而抛出Exception, 而仅仅是返回GMT.如果一个时区的名字来自于不安全的地方,比如用户输入界面, 那么我们需要验证时区名字是否正确。
下面我们来研究Java API 中的时间类.
java.util.Date, java.sql.Date 和java.sql.Timestamp 的关系
通过前面的介绍,我们知道java.util.Date是对UTC时间的一个封装。来看看它的构造器
其实, 就是一个long型数据的封装。
而sql.Date和sql,Timestamp 是对util.Date的包装 (继承)。与sql中的date和timestamp一致, 可以直接插入数据库。
这里说下Timestamp. 此类型由 java.util.Date 和单独的毫微秒值组成。只有整数秒才会存储在 java.util.Date 组件中。小数秒(毫微秒)是独立存在的。传递不是 java.sql.Timestamp 实例的对象时,Timestamp.equals(Object) 方法永远不会返回 true,因为日期的毫微秒组件是未知的。因此,相对于 java.util.Date.equals(Object) 方法而言,Timestamp.equals(Object) 方法是不对称的。此外,hashcode 方法使用底层 java.util.Date 实现并因此在其计算中不包括毫微秒。
鉴于 Timestamp 类和上述 java.util.Date 类之间的不同,建议代码一般不要将 Timestamp 值视为 java.util.Date 的实例。Timestamp 和 java.util.Date 之间的继承关系实际上指的是实现继承,而不是类型继承。
由于Date其实是个long型数字, 但人们往往需要日历型去表示时间。 当然,Calendar为我们封装了很多方便的方法。 其实Date也封装了获得日期的方法, 但已被废弃不用了。 这时,我们可以通过DateFormat来format Date.
下面的例子, 用不同国家的默认格式显示时间。
输出的结果是:
Locale: Chinese (China)
2011年1月13日 星期四 下午05时54分23秒 CST
Locale: English (United States)
Thursday, January 13, 2011 5:54:23 PM CST
由于使用默认TimeZone, 我们跟美国日期一样。 下面,我们加上不同TimeZone, 来看看美国和GMT时区的这个时候日期是多少。
输出的结果是:
China 2011年1月13日 星期四 下午06时14分35秒 CST
US Thursday, January 13, 2011 2:14:35 AM PST
GMT Thursday, January 13, 2011 10:14:35 AM GMT
把String转化为日期, 这在GUI或者XML相关传递时间中经常见到。 我们可以使用DateFormat的一个默认实现SimpleDateFormat来完成我们的需求。 例如:
输出的结果为:
2008-01-01 04:15:00 GMT
下图是常用的日期和时间模式:
模式字母的意义:
这里介绍个取得正确TimeZone的简单技巧。 我们往往知道一个国家的时区, 但不知道这个时区的名字。 这时,我们可以简单的遍历下这个时区的所有名字来获得合法的名字。
java.util.Calendar
这是语言环境敏感类,包含了TimeZone 信息. Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
Calendar 对象能够生成为特定语言和日历风格实现日期-时间格式化所需的所有日历字段值,例如,日语-格里高里历,日语-传统日历。Calendar 定义了某些日历字段返回值的范围,以及这些值的含义。例如,对于所有日历,日历系统第一个月的值是 MONTH == JANUARY。其他值是由具体子类(例如 ERA)定义的。
认识下日历
1.
输出结果是:
China timestamp: 1294915267581
U timestamp: 1294915267581
China hour: 18
US Hour: 2
Calendar.getTimeInMillis() 方法,返回的是UTC时间戳, 所有, 不管处于哪个时区, 这个时间戳是相同的。
但是, Calendar.get(int field) 方法, 可返回当地时区的时间表示。 不用时区的话, 返回的值肯定是不同的。 我们此时是下午6点, 美国是凌晨2点。
那么,日期中的这些年,月,日, 小时,分钟,秒, 毫秒是怎样计算出来的。 因为TimeZone拥有偏移量 (包含夏令时的偏移量), 那么, UTC跟不同用时区的偏移量进行计算, 就能得出不同时期的日历表示。 在Calendar 类中, 通过方法 computeFields来完成。我们来简单看下Calendar类的一个默认实现GregorianCalendar的 computeFields 方法长什么样:
具体的计算还是比较复杂, 有兴趣的朋友可以进一步研究。 这里我想强调的是, 什么时候会调用这个方法。 请参阅下图
同一个时间, 使用Calendar来显示不同时区的时间
比如,通过日历, 来看看美国现在的时间表示是什么。
输出结果是:
Current Time here =Thu Jan 13 19:10:18 CST 2011
Current Time there=Thu Jan 13 05:10:18 CST 2011
请注意方法Calendar.getTime(). 看看它的源码:
它其实就是UTC时间戳。 前面有提到, Date的toString方法用当地时区格式打印出来。 因为同一时间点, UTC是相同的。 我们要用我们的TimeZone打印出美国的现在时间, 必须改变UTC值。 mbCal的时期(年,月,日,小时,分钟,秒 。。。 )其实已经发生了变化。setTimeInMillis 方法会计算偏移量。 这里我表达的可能不清楚, 如果大家有问题的话, 可以给我留言。
日历的字段操作
日历常用的一个地方, 就是字段的操作了。 可以使用三种方法更改日历字段:set()、add() 和 roll()。下面会具体介绍这三个方法的区别。
set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。使用 set() 更改日历字段的结果是,其他日历字段也可能发生更改,这取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段之后,get(f) 没必要通过调用 set 方法返回 value 集合。具体细节是通过具体的日历类确定的。
示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 set(Calendar.MONTH, Calendar.SEPTEMBER) 将该日期设置为 1999 年 9 月 31 日。如果随后调用 getTime(),那么这是解析 1999 年 10 月 1 日的一个暂时内部表示。但是,在调用 getTime() 之前调用 set(Calendar.DAY_OF_MONTH, 30) 会将该日期设置为 1999 年 9 月 30 日,因为在调用 set() 之后没有发生重新计算。
输出结果是
Current Timezone=China Standard Time
Calendar raw = Sat Dec 31 19:26:51 CST 2011
Calendar after change = 2 Thu Mar 03 19:26:51 CST 2011
请注意, 结果不是2月31日。因为没有2月31日。 再继续看下面的代码:
注意: 结果不是3月28号。
add(f, delta) 将 delta 添加到 f 字段中。这等同于调用 set(f, get(f) + delta),但要带以下两个调整:
Add 规则 1。调用后 f 字段的值减去调用前 f 字段的值等于 delta,以字段 f 中发生的任何溢出为模。溢出发生在字段值超出其范围时,结果,下一个更大的字段会递增或递减,并将字段值调整回其范围内。
Add 规则 2。如果期望某一个更小的字段是不变的,但让它等于以前的值是不可能的,因为在字段 f 发生更改之后,或者在出现其他约束之后,比如时区偏移量发生更改,它的最大值和最小值也在发生更改,然后它的值被调整为尽量接近于所期望的值。更小的字段表示一个更小的时间单元。HOUR 是一个比 DAY_OF_MONTH 小的字段。对于不期望是不变字段的更小字段,无需进行任何调整。日历系统会确定期望不变的那些字段。
此外,与 set() 不同,add() 强迫日历系统立即重新计算日历的毫秒数和所有字段。
示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 add(Calendar.MONTH, 13) 将日历设置为 2000 年 9 月 30 日。Add 规则 1 将 MONTH 字段设置为 September,因为向 August 添加 13 个月得出的就是下一年的 September。因为在 GregorianCalendar 中,DAY_OF_MONTH 不可能是 9 月 31 日,所以 add 规则 2 将 DAY_OF_MONTH 设置为 30,即最可能的值。尽管它是一个更小的字段,但不能根据规则 2 调整 DAY_OF_WEEK,因为在 GregorianCalendar 中的月份发生变化时,该值也需要发生变化。
结果是:
Current Timezone=China Standard Time
Calendar raw = Mon Jan 31 19:50:23 CST 2011
Calendar after change = Mon Feb 28 19:50:23 CST 2011
Calendar after change = Mon Mar 28 19:50:23 CST 2011
roll(f, delta) 将 delta 添加到 f 字段中,但不更改更大的字段。这等同于调用 add(f, delta),但要带以下调整:
Roll 规则。在完成调用后,更大的字段无变化。更大的字段表示一个更大的时间单元。DAY_OF_MONTH 是一个比 HOUR 大的字段。
结果是:
Current Timezone=China Standard Time
Calendar raw = Sat Dec 31 20:05:25 CST 2011
Calendar after change = Mon Jan 31 20:05:25 CST 2011
下面是科学的对它们的简单解释。
UTC:科学纪年,时间采自原子时钟。在每过一两年会有一个跳秒,在某个跳点,一分钟有61秒
UT: GMT格林威治时间的科学学名,取自天文学观测。GMT 是标准的“民间”名称;UT 是相同标准的“科学”名称
但java中, GMT的意思是有所不同的。 文中会介绍它们的意义。
UTC
如果我们要回答 "when" 而不是 “30秒之后”, 我们需要一种方法能够表示任何一个时间点。 我们可以通过使用时间刻度 -- 就像尺子 -- 达到我们的目的。在一把尺子的范围内, 它有零刻度, 正刻度(可能也有负刻度)。对于时间, 我们可以使用相同的方法。 我们任意定义一个时间为 “0”。 通过这个零点, 我们可以表示将来的任意时间点, 当然也可以表示过去的时间点。 这就是 UTC (Coordinated Universal Time)所以做的功能。 通过它, 我们能给每一秒命名。例如 "2010-1-13 16:34:58.000 UTC". UTC总是增长的, 它从不会返回。 有时候,一分钟并不总是60秒, 可能为59秒或者61秒。 我们把这些秒叫做跳秒, 他们用于
纠正时间。
通过操作系统表示时间
在IT系统里, 有时间, 我们需要准确的表示一个时间点。 比如
文件上次更改的时间
启动一个设备
什么时候发送的邮件
这些时间点, 都有一个事件的特征。 如果两个时间是在同一时刻发生的。 不管一个是发生在上海, 另一个发生在伦敦。 它们都是发生在同一时刻的。与时区没有关系。 我们经常使用操作系统时间去表示。操作系统的时间戳是一个从1970-1-1 UTC 零点到现在的整型数字, Java中的java.util.Date 类有效的封装了这个时间戳。它跟时区是没有关系的。
注意:Date 的 toString 方法会用JVM所在系统的时区把时间打印出来。
时区 -- Time Zones
您肯定听说过时区, 但您真正理解时区的意义吗? 把他们理解为一种测量时间的单位。就像测量长度的单位。 1米的长度是1000毫米。不管你说成1米或者1000毫米,长度是不会变化的。对于时间也是一样。 一个确切的时间点不会因为在不用的时区下而变化。只是表达不同而已。所有时区的母亲是 Greenwich Mean Time (GMT). GMT是UTC使用的时区。 所有时区的偏移量, 都是通过与GMT的偏移来表示的。 往东的时区有正的偏移量, 那么往西的话就是负的偏移量了。同样我们还可以跟长度比较。 米是所有长度单位的母亲。
1mm = 1/1000m, 1km=1000m.
转为时区的话, 也是同样的道理:
18:00GM = 18:00+00:00 = 19:00+01:00 = 17:00-01:00
Java 中java.util.TimeZone能够表示两个内容。
A time zone
A time zone database of a location
时区不是通过天文学家划分的, 而是政治家。这些划分会变化。 这就是为什么许多城市保留一个历史时区数据库。 许多国家在一年中会跟改时区两次: 夏令时(daylight savings time) 只是一个time zone. 许多关于时间的应用中, 通过TimeZone.getTimeZone("Europe/Paris")使用a location database。 这样可以解决
夏令时中的很多问题。 当然了, 应用也可以简单的使用确切地时区去获得TimeZone: TimeZone.getTimeZone("GMT+04:30").
在java.util.Calendar和java.text.DateFormat正确的使用 time zone是对应用中时区的安全问题至关重要的。应用的时间国际化, 不仅仅是支持不同的LocaleS. 同样需要支持不同的TimeZoneS. 所有, 应用中Locale依赖的, 同样也是TimeZone依赖的。需要注意的一个地方是TimeZone.getTimeZone()不会因为不认识传入的时区名字而抛出Exception, 而仅仅是返回GMT.如果一个时区的名字来自于不安全的地方,比如用户输入界面, 那么我们需要验证时区名字是否正确。
下面我们来研究Java API 中的时间类.
java.util.Date, java.sql.Date 和java.sql.Timestamp 的关系
通过前面的介绍,我们知道java.util.Date是对UTC时间的一个封装。来看看它的构造器
public Date() { this(System.currentTimeMillis()); }
其实, 就是一个long型数据的封装。
而sql.Date和sql,Timestamp 是对util.Date的包装 (继承)。与sql中的date和timestamp一致, 可以直接插入数据库。
这里说下Timestamp. 此类型由 java.util.Date 和单独的毫微秒值组成。只有整数秒才会存储在 java.util.Date 组件中。小数秒(毫微秒)是独立存在的。传递不是 java.sql.Timestamp 实例的对象时,Timestamp.equals(Object) 方法永远不会返回 true,因为日期的毫微秒组件是未知的。因此,相对于 java.util.Date.equals(Object) 方法而言,Timestamp.equals(Object) 方法是不对称的。此外,hashcode 方法使用底层 java.util.Date 实现并因此在其计算中不包括毫微秒。
鉴于 Timestamp 类和上述 java.util.Date 类之间的不同,建议代码一般不要将 Timestamp 值视为 java.util.Date 的实例。Timestamp 和 java.util.Date 之间的继承关系实际上指的是实现继承,而不是类型继承。
由于Date其实是个long型数字, 但人们往往需要日历型去表示时间。 当然,Calendar为我们封装了很多方便的方法。 其实Date也封装了获得日期的方法, 但已被废弃不用了。 这时,我们可以通过DateFormat来format Date.
下面的例子, 用不同国家的默认格式显示时间。
Date date = new Date(); Locale localeEN = Locale.US; Locale localeCH = Locale.CHINA; // Get a date time formatter for display in China. DateFormat fullDateFormatCH = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL, localeCH); // Get a date time formatter for display in the U.S. DateFormat fullDateFormatEN = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL, localeEN); System.out.println("Locale: " + localeCH.getDisplayName()); System.out.println(fullDateFormatCH.format(date)); System.out.println("Locale: " + localeEN.getDisplayName()); System.out.println(fullDateFormatEN.format(date));
输出的结果是:
Locale: Chinese (China)
2011年1月13日 星期四 下午05时54分23秒 CST
Locale: English (United States)
Thursday, January 13, 2011 5:54:23 PM CST
由于使用默认TimeZone, 我们跟美国日期一样。 下面,我们加上不同TimeZone, 来看看美国和GMT时区的这个时候日期是多少。
Date date = new Date(); Locale localeEN = Locale.US; Locale localeCH = Locale.CHINA; Locale locale = Locale.ENGLISH; // Get a date time formatter for display in China. DateFormat fullDateFormatCH = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL, localeCH); // Get a date time formatter for display in the U.S. DateFormat fullDateFormatEN = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL, localeEN); DateFormat fullDateFormat = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL, locale); TimeZone timeZoneChina = TimeZone.getDefault(); TimeZone timeZoneGMT = TimeZone.getTimeZone("GMT"); TimeZone timeZoneUS = TimeZone.getTimeZone("America/Whitehorse"); fullDateFormatCH.setTimeZone(timeZoneChina); fullDateFormatEN.setTimeZone(timeZoneUS); fullDateFormat.setTimeZone(timeZoneGMT); System.out.println(" " + timeZoneChina.useDaylightTime() ); System.out.println(" China " + fullDateFormatCH.format(date)); System.out.println(" US " + fullDateFormatEN.format(date)); System.out.println(" GMT " + fullDateFormat.format(date));
输出的结果是:
China 2011年1月13日 星期四 下午06时14分35秒 CST
US Thursday, January 13, 2011 2:14:35 AM PST
GMT Thursday, January 13, 2011 10:14:35 AM GMT
把String转化为日期, 这在GUI或者XML相关传递时间中经常见到。 我们可以使用DateFormat的一个默认实现SimpleDateFormat来完成我们的需求。 例如:
DateFormat indfm = new SimpleDateFormat("MM/dd/yyyy HH'h'mm"); indfm.setTimeZone(TimeZone.getTimeZone("America/Whitehorse")); Date purchaseDate = indfm.parse("12/31/2007 20h15"); DateFormat outdfm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); outdfm.setTimeZone(TimeZone.getTimeZone("GMT")); System.out.println ( outdfm.format(purchaseDate) +" GMT");
输出的结果为:
2008-01-01 04:15:00 GMT
下图是常用的日期和时间模式:
模式字母的意义:
这里介绍个取得正确TimeZone的简单技巧。 我们往往知道一个国家的时区, 但不知道这个时区的名字。 这时,我们可以简单的遍历下这个时区的所有名字来获得合法的名字。
//+8 * 60 * 60 * 1000 或者 -7 * 60 * 60 * 1000 String[] ids = TimeZone.getAvailableIDs(0 * 60 * 60 * 1000); if (ids.length == 0) System.exit(0); for (int i = 0; i < ids.length; i++) { System.out.println("ids: " + ids[i]); }
java.util.Calendar
这是语言环境敏感类,包含了TimeZone 信息. Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
Calendar 对象能够生成为特定语言和日历风格实现日期-时间格式化所需的所有日历字段值,例如,日语-格里高里历,日语-传统日历。Calendar 定义了某些日历字段返回值的范围,以及这些值的含义。例如,对于所有日历,日历系统第一个月的值是 MONTH == JANUARY。其他值是由具体子类(例如 ERA)定义的。
认识下日历
1.
Calendar now = Calendar.getInstance(); TimeZone timeZoneUS = TimeZone.getTimeZone("America/Whitehorse"); Calendar us_now = new GregorianCalendar(timeZoneUS); System.err.println(" China timestamp: " + now.getTimeInMillis()); System.err.println(" U timestamp: " + us_now.getTimeInMillis()); System.err.println(" China hour: " + now.get(Calendar.HOUR_OF_DAY)); System.err.println(" US Hour: " + us_now.get(Calendar.HOUR_OF_DAY)); SimpleDateFormat sdf = new SimpleDateFormat(" yyyy-MM-dd HH:mm:ss "); Date date = sdf.parse(" 2008-07-10 19:20:00 "); now.setTime(date); us_now.setTime(date); System.out.println(now.getTimeInMillis()); System.out.println(us_now.getTimeInMillis());
输出结果是:
China timestamp: 1294915267581
U timestamp: 1294915267581
China hour: 18
US Hour: 2
Calendar.getTimeInMillis() 方法,返回的是UTC时间戳, 所有, 不管处于哪个时区, 这个时间戳是相同的。
但是, Calendar.get(int field) 方法, 可返回当地时区的时间表示。 不用时区的话, 返回的值肯定是不同的。 我们此时是下午6点, 美国是凌晨2点。
那么,日期中的这些年,月,日, 小时,分钟,秒, 毫秒是怎样计算出来的。 因为TimeZone拥有偏移量 (包含夏令时的偏移量), 那么, UTC跟不同用时区的偏移量进行计算, 就能得出不同时期的日历表示。 在Calendar 类中, 通过方法 computeFields来完成。我们来简单看下Calendar类的一个默认实现GregorianCalendar的 computeFields 方法长什么样:
/** * Converts the time value (millisecond offset from the <a * href="Calendar.html#Epoch">Epoch</a>) to calendar field values. * The time is <em>not</em> * recomputed first; to recompute the time, then the fields, call the * <code>complete</code> method. * * @see Calendar#complete */ protected void computeFields() { int mask = 0; if (isPartiallyNormalized()) { // Determine which calendar fields need to be computed. mask = getSetStateFields(); int fieldMask = ~mask & ALL_FIELDS; // We have to call computTime in case calsys == null in // order to set calsys and cdate. (6263644) if (fieldMask != 0 || calsys == null) { mask |= computeFields(fieldMask, mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)); assert mask == ALL_FIELDS; } } else { mask = ALL_FIELDS; computeFields(mask, 0); } // After computing all the fields, set the field state to `COMPUTED'. setFieldsComputed(mask); }
具体的计算还是比较复杂, 有兴趣的朋友可以进一步研究。 这里我想强调的是, 什么时候会调用这个方法。 请参阅下图
同一个时间, 使用Calendar来显示不同时区的时间
比如,通过日历, 来看看美国现在的时间表示是什么。
package util; public class TimeTest { public static Date getDateInTimeZone(Date currentDate, String timeZoneId) { TimeZone tz = TimeZone.getTimeZone(timeZoneId); Calendar mbCal = new GregorianCalendar(tz); mbCal.setTimeInMillis(currentDate.getTime()); Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, mbCal.get(Calendar.YEAR)); cal.set(Calendar.MONTH, mbCal.get(Calendar.MONTH)); cal.set(Calendar.DAY_OF_MONTH, mbCal.get(Calendar.DAY_OF_MONTH)); cal.set(Calendar.HOUR_OF_DAY, mbCal.get(Calendar.HOUR_OF_DAY)); cal.set(Calendar.MINUTE, mbCal.get(Calendar.MINUTE)); cal.set(Calendar.SECOND, mbCal.get(Calendar.SECOND)); cal.set(Calendar.MILLISECOND, mbCal.get(Calendar.MILLISECOND)); return cal.getTime(); } public static void main(String[] args) { // Canada/Central String timeZoneId = "Canada/Central"; Date now = new Date(); // System.out.println("Getting Time in the timezone="+timeZoneId); System.out.println("Current Time there="+getDateInTimeZone(now,timeZoneId)); } }
输出结果是:
Current Time here =Thu Jan 13 19:10:18 CST 2011
Current Time there=Thu Jan 13 05:10:18 CST 2011
请注意方法Calendar.getTime(). 看看它的源码:
public final Date getTime() { return new Date(getTimeInMillis()); }
它其实就是UTC时间戳。 前面有提到, Date的toString方法用当地时区格式打印出来。 因为同一时间点, UTC是相同的。 我们要用我们的TimeZone打印出美国的现在时间, 必须改变UTC值。 mbCal的时期(年,月,日,小时,分钟,秒 。。。 )其实已经发生了变化。setTimeInMillis 方法会计算偏移量。 这里我表达的可能不清楚, 如果大家有问题的话, 可以给我留言。
日历的字段操作
日历常用的一个地方, 就是字段的操作了。 可以使用三种方法更改日历字段:set()、add() 和 roll()。下面会具体介绍这三个方法的区别。
set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。使用 set() 更改日历字段的结果是,其他日历字段也可能发生更改,这取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段之后,get(f) 没必要通过调用 set 方法返回 value 集合。具体细节是通过具体的日历类确定的。
示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 set(Calendar.MONTH, Calendar.SEPTEMBER) 将该日期设置为 1999 年 9 月 31 日。如果随后调用 getTime(),那么这是解析 1999 年 10 月 1 日的一个暂时内部表示。但是,在调用 getTime() 之前调用 set(Calendar.DAY_OF_MONTH, 30) 会将该日期设置为 1999 年 9 月 30 日,因为在调用 set() 之后没有发生重新计算。
Calendar cal = Calendar.getInstance(); System.out.println("Current Timezone="+cal.getTimeZone().getDisplayName()); cal.set(Calendar.MONTH, Calendar.DECEMBER); cal.set(Calendar.DAY_OF_MONTH, 31); System.out.println("Calendar raw = " + cal.getTime()); cal.set(Calendar.MONTH, Calendar.FEBRUARY); System.out.println("Calendar after change = 2 " + cal.getTime());
输出结果是
Current Timezone=China Standard Time
Calendar raw = Sat Dec 31 19:26:51 CST 2011
Calendar after change = 2 Thu Mar 03 19:26:51 CST 2011
请注意, 结果不是2月31日。因为没有2月31日。 再继续看下面的代码:
Calendar cal = Calendar.getInstance(); System.out.println("Current Timezone="+cal.getTimeZone().getDisplayName()); cal.set(Calendar.MONTH, Calendar.DECEMBER); cal.set(Calendar.DAY_OF_MONTH, 31); System.out.println("Calendar raw = " + cal.getTime()); cal.set(Calendar.MONTH, Calendar.FEBRUARY); cal.set(Calendar.DAY_OF_MONTH, 28); System.out.println("Calendar after change = " + cal.getTime());
注意: 结果不是3月28号。
add(f, delta) 将 delta 添加到 f 字段中。这等同于调用 set(f, get(f) + delta),但要带以下两个调整:
Add 规则 1。调用后 f 字段的值减去调用前 f 字段的值等于 delta,以字段 f 中发生的任何溢出为模。溢出发生在字段值超出其范围时,结果,下一个更大的字段会递增或递减,并将字段值调整回其范围内。
Add 规则 2。如果期望某一个更小的字段是不变的,但让它等于以前的值是不可能的,因为在字段 f 发生更改之后,或者在出现其他约束之后,比如时区偏移量发生更改,它的最大值和最小值也在发生更改,然后它的值被调整为尽量接近于所期望的值。更小的字段表示一个更小的时间单元。HOUR 是一个比 DAY_OF_MONTH 小的字段。对于不期望是不变字段的更小字段,无需进行任何调整。日历系统会确定期望不变的那些字段。
此外,与 set() 不同,add() 强迫日历系统立即重新计算日历的毫秒数和所有字段。
示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 add(Calendar.MONTH, 13) 将日历设置为 2000 年 9 月 30 日。Add 规则 1 将 MONTH 字段设置为 September,因为向 August 添加 13 个月得出的就是下一年的 September。因为在 GregorianCalendar 中,DAY_OF_MONTH 不可能是 9 月 31 日,所以 add 规则 2 将 DAY_OF_MONTH 设置为 30,即最可能的值。尽管它是一个更小的字段,但不能根据规则 2 调整 DAY_OF_WEEK,因为在 GregorianCalendar 中的月份发生变化时,该值也需要发生变化。
Calendar cal = Calendar.getInstance(); System.out.println("Current Timezone="+cal.getTimeZone().getDisplayName()); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 31); System.out.println("Calendar raw = " + cal.getTime()); cal.add(Calendar.MONTH, 1); System.out.println("Calendar after change = " + cal.getTime()); cal.add(Calendar.MONTH, 1); System.out.println("Calendar after change = " + cal.getTime());
结果是:
Current Timezone=China Standard Time
Calendar raw = Mon Jan 31 19:50:23 CST 2011
Calendar after change = Mon Feb 28 19:50:23 CST 2011
Calendar after change = Mon Mar 28 19:50:23 CST 2011
roll(f, delta) 将 delta 添加到 f 字段中,但不更改更大的字段。这等同于调用 add(f, delta),但要带以下调整:
Roll 规则。在完成调用后,更大的字段无变化。更大的字段表示一个更大的时间单元。DAY_OF_MONTH 是一个比 HOUR 大的字段。
Calendar cal = Calendar.getInstance(); System.out.println("Current Timezone="+cal.getTimeZone().getDisplayName()); cal.set(Calendar.MONTH, Calendar.DECEMBER); cal.set(Calendar.DAY_OF_MONTH, 31); System.out.println("Calendar raw = " + cal.getTime()); cal.roll(Calendar.MONTH, 1); System.out.println("Calendar after change = " + cal.getTime());
结果是:
Current Timezone=China Standard Time
Calendar raw = Sat Dec 31 20:05:25 CST 2011
Calendar after change = Mon Jan 31 20:05:25 CST 2011
相关推荐
标题中的“java swing 时间日期选择控件”指的是Swing中用于日期和时间选择的组件。Swing提供了JCalendar和JDatePicker这两个常用的日期选择组件,它们使得用户能够方便地在日历视图中选择日期,或者通过一个文本框...
这个"JAVA时间和日期图解"教程很可能是为了帮助开发者更深入地理解这些概念和API。 首先,`java.util.Date`是Java早期用来表示日期和时间的类,它包含了日期和时间的信息,但它的API设计并不直观,且不支持时区处理...
首先,我们需要理解Java中的日期时间处理。Java提供了`java.util.Date`、`java.util.Calendar`以及`java.text.SimpleDateFormat`等类来处理日期和时间。但这些原生API在处理复杂的用户界面交互时可能会显得不够灵活...
2. **创建DatePicker实例**:创建DatePicker对象,你可以设置其初始日期、日期格式、是否显示时间等属性。 ```java JDatePicker datePicker = new JDatePicker(new LocalDate()); // 创建一个默认显示当前日期的...
如果这个文件包含了代码示例,你可以打开查看具体实现,进一步学习和理解Java日期时间操作的细节。 总之,理解和熟练掌握Java的日期时间操作对于开发人员来说至关重要,无论是在处理用户输入、记录日志还是进行复杂...
Java中的日期和时间处理是程序开发中常见的任务,涉及到各种日期和时间的计算、格式化以及比较等操作。在Java中,主要使用`java.util.Date`、`java.util.Calendar`和`java.text.SimpleDateFormat`这三个核心类来处理...
Java 8引入了`java.time`包,这是对日期时间API的重大改进,它包含了一系列新的类,如`LocalDate`、`LocalTime`、`LocalDateTime`、`ZonedDateTime`等,这些类设计清晰,易于理解和使用。同时,`java.time`包还提供...
Java时间日期处理是编程中常见的任务,特别是在处理与时间相关的逻辑和数据存储时。本文将深入探讨Java中处理时间日期的几个核心类及其用途。 首先,`java.util.Date`是Java标准库中处理时间日期的基本类,它表示自...
首先,我们需要理解Java中的日期时间API。在Java 8之前,日期和时间的处理主要依赖于`java.util.Date`和`java.text.SimpleDateFormat`类,但这两个类的设计并不理想,存在线程安全问题且API不易使用。因此,Java 8...
Java 8引入了全新的日期和时间API (`java.time`),这个API的设计更加现代,易于理解和使用。其中的`LocalDate`、`LocalTime`、`LocalDateTime`分别代表日期、时间、日期时间,而`ZonedDateTime`则包含了时区信息。...
在Java编程语言中,日期和时间处理是常见且重要的任务。Java提供了多种工具类来帮助开发者处理日期和时间,使得我们可以方便地进行日期计算、格式化以及解析等操作。本篇将围绕Java中的日期工具类和时间工具类展开...
在Java编程语言中,日期和时间处理是常见且重要的任务。`java日期对象练习`这个程序旨在帮助初学者掌握如何在Java中操作日期对象。Java提供了多种类来处理日期和时间,包括`java.util.Date`、`java.util.Calendar`...
在Java编程语言中,处理日期和时间是一项常见的任务,尤其是涉及到农历和公历节日的计算。这个主题的核心在于如何利用Java API来实现农历和公历节日的转换与判断。以下是一些关键的知识点: 1. **Java日期时间API**...
Java的日期包是Java编程语言中的一个重要组成部分,主要用于处理日期和时间的相关操作。在Java中,日期相关的功能主要由`java....同时,理解Java内置的日期处理机制也非常重要,这样可以根据项目需求选择最适合的方法。
- `java.time`包:Java 8引入的新时间日期API,提供了`LocalDate`, `LocalTime`, `LocalDateTime`等类,它们提供了丰富的日期和时间操作功能。 - `SimpleDateFormat`:旧版日期格式化工具,用于将日期转换为字符串...
Java时间日期处理是Java开发中不可或缺的一部分,涉及到各种时间日期的操作,如获取当前时间、日期的格式化、日期的加减、日期比较等。在Java中,有多种类和API可以用于时间日期处理,包括`java.util.Date`、`java....
此外,`java.util.Calendar`类提供了更高级的时间处理功能,而`java.text.SimpleDateFormat`则用于日期和时间的格式化和解析。 #### 二、`Calendar`类简介及使用方法 `Calendar`是抽象类,它为特定时刻(相对于...
总结来说,学习Java处理日期和时间不仅涉及理解和使用新API,还包括掌握日期时间的概念,如闰年、时区、夏令时等。通过视频教程资源,你可以系统地学习这些知识,提高你的Java编程技能。在实际项目中,合理利用这些...
在Java编程中,日期处理是一项常见的任务,涉及到各种日期和时间的操作。以下是一些关于Java日期处理的关键知识点,这些知识点在给定的文件中有所提及: 1. **获取当前日期**: Java通过`java.util.Date`类可以...
### Java里日期的用法汇总 #### 一、获取当前日期与时间 在Java中,获取当前日期与时间可以通过多种方式实现。以下是一些常用的方法: ...希望这些知识点能够帮助你更好地理解和使用Java中的日期功能。