Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从日期时间解析和检索时区偏移量_Java_Date_Datetime - Fatal编程技术网

Java 从日期时间解析和检索时区偏移量

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

日期格式:“yyyy-MM-dd'HH:MM:ss.SSSZ

输入日期:“2017-09-18T03:08:20.888+0200

问题:我需要从输入字符串中检索时区偏移量,并在此时区中打印解析的日期。换句话说,我需要输出与输入相同


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.timeJoda-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(如上所述)。看
该项目使用其他类扩展了java.time。这个项目是java.time将来可能添加的一个试验场。您可以在这里找到一些有用的类,如、、和。

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.t