Pergunta

Eu tenho um valor da hora que vem da minha aplicação. O usuário pode estar em qualquer fuso horário local.

Uma vez que esta data é usada para um WebService que assume o tempo fornecido é sempre em GMT, eu tenho uma necessidade de converter parâmetro do usuário a partir digamos (EST) para (GMT). Aqui está o kicker: O usuário é alheio à sua TZ. Ele entra a data de criação que ele quer enviar para o WS, então o que eu preciso é:

O usuário digita: 5/1/2008 6:12 (EST)
O parâmetro para as necessidades WS ser : 5/1/2008 18:12 (GMT)

Eu sei TimeStamps são sempre deveria estar em GMT por padrão, mas ao enviar o parâmetro, mesmo que eu criei o meu Calendário dos TS (que é suposto estar em GMT), as horas são sempre desligado a menos que o usuário é em GMT. O que eu estou ausente?

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

Com o Código anterior, isso é o que eu recebo como resultado (Formato Curto para facilitar a leitura):

[01 maio de 2008 11:12]

Foi útil?

Solução 2

Obrigado a todos por responder. Após uma investigação mais aprofundada cheguei à resposta certa. Como mencionado por Skip Head, o timestamped eu estava ficando do meu aplicativo estava sendo ajustado para TimeZone do usuário. Portanto, se o usuário entrou 6:12 PM (EST) gostaria de obter 14:12 (GMT). O que eu precisava era uma maneira de desfazer a conversão para que o tempo digitado pelo usuário é o momento que enviei ao pedido WebServer. Aqui está como eu conseguido isso:

// 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()));

A saída do código é: (User entrou 5/1/2008 6:12 (EST)

Usuário do atual fuso horário: EST
Corrente de GMT (em horas): - 4 (-5 Normalmente, excepto é ajustado DST)
TS de ACP: 2008-05-01 14: 12: 00.0
Calendário Data convertido a partir de TS usando GMT e US_EN Locale: 5/1/08 06:12 (GMT)

Outras dicas

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

Aqui está a saída se eu passar o tempo atual ( "12:09:05 EDT" de Calendar.getInstance()) em:

DEBUG - calendário de entrada tem data [Qui 23 outubro 12:09:05 EDT 2008]
DEBUG - offset é -14400000
DEBUG - Criado GMT cal com data [Qui 23 outubro 08:09:05 EDT 2008]

12:09:05 GMT é 08:09:05 EDT.

A parte confusa aqui é que Calendar.getTime() Retorna um Date em seu fuso horário atual, e também que não existe um método para modificar o fuso horário de um calendário e ter a data subjacente rolou também. Dependendo do tipo de parâmetro seu serviço web leva, a sua pode apenas querer ter o negócio WS em termos de milissegundos de época.

Você diz que a data é usada em conexão com serviços web, assim que eu supor que é serializado em uma corda em algum ponto.

Se este for o caso, você deve dar uma olhada no método setTimeZone da classe DateFormat. Este ditames que o fuso horário que será usado ao imprimir o carimbo de tempo.

Um exemplo simples:

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());

Você pode resolvê-lo com Joda Tempo :

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"));

Parece que o seu TimeStamp está sendo definido para o fuso horário do sistema de origem.

Esta é obsoleto, mas deve funcionar:

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

A forma não obsoleta é usar

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

mas que precisa ser feito no lado do cliente, uma vez que o sistema sabe o fuso horário é no.

Método para converter de um fuso-horário para outra (provavelmente ele funciona :)).

/**
 * 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;
}

Data e Timestamp objetos são fuso horário-alheio: eles representam um determinado número de segundos desde a época, sem se comprometer com uma interpretação particular desse instante, como horas e dias . Fusos horários entram em cena apenas em GregorianCalendar (não diretamente necessários para esta tarefa) e SimpleDateFormat , que precisa de uma compensação para converter entre os campos e Data separados (fuso horário ou longa ) valores.

O problema do OP é logo no início de sua transformação: as horas de entradas de usuários, que são ambíguas, e eles são interpretados na GMT não fuso horário local,; Neste ponto, o valor é "06:12 EST" , que pode ser facilmente impresso como "11.12 GMT" ou qualquer outro fuso horário, mas nunca vai mudar e "6.12 GMT" .

Não há nenhuma maneira de fazer o SimpleDateFormat que analisa "6:12" como "HH: MM" (falta ao local, fuso horário) padrão para UTC em vez; SimpleDateFormat é um pouco esperto demais para seu próprio bem.

No entanto, você pode convencer qualquer SimpleDateFormat instância para usar o fuso horário certo, se você colocá-lo explicitamente na entrada: basta adicionar uma corda fixa ao recebido (e adequadamente validado) " 06:12 " para analisar " 06:12 GMT " como " HH: MM z ".

Não há necessidade de definição explícita de GregorianCalendar campos ou de recuperação e usando fuso horário e horário de verão deslocamentos de tempo.

O problema real é segregar insumos que padrão para o fuso horário local, insumos que padrão a UTC e insumos que realmente exigem uma indicação fuso horário explícito.

Algo que tem trabalhado para mim no passado foi determinar o offset (em milissegundos) entre fuso horário do usuário e GMT. Depois de ter o deslocamento, você pode simplesmente adicionar / subtrair (dependendo da maneira que a conversão está acontecendo) para obter o momento apropriado em qualquer fuso horário. Eu costumo fazer isso definindo o campo milissegundos de um objeto de calendário, mas eu tenho certeza que você poderia facilmente aplicá-la a um objeto timestamp. Aqui está o código que eu uso para obter o deslocamento

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

TimeZoneID é o ID do fuso horário do usuário (como EST).

java.time

A abordagem moderna usa os java.time classes que suplantaram os legados aulas de data e hora problemáticos empacotados com as primeiras versões do Java.

A classe java.sql.Timestamp é uma dessas classes de legados. Não é mais necessário. Em vez disso use Instant ou outro java.time classes diretamente com seu banco de dados usando JDBC 4.2 e posterior.

O Instant classe representa um momento na linha do tempo em UTC com uma resolução de nanossegundos (até nove (9) dígitos de uma fracção decimal).

Instant instant = myResultSet.getObject( … , Instant.class ) ; 

Se você deve interoperar com um Timestamp existente, converter imediatamente em java.time através dos novos métodos de conversão acrescentado as classes de idade.

Instant instant = myTimestamp.toInstant() ;

Para ajustar em outro fuso horário, especifique o fuso horário como um objeto ZoneId. Especifique um tempo apropriado nome de zona no formato de continent/region, como America/Montreal , Africa/Casablanca , ou Pacific/Auckland. Nunca use os pseudo-zonas 3-4 carta como EST ou IST como eles são não fusos horários verdadeiros, não padronizados, e nem mesmo únicos (!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;

Aplicar ao Instant para produzir um objeto ZonedDateTime.

ZonedDateTime zdt = instant.atZone( z ) ;

Para gerar uma seqüência de exibição para o usuário, pesquisa Stack Overflow para DateTimeFormatter encontrar muitas discussões e exemplos.

A sua pergunta é realmente sobre ir na outra direção, a partir de dados do usuário-entrada para os objetos de data e hora. Geralmente melhor para quebrar o seu de entrada de dados em duas partes, uma data e uma hora do dia.

LocalDate ld = LocalDate.parse( dateInput , DateTimeFormatter.ofPattern( "M/d/uuuu" , Locale.US ) ) ;
LocalTime lt = LocalTime.parse( timeInput , DateTimeFormatter.ofPattern( "H:m a" , Locale.US ) ) ;

Sua pergunta não é clara. Você quer interpretar a data ea hora inseridas pelo usuário para a UTC? Ou em outro fuso horário?

Se você quis dizer UTC, criar um OffsetDateTime com um deslocamento usando a constante para UTC, ZoneOffset.UTC.

OffsetDateTime odt = OffsetDateTime.of( ld , lt , ZoneOffset.UTC ) ;

Se você significou outro fuso horário, combinam juntamente com uma zona de objeto de tempo, um ZoneId. Mas qual fuso horário? Você pode detectar um fuso horário padrão. Ou, se crítico, você deve confirmar com o usuário para ter certeza da sua intenção.

ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;

Para obter um objeto simples que está sempre em UTC, por definição, extrair um Instant.

Instant instant = odt.toInstant() ;

... ou ...

Instant instant = zdt.toInstant() ; 

Enviar para o seu banco de dados.

myPreparedStatement.setObject( … , instant ) ;

Sobre java.time

O java.time quadro é construído em Java 8 e posterior. Essas classes suplantar o problemático idade legados aulas de data e hora, como java.util.Date , Calendar , e SimpleDateFormat .

O Joda-Time projeto, agora em manutenção mode , aconselha a migração para a java.time classes.

Para saber mais, consulte a a Oracle Tutorial . E busca Stack Overflow para muitos exemplos e explicações. Especificação é JSR 310 .

Onde obter as classes java.time?

O ThreeTen-Extra projeto estende java.time com aulas adicionais. Este projeto é um campo de provas para possíveis adições futuras para java.time. Você pode encontrar algumas classes úteis aqui, como Interval , YearWeek , YearQuarter , e mais .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top