Convert Date/Time for given Timezone - java

2018-12-31 19:12发布

问题:

I want to convert this GMT time stamp to GMT+13:

2011-10-06 03:35:05

I have tried about 100 different combinations of DateFormat, TimeZone, Date, GregorianCalendar etc. to try to do this VERY basic task.

This code does what I want for the CURRENT TIME:

Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone(\"GMT\"));

DateFormat formatter = new SimpleDateFormat(\"dd MMM yyyy HH:mm:ss z\");    
formatter.setTimeZone(TimeZone.getTimeZone(\"GMT+13\"));  

String newZealandTime = formatter.format(calendar.getTime());

But what I want is to set the time rather then using the current time.

I found that anytime I try to set the time like this:

calendar.setTime(new Date(1317816735000L));

the local machine\'s TimeZone is used. Why is that? I know that when \"new Date()\" returns UTC+0 time so why when you set the Time in milliseconds does it no longer assume the time is in UTC?

Is possible to:

  1. Set the time on an object (Calendar/Date/TimeStamp)
  2. (Possibly) Set the TimeZone of the initial time stamp (calendar.setTimeZone(...))
  3. Format the time stamp with a new TimeZone (formatter.setTimeZone(...)))
  4. Return a string with new time zone time. (formatter.format(calendar.getTime()))

Thanks in advance for any help :D

回答1:

Understanding how computer time works is very important. With that said I agree that if an API is created to help you process computer time like real time then it should work in such a way that allows you to treat it like real time. For the most part this is the case but there are some major oversights which do need attention.

Anyway I digress!! If you have your UTC offset (better to work in UTC than GMT offsets) you can calculate the time in milliseconds and add that to your timestamp. Note that an SQL Timestamp may vary from a Java timestamp as the way the elapse from the epoch is calculated is not always the same - dependant on database technologies and also operating systems.

I would advise you to use System.currentTimeMillis() as your time stamps as these can be processed more consistently in java without worrying about converting SQL Timestamps to java Date objects etc.

To calculate your offset you can try something like this:

Long gmtTime =1317951113613L; // 2.32pm NZDT
Long timezoneAlteredTime = 0L;

if (offset != 0L) {
    int multiplier = (offset*60)*(60*1000);
    timezoneAlteredTime = gmtTime + multiplier;
} else {
    timezoneAlteredTime = gmtTime;
}

Calendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(timezoneAlteredTime);

DateFormat formatter = new SimpleDateFormat(\"dd MMM yyyy HH:mm:ss z\");

formatter.setCalendar(calendar);
formatter.setTimeZone(TimeZone.getTimeZone(timeZone));

String newZealandTime = formatter.format(calendar.getTime());

I hope this is helpful!



回答2:

For me, the simplest way to do that is:

Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd hh:mm:ss\");

//Here you say to java the initial timezone. This is the secret
sdf.setTimeZone(TimeZone.getTimeZone(\"UTC\"));
//Will print in UTC
System.out.println(sdf.format(calendar.getTime()));    

//Here you set to your timezone
sdf.setTimeZone(TimeZone.getDefault());
//Will print on your default Timezone
System.out.println(sdf.format(calendar.getTime()));


回答3:

As always, I recommend reading this article about date and time in Java so that you understand it.

The basic idea is that \'under the hood\' everything is done in UTC milliseconds since the epoch. This means it is easiest if you operate without using time zones at all, with the exception of String formatting for the user.

Therefore I would skip most of the steps you have suggested.

  1. Set the time on an object (Date, Calendar etc).
  2. Set the time zone on a formatter object.
  3. Return a String from the formatter.

Alternatively, you can use Joda time. I have heard it is a much more intuitive datetime API.



回答4:

tl;dr

If given input of 1317816735000L

Instant.ofEpochMilli( 1_317_816_735_000L )     // Represent a moment in UTC using a count of milliseconds since the epoch reference of 1970-01-01T00:00Z.
.atZone( ZoneId.of( \"Pacific/Auckland\" ) )     // Adjust from UTC into the wall-clock time used by the people of a certain region (a time zone). Returns a `ZonedDateTime` object.
.format(                 
    DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM )
    .withLocale( new Locale( \"en\" , \"NZ\" ) )   // Locale specifies the human language and cultural norms used in localization.
)                                              // Returns a `String` object that represents the value of our `ZonedDateTime` object’s value.

If given input of 2011-10-06 03:35:05

LocalDateTime.parse(                  // Parse input string lacking any indicator of time zone or offset-from-UTC using the `LocalDateTime` class.
    \"2011-10-06 03:35:05\"
    .replace( \" \" , \"T\" )             // Comply with ISO 8601 standard by replacing SPACE in the middle with a `T`.
)                                     // Returns a `LocalDateTime` object.
.atZone(                              // Adjust from UTC into the wall-clock time used by the people of a certain region (a time zone). Returns a `ZonedDateTime` object.
    ZoneId.of( \"Pacific/Auckland\" )   // Always specify a time zone by `Continent/Region` name. Never use 3-4 pseudo-time-zones such as `PST`, `CST`, or `IST`.
)                                     // Returns a `ZonedDateTime` object.

java.time

The Question and most Answers use outdated legacy date-time classes from the earliest versions of Java. These old classes have proven to be troublesome and confusing. Avoid them. Instead use the java.time classes.

ISO 8601

Your input string is nearly in standard ISO 8601 format. Just replace the SPACE in the middle with a T.

String input = \"2011-10-06 03:35:05\".replace( \" \" , \"T\" ) ;  // Comply with ISO 8601 standard format by replacing the SPACE with a `T`.

The java.time classes use these standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.

LocalDateTime

Now parse as a LocalDateTime because the input lacks any information about offset-from-UTC or time zone. A LocalDateTime has no concept of offset nor time zone, so it does not represent an actual moment on the timeline.

LocalDateTime ldt = LocalDateTime.parse( input );

ZoneOffset

You seem to be saying that from the business context you know the intention of this string is to represent a moment that is 13 hours ahead of UTC. So we instantiate a ZoneOffset.

ZoneOffset offset = ZoneOffset.ofHours( 13 ); // 13 hours ahead of UTC, in the far east of the globe.

OffsetDateTime

Apply it to get an OffsetDateTime object. This becomes an actual moment on the timeline.

OffsetDateTime odt = ldt.atOffset( offset);

ZoneId

But then you mention New Zealand. So you had a specific time zone in mind. A time zone is an offset-from-UTC plus a set of rules for handling anomalies such as Daylight Saving Time (DST). So we can specify a ZoneId to a ZonedDateTime rather than a mere offset.

Specify a proper time zone name. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!). For example, Pacific/Auckland.

ZoneId z = ZoneId.of( \"Pacific/Auckland\" );

ZonedDateTime

Apply the ZoneId.

ZonedDateTime zdt = ldt.atZone( z );

You can easily adjust into another zone for the very same moment on the timeline.

ZoneId zParis = ZoneId.of( \"Europe/Paris\" );
ZonedDateTime zdtParis = zdt.withZoneSameInstant( zParis );  // Same moment in time, but seen through lens of Paris wall-clock time.

Count from epoch

I strongly recommend against handling date-time values as a count from epoch, such as milliseconds from the start of 1970 UTC. But if you must, create a Instant from such a number.

Instant instant = Instant.ofEpochMilli( 1_317_816_735_000L );

Then assign a time zone as seen above, if desired, to move away from UTC.

ZoneId z = ZoneId.of( \"Pacific/Auckland\" );
ZonedDateTime zdt = instant.atZone( z );

Your value of 1_317_816_735_000L is:

  • 2011-10-05T12:12:15Z (Wed, 05 Oct 2011 12:12:15 GMT)
  • 2011-10-06T01:12:15+13:00[Pacific/Auckland] (Thursday October 06, 2011 01:12:15 in Auckland New Zealand).

Generate strings

To generate a string in standard ISO 8601 format, simply call toString. Note that ZonedDateTime wisely extends the standard format by appending the name of the time zone in square brackets.

String output = zdt.toString();

For other formats, search Stack Overflow for DateTimeFormatter class. Already covered many times.

Specify a FormatStyle and a Locale.

Locale l = new Locale( \"en\" , \"NZ\" );
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( l );
String output = zdt.format( f );

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

  • Java SE 8, Java SE 9, Java SE 10, and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.



回答5:

Had a look about and I don\'t think theres a timezone in Java that is GMT + 13. So I think you have to use:

Calendar calendar = Calendar.getInstance();
//OR Calendar.getInstance(TimeZone.getTimeZone(\"GMT\"));

calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY)+13);

Date d = calendar.getTime();

(If there is then change \"GMT\" to that Timezone and remove the 2nd line of code)

OR

SimpleDateFormat df = new SimpleDateFormat();
df.setTimeZone(TimeZone.getTimeZone(\"GMT+13\"));
System.out.println(df.format(c.getTime()));

If you want to set a specific time/date you can also use:

    calendar.set(Calendar.DATE, 15);
calendar.set(Calendar.MONTH, 3);
calendar.set(Calendar.YEAR, 2011);
calendar.set(Calendar.HOUR_OF_DAY, 13); 
calendar.set(Calendar.MINUTE, 45);
calendar.set(Calendar.SECOND, 00);


回答6:

The solution is actually quite simple (pure, simple Java):

System.out.println(\" NZ Local Time: 2011-10-06 03:35:05\");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\");
LocalDateTime localNZ = LocalDateTime.parse(\"2011-10-06 03:35:05\",formatter);
ZonedDateTime zonedNZ = ZonedDateTime.of(localNZ,ZoneId.of(\"+13:00\"));
LocalDateTime localUTC = zonedNZ.withZoneSameInstant(ZoneId.of(\"UTC\")).toLocalDateTime();
System.out.println(\"UTC Local Time: \"+localUTC.format(formatter));

OUTPUT IS:

 NZ Local Time: 2011-10-06 03:35:05
UTC Local Time: 2011-10-05 14:35:05


回答7:

Joda-Time

The java.util.Date/Calendar classes are a mess and should be avoided.

Update: The Joda-Time project is in maintenance mode. The team advises migration to the java.time classes.

Here\'s your answer using the Joda-Time 2.3 library. Very easy.

As noted in the example code, I suggest you use named time zones wherever possible so that your programming can handle Daylight Saving Time (DST) and other anomalies.

If you had placed a T in the middle of your string instead of a space, you could skip the first two lines of code, dealing with a formatter to parse the string. The DateTime constructor can take a string in ISO 8601 format.

// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;

// Parse string as a date-time in UTC (no time zone offset).
DateTimeFormatter formatter = org.joda.time.format.DateTimeFormat.forPattern( \"yyyy-MM-dd\' \'HH:mm:ss\" );
DateTime dateTimeInUTC = formatter.withZoneUTC().parseDateTime( \"2011-10-06 03:35:05\" );

// Adjust for 13 hour offset from UTC/GMT.
DateTimeZone offsetThirteen = DateTimeZone.forOffsetHours( 13 );
DateTime thirteenDateTime = dateTimeInUTC.toDateTime( offsetThirteen );

// Hard-coded offsets should be avoided. Better to use a desired time zone for handling Daylight Saving Time (DST) and other anomalies.
// Time Zone list… http://joda-time.sourceforge.net/timezones.html
DateTimeZone timeZoneTongatapu = DateTimeZone.forID( \"Pacific/Tongatapu\" );
DateTime tongatapuDateTime = dateTimeInUTC.toDateTime( timeZoneTongatapu );

Dump those values…

System.out.println( \"dateTimeInUTC: \" + dateTimeInUTC );
System.out.println( \"thirteenDateTime: \" + thirteenDateTime );
System.out.println( \"tongatapuDateTime: \" + tongatapuDateTime );

When run…

dateTimeInUTC: 2011-10-06T03:35:05.000Z
thirteenDateTime: 2011-10-06T16:35:05.000+13:00
tongatapuDateTime: 2011-10-06T16:35:05.000+13:00


回答8:

I have try this code

try{
            SimpleDateFormat sdf = new SimpleDateFormat(\"dd-MM-yyyy hh:mm:ss Z\");
            Date datetime = new Date();

            System.out.println(\"date \"+sdf.format(datetime));

            sdf.setTimeZone(TimeZone.getTimeZone(\"GMT\"));

            System.out.println(\"GMT \"+ sdf.format(datetime));

            sdf.setTimeZone(TimeZone.getTimeZone(\"GMT+13\"));

            System.out.println(\"GMT+13 \"+ sdf.format(datetime));

            sdf.setTimeZone(TimeZone.getTimeZone(\"UTC\"));

            System.out.println(\"utc \"+sdf.format(datetime));

            Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone(\"GMT\"));

            DateFormat formatter = new SimpleDateFormat(\"dd MMM yyyy HH:mm:ss z\");    
            formatter.setTimeZone(TimeZone.getTimeZone(\"GMT+13\"));  

            String newZealandTime = formatter.format(calendar.getTime());

            System.out.println(\"using calendar \"+newZealandTime);

        }catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

and getting this result

date 06-10-2011 10:40:05 +0530
GMT 06-10-2011 05:10:05 +0000 // here getting 5:10:05
GMT+13 06-10-2011 06:10:05 +1300 // here getting 6:10:05
utc 06-10-2011 05:10:05 +0000
using calendar 06 Oct 2011 18:10:05 GMT+13:00


回答9:

We can handle this by using offset value

 public static long convertDateTimeZone(long lngDate, String fromTimeZone,
        String toTimeZone){
    TimeZone toTZ = TimeZone.getTimeZone(toTimeZone);
    Calendar toCal = Calendar.getInstance(toTZ);        

    TimeZone fromTZ = TimeZone.getTimeZone(fromTimeZone);
    Calendar fromCal = Calendar.getInstance(fromTZ);
    fromCal.setTimeInMillis(lngDate);
    toCal.setTimeInMillis(fromCal.getTimeInMillis()
            + toTZ.getOffset(fromCal.getTimeInMillis())
            - TimeZone.getDefault().getOffset(fromCal.getTimeInMillis()));      
    return toCal.getTimeInMillis();
}

Test Code snippet:

 System.out.println(new Date().getTime())
 System.out.println(convertDateTimeZone(new Date().getTime(), TimeZone
                .getDefault().getID(), \"EST\"));

Output: 1387353270742 1387335270742



回答10:

We can get the UTC/GMT time stamp from the given date.

/**
 * Get the time stamp in GMT/UTC by passing the valid time (dd-MM-yyyy HH:mm:ss)
 */
public static long getGMTTimeStampFromDate(String datetime) {
    long timeStamp = 0;
    Date localTime = new Date();

    String format = \"dd-MM-yyyy HH:mm:ss\";
    SimpleDateFormat sdfLocalFormat = new SimpleDateFormat(format);
    sdfLocalFormat.setTimeZone(TimeZone.getDefault());

    try {

        localTime = (Date) sdfLocalFormat.parse(datetime); 

        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(\"UTC\"),
                Locale.getDefault());
        TimeZone tz = cal.getTimeZone();

        cal.setTime(localTime);

        timeStamp = (localTime.getTime()/1000);
        Log.d(\"GMT TimeStamp: \", \" Date TimegmtTime: \" + datetime
                + \", GMT TimeStamp : \" + localTime.getTime());

    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return timeStamp;

}

It will return the UTC time based on passed date.

  • We can do reverse like UTC time stamp to current date and time(vice versa)

        public static String getLocalTimeFromGMT(long gmtTimeStamp) {
                 try{
                        Calendar calendar = Calendar.getInstance();
                        TimeZone tz = TimeZone.getDefault();
                        calendar.setTimeInMillis(gmtTimeStamp * 1000);
        //              calendar.add(Calendar.MILLISECOND, tz.getOffset(calendar.getTimeInMillis())); 
                        SimpleDateFormat sdf = new SimpleDateFormat(\"dd-MM-yyyy HH:mm:ss\");
                        Date currenTimeZone = (Date) calendar.getTime();
                        return sdf.format(currenTimeZone);
                    }catch (Exception e) {
                    }
                    return \"\";  
                }
    

I hope this will help others. Thanks!!



回答11:

A quick way is :

String dateText =\"Thu, 02 Jul 2015 21:51:46\";
long hours = -5; // time difference between places

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(E, dd MMM yyyy HH:mm:ss, Locale.ENGLISH);     
LocalDateTime date = LocalDateTime.parse(dateText, formatter);        
date = date.with(date.plusHours(hours));

System.out.println(\"NEW DATE: \"+date);

Output

NEW DATE: 2015-07-02T16:51:46



回答12:

public Timestamp convertLocalTimeToServerDatetime(String dt,String timezone){

    String clientDnT = dt ;// \"2017-06-01 07:20:00\";
    try{
    SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
    Date date = sdf.parse(clientDnT);
    TimeZone tz = TimeZone.getTimeZone(timezone.trim()); // get time zone of user
    sdf.setTimeZone(tz);

    // Convert to servertime zone 
    SimpleDateFormat sdf1 = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
    TimeZone tzInAmerica = TimeZone.getDefault();
    sdf1.setTimeZone(tzInAmerica);

    // assign date to date
    String serverDate = sdf1.format(date);

    // Convert to servertime zone to Timestamp
    Date date2 =  sdf.parse(serverDate);
    Timestamp tsm = new Timestamp(date2.getTime());
    return  tsm;
    }
    catch(Exception e){
        System.err.println(e);
    }

    return null;
}


回答13:

Some easy way. One important thing to remember that Date does not maintain the timezone, its only long millis number, as it treated here.

/**
 * Converts source in GMT+0 to timezone specified by server
 * 
 * @throws IllegalStateException if server timezone wasnt set
 */
public static Date convertGmt(Date source)
{
    if (timeZoneServer == null)
    {
        throw new IllegalStateException(\"Server timezone wasnt set\");
    }

    long rawOffset = timeZoneServer.getRawOffset();

    Date dest = new Date(source.getTime() + rawOffset);

    return dest;
}

/**
 * Converts source in device timezone format to GMT
 */
public static Date convertToGmt(Date source)
{
    int rawOffset = TimeZone.getDefault().getRawOffset();

    Date dest = new Date(source.getTime() - rawOffset);

    return dest;
}

TimezoneServer is something like TimeZone timeZoneServer = TimeZone.getTimeZone(\"GMT+1\")



回答14:

Your approach works without any modification.

Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(\"GMT\"));
// Timestamp for 2011-10-06 03:35:05 GMT
calendar.setTime(new Date(1317872105000L));

DateFormat formatter = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss z\"); 
formatter.setTimeZone(TimeZone.getTimeZone(\"GMT+13\"));

// Prints 2011-10-06 16:35:05 GMT+13:00
System.out.println(formatter.format(calendar.getTime()));