Java 从日期时间解析和检索时区偏移量
日期格式:“yyyy-MM-dd'HH:MM:ss.SSSZ” 输入日期:“2017-09-18T03:08:20.888+0200” 问题:我需要从输入字符串中检索时区偏移量,并在此时区中打印解析的日期。换句话说,我需要输出与输入相同Java 从日期时间解析和检索时区偏移量,java,date,datetime,Java,Date,Datetime,日期格式:“yyyy-MM-dd'HH:MM:ss.SSSZ” 输入日期:“2017-09-18T03:08:20.888+0200” 问题:我需要从输入字符串中检索时区偏移量,并在此时区中打印解析的日期。换句话说,我需要输出与输入相同 SimpleDataFormat成功解析输入日期并返回java.util.date对象,日期没有时区字段SimpleDataFormat将解析的日期转换为其时区,默认情况下为系统时区。当我打印这个日期时,它是在系统时区中打印的 简单演示 private stat
SimpleDataFormat
成功解析输入日期并返回java.util.date
对象,日期没有时区字段SimpleDataFormat
将解析的日期转换为其时区,默认情况下为系统时区。当我打印这个日期时,它是在系统时区中打印的
简单演示
private static void runDemoTask() throws ParseException {
final String dateTimeTimezoneFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
final SimpleDateFormat inputSdf = new SimpleDateFormat(dateTimeTimezoneFormat);
final String inputDate = "2017-09-18T01:08:20.888+0200";
Date parsedDate = inputSdf.parse(inputDate);
final SimpleDateFormat outputSdf = new SimpleDateFormat(dateTimeTimezoneFormat);
//outputSdf.setTimeZone("X_TIMEZONE_WHICH_I_NEED");
String output = outputSdf.format(parsedDate);
System.out.println(output);
}
输出
Mon Sep 18 00:08:20 GMT+01:00 2017
注意,输出日期有系统时区,这与输入字符串不同
注意,我不会使用java.time、Joda-time和其他库,因为我需要支持现有代码
可能的不愉快解决方案 我尝试使用正则表达式来检索符号和偏移量
private static String parseTimeZone(String input) {
final int singGroup = 1;
final int offsetGroup = 2;
final String timezonePatternStr = "([+-])(\\d{4})$";
final Pattern timezonePattern = Pattern.compile(timezonePatternStr);
Matcher matcher = timezonePattern.matcher(input);
if (matcher.find()) {
String sign = matcher.group(singGroup);
String offset = matcher.group(offsetGroup);
System.out.println(sign + " " + offset);
}
return "";
}
它打印
+ 0200
SimpleDateFormat
扩展了DateFormat
,因此在内部使用了日历
。解析日历正在更新的日期时,以便在解析后从中获取时区:
这也说明了为什么DateFormat
不是线程安全的
编辑:
内部日历的时区似乎没有更新,但区域偏移字段已更新。因此,您可以这样做:
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 ) );
请注意,您不能只设置输出格式的区域偏移量,因为这不会更新格式化时使用的时区引用
正如你所看到的那样,这样做看起来有点“哈克”,因此你应该认真考虑你是否真的需要时区。在大多数情况下,您可能会以不同的方式定义输出时区,例如通过获取用户的位置、输入等。
SimpleDateFormat
扩展日期格式
,从而在内部使用日历
。解析日历正在更新的日期时,以便在解析后从中获取时区:
这也说明了为什么DateFormat
不是线程安全的
编辑:
内部日历的时区似乎没有更新,但区域偏移字段已更新。因此,您可以这样做:
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 ) );
请注意,您不能只设置输出格式的区域偏移量,因为这不会更新格式化时使用的时区引用
正如你所看到的那样,这样做看起来有点“哈克”,因此你应该认真考虑你是否真的需要时区。在大多数情况下,您会以不同的方式定义输出时区,例如通过获取用户的位置、输入等。谢谢大家:@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));
谢谢你们,伙计们:@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));
核心解决方案
解析时区并检索小时和分钟,签名并构建时区
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;
}
}
核心解决方案
解析时区并检索小时和分钟,签名并构建时区
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;
}
}
tl;博士
直接从现代时区偏移量
转换为传统时区
这里看到的代码与Yan Khonski的答案类似,但使用的变体直接将现代java.time类(ZoneOffset
&ZoneID
)转换为传统的TimeZone
类
虽然最终结果没有差异,但这种方法使用了显式转换方法。这是添加到旧日期时间类中的许多新方法之一,用于转换java.time对象
使用这种转换方法可以使代码更加自我记录。这也让你更清楚地意识到,你正在有意识地在现代和传统课程之间移动
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`.
转储到控制台
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”,偏移量=7200000,DSTSAVAINGS=0,useDaylight=false,transitions=0,lastRule=null]
关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,& 该项目现已启动,建议迁移到类 要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是 从哪里获得java.time类
- ,及以后
- 内置的李>
- 标准JavaAPI的一部分,带有捆绑实现
- Java9添加了一些次要功能和修复
- 及
- 大部分java.time功能都在中向后移植到Java6和Java7
-
- 更高版本的Android捆绑包实现了java.time类
- 对于早期的Android,该项目采用了ThreeTen Backport(如上所述)。看
时区偏移量
转换为传统时区
这里看到的代码与Yan Khonski的答案类似,但使用的变体直接将现代java.time类(ZoneOffset
&ZoneID
)转换为传统的TimeZone
类
虽然最终结果没有差异,但这种方法使用了显式转换方法。这是添加到旧日期时间类中的许多新方法之一,用于转换java.time对象
使用这种转换方法可以使代码更加自我记录。这也让你更清楚地意识到,你正在有意识地在现代和传统课程之间移动
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`.
转储到控制台
System.out.println( "odt.toString(): " + odt );
System.out.println( "offset.toString(): " + offset );
System.out.println( "tz.toString(): " + tz );
odt.t