作者PsMonkey (痞子军团团长)
看板java
标题GWT、GAE 的日期问题
时间Tue Feb 14 02:15:43 2012
有图(?)有连结的网页版:
http://blog.dontcareabout.us/2012/02/gwtgae.html
========
开发环境:GWT 2.4.0、GAE 1.6.1、GPE 2.5.1、Objectify 3.0
正在作一个预约系统,很自然会有「取出某天所有预约」之类的需求,
所以将 class 设计成这样,期望能节省一些麻烦:
class Booking{
Date date;
int hour; //时间间隔以「小时」为单位
//skip other field
}
由於 GAE 的 Datastore 理论上没有提供在 query 时可以卡入日期函数,
所以如果不对日期作处理,下面这段「取得今天的预约」会完全取不到任何东西:
Objectify ofy = ObjectifyService.begin();
ofy.query(Booking.class).filter("date", new Date()).list(); //empty list
原因应该是在於 Datastore 作比对时是直接使用 equal()(个人猜测,没有实际证据),
而 java.util.Date 的 equal() 是判断 getTime()......
这取得到东西的或然率真的接近零了。
最直觉的反应是将日期中「小时」以下的单位全部归零,
在 GWT 的 CalendarUtil 可以找到 resetTime() 的写法:
@SuppressWarnings("deprecation") // GWT requires Date
private static void resetTime(Date date) {
long msec = date.getTime();
msec = (msec / 1000) * 1000;
date.setTime(msec);
// Daylight savings time occurs at midnight in some time zones, so we reset
// the time to noon instead.
date.setHours(12);
date.setMinutes(0);
date.setSeconds(0);
}
用这个方法,将 client 端传上 server 的 date 都先「整理」过。
当然,妈妈说:「不要总是相信 client 端给的值」,
抱持着能省则省的白痴想法,所以忽略 deprecated 的讯息,
让 server 也用同样方法......
神奇的事情发生了,
有时候会正确、有时候会失败......
问题可以简化成「client 端传递一个 reset 过的 date 给 server、
server 再 reset 一次回传回来」,
把两个 date 分别印出来,在 GPE 的 Development Mode 中,
有时候会得到这样的结果:
Sat Feb 11 12:00:00 CST 2012
Sat Feb 11 20:00:00 CST 2012
整整差了八小时...... 是的,是时区的问题。
如果 server 端在 reset 之前先把 date 印出来,
会得到「Sat Feb 11 04:00:00 UTC 2012」。
server 用 UTC 时区,这很合情合理,
实际 deploy 上 AppEngine 也是同样的结果,
完全就是自己不应该硬要用 deprecated 的东西。
但那「
有时候会正确、有时候会失败」的灵异事件又是怎样?
事实的真相是:
在一开始启动 Development Mode 的 server 时,
server 的时区是 UTC(所以会失败);
但是在 reload web server 之後,
server 的时区就会变成本地时区(所以会成功)。
这大概是最近发现第三个 GPE 的不思议之谜
(第一个是「更改 gwt.xml 需 refresh 两次才见效」、
第二个是「build project 进度会卡住,关掉网路就没事」)
好了,就让系统一个日期各自表述,
server 改用 Calendar 来设定日期,目前看起来运作正常...... [抖]
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT+800"));
c.setTime(new Date((date.getTime()/1000)*1000));
c.set(Calendar.HOUR_OF_DAY, 12);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
return c.getTime();
以下是延伸阅读时间 XD:
→在 objectify-appengine 的 Google Group,Jeff Schnitzer 提到
用 java.sql.Date。虽然说 Objectify 2.2.2 版就可以处理 java.sql.Date
(GAE 只能储存 java.util.Date)、GWT 也能使用 java.sql.Date,
不过不知道是我误会意思还是?
实测的结果还是得先 reset 过才能符合需求——
而且 java.sql.Date 基本上不能设定时间...
→tkcn 提供的 reference(1、2)提到
可以用「Joda-Time」这个 library 来处理、以及一些其他资讯。
但是目前安逸於现在这个解法...... [殴飞]
→原始 G+ 讨论串
结语:这是一篇看起来好像很复杂,实际上根本就是技术太弱才会搞出来的飞机......
--
钱锺书:
说出来的话
http://www.psmonkey.org
比不上不说出来的话
Java 版 cookcomic 版
只影射着说不出来的话
and more......
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 114.25.29.97