Java DateTimeFormatter:根据另一个字段的值包括可选字段

Java DateTimeFormatter:根据另一个字段的值包括可选字段,java,datetime-format,java-time,localtime,Java,Datetime Format,Java Time,Localtime,我想格式化一个java.time.LocalTime,但格式可以根据其值而变化: 如果一天中的小时数为12或0,请使用格式HH:mm 否则,请使用格式HH:mm:ss 我当然可以这样做: if (t.getHour() == 12 || t.getHour() == 0) { // use "HH:mm" formatter } else { // use "HH:mm:ss" formatter } 但为此,我需要创建两个不同的格式化程序 我只想使用一个可重复使用多次的格

我想格式化一个
java.time.LocalTime
,但格式可以根据其值而变化:

  • 如果一天中的小时数为12或0,请使用格式
    HH:mm
  • 否则,请使用格式
    HH:mm:ss
我当然可以这样做:

if (t.getHour() == 12 || t.getHour() == 0) {
    // use "HH:mm" formatter
} else {
    // use "HH:mm:ss" formatter
}
但为此,我需要创建两个不同的格式化程序

我只想使用一个可重复使用多次的格式化程序:

DateTimeFormatter fmt = // create this "conditional" formatter
fmt.format(LocalTime.of(14, 0))); // 14:00:00
fmt.format(LocalTime.of(12, 0))); // 12:00
我正在尝试使用
DateTimeFormatterBuilder

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    .appendPattern("HH:mm")
    // how to append seconds (and the ":" before it) only if hour of day is not (12 or 0)?
    .appendLiteral(":").appendValue(ChronoField.SECOND_OF_MINUTE, 2)
    .toFormatter();
我尝试了
DateTimeFormatterBuilder.optionalStart()
optionalEnd()
方法,也尝试了
appendOptional()
,但这些方法只检查要追加的字段是否存在。 它们没有基于另一个字段值的可选行为

如何仅使用一个格式化程序(如果可能)?

尝试以下操作:

SimpleDateFormatter("HH:mm" + (((t.getHour() % 12) == 0) ? "" : ":ss");
请尝试以下操作:

SimpleDateFormatter("HH:mm" + (((t.getHour() % 12) == 0) ? "" : ":ss");

我不知道你为什么要这样做,但你几乎做到了:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder()
    .appendPattern("HH:mm");
if (t.getHour() % 12 != 0) {
    dtfb.appendLiteral(":").appendValue(ChronoField.SECOND_OF_MINUTE, 2);
}
DateTimeFormatter fmt = dtfb.toFormatter();

我不知道你为什么要这样做,但你几乎做到了:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder()
    .appendPattern("HH:mm");
if (t.getHour() % 12 != 0) {
    dtfb.appendLiteral(":").appendValue(ChronoField.SECOND_OF_MINUTE, 2);
}
DateTimeFormatter fmt = dtfb.toFormatter();

您可以使用
DateTimeFormatterBuilder.optionalStart()
,就像您尝试过的那样,但是您还必须创建一个包装类来包装
LocalTime
,其中
isSupported(秒/分)
如果小时为0或12,则返回
false


在我看来,这太麻烦了,并不能真正提高清晰度(无论如何也不能提高太多)或性能。使用两个单独的格式化程序更容易。

您可以使用
DateTimeFormatterBuilder.optionalStart()
,就像您已经尝试过的那样,但是您还必须创建一个包装类来包装
LocalTime
,其中
IssuSupported(秒/分)
在小时为0或12时返回
false


在我看来,这太麻烦了,并不能真正提高清晰度(无论如何也不能提高太多)或性能。使用两个单独的格式化程序更容易。

我可以按照@Klitos(创建包装器)的建议来做。 但是覆盖
的isSupported
(正如他所建议的)不起作用

实际上,我必须重写
getLong
方法,并在字段不应格式化时抛出一个
UnsupportedTemporalTypeException
。我的解决方案的简短版本:

public class LocalTimeWrapper
    implements Temporal, TemporalAdjuster, Comparable<LocalTimeWrapper>, Serializable {

    private LocalTime time;

    public static LocalTimeWrapper of(int hour, int minute) {
        return new LocalTimeWrapper(LocalTime.of(hour, minute));
    }

    private LocalTimeWrapper(LocalTime time) {
        this.time = time;
    }

    // implement Temporal and TemporalAdjuster
    // (interface methods like isSupported() and until() delegate to "time" field)

    @Override
    public long getLong(TemporalField field) {
        if (field == ChronoField.SECOND_OF_MINUTE) {
            if (this.time.getHour() % 12 == 0) {
                // don't format seconds when hour is 12 or 0
                throw new UnsupportedTemporalTypeException("some message");
            }
        }

        return this.time.getLong(field);
    }
}
但正如你们中的许多人告诉我的,这似乎是太多的工作,可能更好地使用许多不同的格式化程序(或)-我必须承认,我试图完成这段代码只是为了知道它是否可能


我使用了jdk1.8.0_111来做这个测试。

我可以按照@Klitos的建议来做(创建一个包装器)。 但是覆盖
的isSupported
(正如他所建议的)不起作用

实际上,我必须重写
getLong
方法,并在字段不应格式化时抛出一个
UnsupportedTemporalTypeException
。我的解决方案的简短版本:

public class LocalTimeWrapper
    implements Temporal, TemporalAdjuster, Comparable<LocalTimeWrapper>, Serializable {

    private LocalTime time;

    public static LocalTimeWrapper of(int hour, int minute) {
        return new LocalTimeWrapper(LocalTime.of(hour, minute));
    }

    private LocalTimeWrapper(LocalTime time) {
        this.time = time;
    }

    // implement Temporal and TemporalAdjuster
    // (interface methods like isSupported() and until() delegate to "time" field)

    @Override
    public long getLong(TemporalField field) {
        if (field == ChronoField.SECOND_OF_MINUTE) {
            if (this.time.getHour() % 12 == 0) {
                // don't format seconds when hour is 12 or 0
                throw new UnsupportedTemporalTypeException("some message");
            }
        }

        return this.time.getLong(field);
    }
}
但正如你们中的许多人告诉我的,这似乎是太多的工作,可能更好地使用许多不同的格式化程序(或)-我必须承认,我试图完成这段代码只是为了知道它是否可能


我使用了jdk1.8.0_111进行此测试。

请参见
DateTimeFormatterBuilder
及其方法。它允许您将每个数字转换为特定的文本。但是,由于您希望以小时为单位计算秒数,因此必须设置一个包含86400个条目的地图,一天中每秒钟一个条目。但它将作为一个单独的格式化程序工作

Map<Long, String> map = new HashMap<>();
map.put(0L, "00:00");
map.put(1L, "00:00");  // would normally be 00:00:01
map.put(2L, "00:00");  // would normally be 00:00:02
map.put(60L, "00:01");
// and so on
map.put(3600L, "01:00:00");
map.put(3601L, "01:00:01");
// and so on to 86399
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
  .appendText(ChronoField.SECOND_OF_DAY, map)
  .toFormatter();
Map Map=newhashmap();
地图放置(0升,“00:00”);
地图放置(1L,“00:00”);//通常是00:00:01
地图放置(2L,“00:00”);//通常是00:00:02
地图放置(60L,“00:01”);
//等等
地图放置(3600L,“01:00:00”);
地图放置(3601L,“01:00:01”);
//以此类推至86399
DateTimeFormatter fmt=新的DateTimeFormatterBuilder()
.appendText(ChronoField.SECOND\u OF_DAY,map)
.toFormatter();

一个包含86400个条目的映射当然是愚蠢的,但它是使用API所能做到的最好的。实际上,需要在
DateTimeFormatterBuilder
中添加一个
appendText(TemporalField,LongFunction)
方法(这将导致无法用于解析的格式化程序)。

请参见
DateTimeFormatterBuilder
和该方法。它允许您将每个数字转换为特定的文本。但是,由于您希望以小时为单位计算秒数,因此必须设置一个包含86400个条目的地图,一天中每秒钟一个条目。但它将作为一个单独的格式化程序工作

Map<Long, String> map = new HashMap<>();
map.put(0L, "00:00");
map.put(1L, "00:00");  // would normally be 00:00:01
map.put(2L, "00:00");  // would normally be 00:00:02
map.put(60L, "00:01");
// and so on
map.put(3600L, "01:00:00");
map.put(3601L, "01:00:01");
// and so on to 86399
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
  .appendText(ChronoField.SECOND_OF_DAY, map)
  .toFormatter();
Map Map=newhashmap();
地图放置(0升,“00:00”);
地图放置(1L,“00:00”);//通常是00:00:01
地图放置(2L,“00:00”);//通常是00:00:02
地图放置(60L,“00:01”);
//等等
地图放置(3600L,“01:00:00”);
地图放置(3601L,“01:00:01”);
//以此类推至86399
DateTimeFormatter fmt=新的DateTimeFormatterBuilder()
.appendText(ChronoField.SECOND\u OF_DAY,map)
.toFormatter();

一个包含86400个条目的映射当然是愚蠢的,但它是使用API所能做到的最好的。实际上,需要在
DateTimeFormatterBuilder
中添加
appendText(TemporalField,LongFunction)
方法(这将导致无法用于解析的格式化程序)。

首先,我真的不确定仅使用两个格式化程序的简单解决方案是否更适合您的问题。

与简单方法(伪代码)相比,每种仅使用一个格式化程序的方法都不可避免地会导致一些额外的性能损失:

这只是一个额外的格式化程序请记住,最好将两个不可变的格式化程序存储为静态常量。否则,如果坚持定义一个格式化程序,请进一步阅读:


正如S.Colebourne和Jesper所指出的,在t