Date format: \"yyyy-MM-dd\'T\'HH:mm:ss.SSSZ\"
Input date: \"2017-09-18T03:08:20.888+0200\"
Problem: I need retrieve timezon
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.
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));
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;
}
}
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`.
)
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]
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?
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.