6

I'm receiving a JSON object with date value like this:

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

And I want to parse it in Java code to Date or getting it as a String.

I want to know what is the easy way of doing it.

Adler
  • 1,432
  • 3
  • 15
  • 27
  • 3
    It should be noted that this is not "JSON date format" -- the [JSON standard](http://www.json.org/) does not include a date format. – Hot Licks Jan 02 '12 at 13:53
  • By the way, this is a terrible way to communicate a date-time value. See the [ISO 8601](https://en.m.wikipedia.org/wiki/ISO_8601) standard for practical, unambiguous, and easy-to-parse formats for date-time values. – Basil Bourque Oct 19 '17 at 14:46

5 Answers5

5

I take it the first number (1325134800000) is the number of milliseconds since epoch, and -0500 is the time zone. This appears to be the case given the sample code below, which seems to do what you want.

The following code parses the JSON input using Jackson, which I recommend if you don't have a JSON parsing library of choice yet. It lacks error checking etc.

Sample code:

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);
    }
}

Output:

Date: 2011/12/29 06:12:00 -0500
fge
  • 119,121
  • 33
  • 254
  • 329
  • 1
    This special syntax has been invented by Microsoft to make those Date literals distinguishable from normal strings, but still deliver valid JSON. See http://weblogs.asp.net/bleroy/archive/2008/01/18/dates-and-json.aspx – Erich Kitzmueller Jan 02 '12 at 13:24
  • But the timezone part is a new one, though... looks like someone "improved" MS' hack. – Erich Kitzmueller Jan 02 '12 at 13:32
  • I'm trying doing what you worte but I get error when calling matcher.group(1) and the attribute matchFound of the matcher says false.. could it be problem with the regex (i have tried more options but no good).. ??!? – Adler Jan 02 '12 at 13:36
  • Can you post the string literal you are trying to match? Beware that Java requires that you double backslashes. Also, what is the result of calling `.toString()` on the first JsonNode? – fge Jan 02 '12 at 13:38
  • The string I get from this code: this.getJSONObject().getString("PostingDate") is: "/Date(1325134800000-0500)/" and this is what been given to the matcher: pattern.matcher(this.getJSONObject().getString("PostingDate")) – Adler Jan 02 '12 at 13:53
  • found it! I just had to call matcher.find(), I guess this is what matcher.matches() in your code does and I skeep it.. :) Thanks! – Adler Jan 02 '12 at 14:24
  • Still, this is not normal. According to the link posted by @ammoQ, the format should be _exactly_ `\/Date(xxxxx)\/` (unquoted, and with the added timezone spec in your case). You should not have to use find() here. What does your JSON library return as the string literal? It definitely looks wrong -- and which is why I use Jackson – fge Jan 02 '12 at 14:32
  • I don't recommend Jackson because it relies too much on auto-magically converting beans to JSON. I prefer Jettison instead. – smcg Jun 08 '12 at 19:29
  • @smcg I don't use any kind of POJO mapping here. The – fge Jun 17 '12 at 15:04
0

Hier is a working parsing method based on the version of fge but improved as

  1. it used jode's DateTime with the correct timezone initialized
  2. minor change to pattern to accept +0200 too

=>

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));
}
Karussell
  • 17,085
  • 16
  • 97
  • 197
  • Actually you have to compile the pattern like so: "\\\\/Date\\((\\d+)([-+]\\d+)?\\)\\\\/". you have to double escape the first and last \ characters. once for java string and again for regex. also tz == null if there is no timezone so if statement should be if(tz == null || tz.isEmpty()) – Dave Jun 20 '12 at 14:32
  • no, it works like expected for me. check for null is good, but better checking for existing group(2) ... – Karussell Jun 20 '12 at 15:24
0

I have created a simple JavaScript function using Jquery DatePicker

    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)));

Ravi Ram
  • 24,078
  • 21
  • 82
  • 113
0

Simple thing but handling my job. Extract your object value from JSON and apply substring.
e.g:

      String postingDateObjectValue = "\\/Date(1442436473422)\\/";

      String dateStringInMillis = postingDateObjectValue .substring(7,20);

now parse millis and use them where ever you want.

M-sAnNan
  • 704
  • 8
  • 15
0

In Java >= 8 you can use the new java.time API.

The input contains:

  • a unix timestamp (1325134800000), which is the number of milliseconds since unix epoch (1970-01-01T00:00Z)
  • a UTC offset (-0500), which is the difference from UTC (in this case, 5 hours behind UTC)

In the new java.time API, there are lots of different types of date/time objects. In this case, we can choose to use a java.time.Instant (which represent a count of nanoseconds since unix epoch) or a java.time.OffsetDateTime (which represents the Instant converted to a date/time in a specific offset).

To parse the String, I use a java.time.format.DateTimeFormatterBuilder to create a java.time.format.DateTimeFormatter. I also use a java.time.temporal.ChronoField to specify which fields I'm parsing:

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

I also use a regex to extract just the relevant part from the input String (although you could also use substring() to get it):

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");

Then I can parse to the type I want:

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

The Instant is equivalent to 2011-12-29T05:00:00Z (Instant is just a point in the timeline, and you can think that's always in UTC). The OffsetDateTime has the same instant, but converted to -0500 offset, so its value is 2011-12-29T00:00-05:00. But both Instant and OffsetDateTime represents the same point in time.


To convert to java.util.Date, use the 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());

That's because the java.util.Date has no timezone/offset information and it just represents the count of milliseconds since unix epoch (the same concept of Instant), so it can be easily converted from an Instant.


Java 6 and 7

For Java 6 and 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).

The difference from Java 8 is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same. So the formatter creation and the parsing code to Instant and OffsetDateTime are the same.

Another difference is that, in Java <= 7, the java.util.Date class has no from() method. But you can use the org.threeten.bp.DateTimeUtils class to do the conversion:

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

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