如何使用Java处理日历时区?
我有一个来自我的应用程序的Timestamp值。 用户可以在任何给定的本地TimeZone中。
由于这个date是用于一个Web服务,假设给定的时间总是在GMT,我需要将用户的参数从说(EST)转换为(GMT)。 这是踢球者:用户不知道他的TZ。 他input他想发送给WS的创builddate,所以我需要的是:
用户input: 5/1/2008 6:12 PM(EST)
WS的参数需要是 :5/1/2008 6:12 PM(GMT)
我知道TimeStamp总是默认情况下是GMT,但是当发送参数时,即使我从TS(应该是GMT)创build了我的日历,小时总是closures,除非用户在GMT。 我错过了什么?
Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate"); Calendar issueDate = convertTimestampToJavaCalendar(issuedDate); ... private static java.util.Calendar convertTimestampToJavaCalendar(Timestamp ts_) { java.util.Calendar cal = java.util.Calendar.getInstance( GMT_TIMEZONE, EN_US_LOCALE); cal.setTimeInMillis(ts_.getTime()); return cal; }
与以前的代码,这就是我得到的结果(短格式容易阅读):
[2008年5月1日下午11点12分]
public static Calendar convertToGmt(Calendar cal) { Date date = cal.getTime(); TimeZone tz = cal.getTimeZone(); log.debug("input calendar has date [" + date + "]"); //Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT long msFromEpochGmt = date.getTime(); //gives you the current offset in ms from GMT at the current date int offsetFromUTC = tz.getOffset(msFromEpochGmt); log.debug("offset is " + offsetFromUTC); //create a new calendar in GMT timezone, set to this date and add the offset Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.setTime(date); gmtCal.add(Calendar.MILLISECOND, offsetFromUTC); log.debug("Created GMT cal with date [" + gmtCal.getTime() + "]"); return gmtCal; }
如果我在当前时间( Calendar.getInstance()
“12:09:05 EDT”)中传递下面的输出:
DEBUG – input日历有date[Thu Oct 23 12:09:05 EDT 2008]
DEBUG – 偏移量是-14400000
debugging – 用date创buildGMT时间[Thu Oct 23 08:09:05 EDT 2008]
美国东部时间12:09:05是美国东部时间8:09:05。
这里令人困惑的部分是Calendar.getTime()
在当前时区中返回一个Date
,并且没有方法修改日历的时区,并且还打滚了基础date。 根据您的Web服务所采用的参数types,您可能只想让WS以时间毫秒为单位进行交易。
谢谢大家的回应。 经过进一步的调查,我得到了正确的答案。 正如Skip Head所提到的,TimeStamped是我从应用程序中获得的,正在调整到用户的TimeZone。 所以如果用户input6:12 PM(EST),我会得到2:12 PM(GMT)。 我需要的是一种撤消转换的方法,以便用户input的时间是我发送到WebServer请求的时间。 我是这样做的:
// Get TimeZone of user TimeZone currentTimeZone = sc_.getTimeZone(); Calendar currentDt = new GregorianCalendar(currentTimeZone, EN_US_LOCALE); // Get the Offset from GMT taking DST into account int gmtOffset = currentTimeZone.getOffset( currentDt.get(Calendar.ERA), currentDt.get(Calendar.YEAR), currentDt.get(Calendar.MONTH), currentDt.get(Calendar.DAY_OF_MONTH), currentDt.get(Calendar.DAY_OF_WEEK), currentDt.get(Calendar.MILLISECOND)); // convert to hours gmtOffset = gmtOffset / (60*60*1000); System.out.println("Current User's TimeZone: " + currentTimeZone.getID()); System.out.println("Current Offset from GMT (in hrs):" + gmtOffset); // Get TS from User Input Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate"); System.out.println("TS from ACP: " + issuedDate); // Set TS into Calendar Calendar issueDate = convertTimestampToJavaCalendar(issuedDate); // Adjust for GMT (note the offset negation) issueDate.add(Calendar.HOUR_OF_DAY, -gmtOffset); System.out.println("Calendar Date converted from TS using GMT and US_EN Locale: " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT) .format(issueDate.getTime()));
代码的输出是:(用户input5/1/2008 6:12 PM(EST)
当前用户的时区:EST
格林尼治标准时间的当前偏移量(小时): – 4(通常为-5,除了调整DST)
来自ACP的TS:2008-05-01 14:12:00.0
日历使用GMT和US_EN从TS转换来的date区域设置:5/1/08 6:12 PM(GMT)
你说这个date是和Web服务有关的,所以我假设它在某个时候被序列化为一个string。
如果是这样的话,你应该看看DateFormat类的setTimeZone方法 。 这决定了打印时间戳时将使用哪个时区。
一个简单的例子:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); formatter.setTimeZone(TimeZone.getTimeZone("UTC")); Calendar cal = Calendar.getInstance(); String timestamp = formatter.format(cal.getTime());
你可以用乔达时间解决它:
Date utcDate = new Date(timezoneFrom.convertLocalToUTC(date.getTime(), false)); Date localDate = new Date(timezoneTo.convertUTCToLocal(utcDate.getTime()));
Java 8:
LocalDateTime localDateTime = LocalDateTime.parse("2007-12-03T10:15:30"); ZonedDateTime fromDateTime = localDateTime.atZone( ZoneId.of("America/Toronto")); ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant( ZoneId.of("Canada/Newfoundland"));
它看起来像你的TimeStamp被设置为原始系统的时区。
这是弃用,但它应该工作:
cal.setTimeInMillis(ts_.getTime() - ts_.getTimezoneOffset());
非弃用的方式是使用
Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)
但这需要在客户端完成,因为该系统知道它在什么时区。
从一个时区转换到其他(可能它工作:))的方法。
/** * Adapt calendar to client time zone. * @param calendar - adapting calendar * @param timeZone - client time zone * @return adapt calendar to client time zone */ public static Calendar convertCalendar(final Calendar calendar, final TimeZone timeZone) { Calendar ret = new GregorianCalendar(timeZone); ret.setTimeInMillis(calendar.getTimeInMillis() + timeZone.getOffset(calendar.getTimeInMillis()) - TimeZone.getDefault().getOffset(calendar.getTimeInMillis())); ret.getTime(); return ret; }
date和时间戳对象是不区分时区的:它们代表了自纪元以来的特定秒数,而不是以时间和date对该时刻的特定解释作出承诺。 时区仅在GregorianCalendar (不是直接需要执行此任务)和SimpleDateFormat (需要时区偏移量才能在单独的字段和Date(或long )值之间转换)中input图片。
OP的问题在他的处理开始时是正确的:用户input不明确的小时,并且在当地非GMT时区解释; 此时的值为“6:12 EST” ,可以很容易地打印为“11.12 GMT”或任何其他时区,但不会更改为“6.12 GMT” 。
没有办法使SimpleDateFormatparsing“06:12”为“HH:MM” (默认为本地时区),默认为UTC; SimpleDateFormat对于自己的好处太聪明了。
然而,如果你明确地把它input到input中,你可以说服任何SimpleDateFormat实例使用正确的时区:只需要把一个固定的string添加到接收到的(经过充分validation的) “06:12”来parsing“06:12 GMT” “HH:MM z” 。
不需要显式设置GregorianCalendar字段,也不需要检索和使用时区和夏令时偏移量。
真正的问题是将默认input分隔到本地时区,默认inputUTC,以及确实需要显式时区指示的input。
过去曾经为我工作的东西是确定用户的时区和GMT之间的偏移量(以毫秒为单位)。 一旦你有了抵消,你可以简单地加/减(取决于转换的方式),以获得在任何时区的适当时间。 我通常会通过设置Calendar对象的毫秒字段来实现这一点,但我相信你可以很容易地将它应用到时间戳对象。 这是我用来获得偏移量的代码
int offset = TimeZone.getTimeZone(timezoneId).getRawOffset();
timezoneId是用户时区的标识(如EST)。