Vaadin流渲染器,用于java.time日期时间类型,不限于LocalDateTime&;LocalDate类
在Vaadin Flow 14.1版中,我发现只有两种日期时间类型的渲染器实现:Vaadin流渲染器,用于java.time日期时间类型,不限于LocalDateTime&;LocalDate类,java,vaadin,java-time,vaadin-flow,dateformatter,Java,Vaadin,Java Time,Vaadin Flow,Dateformatter,在Vaadin Flow 14.1版中,我发现只有两种日期时间类型的渲染器实现: 第一个是类中仅日期的值,没有一天中的时间,也没有时区。那很好 第二种是用于表示日期和时间的类,但故意缺少a或a的上下文。很好 问题是我找不到其他几种java.time数据类型的渲染器。下面是一个由各种日期时间类型、现代java.time类型以及它们取代的遗留日期时间类组成的图表,以及一个SQL标准等效数据类型列表 具体而言,在商务应用程序中,我们倾向于使用LocalDateTime的频率较低,主要用于预订
LocalDateTime
的频率较低,主要用于预订未来的约会,因为政客可以更改时区的定义(世界各地的政客都经常这样做)。LocalDateTime
类不能代表一个时刻。例如,以今年1月23日下午3点为例。如果没有时区或UTC偏移,我们不知道这是否意味着日本东京下午3点,法国图卢兹下午3点,或美国俄亥俄州托莱多下午3点——三个截然不同的时刻相隔几个小时
为了表示一个时刻,我们必须使用,或类。瞬间
是一个瞬间,定义上总是UTC。AOffsetDateTime
表示从UTC偏移数小时分秒的时刻。ZoneDateTime
是一个特定地区的人们通过墙上的时钟看到的时刻,一个时区。此类时区是该区域中使用的偏移的过去、现在和未来更改的历史
➥ Vaadin 14是否为任何其他类型提供渲染器?如果没有,是否有解决方法或制作渲染器的方法?MyInstantTrenderer
class
您可以轻松创建自己的渲染器实现
下面是我编写的渲染器,用于处理显示包含对象的对象的网格
小部件。瞬间
是一个时刻,是时间线上的一个特定点,如UTC所示(零小时分秒的偏移量)。Instant
类是java.time框架中使用的基本构建块类
这里的想法是,我们使用即时
对象,应用指定的。该ZonedDateTime
对象使用指定的对象在字符串中生成文本。文本代表ZonedDateTime
对象的内容,符合指定对象的人类语言和文化规范
ZoneId
和Locale
附加到调用程序员传递的DateTimeFormatter
我这里的代码是基于Vaadin有限公司为他们的类发布的代码
我删减了那个类的API。他们的API允许传递格式化模式字符串,而不是DateTimeFormatter
对象。我认为渲染器不应该负责从这样的字符串生成格式化程序对象,因此也不应该处理由此产生的任何错误情况。它们的API允许传递区域设置
对象。Locale
对象可以附加到调用程序员传递的DateTimeFormatter
对象。我看不出这个呈现器类在将传递的区域设置分配给传递的格式化程序时应该如何不必要地参与。调用程序可以在将格式化程序传递给呈现程序之前完成该赋值
以下是定义InstantTrender
的典型用法,用于渲染Instant
对象,以便在Vaadin 14中的网格中显示
invoicesGrid
.addColumn(
new InstantRenderer <>( Invoice :: getWhenCreated ,
DateTimeFormatter
.ofLocalizedDateTime( FormatStyle.SHORT , FormatStyle.MEDIUM )
.withLocale( Locale.CANADA_FRENCH )
.withZone( ZoneId.of( "America/Montreal" ) )
)
)
.setHeader( "Created" )
;
然后在代码的其他地方,应用每个用户自己的首选区域和区域设置。您将获得另一个专用的
DateTimeFormatter
对象,而由于java.time中使用的不可变对象模式,原始对象不受影响
也许我以后可以为问题中列出的其他java.time类型做类似的事情
DateTimeFormatter f = DateTimeFormatter
.ofLocalizedDateTime(
FormatStyle.SHORT , // Length of date portion.
FormatStyle.MEDIUM // Length of time-of-day portion.
)
;
invoicesGrid
.addColumn(
new InstantRenderer <>( Invoice :: getWhenCreated ,
f
.withLocale( user.getPreferredLocale() )
.withZone( user.getPreferredZone() )
)
)
.setHeader( "Created" )
;
package work.basil.example.ui;
/*
* Copyright 2000-2020 Vaadin Ltd.
* Copyright 2020 Basil Bourque.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
import java.util.Objects;
import com.vaadin.flow.data.renderer.BasicRenderer;
import com.vaadin.flow.function.ValueProvider;
/*
* This class is based on source-code directly copied from
* `LocalDateTimeRenderer.java` of Vaadin 14.1.x
* as written and published by Vaadin Ltd. from their GitHub page.
*
* https://github.com/vaadin/flow/blob/master/flow-data/src/main/java/com/vaadin/flow/data/renderer/LocalDateTimeRenderer.java
*
* I re-purposed that class to handle `Instant` objects rather than `LocalDateTime`
* objects. An `Instant` represents a moment, whereas `LocalDateTime` cannot because
* of it lacking any concept of time zone or offset-from-UTC. In contrast, `Instant`
* represents a moment in UTC (an offset-from-UTC of zero hours-minutes-seconds).
*
* By default, a `Instant` object renders in Vaadin by way of its `toString` method
* generating text in standard ISO 8601 format YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ.
*
* If you want other than ISO 8601 format in UTC, use this class. In this class, we
* apply a time zone (`ZoneId`) to the `Instant` to adjust from UTC.
*
* The `ZoneId` object comes from one of three places:
* - Passed implicitly by being set as a property on a `DateTimeFormatter`
* object passed as an argument. This is the best case.
* - Defaults to calling `ZoneId.systemDefault` if not found
* on the `DateTimeFormatter` object (where `getZone` returns null).
*
* I deleted the constructors taking a formatting pattern string. Parsing such a string
* and instantiating a `DateTimeFormatter` and handling resulting error conditions
* should *not* be the job of this class. I believe the Vaadin team made a poor choice
* in having constructors taking a string formatting pattern rather than just a
* `DateTimeFormatter` object.
*
* Locale is another critical issue. A `Locale` object determines:
*
* (a) The human language used for translating items such as name of month and
* name of day.
*
* (b) The cultural norms used in deciding localization issues such as the ordering
* of elements (ex: day comes before or after month), abbreviation, capitalization,
* punctuation, and so on.
*
* Again, I deleted the constructors taking a `Locale` object. The `DateTimeFormatter`
* object passed by the calling programmer carries a `Locale`. That calling programmer
* should have attached their intended locale object to that `DateTimeFormatter` object
* by calling `DateTimeFormatter::withLocale`. Usually a `DateTimeFormatter` has a default
* `Locale` assigned. But if found lacking, here we attach the JVM’s current default locale.
*
* Following the logic discussed above, I chose to not take a `ZoneId` as an argument.
* A `ZoneId` can be attached to the `DateTimeFormatter` by calling `withZoneId`.
* If the passed `DateTimeFormatter` is found lacking, here we attach the JVM’s current
* default time zone.
*
* Typical usage, passing 2 arguments, a method reference and a `DateTimeFormatter` object
* while omitting 3rd optional argument for null-representation to go with an blank empty string:
*
* myGrid
* .addColumn(
* new InstantRenderer <>( TheBusinessObject :: getWhenCreated ,
* DateTimeFormatter
* .ofLocalizedDateTime( FormatStyle.SHORT , FormatStyle.MEDIUM )
* .withLocale( Locale.CANADA_FRENCH )
* .withZone( ZoneId.of( "America/Montreal" ) )
* )
* )
*
* This code is written for Java 8 or later.
*
* For criticisms and suggestions, contact me via LinkedIn at: basilbourque
*/
/**
* A template renderer for presenting {@code Instant} objects.
*
* @param <SOURCE> the type of the input item, from which the {@link Instant}
* is extracted
* @author Vaadin Ltd
* @since 1.0.
*/
public class InstantRenderer < SOURCE >
extends BasicRenderer < SOURCE, Instant >
{
private DateTimeFormatter formatter;
private String nullRepresentation;
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the format style
* {@code FormatStyle.LONG} for the date and {@code FormatStyle.SHORT} for
* time, with an empty string as its null representation.
*
* @param valueProvider the callback to provide a {@link Instant} to the
* renderer, not <code>null</code>
* @see <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html#LONG">
* FormatStyle.LONG</a>
* @see <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html#SHORT">
* FormatStyle.SHORT</a>
*/
public InstantRenderer (
ValueProvider < SOURCE, Instant > valueProvider )
{
this(
valueProvider ,
DateTimeFormatter
.ofLocalizedDateTime( FormatStyle.LONG )
.withZone( ZoneId.systemDefault() )
.withLocale( Locale.getDefault() ) ,
""
);
}
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the given formatter, with the
* empty string as its null representation.
*
* @param valueProvider the callback to provide a {@link Instant} to the
* renderer, not <code>null</code>
* @param formatter the formatter to use, not <code>null</code>
*/
public InstantRenderer (
ValueProvider < SOURCE, Instant > valueProvider ,
DateTimeFormatter formatter
)
{
this(
valueProvider ,
formatter ,
""
);
}
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the given formatter.
*
* @param valueProvider the callback to provide a {@link Instant} to the
* renderer, not <code>null</code>
* @param formatter the formatter to use, not <code>null</code>
* @param nullRepresentation the textual representation of the <code>null</code> value
*/
public InstantRenderer (
final ValueProvider < SOURCE, Instant > valueProvider ,
final DateTimeFormatter formatter ,
final String nullRepresentation
)
{
super( valueProvider );
this.formatter = Objects.requireNonNull( formatter , "formatter may not be null" );
this.nullRepresentation = Objects.requireNonNull( nullRepresentation , "null-representation may not be null" );
// If the formatter provided by the calling programmer lacks a time zone, apply the JVM's current default zone.
// This condition is less than ideal. The calling programmer should have set an appropriate zone.
// Often the appropriate zone is one specifically chosen or confirmed by the user.
if ( Objects.isNull( this.formatter.getZone() ) )
{
this.formatter = this.formatter.withZone( ZoneId.systemDefault() );
}
// If the formatter provided by the calling programmer lacks a locale, apply the JVM's current default locale.
// This condition is less than ideal. The calling programmer should have set an appropriate locale.
// Often the appropriate locale is one specifically chosen or confirmed by the user.
if ( Objects.isNull( this.formatter.getLocale() ) )
{
this.formatter = this.formatter.withLocale( Locale.getDefault() );
}
}
@Override
protected String getFormattedValue ( final Instant instant )
{
// If null, return the null representation.
// If not null, adjust the `Instant` from UTC into the time zone attached to the `DateTimeFormatter` object.
// This adjustment, made by calling `Instant::atZone`, produces a `ZonedDateTime` object.
// We then create a `String` with text representing the value of that `ZonedDateTime` object.
// That text is automatically localized per the `Locale` attached to the `DateTimeFormatter` object.
String s = Objects.isNull( instant ) ? nullRepresentation : formatter.format( instant.atZone( this.formatter.getZone() ) );
return s;
}
}