95

我有一个来自我的应用程序的时间戳值。用户可以在任何给定的本地时区。

由于此日期用于假定给定时间始终为 GMT 的 WebService,因此我需要将用户的参数从(EST)转换为(GMT)。这是踢球者:用户忘记了他的 TZ。他输入了他想发送给 WS 的创建日期,所以我需要的是:

用户输入: 5/1/2008 6:12 PM (EST)
WS 的参数需要是:5/1/2008 6:12 PM (GMT)

我知道默认情况下 TimeStamps 总是应该在 GMT 中,但是在发送参数时,即使我从 TS(应该在 GMT 中)创建了我的日历,除非用户在 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]

4

9 回答 9

62
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;
}

如果我将当前时间(“12:09:05 EDT” from Calendar.getInstance())传递到以下位置,则输出如下:

调试 - 输入日历的日期 [Thu Oct 23 12:09:05 EDT 2008]
调试 - 偏移量为 -14400000
调试 - 使用日期创建 GMT 校准 [Thu Oct 23 08:09:05 EDT 2008]

格林威治标准时间 12:09:05 是美国东部时间 8:09:05。

这里令人困惑的部分是它会在您当前的时区中Calendar.getTime()返回 a Date,并且没有任何方法可以修改日历的时区并滚动基础日期。根据您的 Web 服务采用哪种类型的参数,您可能只想让 WS 以从纪元开始的毫秒数为单位进行处理。

于 2008-10-23T16:17:38.410 回答
29

谢谢大家的回复。经过进一步的调查,我得到了正确的答案。正如 Skip Head 所提到的,我从应用程序中获得的时间戳正在调整为用户的时区。因此,如果用户输入 6:12 PM (EST) 我会得到 2:12 PM (GMT)。我需要的是一种撤消转换的方法,这样用户输入的时间就是我发送到 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()));

代码的输出是:(用户输入 5/1/2008 6:12PM (EST)

当前用户的时区:EST
当前与 GMT 的偏移量(以小时为单位):-4(通常为 -5,DST 调整除外)
ACP 的 TS:2008-05-01 14:12:00.0
使用 GMT 和 US_EN 区域设置从 TS 转换的日历日期: 2008 年 5 月 1 日下午 6:12(格林威治标准时间)

于 2008-10-23T18:03:18.260 回答
20

您说日期与 Web 服务一起使用,所以我假设它在某个时候被序列化为字符串。

如果是这种情况,您应该查看 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());
于 2009-02-02T12:41:36.283 回答
12

您可以使用Joda Time解决它:

Date utcDate = new Date(timezoneFrom.convertLocalToUTC(date.getTime(), false));
Date localDate = new Date(timezoneTo.convertUTCToLocal(utcDate.getTime()));

爪哇 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"));
于 2011-12-25T04:03:48.943 回答
8

看起来您的时间戳被设置为原始系统的时区。

这已被弃用,但它应该可以工作:

cal.setTimeInMillis(ts_.getTime() - ts_.getTimezoneOffset());

不推荐使用的方法是使用

Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)

但这需要在客户端完成,因为该系统知道它所在的时区。

于 2008-10-23T16:01:23.013 回答
7

从一个 timeZone 转换为另一个 timeZone 的方法(可能有效:))。

/**
 * 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;
}
于 2009-04-06T09:42:02.693 回答
6

DateTimestamp对象与时区无关:它们表示自纪元以来的特定秒数,而无需将该时刻的特定解释为小时和天。时区仅在GregorianCalendar(此任务不直接需要)和SimpleDateFormat中输入图片,它们需要时区偏移量才能在单独的字段和 Date (或long)值之间进行转换。

OP 的问题就在他的处理开始时:用户输入的小时数不明确,并且在本地非 GMT 时区进行解释;此时值为"6:12 EST",可以轻松打印为"11.12 GMT"或任何其他时区,但永远不会更改"6.12 GMT"

无法使将“06:12”解析为“HH:MM”(默认为本地时区)的SimpleDateFormat改为默认为 UTC;SimpleDateFormat本身就有点太聪明了。

但是,您可以说服任何SimpleDateFormat实例使用正确的时区,如果您将它明确地放在输入中:只需将一个固定字符串附加到接收(并充分验证)“06:12”以将“06:12 GMT”解析为“HH:MM z”

不需要显式设置GregorianCalendar字段或检索和使用时区和夏令时偏移。

真正的问题是分离默认为本地时区的输入、默认为 UTC 的输入以及真正需要明确时区指示的输入。

于 2012-02-06T11:28:47.217 回答
4

过去对我有用的是确定用户时区和 GMT 之间的偏移量(以毫秒为单位)。获得偏移量后,您可以简单地加/减(取决于转换的方式)以获得任一时区的适当时间。我通常会通过设置 Calendar 对象的毫秒字段来完成此操作,但我相信您可以轻松地将其应用于时间戳对象。这是我用来获取偏移量的代码

int offset = TimeZone.getTimeZone(timezoneId).getRawOffset();

timezoneId 是用户所在时区的 id(如 EST)。

于 2008-10-23T15:59:25.107 回答
2
于 2018-01-26T06:43:50.537 回答