Parse and retrieve timezone offset from date-time

后端 未结 4 397
深忆病人
深忆病人 2020-12-22 05:37

Date format: \"yyyy-MM-dd\'T\'HH:mm:ss.SSSZ\"

Input date: \"2017-09-18T03:08:20.888+0200\"

Problem: I need retrieve timezon

相关标签:
4条回答
  • 2020-12-22 06:21

    SimpleDateFormat extends DateFormat and thus internally uses a Calendar. When parsing the date that calendar is being updated so you can get the timezone from it after parsing:

    //use the timezone of the internally stored calendar
    outputSdf.setTimeZone( inputSdf.getTimezone() );
    

    That also shows why DateFormat is not threadsafe.

    EDIT:

    It seems the internal calendar's timezone isn't updated but the ZONE_OFFSET field is. Hence you could do something like this:

    int zoneOffset = inputSdf.getCalendar().get( Calendar.ZONE_OFFSET );
    //length check etc. left for you
    String matchingZoneId = TimeZone.getAvailableIDs( zoneOffset )[0];
    outputSdf.setTimeZone( TimeZone.getTimeZone( matchingZoneId ) );
    

    Note that you can't just set the zone offset of the output format since that won't update the timezone reference which is used when formatting.

    As you can see doing it this way looks a little "hacky" and thus you should think hard on whether you really need the timezone. In most cases you'd define the output timezone in a different way anyways, e.g. by getting the user's location, input, etc.

    0 讨论(0)
  • 2020-12-22 06:27

    Thank you, guys: @Thomas, @ole-v-v

    final DateTimeFormatter inputSdf1 = DateTimeFormatter.ofPattern(dateTimeTimezoneFormat);
    OffsetDateTime d = OffsetDateTime.parse(inputDate, inputSdf1);
    
    ZoneOffset zo = d.getOffset();  //Offset from the input.
    TimeZone tz = TimeZone.getTimeZone(zo.normalized());
    
    outputSdf.setTimeZone(tz);
    System.out.println(outputSdf.format(parsedDate));
    
    0 讨论(0)
  • 2020-12-22 06:27

    Hardcore solution!

    Parse timezone and retrieve hours and minutes and sign and build a timezone!

    public class ParseTimeZone {
    
        private static final String TIME_ZONE_REGEX = "([+-])(\\d{2})[:]?(\\d{2})$";
        private static final Pattern TIME_ZONE_PATTERN = Pattern.compile(TIME_ZONE_REGEX);
    
        private static final int SING_GROUP = 1;
        private static final int HOURS_GROUP = 2;
        private static final int MINUTES_GROUP = 3;
    
        private static final String PLUS = "+";
        private static final String MINUS = "-";
    
        private static final int MINUTES_IN_HOUR = 60;
        private static final int SECONDS_IN_MINUTE = 60;
    
        public static void main(String[] args) {
            final String inputDate = "2017-09-18T01:08:20.888Z";
            parseTimeZone(inputDate);
        }
    
        private static String parseTimeZone(String input) {
            input = fixDateStringWithZeroZoneOffset(input);
            Matcher matcher = TIME_ZONE_PATTERN.matcher(input);
            if (!matcher.find()) {
                return "";
            }
    
            String sign = matcher.group(SING_GROUP);
            String hours = matcher.group(HOURS_GROUP);
            String minutes = matcher.group(MINUTES_GROUP);
    
            int offsetSeconds = calculateOffsetSeconds(sign, hours, minutes);
    
            ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(offsetSeconds);
            System.out.println(zoneOffset);
    
            TimeZone timeZone = TimeZone.getTimeZone(zoneOffset);
            System.out.println(timeZone);
    
            return "";
        }
    
        private static int calculateOffsetSeconds(String signStr, String hoursStr, String minutesStr) {
            try {
                int hours = Integer.parseInt(hoursStr);
                int minutes = Integer.parseInt(minutesStr);
                int sign = parseSign(signStr);
    
                int seconds = sign * ((hours * MINUTES_IN_HOUR + minutes) * SECONDS_IN_MINUTE);
                return seconds;
    
            } catch (NumberFormatException e) {
                throw new RuntimeException("It should not happen because it matches the regular expression. There should be numbers.", e);
            }
        }
    
        private static int parseSign(String sign) {
            if (sign.equals(PLUS)) {
                return 1;
            } else if (sign.equals(MINUS)) {
                return -1;
            } else {
                throw new RuntimeException("Offset sign should be + or -.");
            }
        }
    
        private static String fixDateStringWithZeroZoneOffset(String dateString) {
            if (dateString.endsWith("Z")) {
                return dateString.replaceAll("Z$", "+0000");
            }
            return dateString;
        }
    }
    
    0 讨论(0)
  • 2020-12-22 06:29

    tl;dr

    TimeZone.getTimeZone(       // Convert from modern java.time type (`ZoneOffset`/`ZoneId`) to legacy type (`TimeZone`)
        OffsetDateTime.parse (  // Instantiate a `OffsetDateTime`, a moment on the timeline.
            "2017-09-18T03:08:20.888+0200" ,
            DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" )
        ).getOffset()           // Extract a `ZoneOffset`, which is a subclass of `ZoneId`.
    )
    

    Convert directly from modern ZoneOffset to legacy TimeZone

    The code seen here is similar to Answers by Yan Khonski, but using the variation of TimeZone.getTimeZone that directly converts from the modern java.time classes (ZoneOffset & ZoneID) to the legacy TimeZone class.

    While there is no difference in the end result, this approach uses a an explicit conversion method. This is one of many new methods added to the old date-time classes for converting to/from java.time objects.

    Using such a conversion method makes your code more self-documenting. Also makes more clear your awareness that you are consciously moving between the modern & legacy classes.

    String input = "2017-09-18T03:08:20.888+0200";
    DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" );
    
    OffsetDateTime odt = OffsetDateTime.parse( input , f );  // Parse input.
    ZoneOffset offset = odt.getOffset( );                    // Interrogate for the `ZoneOffset` object representing this moment’s offset-from-UTC (a number of hours/minutes/seconds).
    
    TimeZone tz = TimeZone.getTimeZone( offset );            // Convert from modern java.time object (a `ZoneOffset`/`ZoneId`) to the legacy class `TimeZone`.
    

    Dump to console.

    System.out.println( "odt.toString(): " + odt );
    System.out.println( "offset.toString(): " + offset );
    System.out.println( "tz.toString(): " + tz );
    

    odt.toString(): 2017-09-18T03:08:20.888+02:00

    offset.toString(): +02:00

    tz.toString(): sun.util.calendar.ZoneInfo[id="GMT+02:00",offset=7200000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]


    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.

    Where to obtain the java.time classes?

    • Java SE 8, Java SE 9, 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, 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.

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