Convert Date/Time for given Timezone - java

前端 未结 16 2273
孤城傲影
孤城傲影 2020-11-22 12:36

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,

相关标签:
16条回答
  • 2020-11-22 12:54

    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!

    0 讨论(0)
  • 2020-11-22 12:54

    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

    0 讨论(0)
  • 2020-11-22 12:56

    tl;dr

    Instant.ofEpochMilli( 1_317_816_735_000L )
        .atZone( ZoneId.of( "Pacific/Auckland" ) )
        .format( DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( new Locale( "en" , "NZ" ) ) )
    

    …also…

    LocalDateTime.parse( "2011-10-06 03:35:05".replace( " " , "T" ) )
        .atZone( ZoneId.of( "Pacific/Auckland" ) )
    

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

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

    Note that time zone has nothing to do with locale. You can have a Europe/Paris date-time displayed in Japanese language & cultural norms, or a Asia/Kolkata date-time displayed in Portuguese language and Brazil cultural norms.

    About java.time

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

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

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

    Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).

    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.

    0 讨论(0)
  • 2020-11-22 12:56

    I should like to provide the modern answer.

    You shouldn’t really want to convert a date and time from a string at one GMT offset to a string at a different GMT offset and with in a different format. Rather in your program keep an instant (a point in time) as a proper date-time object. Only when you need to give string output, format your object into the desired string.

    java.time

    Parsing input

        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .append(DateTimeFormatter.ISO_LOCAL_DATE)
                .appendLiteral(' ')
                .append(DateTimeFormatter.ISO_LOCAL_TIME)
                .toFormatter();
    
        String dateTimeString = "2011-10-06 03:35:05";
        Instant instant = LocalDateTime.parse(dateTimeString, formatter)
                .atOffset(ZoneOffset.UTC)
                .toInstant();
    

    For most purposes Instant is a good choice for storing a point in time. If you needed to make it explicit that the date and time came from GMT, use an OffsetDateTime instead.

    Converting, formatting and printing output

        ZoneId desiredZone = ZoneId.of("Pacific/Auckland");
        Locale desiredeLocale = Locale.forLanguageTag("en-NZ");
        DateTimeFormatter desiredFormatter = DateTimeFormatter.ofPattern(
                "dd MMM uuuu HH:mm:ss OOOO", desiredeLocale);
    
        ZonedDateTime desiredDateTime = instant.atZone(desiredZone);
        String result = desiredDateTime.format(desiredFormatter);
        System.out.println(result);
    

    This printed:

    06 Oct 2011 16:35:05 GMT+13:00

    I specified time zone Pacific/Auckland rather than the offset you mentioned, +13:00. I understood that you wanted New Zealand time, and Pacific/Auckland better tells the reader this. The time zone also takes summer time (DST) into account so you don’t need to take this into account in your own code (for most purposes).

    Since Oct is in English, it’s a good idea to give the formatter an explicit locale. GMT might be localized too, but I think that it just prints GMT in all locales.

    OOOO in the format patterns string is one way of printing the offset, which may be a better idea than printing the time zone abbreviation you would get from z since time zone abbreviations are often ambiguous. If you want NZDT (for New Zealand Daylight Time), just put z there instead.

    Your questions

    I will answer your numbered questions in relation to the modern classes in java.time.

    Is possible to:

    1. Set the time on an object

    No, the modern classes are immutable. You need to create an object that has the desired date and time from the outset (this has a number of advantages including thread safety).

    1. (Possibly) Set the TimeZone of the initial time stamp

    The atZone method that I use in the code returns a ZonedDateTime with the specified time zone. Other date-time classes have a similar method, sometimes called atZoneSameInstant or other names.

    1. Format the time stamp with a new TimeZone

    With java.time converting to a new time zone and formatting are two distinct steps as shown.

    1. Return a string with new time zone time.

    Yes, convert to the desired time zone as shown and format as shown.

    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?

    It’s not the way you think, which goes nicely to show just a couple of the (many) design problems with the old classes.

    • A Date hasn’t got a time zone. Only when you print it, its toString method grabs your local time zone and uses it for rendering the string. This is true for new Date() too. This behaviour has confused many, many programmers over the last 25 years.
    • A Calender has got a time zone. It doesn’t change when you do calendar.setTime(new Date(1317816735000L));.

    Link

    Oracle tutorial: Date Time explaining how to use java.time.

    0 讨论(0)
  • 2020-11-22 12:58

    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()));
    
    0 讨论(0)
  • 2020-11-22 12:59

    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.

    0 讨论(0)
提交回复
热议问题