【问题标题】:Convert JSON date format转换 JSON 日期格式
【发布时间】:2012-01-31 18:59:31
【问题描述】:

我收到一个带有如下日期值的 JSON 对象:

{"PostingDate":"\/Date(1325134800000-0500)\/"}

我想在 Java 代码中将其解析为 Date 或将其作为 String 获取。

我想知道什么是最简单的方法。

【问题讨论】:

  • 请注意,这不是“JSON 日期格式”——JSON standard 不包含日期格式。
  • 顺便说一句,这是传达日期时间值的糟糕方式。有关日期时间值的实用、明确且易于解析的格式,请参阅 ISO 8601 标准。

标签: java json date parsing


【解决方案1】:

在 Java >= 8 中,您可以使用 new java.time API

输入包含:

  • a unix timestamp (1325134800000),这是自 unix 纪元 (1970-01-01T00:00Z) 以来的毫秒数
  • UTC offset (-0500),这是与 UTC 的区别(在这种情况下,比 UTC 晚 5 小时)

在新的java.time API 中,有lots of different types 的日期/时间对象。在这种情况下,我们可以选择使用 java.time.Instant(表示自 unix 纪元以来的纳秒计数)或 java.time.OffsetDateTime(表示将 Instant 转换为特定偏移量中的日期/时间)。

为了解析String,我使用java.time.format.DateTimeFormatterBuilder 来创建java.time.format.DateTimeFormatter。我还使用java.time.temporal.ChronoField 来指定我正在解析的字段:

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // epoch seconds
    .appendValue(ChronoField.INSTANT_SECONDS)
    // milliseconds
    .appendValue(ChronoField.MILLI_OF_SECOND, 3)
    // offset
    .appendPattern("xx")
    // create formatter
    .toFormatter();

我还使用正则表达式从输入 String 中提取相关部分(尽管您也可以使用 substring() 来获取它):

String s = "/Date(1325134800000-0500)/";

// get just the "1325134800000-0500" part - you can also do s.substring(6, 24)
s = s.replaceAll(".*/Date\\(([\\d\\+\\-]+)\\)/.*", "$1");

然后我就可以解析成我想要的类型了:

// parse to Instant
Instant instant = Instant.from(fmt.parse(s));
// parse to OffsetDateTime
OffsetDateTime odt = OffsetDateTime.parse(s, fmt);

Instant 等价于 2011-12-29T05:00:00ZInstant 只是时间线中的一个点,您可以认为它始终是 UTC)。 OffsetDateTime 具有相同的瞬间,但转换为 -0500 偏移量,因此其值为 2011-12-29T00:00-05:00。但InstantOffsetDateTime 都代表同一个时间点。


要转换为java.util.Date,请使用Instant

// convert to java.util.Date
Date date = Date.from(instant);

// if you have an OffsetDateTime, you can do this:
Date date = Date.from(odt.toInstant());

那是因为java.util.Datehas no timezone/offset information 仅表示自 unix 纪元以来的毫秒数(与 Instant 的概念相同),因此可以轻松地从 Instant 转换。


Java 6 和 7

对于 Java 6 和 7,您可以使用 ThreeTen Backport,这是 Java 8 新日期/时间类的一个很好的反向移植。对于Android,您还需要ThreeTenABP(更多关于如何使用它here)。

与 Java 8 的区别在于包名(在 Java 8 中是 java.time,而在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但类和方法 names 是相同的。所以格式化程序的创建和InstantOffsetDateTime的解析代码是一样的。

另一个区别是,在 Java java.util.Date 类没有 from() 方法。但是您可以使用org.threeten.bp.DateTimeUtils 类进行转换:

// convert to java.util.Date
Date date = DateTimeUtils.toDate(instant);

// or from the OffsetDateTime
Date date = DateTimeUtils.toDate(odt.toInstant());

【讨论】:

    【解决方案2】:

    简单的事情,但处理我的工作。 从 JSON 中提取对象值并应用子字符串。
    例如:

          String postingDateObjectValue = "\\/Date(1442436473422)\\/";
    
          String dateStringInMillis = postingDateObjectValue .substring(7,20);
    

    现在解析millis并在任何你想要的地方使用它们。

    【讨论】:

      【解决方案3】:

      我使用 Jquery DatePicker 创建了一个简单的 JavaScript 函数

          function JsonToJSDate(jsonDate) {
              var reg = /-?\d+/;
              var m = reg.exec(jsonDate);
              return new Date(parseInt(m[0]));            
          }
      

      $('#Started').val($.datepicker.formatDate('mm/dd/yy', JsonToJSDate(yourDateVarHere)));

      【讨论】:

        【解决方案4】:

        Hier 是一种基于 fge 版本的工作解析方法,但改进为

        1. 它使用了 jode 的 DateTime 并初始化了正确的时区
        2. 对模式进行细微更改以接受 +0200

        =>

        private static final Pattern bingTimePattern = Pattern.compile("\\/Date\\((\\d+)([-+]\\d+)?\\)\\/");
        
        public static DateTime parseBingTime(String timeAsString) throws ParseException {
            Matcher matcher = bingTimePattern.matcher(timeAsString);
            if (!matcher.find())
                throw new ParseException("wrong date time format " + timeAsString, 0);
        
            final long millis = Long.parseLong(matcher.group(1));
            String tz = matcher.group(2);
            if (tz.isEmpty())
                tz = "+0000";
        
            return new DateTime(millis, DateTimeZone.forID(tz));
        }
        

        【讨论】:

        • 实际上你必须像这样编译模式:"\\\\/Date\((\\d+)([-+]\\d+)?\)\\\\/" .您必须双重转义第一个和最后一个 \ 字符。一次用于 java 字符串,一次用于正则表达式。如果没有时区,还有 tz == null 所以 if 语句应该是 if(tz == null || tz.isEmpty())
        • 不,它对我来说就像预期的那样工作。检查 null 很好,但更好地检查现有组 (2) ...
        【解决方案5】:

        我认为第一个数字 (1325134800000) 是自纪元以来的毫秒数,-0500 是时区。鉴于下面的示例代码似乎就是这种情况,这似乎可以满足您的需求。

        以下代码使用 Jackson 解析 JSON 输入,如果您还没有选择的 JSON 解析库,我建议您使用它。它缺乏错误检查等。

        示例代码:

        public final class Foo
        {
            public static void main(final String... args)
                throws IOException
            {
                // What the JSON value must match exactly
                // Not anchored since it will be used with the (misnamed) .matches() method
                final Pattern pattern
                    = Pattern.compile("\\\\/Date\\((\\d+)(-\\d+)?\\)\\\\/");
        
                final ObjectMapper mapper = new ObjectMapper();
        
                // Parse JSON...
                final JsonNode node = mapper.readTree(
                    "{\"PostingDate\": \"\\/Date(1325134800000-0500)\\/\"}");
        
                if (!node.has("PostingDate")) {
                    System.err.println("Bad JSON input!");
                    System.exit(1);
                }
        
                // Get relevant field
                final String dateSpec = node.get("PostingDate").getTextValue();
        
                // Try and match the input.
                final Matcher matcher = pattern.matcher(dateSpec);
        
                if (!matcher.matches()) {
                    System.err.println("Bad pattern!"); // Yuck
                    System.exit(1);
                }
        
                // The first group capture the milliseconds, the second one the time zone
        
                final long millis = Long.parseLong(matcher.group(1));
                String tz = matcher.group(2);
                if (tz.isEmpty()) // It can happen, in which case the default is assumed to be...
                    tz = "+0000";
        
                // Instantiate a date object...    
                final Date date = new Date(millis);
        
                // And print it using an appropriate date format
                System.out.printf("Date: %s %s\n",
                    new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(date), tz);
            }
        }
        

        输出:

        Date: 2011/12/29 06:12:00 -0500
        

        【讨论】:

        • 微软发明了这种特殊的语法,以使这些日期文字与普通字符串区分开来,但仍然提供有效的 JSON。见weblogs.asp.net/bleroy/archive/2008/01/18/dates-and-json.aspx
        • 但时区部分是新的,不过……看起来有人“改进”了 MS 的 hack。
        • 我正在尝试做你所说的,但是在调用 matcher.group(1) 并且匹配器的属性 matchFound 显示为 false 时出现错误.. 正则表达式是否有问题(我试过更多选择,但没有好处).. ??!?
        • 您可以发布您要匹配的字符串文字吗?请注意,Java 要求您使用双反斜杠。还有,在第一个JsonNode上调用.toString()的结果是什么?
        • 我从这段代码中得到的字符串:this.getJSONObject().getString("PostingDate") is: "/Date(1325134800000-0500)/" 这就是匹配器的内容: pattern.matcher(this.getJSONObject().getString("PostingDate"))
        猜你喜欢
        • 1970-01-01
        • 2017-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-12
        • 2020-01-09
        • 2011-10-29
        • 1970-01-01
        相关资源
        最近更新 更多