Domanda

Ho un valore Timestamp che proviene dalla mia applicazione. L'utente può trovarsi in un determinato fuso orario locale.

Poiché questa data viene utilizzata per un servizio Web che presuppone che l'ora indicata sia sempre in GMT, ho bisogno di convertire il parametro dell'utente da dire (EST) a (GMT). Ecco il kicker: l'utente è ignaro della sua TZ. Inserisce la data di creazione che desidera inviare al WS, quindi quello di cui ho bisogno è:

Utente inserito: 01/05/2008 18:12 (EST)
Il parametro per WS deve essere : 01/05/2008 18:12 (GMT)

So che i TimeStamp dovrebbero sempre essere in GMT per impostazione predefinita, ma quando invio il parametro, anche se ho creato il mio Calendario dal TS (che dovrebbe essere in GMT), le ore sono sempre inattive a meno che l'utente non sia in GMT. Cosa mi sto perdendo?

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

Con il codice precedente, questo è ciò che ottengo di conseguenza (formato breve per una facile lettura):

[1 maggio 2008 23:12]

È stato utile?

Soluzione 2

Grazie a tutti per aver risposto. Dopo un'ulteriore indagine sono arrivato alla risposta giusta. Come menzionato da Skip Head, il TimeStamped che stavo ricevendo dalla mia applicazione veniva adattato al TimeZone dell'utente. Quindi, se l'utente inserisse le 18:12 (EST), otterrei le 14:12 (GMT). Ciò di cui avevo bisogno era un modo per annullare la conversione in modo che l'ora immessa dall'utente fosse l'ora che ho inviato alla richiesta WebServer. Ecco come l'ho realizzato:

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

L'output del codice è: (Utente inserito il 5/1/2008 alle 18:12 (EST)

Fuso orario dell'utente attuale: EST
Scostamento corrente dal GMT (in ore): - 4 (normalmente -5, tranne l'ora legale regolata)
TS da ACP: 2008-05-01 14: 12: 00.0
Data del calendario convertita da TS utilizzando GMT e US_EN Impostazioni internazionali: 01/05/08 18:12 (GMT)

Altri suggerimenti

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

Ecco l'output se passo l'ora corrente (" 12: 09: 05 EDT " da Calendar.getInstance () ) in:

  

DEBUG - il calendario di input ha una data [gio 23 ott 12:09:05 EDT 2008]
  DEBUG - L'offset è -14400000
  DEBUG - Creato GMT cal con data [Gio 23 Ott 08:09:05 EDT 2008]

12:09:05 GMT è 8:09:05 EDT.

La parte confusa qui è che Calendar.getTime () ti restituisce una Date nel tuo fuso orario corrente, e anche che non esiste un metodo per modificare il fuso orario di un calendario e hanno anche la data sottostante rotolata. A seconda del tipo di parametro che il tuo servizio web accetta, potresti voler avere il contratto WS in termini di millisecondi dall'epoca.

Dici che la data viene utilizzata in connessione con i servizi web, quindi suppongo che sia serializzato in una stringa ad un certo punto.

In questo caso, dovresti dare un'occhiata a metodo setTimeZone della classe DateFormat. Ciò determina quale fuso orario verrà utilizzato durante la stampa del timestamp.

Un semplice esempio:

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

Puoi risolverlo con Joda Time :

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

Sembra che TimeStamp sia impostato sul fuso orario del sistema di origine.

Questo è obsoleto, ma dovrebbe funzionare:

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

Il modo non deprecato è usare

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

ma ciò dovrebbe essere fatto sul lato client, dal momento che quel sistema sa in quale fuso orario si trova.

Metodo per la conversione da un fuso orario all'altro (probabilmente funziona :)).

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

?? Data e Timestamp sono ignari del fuso orario: rappresentano un certo numero di secondi dall'epoca, senza impegnarsi per una particolare interpretazione di quell'istante come ore e giorni . I fusi orari inseriscono l'immagine solo in GregorianCalendar (non direttamente necessario per questa attività) e SimpleDateFormat , che richiedono un offset del fuso orario per la conversione tra campi separati e Data (o lungo ) valori.

Il problema del PO è proprio all'inizio della sua elaborazione: l'utente inserisce le ore, che sono ambigue e vengono interpretate nel fuso orario locale, non GMT; a questo punto il valore è " 6: 12 EST " , che può essere facilmente stampato come " 11.12 GMT " o qualsiasi altro fuso orario ma non andrà mai cambia in " 6.12 GMT " .

Non è possibile impostare SimpleDateFormat che analizza " 06: 12 " come " HH: MM " (impostazione predefinita a invece il fuso orario locale) per impostazione predefinita UTC; SimpleDateFormat è un po 'troppo intelligente per il suo bene.

Tuttavia, puoi convincere qualsiasi istanza SimpleDateFormat a utilizzare il fuso orario corretto se lo inserisci esplicitamente nell'input: basta aggiungere una stringa fissa al ricevuto (e adeguatamente convalidato) " ; 06: 12 " per analizzare " 06: 12 GMT " come " HH: MM z " .

Non è necessario impostare esplicitamente i campi Calendario Gregoriano o recuperare e utilizzare le differenze di fuso orario e ora legale.

Il vero problema è la separazione degli input predefiniti nel fuso orario locale, gli input predefiniti in UTC e gli input che richiedono realmente un'indicazione del fuso orario esplicito.

Qualcosa che ha funzionato per me in passato era determinare l'offset (in millisecondi) tra fuso orario dell'utente e GMT. Una volta ottenuto l'offset, puoi semplicemente aggiungere / sottrarre (a seconda del modo in cui sta andando la conversione) per ottenere il tempo appropriato in entrambi i fusi orari. Di solito lo realizzo impostando il campo in millisecondi di un oggetto Calendar, ma sono sicuro che potresti facilmente applicarlo a un oggetto timestamp. Ecco il codice che uso per ottenere l'offset

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

timezoneId è l'id del fuso orario dell'utente (come EST).

java.time

L'approccio moderno utilizza le classi java.time che hanno soppiantato le problematiche classi data-ora legacy in bundle con le prime versioni di Java.

La classe java.sql.Timestamp è una di quelle classi legacy. Non è più necessario. Invece usa Instant o altre classi java.time direttamente con il tuo database usando JDBC 4.2 e versioni successive.

Il Instant rappresenta un momento nella sequenza temporale di UTC con una risoluzione di nanosecondi (fino a nove (9) cifre di una frazione decimale).

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

Se devi interagire con un Timestamp esistente, converti immediatamente in java.time tramite i nuovi metodi di conversione aggiunti alle vecchie classi.

Instant instant = myTimestamp.toInstant() ;

Per adattarsi a un altro fuso orario, specificare il fuso orario come oggetto ZoneId . Specifica un nome del fuso orario corretto nel formato continente / regione , come America / Montreal , Africa / Casablanca o Pacifico / Auckland . Non usare mai le pseudo-zone di 3-4 lettere come EST o IST poiché sono non veri fusi orari, non standardizzati e nemmeno unico(!).

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

Applica al Instant per produrre un oggetto ZonedDateTime .

ZonedDateTime zdt = instant.atZone( z ) ;

Per generare una stringa da visualizzare all'utente, cerca Stack Overflow per DateTimeFormatter per trovare molte discussioni ed esempi.

La tua domanda riguarda davvero la direzione opposta, dall'inserimento dei dati dell'utente agli oggetti data-ora. Generalmente è meglio suddividere l'inserimento dei dati in due parti, una data e un'ora del giorno.

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

La tua domanda non è chiara. Vuoi interpretare la data e l'ora immesse dall'utente in UTC? O in un altro fuso orario?

Se intendevi UTC, crea un OffsetDateTime con un offset usando la costante per UTC, ZoneOffset.UTC .

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

Se intendevi un altro fuso orario, combinalo con un oggetto fuso orario, un ZoneId . Ma quale fuso orario? È possibile rilevare un fuso orario predefinito. Oppure, se critico, devi confermare con l'utente di essere certo delle sue intenzioni.

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

Per ottenere un oggetto più semplice che è sempre in UTC per definizione, estrarre un Instant .

Instant instant = odt.toInstant() ;

& # 8230; o & # 8230;

Instant instant = zdt.toInstant() ; 

Invia al tuo database.

myPreparedStatement.setObject( … , instant ) ;

Informazioni su java.time

Il java.time framework è integrato in Java 8 e versioni successive. Queste classi soppiantano le vecchie e problematiche legacy classi data-ora come java.util.Date , Calendar , & amp ; SimpleDateFormat .

Il Joda-Time , ora in modalità di manutenzione , consiglia la migrazione a classi java.time .

Per saperne di più, consulta il Tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .

Dove ottenere le classi java.time?

Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un banco di prova per possibili aggiunte future a java.time. Puoi trovare alcune lezioni utili qui come Interval , YearWeek , < code> YearQuarter e altro .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top