1

I'm looking for a clean solution for formatting Date objects with optional parts. My date object can be a timestamp value, a time-only value or a date-only value.

Expected output

I would like to display the date value like this:

If the date's ms value is less than (86400*1000) then it means the value is a really a time-only value. If so display like this:

HH:mm:ss.SSS

If the date's value is exactly at midnight then it means it is a date-only value. If so display like this:

yyyy-MM-dd

Otherwise display like this:

yyyy-MM-dd HH:mm:ss.SSS

Solution constraints

Unfortunately I'm working with Date as the input. Can't be changed. I'm only interested in formatting/rendering, not in parsing. External libraries are a hard sell in my organization.

For the purpose of formatting it would of course be acceptable to first convert to one of new Java 8 date/time objects if that will make the solution simpler.

Platform : Java 8 SE

peterh
  • 18,404
  • 12
  • 87
  • 115
  • 2
    If you are working with Date you can not have time without date – Jens Sep 29 '16 at 05:56
  • @Jens. Is that your reason for downvote of the question? You only read first line of question? My date value comes from a library and I do not have control over it. It is what it is. By convention - in this case - values that are less than 86400*1000 are to be interpreted as a time-only value. I believe I had made that clear. – peterh Sep 29 '16 at 06:00
  • 1
    Unfortunately you forgot to ask a question in your question. You only gave us a problem description (fine) but you did not tell us where exactly you have a problem. Code is also missing. – Seelenvirtuose Sep 29 '16 at 06:13
  • But to suggest a beginning path: You obviously have identified some concept in your application. Make it obvious by introducing a class that represents that concept and simply takes the `Date` instance in its constructor. Then provide some useful methods in this class, especially a one for getting a formatted string. Et voila. – Seelenvirtuose Sep 29 '16 at 06:15

4 Answers4

3

First step

Convert Date into a Java 8 equivalent structure:

private static TemporalAccessor toJava8Time(Date date) {
    Instant instant = date.toInstant();

    if (instant.isBefore(Instant.ofEpochSecond(86400))) {
        return instant.atZone(ZoneId.systemDefault()).toLocalTime();
    } else if (instant.equals(instant.truncatedTo(ChronoUnit.HOURS))) {
        return instant.atZone(ZoneId.systemDefault()).toLocalDate();
    } else {
        return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
}

Second step

Create a formatter generic enough to stringify LocalTime, LocalDate and LocalDateTime as you need:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("[yyyy-MM-dd] [HH:mm:ss]");

Usage

Date time = new Date(39733000L); // 1970-01-01T12:02:13
Date date = new Date(1462399200000L); // 2016-05-05T00:00:00
Date dateTime = new Date(1462442533000L); // 2016-05-05T12:02:13

Stream.of(time, date, dateTime)
        .map(d -> toJava8Time(d))
        .map(t -> formatter.format(t).trim())
        .forEach(System.out::println);

//Outputs "12:02:13", "2016-05-05", "2016-05-05 12:02:13"

Note: I had to call trim() otherwise the time would begin with a space.

Spotted
  • 4,021
  • 17
  • 33
  • I'm curious about your `LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())`, in particular the part about the zoneId. My date objects are in UTC. Date objects always are. – peterh Sep 29 '16 at 07:54
  • @peterh `java.util.Date` objects have no concept of timezone. See [this answer](http://stackoverflow.com/a/23885950/4370629) for a more detailled explanation. – Spotted Sep 29 '16 at 08:01
1

Instead of java.util.Date use the appropriate class in the java.time package. This means you can explicitly have separate methods for your LocalDate, LocalTime or LocalDateTime, and it makes the code cleaner.

Now, if you're stuck with a java.util.Date and there's nothing you can do about it, you should use the getTime() method (which you already know about) to decide among various SimpleDateFormat implementations for each of your output types.

UserF40
  • 3,533
  • 2
  • 23
  • 34
Joe C
  • 15,324
  • 8
  • 38
  • 50
1

You can try importing and using java.sql.Time similar to this:

import java.sql.Time;

public class Example {
    public static void main(String[] args) {
        Time ex = new Time( Date.getTime() ); //param: long
        System.out.println(ex.toString()); // prints 00:05:28
    }
}

EDIT: Joe C's method of going about this problem is arguably better than my implementation.

Justin Singh-M.
  • 378
  • 2
  • 8
0

I think you need multiple SimpleDateFormat objects for it. Your code may look like this:

SimpleDateFormat timeOnlySdf = new SimpleDateFormat( "HH:mm:ss.SSS");
SimpleDateFormat dateOnlySdf = new SimpleDateFormat( "yyyy-MM-dd");
SimpleDateFormat dateAndTimeSdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS");

Date epochPlusOne = new Date( 86400 * 1000);
Calendar cal = Calendar.getInstance();
cal.setTime( myDate);

if( myDate.before( epochPlusOne))
{
    System.out.println( timeOnlySdf.format( myDate));
}
else if( cal.get( Calendar.HOUR) == 0 && 
        cal.get( Calendar.MINUTE) == 0 && 
        cal.get( Calendar.SECOND) == 0 && 
        cal.get( Calendar.MILLISECOND) == 0)
{
    System.out.println( dateOnlySdf.format( myDate));
}
else
{
    System.out.println( dateAndTimeSdf.format( myDate));
}
uoyilmaz
  • 3,035
  • 14
  • 25