使用可选部分格式化日期

Formatting a Date with optional parts

我正在寻找一个干净的解决方案来格式化带有可选部分的 Date 对象。我的日期对象可以是时间戳值、仅时间值或仅日期值。

预期输出

我想像这样显示日期值:

如果日期的 ms value 小于 (86400*1000) 那么这意味着该值是一个真正的时间值。如果是这样显示:

HH:mm:ss.SSS

如果日期的值恰好在午夜,则表示它是一个仅限日期的值。如果是这样显示:

yyyy-MM-dd

否则显示如下:

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

解约束

不幸的是,我正在使用 Date 作为输入。无法更改。我只对 formatting/rendering 感兴趣,对解析不感兴趣。外部图书馆在我的组织中很难销售。

出于格式化的目的,首先转换为新的 Java 8 date/time 对象之一当然是可以接受的,如果这样可以使解决方案更简单。

平台:Java 8 SE

而不是 java.util.Date 使用 java.time 包中适当的 class。这意味着您可以为您的 LocalDateLocalTimeLocalDateTime 显式地使用单独的方法,并且它使代码更清晰。

现在,如果您被 java.util.Date 困住了并且对此无能为力,您应该使用 getTime() 方法(您已经知道)在各种 SimpleDateFormat 每种输出类型的实现。

您可以尝试导入和使用 java.sql.Time 类似于:

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

编辑:Joe C 解决这个问题的方法可以说比我的实现要好。

我认为您需要多个 SimpleDateFormat 对象。您的代码可能如下所示:

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

第一步

Date转换为Java 8等价结构:

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

第二步

创建足够通用的格式化程序以根据需要对 LocalTimeLocalDateLocalDateTime 进行字符串化:

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

用法

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"

注意:我必须调用 trim(),否则时间将以 space 开始。