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"); } }
相关推荐
What’s New in Python What’s New In Python 3.6 Summary – Release highlights New Features PEP 498: Formatted string literals PEP 526: Syntax for variable annotations PEP 515: Underscores in ...
3 Android SqliteManager 源码.zip
内容概要:本文详细介绍了基于S7-200 PLC的煤矿排水系统智能控制方案,重点讨论了三台水泵(两台工作水泵和一台备用水泵)的联动与备援策略。系统通过超声波液位传感器实时监测水位,根据不同水位情况自动控制水泵的启停。具体而言,水位低时不启动水泵,水位介于中水位和高水位之间时启动1号水泵,水位超过高水位则启动1号和2号水泵共同工作。若1号或2号水泵出现故障,系统会自动启用3号备用水泵。此外,MCGS6.2组态画面用于实时监控水位和水泵状态,帮助操作员及时应对异常情况,确保矿井安全。 适合人群:从事煤矿自动化控制领域的技术人员、矿业工程管理人员及相关研究人员。 使用场景及目标:适用于需要提高煤矿排水系统自动化水平的场合,旨在提升矿井排水效率和安全性,减少人工干预,确保矿井生产安全。 其他说明:文中提到的技术方案不仅提高了排水系统的可靠性,还为未来的智能化矿山建设提供了有益借鉴。
scratch少儿编程逻辑思维游戏源码-灌篮之王.zip
scratch少儿编程逻辑思维游戏源码-飞翔马里奥(2).zip
scratch少儿编程逻辑思维游戏源码-火柴人大战 中世纪战争.zip
scratch少儿编程逻辑思维游戏源码-几何冲刺(2).zip
南京证券-低轨卫星互联网启动,天地一体通信迈向6G
nginx-1.20.1
sshpass-1.06-8.ky10.aarch
少儿编程scratch项目源代码文件案例素材-我的世界2D(更新北极).zip
通信行业专题研究:车载全息数字人——AI+Agent新场景,全息投影新方向-20231121-国盛证券-13页
内容概要:本文详细介绍了利用西门子S7-200 PLC和组态王软件构建的邮件分拣系统的具体设计方案和技术细节。首先,文中阐述了硬件部分的设计,包括光电传感器、传送带电机以及分拣机械臂的连接方式,特别是旋转编码器用于精确测量包裹位移的技术要点。接着,展示了PLC编程中的关键代码段,如初始化分拣计数器、读取编码器数据并进行位置跟踪等。然后,描述了组态王作为上位机软件的作用,它不仅提供了直观的人机交互界面,还允许通过简单的下拉菜单选择不同的分拣规则(按省份、按重量或加急件)。此外,针对可能出现的通信问题提出了有效的解决方案,比如采用心跳包机制确保稳定的数据传输,并解决了因电磁干扰导致的问题。最后,分享了一些现场调试的经验教训,例如为减少编码器安装误差对分拣精度的影响而引入的位移补偿算法。 适合人群:从事自动化控制领域的工程师或者对此感兴趣的初学者。 使用场景及目标:适用于需要提高邮件或其他物品自动分拣效率的企业或机构,旨在降低人工成本、提升工作效率和准确性。 其他说明:文中提到的实际案例表明,经过优化后的系统能够显著改善分拣性能,将分拣错误率大幅降至0.3%,并且日均处理量可达2万件包裹。
scratch少儿编程逻辑思维游戏源码-机械汽车.zip
内容概要:本文详细探讨了在连续介质中利用束缚态驱动设计并实现具有最大和可调谐手征光学响应的平面手征超表面的方法。文中首先介绍了comsol三次谐波和本征手性BIC(束缚态诱导的透明)两种重要光学现象,随后阐述了具体的手征超表面结构设计,包括远场偏振图、手性透射曲线、二维能带图、Q因子图和电场图的分析。最后,通过大子刊nc复现实验验证了设计方案的有效性,并对未来的研究方向进行了展望。 适合人群:从事光学研究的专业人士、高校物理系师生、对光与物质相互作用感兴趣的科研工作者。 使用场景及目标:适用于希望深入了解手征超表面设计原理及其光学响应机制的研究人员,旨在推动新型光学器件的研发和技术进步。 其他说明:本文不仅展示了理论分析和模拟计算,还通过实验证明了设计方法的可行性,为后续研究奠定了坚实的基础。
少儿编程scratch项目源代码文件案例素材-位图冒险.zip
少儿编程scratch项目源代码文件案例素材-校园困境2.zip
少儿编程scratch项目源代码文件案例素材-兔子吃萝卜.zip
scratch少儿编程逻辑思维游戏源码-海洋战争.zip