`
leonzhx
  • 浏览: 797166 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Java's Calendar Date and TimeZone - What is all about it?

 
阅读更多

Zz Java's Calendar Date and TimeZone - What is all about it?

(Original article : http://blog.sarathonline.com/2009/01/javas-calendar-date-and-timezone-what.html)

 

Intenally, A Date object is nothing but a long value that holds the number of milliseconds since January 1, 1970, 00:00:00 GMT. So new Date() on Jan 21 13:53:58 EST 2009 would be the same as Jan 21 10:53:58 PST 2009 = 1232564038800 (precise to the last 3 digits). So how are those two different? TimeZoneOffset. This is a int value that give millisecs diference between GMT and the Specified TimeZone. So When *printing* or getting the value of date in TimeZone, this timezone offset is added to the long utc secs. This gives the new print value (or face value - Jan 21 13:53:58 EST 2009 ). The face value includes all calculations for Timezone and is meaningfully correct only when displayed with the Timezone information (as above). Just reading and writing "yyyy-MM-dd hh:mm:ss" is incorrect. To start with let me show a small example:

Date t =newDate();System.out.println(t);//internal value - same (precise to last 3 digits)System.out.println(t.getTime());System.out.println(System.currentTimeMillis());//The following will change where you are running this codeSystem.out.println(t.getTimezoneOffset());

Run the above program twice. Second time around set your system time to a different timezone. You will see in java.util.Date, a timezoneOffset is always set to match VM's Default TimeZone. What this means is that, the face value of [new Date()] is different on  VMs running on different Timezones, even when run at the same time.  And also, this TimeZone is not mutable on a Date. So When you need to SPECIFY a timezone, you use java.util.Calendar. The Calendar encapsulates a Date (internally the other way around, which is way complex and out of the scope this article) to spit information in TimeZone specified. So you could run on a VM in EST at Jan 21 13:53:58 EST 2009 something like

Calendar c =Calendar.getInstance(TimeZone.getTimeZone("PST"));

c holds the current time in PST = Jan 21 10:53:58 PST 2009.

If you do sysout on c, you will get a long GregorianCalendar Output. You should print it as

System.out.printf("%02d/%02d/%04d %02d:%02d:%02d in PST", c.get(c.MONTH)+1, 
    c.get(c.DATE), c.get(c.YEAR), c.get(c.HOUR_OF_DAY),
    c.get(c.MINUTE), c.get(c.SECOND));

Ouput will be 01/21/2009 10:53:58 in PST
However, The Date inside this calendar will not be in PST. It will be on System Timezone.

So If I print c.getTime() it will show Jan 21 13:53:58 EST 2009 instead of Jan 21 1:53:58 PST 2009.

Suppose you want your program to be independent of the TimeZone of the end users' VM. Example, You have an applet (or an application deployable on network) that sends information about client events and their timing, and you want to collect them in to a global list. And that the timing be reported in GMT all the time. You can set the Default TimeZone by doing TimeZone.setDefault(TimeZone.getTimeZone("GMT")). Warning: Do this with care. Because, all future Calendar.getInstance() calls will be in GMT.

Another important gotcha is when we are parsing a DATE string. Simply the innocent looking following code is soooo Evil

SimpleDateFormat sdf =newSimpleDateFormat("yyyy-MM-dd hh:mm:ss");Date userDate = sdf.parse("2009-01-21 13:53:58");

Running this in two different (in timezone) VM at the same time(like on a network or something) will yeild in DIFFERENT Date object. To eliminate that bug, Either Read it with timeZone or setTimeZone on sdf like this

SimpleDateFormat sdf =newSimpleDateFormat("yyyy-MM-dd hh:mm:ss z");//or
sdf.setTimeZone(TimeZone.getTimeZone("EST"));


Update: A small test case to complement theory:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import junit.framework.TestCase;

import org.apache.commons.lang.time.FastDateFormat;

public class DateExampleTest extends TestCase {

	String userEntered = "2009-01-31 00:00:00";
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	TimeZone userTimeZone = TimeZone.getTimeZone("PST");

	public void testOpSystem() throws Exception {

		System.out.println("========== OUTPUT TEST =================");
		// This test only proves that internally, all dates and cals
		// use same timeinmills if System date is considered.
		Date nowDate = new Date();
		Calendar cal = Calendar.getInstance();
		System.out.println("All 3 Should be the same");
		System.out.println(nowDate + " :: " + nowDate.getTime());
		System.out.println(cal.getTime() + " :: " + cal.getTimeInMillis());
		System.out.println(decorate(cal) + " :: " + cal.getTimeInMillis());

		System.out.println("\nUnderlying  Time is same, but cal will *show* offset");
		System.out.println("SYS:" + nowDate + " :: " + nowDate.getTime());
		cal = Calendar.getInstance(TimeZone.getTimeZone("PST"));
		System.out.println("CAL:" + cal.getTime() + " :: " + cal.getTimeInMillis());
		System.out.println("PST:" + decorate(cal) + " :: " + cal.getTimeInMillis());

		System.out.println("\nChanging timezone AFTER it is set, but cal will *show* offset");
		System.out.println("SYS:" + nowDate + " :: " + nowDate.getTime());
		cal = Calendar.getInstance();
		System.out.println("000:" + cal.getTime() + " :: " + cal.getTimeInMillis());
		System.out.println("EST:" + decorate(cal) + " :: " + cal.getTimeInMillis());
		cal.setTimeZone(TimeZone.getTimeZone("PST"));
		System.out.println("111:" + cal.getTime() + " :: " + cal.getTimeInMillis());
		System.out.println("PST:" + decorate(cal) + " :: " + cal.getTimeInMillis());
		System.out.println("set time zone MST will *show* offsetted time ");
		cal.setTimeZone(TimeZone.getTimeZone("CST"));
		System.out.println("CST:" + decorate(cal) + " :: " + cal.getTimeInMillis());
	}

	/**
	 * Wrong way of doing, changes with VM
	 * @throws Exception
	 */
	public void testInSimpleParseSystem() throws Exception {
		System.out.println("========== Parse def ================");
		String userEntered = "2009-01-31 00:00:00";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		TimeZone userTimeZone = TimeZone.getTimeZone("PST");

		System.out.println("No tz, takes system timezone");
		Date userDate = sdf.parse(userEntered);
		// timeInMilliSecs changes with system!!!
		System.out.println("SYS:" + userDate + " :: " + userDate.getTime());
		// setting this date to a cal and changing timezone is not right
		Calendar cal = Calendar.getInstance();
		cal.setTime(userDate);
		cal.setTimeZone(userTimeZone);
		// Observe TimeZone in Output
		System.out.println("CAL in SYS:" + cal.getTime() + " :: " + cal.getTimeInMillis());
		System.out.println("CAL:" + decorate(cal) + " :: " + cal.getTimeInMillis());
	}
	
	/**
	 * Correct way of doing
	 * @throws Exception
	 */
	public void testInSimpleParseTZParam() throws Exception {
		System.out.println("========== Parse Specific TZ ================");
		// Right way is to PARSE with TZ // parser sets mill secs
		System.out.println("\nparse with tz, independent of System");
		sdf.setTimeZone(userTimeZone);
		Date userDate = sdf.parse(userEntered);
		// will show in Sytem Time Zone but is = userDate in userTimeZone
		System.out.println("ENT:" + userDate + " :: " + userDate.getTime());
		// timeInMilliSecs DOESNOT change with system
		Calendar cal = Calendar.getInstance(userTimeZone);
		cal.setTime(userDate);
		System.out.println("USR:" + decorate(cal) + " :: " + cal.getTimeInMillis());
	}

	/**
	 * Correct way of doing
	 * @throws Exception
	 */
	public void testInSimpleCalTZManualSet() throws Exception {
		System.out.println("========== Parse def ================");
		// set values as if they were manually set (mutates mill secs)
		System.out.println("\nManaul setting with tz, independent of System");
		Calendar cal = Calendar.getInstance();
		System.out.println("CAL in SYS:" + cal.getTime() + " :: " + cal.getTimeInMillis());
		System.out.println("CAL:" + decorate(cal) + " :: " + cal.getTimeInMillis());
		cal.clear(); // This is important, otherwise unpredictible.
		cal.setTimeZone(userTimeZone);
		cal.set(Calendar.DATE, 31);
		cal.set(Calendar.YEAR, 2009);
		cal.set(Calendar.MONTH, Calendar.JANUARY);
		cal.set(Calendar.HOUR, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		System.out.println("CAL in SYS:" + cal.getTime() + " :: " + cal.getTimeInMillis());
		System.out.println("CAL:" + decorate(cal) + " :: " + cal.getTimeInMillis());
	}

	public void testOpDBRead() throws Exception {

		System.out.println("========== DATABASE READ TEST =================");
		Connection conn = null;
		try {
			conn = getConnection();
			PreparedStatement ps = conn.prepareStatement("select dt, tms, faceval from datex where id = ?");
			System.out.println("========== Row 1 =================");
			ps.setInt(1, 1); // First Row
			printRow(ps.executeQuery());
			System.out.println("========== Row 2 =================");
			ps.setInt(1, 3); // Second Row
			printRow(ps.executeQuery());

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				conn.close();
			}
		}

	}

	public void testDBWrite() throws Exception {

		String userEntered = "2009-01-31 00:00:00";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		TimeZone userTimeZone = TimeZone.getTimeZone("EST");

		Date userDate = sdf.parse(userEntered);

		System.out.println("========== DATABASE Write TEST =================");
		Connection conn = null;
		try {
			conn = getConnection();
			PreparedStatement ps = conn.prepareStatement("insert into datex(dt, tms, faceval) values (?,?,?)");
			ps.setDate(1, new java.sql.Date(userDate.getTime()));
			ps.setTimestamp(2, new Timestamp(userDate.getTime()));
			ps.setString(3, userDate.toString());
			ps.execute();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				conn.close();
			}
		}

	}

	private void printRow(ResultSet rs) throws Exception {
		rs.next();
		// JDBC driver reads by face value, loses date info
		java.sql.Date sqlDate = rs.getDate("dt");
		Calendar cal = Calendar.getInstance();
		cal.setTime(sqlDate);
		System.out.println("Date Information");
		System.out.println(sqlDate + "::" + sqlDate.getTime() 
				+ " :: " + cal.getTime() + " :: " + cal.getTimeInMillis() + " :: " + decorate(cal));

		// JDBC driver reads by face value, applies system tz, (mutates mill secs)
		Timestamp sqlTS = rs.getTimestamp("tms");
		cal = Calendar.getInstance();
		cal.setTime(sqlTS);
		System.out.println("Timestamp Information");
		System.out.println(sqlTS + "::" + sqlDate.getTime() 
				+ " :: " + cal.getTime() + " :: " + cal.getTimeInMillis() + " :: " + decorate(cal));


		// JDBC driver reads by face value, applies the supplied tz that is supplied (mutates mill secs)
		cal = Calendar.getInstance(TimeZone.getTimeZone("MST"));
		sqlTS = rs.getTimestamp("tms", cal);
		cal.setTime(sqlTS);
		System.out.println("Timestamp Information read with a different Cal");
		System.out.println(sqlTS + "::" + sqlDate.getTime() 
				+ " :: " + cal.getTime() + " :: " + cal.getTimeInMillis() + " :: " + decorate(cal));

	}

	private String decorate(Calendar cal) {

		FastDateFormat f = FastDateFormat.getInstance("EEE MMM dd HH:mm:ss z yyyy");
		return f.format(cal);
	}

	private Connection getConnection() throws Exception {
		Class.forName("oracle.jdbc.OracleDriver");
		String url = "jdbc:oracle:thin:@localhost:1521:xe";

		return DriverManager.getConnection(url, "sarath", "pass");
	}

}

 

分享到:
评论

相关推荐

    Java 之 Date 和 Calendar 实例

    在Java编程语言中,`Date`和`Calendar`类是处理日期和时间的核心组件。这两个类在不同的Java版本中有着不同的使用方式和功能,对于理解Java时间处理机制至关重要。本篇将深入探讨`Date`和`Calendar`类的实例应用。 ...

    android-date_and_time.zip_Time_java-time-and-date

    Java Time API,自Java 8引入,为处理日期、时间和时区提供了强大的工具,显著优于之前的`java.util.Date`和`Calendar`类。本教程将深入探讨如何在Android中利用Java Time API(`java.time`包)来管理日期和时间。 ...

    JAVA Calendar,Date类详解

    在Java编程语言中,`Calendar`和`Date`类是处理日期和时间的核心组件。这两个类在处理日期、时间计算以及格式化等任务时扮演着关键角色。理解并熟练运用它们,对于提升Java开发能力至关重要。 `Date`类是Java早期...

    JavaChineseCalendar

    # 農曆 Class **農曆** class is in the *org.magiclen...It will show today's date in Gregorian calendar and Chinese calendar. The result just likes 西曆:2015-12-16 農曆:2015(乙未、羊年) 冬月 初六 Yo

    jdk-8u152-docs-all

    5. **日期和时间API**:Java 8用全新的`java.time`包替换了过时的`java.util.Date`和`java.util.Calendar`,提供了更强大和易用的时间日期处理能力。 6. **Optional类**:用来处理可能为null的对象,防止空指针异常...

    Java Calendar手机上期选择-日历实现.rar

    Java Calendar手机上期选择-日历实现  // 重载抽象类MIDlet的抽象方法startApp()  protected void startApp() {  Date dd = new Date(); // 表示当前的日期和时间  TimeZone tz = TimeZone.getTimeZone("GMT...

    Java基础之Date和Calendar类的使用方法

    Java中的Date和Calendar类是处理日期和时间的核心组件。Date类主要用于表示特定的瞬间,精确到毫秒。在早期版本的Java中,Date类的设计受到批评,因为它的API不直观且容易导致错误。为了解决这些问题,大部分日期和...

    JAVA new Date()或Calendar.getInstance().getTime()得到的时间不对

    在Java编程中,`new Date()` 和 `Calendar.getInstance().getTime()` 都是获取当前系统时间的方法,但有时可能会出现获取到的时间与预期不符的情况。这种问题通常与时区设置、系统时间、Java运行环境以及代码逻辑...

    java1.8 API 官方帮助文档(jdk-8u151-docs-all HTML版)

    5. **日期和时间API**:Java 8引入了全新的日期和时间API (`java.time`包),替代了过时的`java.util.Date`和`java.util.Calendar`。新API更加直观,易于使用,如`LocalDate`,`LocalTime`,`LocalDateTime`等类。 6...

    Set_System_Date_and_Time.rar_java-time-and-date_system date设置_计算

    在旧版本的Java中,我们通常使用`java.util.Date`和`java.util.Calendar`类来处理日期和时间,但它们的设计并不直观,易用性较差。现在,`java.time`包提供了一套更现代、更简洁的API,包括`LocalDate`, `LocalTime`...

    java中时间类Date和Calendar的使用

    Java中的时间类`Date`和`Calendar`是处理日期和时间的核心组件,它们在Java编程中扮演着重要的角色。在Java中,`Date`类主要用来表示特定的瞬间,精确到毫秒。而`Calendar`类则是一个抽象类,提供了比`Date`更灵活的...

    Java-time-value-is-compared.rar_40

    1. **日期和时间类**:在Java 8之前,日期和时间处理主要依赖`java.util.Date`和`java.util.Calendar`类,这两个类虽然功能强大,但使用起来相对复杂。Java 8引入了新的`java.time`包,提供了更直观且易于使用的API...

    vue-ctk-date-time-picker:VueJS组件选择日期和时间,包括范围模式

    查找v1文档暗模式演示版安装纱yarn add vue-ctk-date-time-picker NPM npm i --save vue-ctk-date-time-picker用法ES6模块/ CommonJS import VueCtkDateTimePicker from 'vue-ctk-date-time-picker' ;import 'vue-...

    JAVA LunarCalendar返回农历(阴历)日期 JAR包 有包括详细DOC文档

    LunarCalendar返回农历(阴历)日期的JAR包 根据指定日期计算对应农历日期(这个计算方法是网上找的,最初的作者是谁已经无法考证了,感谢网络资源吧!),本人封装成好用的JAR包后发不出来,供大家免费下载! ...

    java8的64位安装包jdk-8u181-windows-x64

    4. **日期和时间API的改进**:Java 8用新的`java.time`包替换了原有的`java.util.Date`和`java.util.Calendar`,提供了更强大、更直观的时间日期处理能力。 5. **默认方法**:在接口中添加默认方法允许接口具有实现...

    Calendar和Date的转化

    接下来,使用`Calendar`类的`setTime(Date date)`方法将之前创建的`Date`对象赋值给这个`Calendar`实例。这样,`Calendar`实例就会根据提供的`Date`对象来设置其内部状态。 ### 总结 通过以上两个示例,我们可以...

    Java_Date_日期时间整理

    Java_Date_日期时间整理 Java 中的日期时间处理是非常重要的,以下是 Java 中日期时间整理的知识点总结: 一、获取服务器端当前日期 在 Java 中,可以使用 `java.util.Date` 类来获取服务器端当前日期。可以使用...

    Java 之 Date 和 Calendar

    在Java编程语言中,`Date`和`Calendar`类是处理日期和时间的核心组件。这两个类在不同的Java版本中有着不同的用法和功能,对于理解并正确使用它们是Java开发者必备的知识点。 `Date`类在早期的Java版本中被广泛使用...

    java 时间转换date time = new date()

    根据给定文件的信息,我们可以总结出以下几个重要的Java时间处理知识点: ### 1. Java日期与时间的基本操作 #### 1.1 创建当前日期与时间 ```java Date date = new Date(); ``` 这段代码创建了一个`Date`对象,它...

    java的calendar具体用法

    ### Java中的Calendar类详解 #### 一、引言 在Java中处理日期和时间非常常见,而`java.util.Calendar`类则是进行此类操作的核心工具之一。`Calendar`类提供了一系列的功能来帮助开发者处理复杂的日期计算问题,...

Global site tag (gtag.js) - Google Analytics