DateTimeFormatter:包含基于另一个字段值的可选字段

时间:2017-05-16 11:52:06

标签: java datetime-format java-time localtime

我想格式化HH:mm,但格式可能会因其值而异:

  • 如果一天中的小时为12或0,请使用格式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

但为此,我需要创建2种不同的格式化程序。

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

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()方法,也尝试使用<?php namespace VendorName\ExtensionName\Domain\Model; /** * Users */ class User extends \In2code\Femanager\Domain\Model\User { /** * image * * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference> */ protected $image = null; /** * Returns the image * * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference> $image */ public function getImage() { return $this->image; } /** * Sets the image * * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference> $image image * * @return void */ public function setImage(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $image) { $this->image = $image; } } ,但这些方法仅检查附加的字段是否存在。 他们没有基于其他字段值的可选行为。

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

6 个答案:

答案 0 :(得分:1)

不知道你为什么要这样做,但你几乎就在那里:

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

答案 1 :(得分:1)

您可以像尝试过一样使用198,但您还必须创建一个包装DateTimeFormatterBuilder.optionalStart() LocalTime的包装类,其中isSupported(SECONDS_OF_MINUTE)返回false如果小时是0或12。

在我看来,这太麻烦了,并没有真正提高清晰度(不管多少)或表现。使用两个单独的格式化器更容易。

答案 2 :(得分:1)

我可以按照@Klitos in his answer的建议(创建一个包装器)来实现。 但是覆盖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);
    }
}

使用此代码,它可以工作:

DateTimeFormatter f = new DateTimeFormatterBuilder().appendPattern("HH:mm")
    .optionalStart()
    .appendLiteral(":")
    .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
    .optionalEnd()
    .toFormatter();

f.format(LocalTimeWrapper.of(14, 0));  // 14:00:00
f.format(LocalTimeWrapper.of(12, 0));  // 12:00
f.format(LocalTimeWrapper.of(0, 15));  // 00:15
f.format(LocalTimeWrapper.of(15, 30)); // 15:30:00

但正如你们许多人告诉我的那样,这似乎是太多的工作,而且使用许多不同的格式化程序(或this other solution) - 可能更好,我必须承认我试图完成这段代码只是为了知道它是否可能

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

答案 3 :(得分:1)

请参阅DateTimeFormatterBuilder和方法appendText(TemporalField,Map)。它允许您将每个数字转换为特定的文本。但是,由于您希望将秒数基于小时,因此您必须设置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();

86400个条目的地图当然是愚蠢的,但它可以用API做到最好。实际上,需要在appendText(TemporalField, LongFunction<String>)中添加DateTimeFormatterBuilder方法(这会导致格式化程序无法用于解析)。

答案 4 :(得分:1)

首先要说的是,我真的不确定使用两个格式化程序的简单解决方法是否不足以解决您的问题。

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

if (time.getHour() == 0 || time.getHour() == 12) {
  formatterHHMM.format(time);
} else {
  formatterHHMMSS.format(time);
}

这只是一个格式化程序。 请记住,将两个不可变格式化程序存储为静态常量是个好主意。否则,如果您坚持定义一个格式化程序,请继续阅读:

正如S. Colebourne和Jesper所指出的那样,在JSR-310的背景下没有特殊的方法来做你想做的事情。但是,我的图书馆Time4J会 通过以下代码启用此功能。我甚至给出了两种略有不同的方法,因为我对你如何处理亚小时精度问题不是很清楚。

第一种方式始终打印&#34; HH:mm&#34;如果小时为0或12,则不考虑可能的分钟或秒钟部分。

    ChronoFormatter<LocalTime> f1 =
        ChronoFormatter.setUp(PlainTime.axis(TemporalType.LOCAL_TIME), Locale.ROOT)
            .startOptionalSection((t) -> t.getInt(PlainTime.CLOCK_HOUR_OF_AMPM) == 12)
            .addCustomized(
                PlainTime.COMPONENT,
                ChronoFormatter.ofTimePattern("HH:mm", PatternType.CLDR, Locale.ROOT)
            )
            .endSection()
            .startOptionalSection((t) -> t.getInt(PlainTime.CLOCK_HOUR_OF_AMPM) != 12)
            .addCustomized(
                PlainTime.COMPONENT,
                ChronoFormatter.ofTimePattern("HH:mm:ss", PatternType.CLDR, Locale.ROOT)
            )
            .endSection()
        .build();
    System.out.println(f1.format(LocalTime.now())); // 16:35:29
    System.out.println(f1.format(LocalTime.of(12, 15))); // 12:15
    System.out.println(f1.format(LocalTime.of(12, 0))); // 12:00

第二种方式只打印&#34; HH:mm&#34;如果时间等于正午或午夜。当然,print condition也可以指定为t.equals(PlainTime.of(0)) || t.equals(PlainTime.of(12))

    ChronoFormatter<LocalTime> f2 =
        ChronoFormatter.setUp(PlainTime.axis(TemporalType.LOCAL_TIME), Locale.ROOT)
            .startOptionalSection(
                (t) -> t.getInt(PlainTime.CLOCK_HOUR_OF_AMPM) == 12
                    && t.get(PlainTime.PRECISION) == ClockUnit.HOURS)
            .addCustomized(
                PlainTime.COMPONENT,
                ChronoFormatter.ofTimePattern("HH:mm", PatternType.CLDR, Locale.ROOT)
            )
            .endSection()
            .startOptionalSection(
                (t) -> t.getInt(PlainTime.CLOCK_HOUR_OF_AMPM) != 12
                    || t.get(PlainTime.PRECISION) != ClockUnit.HOURS)
            .addCustomized(
                PlainTime.COMPONENT,
                ChronoFormatter.ofTimePattern("HH:mm:ss", PatternType.CLDR, Locale.ROOT)
            )
            .endSection()
            .build();
    System.out.println(f2.format(LocalTime.now())); // 16:35:29
    System.out.println(f2.format(LocalTime.of(12, 15))); // 12:15:00
    System.out.println(f2.format(LocalTime.of(12, 0))); // 12:00

此解决方案不需要包含86400条或更多条目的地图。

appendText(TemporalField, LongFunction<String>)背后的建议理念相反,这种格式化程序也可以启用解析(虽然这里单个模式&#34; HH:mm [:ss]&#34;就足够了)。这也是我拒绝这个想法作为Time4J的增强的原因(我不想阻止解析)。

Time4J的独立格式引擎是为填补Java-8的空白而开发的。顺便说一句,我建议将Time4J的格式化程序也存储为静态常量(它是不可变的)。

答案 5 :(得分:0)

尝试以下方法:

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