mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-11 08:01:32 +00:00
ICU-22853 Support formatting types in java.time
This commit is contained in:
parent
51e21af692
commit
01d755749c
9 changed files with 884 additions and 5 deletions
|
@ -0,0 +1,309 @@
|
|||
// © 2024 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: https://www.unicode.org/copyright.html
|
||||
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.chrono.ChronoLocalDate;
|
||||
import java.time.chrono.ChronoLocalDateTime;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Date;
|
||||
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.GregorianCalendar;
|
||||
import com.ibm.icu.util.SimpleTimeZone;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
|
||||
/**
|
||||
* This class provides utility methods for converting between Java 8's {@code java.time}
|
||||
* classes and the {@link com.ibm.icu.util.Calendar} and related classes from the
|
||||
* {@code com.ibm.icu.util} package.
|
||||
*
|
||||
* <p>
|
||||
* The class includes methods for converting various temporal types, such as
|
||||
* {@link Instant}, {@link ZonedDateTime}, {@link OffsetTime}, {@link OffsetDateTime}, {@link LocalTime},
|
||||
* {@link ChronoLocalDate}, and {@link ChronoLocalDateTime}, to {@link Calendar} instances.
|
||||
*
|
||||
* <p>
|
||||
* Additionally, it provides methods to convert between {@link ZoneId} and {@link TimeZone}, and
|
||||
* {@link ZoneOffset} and {@link TimeZone}.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public class JavaTimeConverters {
|
||||
// Milliseconds per day
|
||||
private static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1_000;
|
||||
|
||||
private JavaTimeConverters() {
|
||||
// Prevent instantiation, making this an utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the current instant from a {@link Clock} to a {@link Calendar}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link Calendar} instance that represents the current
|
||||
* instant as provided by the specified {@link Clock}.
|
||||
*
|
||||
* @param clock The {@link Clock} providing the current instant.
|
||||
* @return A {@link Calendar} instance representing the current instant as
|
||||
* provided by the specified {@link Clock}.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Calendar temporalToCalendar(Clock clock) {
|
||||
long epochMillis = clock.millis();
|
||||
String timeZone = clock.getZone().getId();
|
||||
TimeZone icuTimeZone = TimeZone.getTimeZone(timeZone);
|
||||
return millisToCalendar(epochMillis, icuTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link Instant} to a {@link Calendar}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link Calendar} instance that represents the same
|
||||
* point in time as the specified {@link Instant}. The resulting
|
||||
* {@link Calendar} will be in the default time zone of the JVM.
|
||||
*
|
||||
* @param instant The {@link Instant} to convert.
|
||||
* @return A {@link Calendar} instance representing the same point in time as
|
||||
* the specified {@link Instant}.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Calendar temporalToCalendar(Instant instant) {
|
||||
long epochMillis = instant.toEpochMilli();
|
||||
return millisToCalendar(epochMillis, TimeZone.GMT_ZONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link ZonedDateTime} to a {@link Calendar}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link Calendar} instance that represents the same date
|
||||
* and time as the specified {@link ZonedDateTime}, taking into account the time
|
||||
* zone information associated with the {@link ZonedDateTime}.
|
||||
*
|
||||
* @param dateTime The {@link ZonedDateTime} to convert.
|
||||
* @return A {@link Calendar} instance representing the same date and time as
|
||||
* the specified {@link ZonedDateTime}, with the time zone set
|
||||
* accordingly.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Calendar temporalToCalendar(ZonedDateTime dateTime) {
|
||||
long epochMillis = dateTime.toEpochSecond() * 1_000 + dateTime.get(MILLI_OF_SECOND);
|
||||
TimeZone icuTimeZone = zoneIdToTimeZone(dateTime.getZone());
|
||||
return millisToCalendar(epochMillis, icuTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link OffsetTime} to a {@link Calendar}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link Calendar} instance that represents the same time
|
||||
* of day as the specified {@link OffsetTime}, taking into account the offset
|
||||
* from UTC associated with the {@link OffsetTime}. The resulting
|
||||
* {@link Calendar} will have its date components (year, month, day) set to the
|
||||
* current date in the time zone represented by the offset.
|
||||
*
|
||||
* @param time The {@link OffsetTime} to convert.
|
||||
* @return A {@link Calendar} instance representing the same time of day as the
|
||||
* specified {@link OffsetTime}, with the time zone set accordingly and
|
||||
* date components set to the current date in that time zone.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Calendar temporalToCalendar(OffsetTime time) {
|
||||
return temporalToCalendar(time.atDate(LocalDate.now()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link OffsetDateTime} to a {@link Calendar}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link Calendar} instance that represents the same date
|
||||
* and time as the specified {@link OffsetDateTime}, taking into account the
|
||||
* offset from UTC associated with the {@link OffsetDateTime}.
|
||||
*
|
||||
* @param dateTime The {@link OffsetDateTime} to convert.
|
||||
* @return A {@link Calendar} instance representing the same date and time as
|
||||
* the specified {@link OffsetDateTime}, with the time zone set
|
||||
* accordingly.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Calendar temporalToCalendar(OffsetDateTime dateTime) {
|
||||
long epochMillis = dateTime.toEpochSecond() * 1_000 + dateTime.get(MILLI_OF_SECOND);
|
||||
TimeZone icuTimeZone = zoneOffsetToTimeZone(dateTime.getOffset());
|
||||
return millisToCalendar(epochMillis, icuTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link ChronoLocalDate} to a {@link Calendar}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link Calendar} instance that represents the same date
|
||||
* as the specified {@link ChronoLocalDate}. The resulting {@link Calendar} will
|
||||
* be in the default time zone of the JVM and will have its time components
|
||||
* (hour, minute, second, millisecond) set to zero.
|
||||
*
|
||||
* @param date The {@link ChronoLocalDate} to convert.
|
||||
* @return A {@link Calendar} instance representing the same date as the
|
||||
* specified {@link ChronoLocalDate}, with time components set to zero.
|
||||
*/
|
||||
@Deprecated
|
||||
static Calendar temporalToCalendar(ChronoLocalDate date) {
|
||||
long epochMillis = date.toEpochDay() * MILLIS_PER_DAY;
|
||||
return millisToCalendar(epochMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link LocalTime} to a {@link Calendar}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link Calendar} instance that represents the same time
|
||||
* of day as the specified {@link LocalTime}. The resulting {@link Calendar}
|
||||
* will be in the default time zone of the JVM and will have its date components
|
||||
* (year, month, day) set to the current date in the default time zone.
|
||||
*
|
||||
* @param time The {@link LocalTime} to convert.
|
||||
* @return A {@link Calendar} instance representing the same time of day as the
|
||||
* specified {@link LocalTime}, with date components set to the current
|
||||
* date in the default time zone.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Calendar temporalToCalendar(LocalTime time) {
|
||||
long epochMillis = time.toNanoOfDay() / 1_000_000;
|
||||
return millisToCalendar(epochMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link ChronoLocalDateTime} to a {@link Calendar}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link Calendar} instance that represents the same date
|
||||
* and time as the specified {@link ChronoLocalDateTime}. The resulting
|
||||
* {@link Calendar} will be in the default time zone of the JVM.
|
||||
*
|
||||
* @param dateTime The {@link ChronoLocalDateTime} to convert.
|
||||
* @return A {@link Calendar} instance representing the same date and time as
|
||||
* the specified {@link ChronoLocalDateTime}.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Calendar temporalToCalendar(LocalDateTime dateTime) {
|
||||
ZoneOffset zoneOffset = ZoneId.systemDefault().getRules().getOffset(dateTime);
|
||||
long epochMillis = dateTime.toEpochSecond(zoneOffset) * 1_000 + dateTime.get(MILLI_OF_SECOND);
|
||||
return millisToCalendar(epochMillis, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link Temporal} to a {@link Calendar}.
|
||||
*
|
||||
* @param temp The {@link Temporal} to convert.
|
||||
* @return A {@link Calendar} instance representing the same date and time as
|
||||
* the specified {@link Temporal}.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Calendar temporalToCalendar(Temporal temp) {
|
||||
if (temp instanceof Clock) {
|
||||
return temporalToCalendar((Clock) temp);
|
||||
} else if (temp instanceof Instant) {
|
||||
return temporalToCalendar((Instant) temp);
|
||||
} else if (temp instanceof ZonedDateTime) {
|
||||
return temporalToCalendar((ZonedDateTime) temp);
|
||||
} else if (temp instanceof OffsetDateTime) {
|
||||
return temporalToCalendar((OffsetDateTime) temp);
|
||||
} else if (temp instanceof OffsetTime) {
|
||||
return temporalToCalendar((OffsetTime) temp);
|
||||
} else if (temp instanceof LocalDate) {
|
||||
return temporalToCalendar((LocalDate) temp);
|
||||
} else if (temp instanceof LocalDateTime) {
|
||||
return temporalToCalendar((LocalDateTime) temp);
|
||||
} else if (temp instanceof LocalTime) {
|
||||
return temporalToCalendar((LocalTime) temp);
|
||||
} else if (temp instanceof ChronoLocalDate) {
|
||||
return temporalToCalendar((ChronoLocalDate) temp);
|
||||
} else if (temp instanceof ChronoLocalDateTime) {
|
||||
return temporalToCalendar((ChronoLocalDateTime<?>) temp);
|
||||
} else {
|
||||
System.out.println("WTF is " + temp.getClass());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link ZoneId} to a {@link TimeZone}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link TimeZone} from the specified {@link ZoneId}. The
|
||||
* resulting {@link TimeZone} will represent the time zone rules associated with
|
||||
* the given {@link ZoneId}.
|
||||
*
|
||||
* @param zoneId The zone ID to convert.
|
||||
* @return A {@link TimeZone} representing the time zone rules associated with
|
||||
* the given {@link ZoneId}.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static TimeZone zoneIdToTimeZone(ZoneId zoneId) {
|
||||
return TimeZone.getTimeZone(zoneId.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link ZoneOffset} to a {@link TimeZone}.
|
||||
*
|
||||
* <p>
|
||||
* This method creates a {@link TimeZone} that has a fixed offset from UTC,
|
||||
* represented by the given {@link ZoneOffset}.
|
||||
*
|
||||
* @param zoneOffset The zone offset to convert.
|
||||
* @return A {@link TimeZone} that has a fixed offset from UTC, represented by
|
||||
* the given {@link ZoneOffset}.
|
||||
*
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static TimeZone zoneOffsetToTimeZone(ZoneOffset zoneOffset) {
|
||||
return new SimpleTimeZone(zoneOffset.getTotalSeconds() * 1_000, zoneOffset.getId());
|
||||
}
|
||||
|
||||
private static Calendar millisToCalendar(long epochMillis) {
|
||||
return millisToCalendar(epochMillis, TimeZone.GMT_ZONE);
|
||||
}
|
||||
|
||||
private static Calendar millisToCalendar(long epochMillis, TimeZone timeZone) {
|
||||
GregorianCalendar calendar = new GregorianCalendar(timeZone, ULocale.US);
|
||||
// java.time doesn't switch to Julian calendar
|
||||
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
|
||||
calendar.setTimeInMillis(epochMillis);
|
||||
return calendar;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
package com.ibm.icu.message2;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
@ -10,6 +12,7 @@ import java.util.TimeZone;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.ibm.icu.impl.JavaTimeConverters;
|
||||
import com.ibm.icu.text.DateFormat;
|
||||
|
||||
/**
|
||||
|
@ -349,7 +352,12 @@ class DateTimeFormatterFactory implements FormatterFactory {
|
|||
return new FormattedPlaceholder(
|
||||
toFormat, new PlainStringFormattedValue("{|" + toFormat + "|}"));
|
||||
}
|
||||
} else if (toFormat instanceof Clock) {
|
||||
toFormat = JavaTimeConverters.temporalToCalendar((Clock) toFormat);
|
||||
} else if (toFormat instanceof Temporal) {
|
||||
toFormat = JavaTimeConverters.temporalToCalendar((Temporal) toFormat);
|
||||
}
|
||||
// Not an else-if here, because the `Clock` & `Temporal` conditions before make `toFormat` a `Calendar`
|
||||
if (toFormat instanceof Calendar) {
|
||||
TimeZone tz = ((Calendar) toFormat).getTimeZone();
|
||||
long milis = ((Calendar) toFormat).getTimeInMillis();
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
package com.ibm.icu.message2;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
@ -62,6 +64,8 @@ class MFDataModelFormatter {
|
|||
.setDefaultFormatterNameForType(Date.class, "datetime")
|
||||
.setDefaultFormatterNameForType(Calendar.class, "datetime")
|
||||
.setDefaultFormatterNameForType(java.util.Calendar.class, "datetime")
|
||||
.setDefaultFormatterNameForType(Clock.class, "datetime")
|
||||
.setDefaultFormatterNameForType(Temporal.class, "datetime")
|
||||
|
||||
// Number formatting
|
||||
.setFormatter("number", new NumberFormatterFactory("number"))
|
||||
|
|
|
@ -14,6 +14,8 @@ import java.text.FieldPosition;
|
|||
import java.text.Format;
|
||||
import java.text.ParseException;
|
||||
import java.text.ParsePosition;
|
||||
import java.time.Clock;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
|
@ -24,6 +26,7 @@ import java.util.Map;
|
|||
import java.util.MissingResourceException;
|
||||
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.JavaTimeConverters;
|
||||
import com.ibm.icu.impl.RelativeDateFormat;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.GregorianCalendar;
|
||||
|
@ -623,16 +626,22 @@ public abstract class DateFormat extends UFormat {
|
|||
public final StringBuffer format(Object obj, StringBuffer toAppendTo,
|
||||
FieldPosition fieldPosition)
|
||||
{
|
||||
if (obj instanceof Calendar)
|
||||
if (obj instanceof Calendar) {
|
||||
return format( (Calendar)obj, toAppendTo, fieldPosition );
|
||||
else if (obj instanceof Date)
|
||||
} else if (obj instanceof Date) {
|
||||
return format( (Date)obj, toAppendTo, fieldPosition );
|
||||
else if (obj instanceof Number)
|
||||
} else if (obj instanceof Number) {
|
||||
return format( new Date(((Number)obj).longValue()),
|
||||
toAppendTo, fieldPosition );
|
||||
else
|
||||
} else if (obj instanceof Clock) {
|
||||
return format(JavaTimeConverters.temporalToCalendar((Clock) obj),
|
||||
toAppendTo, fieldPosition);
|
||||
} else if (obj instanceof Temporal) {
|
||||
return format( (Temporal)obj, toAppendTo, fieldPosition );
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot format given Object (" +
|
||||
obj.getClass().getName() + ") as a Date");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -706,6 +715,46 @@ public abstract class DateFormat extends UFormat {
|
|||
return format(date, new StringBuffer(64),new FieldPosition(0)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a {@link Temporal} into a date/time string.
|
||||
*
|
||||
* @param date a {@link Temporal} to be formatted into a date/time string.
|
||||
* @param toAppendTo the string buffer for the returning date/time string.
|
||||
* @param fieldPosition keeps track of the position of the field within the returned string.<br>
|
||||
* On input: an alignment field, if desired.<br>
|
||||
* On output: the offsets of the alignment field.<br>
|
||||
* For example, given a time text "1996.07.10 AD at 15:08:56 PDT",
|
||||
* if the given {@code fieldPosition} is {@code DateFormat.YEAR_FIELD}, the begin index and end index
|
||||
* of {@code fieldPosition} will be set to 0 and 4, respectively.<br>
|
||||
* Notice that if the same time field appears more than once in a pattern, the fieldPosition will
|
||||
* be set for the first occurrence of that time field. For instance, formatting a {@link Temporal}
|
||||
* to the time string "1 PM PDT (Pacific Daylight Time)" using the pattern "h a z (zzzz)" and the
|
||||
* alignment field {@code DateFormat.TIMEZONE_FIELD}, the begin index and end index
|
||||
* of {@code fieldPosition} will be set to 5 and 8, respectively, for the first occurrence of the
|
||||
* timezone pattern character 'z'.
|
||||
*
|
||||
* @return the formatted date/time string.
|
||||
*
|
||||
* @draft ICU 76
|
||||
*/
|
||||
public StringBuffer format(Temporal date, StringBuffer toAppendTo,
|
||||
FieldPosition fieldPosition) {
|
||||
return format(JavaTimeConverters.temporalToCalendar(date), toAppendTo, fieldPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a {@link Temporal} into a date/time string.
|
||||
*
|
||||
* @param date the time value to be formatted into a time string.
|
||||
* @return the formatted time string.
|
||||
*
|
||||
* @draft ICU 76
|
||||
*/
|
||||
public final String format(Temporal date)
|
||||
{
|
||||
return format(date, new StringBuffer(64),new FieldPosition(0)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date/time string. For example, a time text "07/10/96 4:5 PM, PDT"
|
||||
* will be parsed into a Date that is equivalent to Date(837039928046).
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.io.ObjectInputStream;
|
|||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -24,6 +25,7 @@ import com.ibm.icu.impl.FormattedValueFieldPositionIteratorImpl;
|
|||
import com.ibm.icu.impl.ICUCache;
|
||||
import com.ibm.icu.impl.ICUData;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.JavaTimeConverters;
|
||||
import com.ibm.icu.impl.SimpleCache;
|
||||
import com.ibm.icu.impl.SimpleFormatterImpl;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
|
@ -889,6 +891,34 @@ public class DateIntervalFormat extends UFormat {
|
|||
return formatImpl(fromCalendar, toCalendar, appendTo, pos, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format two {@link Temporal}s to produce a string.
|
||||
*
|
||||
* @param fromTemporal temporal set to the start of the interval
|
||||
* to be formatted into a string
|
||||
* @param toTemporal temporal set to the end of the interval
|
||||
* to be formatted into a string
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param pos On input: an alignment field, if desired.
|
||||
* On output: the offsets of the alignment field.
|
||||
* There may be multiple instances of a given field type
|
||||
* in an interval format; in this case the fieldPosition
|
||||
* offsets refer to the first instance.
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @throws IllegalArgumentException if the two calendars are not equivalent.
|
||||
*
|
||||
* @draft ICU 76
|
||||
*/
|
||||
public final StringBuffer format(Temporal fromTemporal,
|
||||
Temporal toTemporal,
|
||||
StringBuffer appendTo,
|
||||
FieldPosition pos) {
|
||||
Calendar fromCalendar = JavaTimeConverters.temporalToCalendar(fromTemporal);
|
||||
Calendar toCalendar = JavaTimeConverters.temporalToCalendar(toTemporal);
|
||||
return formatImpl(fromCalendar, toCalendar, appendTo, pos, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format 2 Calendars to produce a FormattedDateInterval.
|
||||
*
|
||||
|
@ -915,6 +945,25 @@ public class DateIntervalFormat extends UFormat {
|
|||
return new FormattedDateInterval(sb, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format two {@link Temporal}s to produce a FormattedDateInterval.
|
||||
*
|
||||
* The FormattedDateInterval exposes field information about the formatted string.
|
||||
*
|
||||
* @param fromTemporal temporal set to the start of the interval
|
||||
* to be formatted into a string
|
||||
* @param toTemporal temporal set to the end of the interval
|
||||
* to be formatted into a string
|
||||
* @return A FormattedDateInterval containing the format result.
|
||||
*
|
||||
* @draft ICU 76
|
||||
*/
|
||||
public FormattedDateInterval formatToValue(Temporal fromTemporal, Temporal toTemporal) {
|
||||
Calendar fromCalendar = JavaTimeConverters.temporalToCalendar(fromTemporal);
|
||||
Calendar toCalendar = JavaTimeConverters.temporalToCalendar(toTemporal);
|
||||
return formatToValue(fromCalendar, toCalendar);
|
||||
}
|
||||
|
||||
private synchronized StringBuffer formatImpl(Calendar fromCalendar,
|
||||
Calendar toCalendar,
|
||||
StringBuffer appendTo,
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.text.FieldPosition;
|
|||
import java.text.Format;
|
||||
import java.text.ParseException;
|
||||
import java.text.ParsePosition;
|
||||
import java.time.Clock;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
@ -41,6 +43,7 @@ import com.ibm.icu.text.MessagePattern.ArgType;
|
|||
import com.ibm.icu.text.MessagePattern.Part;
|
||||
import com.ibm.icu.text.PluralRules.IFixedDecimal;
|
||||
import com.ibm.icu.text.PluralRules.PluralType;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.ICUUncheckedIOException;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.ULocale.Category;
|
||||
|
@ -1745,9 +1748,18 @@ public class MessageFormat extends UFormat {
|
|||
if (arg instanceof Number) {
|
||||
// format number if can
|
||||
dest.formatAndAppend(getStockNumberFormatter(), arg);
|
||||
} else if (arg instanceof Date) {
|
||||
} else if (arg instanceof Date) {
|
||||
// format a Date if can
|
||||
dest.formatAndAppend(getStockDateFormatter(), arg);
|
||||
} else if (arg instanceof Calendar) {
|
||||
// format a Calendar if can
|
||||
dest.formatAndAppend(getStockDateFormatter(), arg);
|
||||
} else if (arg instanceof Clock) {
|
||||
// format a Clock if can
|
||||
dest.formatAndAppend(getStockDateFormatter(), arg);
|
||||
} else if (arg instanceof Temporal) {
|
||||
// format a Temporal if can
|
||||
dest.formatAndAppend(getStockDateFormatter(), arg);
|
||||
} else {
|
||||
dest.append(arg.toString());
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ import java.text.AttributedString;
|
|||
import java.text.FieldPosition;
|
||||
import java.text.Format;
|
||||
import java.text.ParsePosition;
|
||||
import java.time.Clock;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
@ -30,6 +32,7 @@ import com.ibm.icu.impl.DayPeriodRules;
|
|||
import com.ibm.icu.impl.ICUCache;
|
||||
import com.ibm.icu.impl.ICUData;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.JavaTimeConverters;
|
||||
import com.ibm.icu.impl.PatternProps;
|
||||
import com.ibm.icu.impl.SimpleCache;
|
||||
import com.ibm.icu.impl.SimpleFormatterImpl;
|
||||
|
@ -3917,6 +3920,10 @@ public class SimpleDateFormat extends DateFormat {
|
|||
calendar.setTime((Date)obj);
|
||||
} else if (obj instanceof Number) {
|
||||
calendar.setTimeInMillis(((Number)obj).longValue());
|
||||
} else if (obj instanceof Clock) {
|
||||
cal = JavaTimeConverters.temporalToCalendar((Clock) obj);
|
||||
} else if (obj instanceof Temporal) {
|
||||
cal = JavaTimeConverters.temporalToCalendar((Temporal) obj);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot format given Object as a Date");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
// © 2024 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: https://www.unicode.org/copyright.html
|
||||
|
||||
package com.ibm.icu.dev.test.format;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.Month;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.chrono.HijrahDate;
|
||||
import java.time.chrono.JapaneseDate;
|
||||
import java.time.chrono.JapaneseEra;
|
||||
import java.time.chrono.MinguoDate;
|
||||
import java.time.chrono.ThaiBuddhistDate;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import com.ibm.icu.dev.test.CoreTestFmwk;
|
||||
import com.ibm.icu.impl.JavaTimeConverters;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.GregorianCalendar;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
|
||||
/* This class tests the raw conversion, java.time classes to an ICU Calendar. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class JavaTimeConvertersTest extends CoreTestFmwk {
|
||||
|
||||
/*
|
||||
* Fields that we expect in the calendar when formatting dates.
|
||||
*
|
||||
* A LocalDate object will not have hour, minutes, seconds, etc.
|
||||
* So when we convert it to a Calendar the result can't be directly compared
|
||||
* to the expected Calendar because some fields are different.
|
||||
*
|
||||
* Think of this field list as a "mask" we use when we compare a calendar
|
||||
* from conversion with the expected Calendar.
|
||||
*/
|
||||
private final static int[] DATE_ONLY_FIELDS = {
|
||||
Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR,
|
||||
Calendar.DAY_OF_WEEK, Calendar.DAY_OF_YEAR, Calendar.ERA,
|
||||
Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.DOW_LOCAL,
|
||||
Calendar.WEEK_OF_MONTH, Calendar.WEEK_OF_YEAR, Calendar.EXTENDED_YEAR
|
||||
};
|
||||
|
||||
// Fields that we expect in the calendar when formatting time
|
||||
private final static int[] TIME_ONLY_FIELDS = {
|
||||
Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND,
|
||||
Calendar.AM_PM, Calendar.MILLISECONDS_IN_DAY
|
||||
};
|
||||
|
||||
// Make it easier to build all kind of temporal objects
|
||||
final static LocalDateTime LOCAL_DATE_TIME = LocalDateTime.of(2018, Month.SEPTEMBER, 23,
|
||||
19, 42, 57, /*nanoseconds*/ 123_000_000);
|
||||
|
||||
final static String TIME_ZONE_ID = "Europe/Paris";
|
||||
|
||||
// Match the fields in the LOCAL_DATE_TIME above
|
||||
final static Calendar EXPECTED_CALENDAR = new GregorianCalendar(2018, Calendar.SEPTEMBER,
|
||||
23, 19, 42, 57);
|
||||
static {
|
||||
EXPECTED_CALENDAR.setTimeZone(TimeZone.getTimeZone(TIME_ZONE_ID));
|
||||
EXPECTED_CALENDAR.setTimeInMillis(EXPECTED_CALENDAR.getTimeInMillis() + 123);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateOnly() {
|
||||
LocalDate ld = LOCAL_DATE_TIME.toLocalDate();
|
||||
Calendar calendar = JavaTimeConverters.temporalToCalendar(ld);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
|
||||
HijrahDate hd = HijrahDate.of(1440, 1, 13);
|
||||
calendar = JavaTimeConverters.temporalToCalendar(hd);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
|
||||
JapaneseDate jd = JapaneseDate.of(JapaneseEra.HEISEI, 30, Month.SEPTEMBER.getValue(), 23);
|
||||
calendar = JavaTimeConverters.temporalToCalendar(jd);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
|
||||
MinguoDate md = MinguoDate.of(107, Month.SEPTEMBER.getValue(), 23);
|
||||
calendar = JavaTimeConverters.temporalToCalendar(md);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
|
||||
ThaiBuddhistDate td = ThaiBuddhistDate.of(2561, Month.SEPTEMBER.getValue(), 23);
|
||||
calendar = JavaTimeConverters.temporalToCalendar(td);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimesOnly() {
|
||||
LocalTime lt = LOCAL_DATE_TIME.toLocalTime();
|
||||
Calendar calendar = JavaTimeConverters.temporalToCalendar(lt);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, TIME_ONLY_FIELDS);
|
||||
|
||||
OffsetTime ot = OffsetTime.of(lt, ZoneOffset.ofHours(1));
|
||||
calendar = JavaTimeConverters.temporalToCalendar(ot);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, TIME_ONLY_FIELDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateAndTimes() {
|
||||
Calendar calendar = JavaTimeConverters.temporalToCalendar(LOCAL_DATE_TIME);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, TIME_ONLY_FIELDS);
|
||||
|
||||
ZonedDateTime zdt = ZonedDateTime.of(LOCAL_DATE_TIME, ZoneId.of(TIME_ZONE_ID)); // Date + Time + TimeZone
|
||||
calendar = JavaTimeConverters.temporalToCalendar(zdt);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, TIME_ONLY_FIELDS);
|
||||
assertEquals("", EXPECTED_CALENDAR.getTimeZone().getID(), calendar.getTimeZone().getID());
|
||||
|
||||
OffsetDateTime odt = OffsetDateTime.of(LOCAL_DATE_TIME, ZoneOffset.ofHours(1)); // Date + Time + TimeZone
|
||||
calendar = JavaTimeConverters.temporalToCalendar(odt);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, TIME_ONLY_FIELDS);
|
||||
assertEquals("", EXPECTED_CALENDAR.getTimeZone().getRawOffset(), calendar.getTimeZone().getRawOffset());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstantAndClock() {
|
||||
// Instant has no time zone, assumes GMT.
|
||||
EXPECTED_CALENDAR.setTimeZone(TimeZone.GMT_ZONE);
|
||||
Instant instant = Instant.ofEpochMilli(EXPECTED_CALENDAR.getTimeInMillis());
|
||||
Calendar calendar = JavaTimeConverters.temporalToCalendar(instant);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, TIME_ONLY_FIELDS);
|
||||
assertEquals("", EXPECTED_CALENDAR.getTimeZone().getID(), calendar.getTimeZone().getID());
|
||||
assertEquals("", EXPECTED_CALENDAR.getTimeZone().getRawOffset(), calendar.getTimeZone().getRawOffset());
|
||||
// Restore the time zone on the expected calendar
|
||||
EXPECTED_CALENDAR.setTimeZone(TimeZone.getTimeZone(TIME_ZONE_ID));
|
||||
|
||||
Clock clock = Clock.fixed(instant, ZoneId.of(TIME_ZONE_ID));
|
||||
calendar = JavaTimeConverters.temporalToCalendar(clock);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, DATE_ONLY_FIELDS);
|
||||
assertCalendarsEquals(EXPECTED_CALENDAR, calendar, TIME_ONLY_FIELDS);
|
||||
assertEquals("", EXPECTED_CALENDAR.getTimeZone().getID(), calendar.getTimeZone().getID());
|
||||
assertEquals("", EXPECTED_CALENDAR.getTimeZone().getRawOffset(), calendar.getTimeZone().getRawOffset());
|
||||
}
|
||||
|
||||
// Compare the expected / actual calendar, but using an allowlist
|
||||
private static void assertCalendarsEquals(Calendar exected, Calendar actual, int[] fieldsToCheck) {
|
||||
for (int field : fieldsToCheck) {
|
||||
assertEquals("Bad conversion", exected.get(field), actual.get(field));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
// © 2024 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: https://www.unicode.org/copyright.html
|
||||
|
||||
package com.ibm.icu.dev.test.format;
|
||||
|
||||
import java.text.FieldPosition;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.Month;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.chrono.HijrahDate;
|
||||
import java.time.chrono.JapaneseDate;
|
||||
import java.time.chrono.MinguoDate;
|
||||
import java.time.chrono.ThaiBuddhistDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import com.ibm.icu.dev.test.CoreTestFmwk;
|
||||
import com.ibm.icu.message2.MessageFormatter;
|
||||
import com.ibm.icu.text.DateFormat;
|
||||
import com.ibm.icu.text.DateIntervalFormat;
|
||||
import com.ibm.icu.text.MessageFormat;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class JavaTimeFormatTest extends CoreTestFmwk {
|
||||
final static LocalDateTime LDT =
|
||||
LocalDateTime.of(/*year*/ 2013, Month.SEPTEMBER, 27,
|
||||
/*hour*/ 19, /*min*/43, /*sec*/ 56, /*nanosec*/ 123_456_789);
|
||||
|
||||
@Test
|
||||
public void testLocalDateFormatting() {
|
||||
LocalDate ld = LDT.toLocalDate();
|
||||
|
||||
// Formatting with skeleton
|
||||
DateFormat formatFromSkeleton = DateFormat.getInstanceForSkeleton("EEEEyMMMMd", Locale.US);
|
||||
assertEquals("", "Friday, September 27, 2013", formatFromSkeleton.format(ld));
|
||||
|
||||
// Format with style
|
||||
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG, Locale.US);
|
||||
assertEquals("", "September 27, 2013", dateFormat.format(ld));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalDateTimeFormatting() {
|
||||
// Formatting with skeleton
|
||||
DateFormat formatFromSkeleton = DateFormat.getInstanceForSkeleton("EEEEyMMMMd jmsSSS", Locale.US);
|
||||
assertEquals("", "Friday, September 27, 2013 at 7:43:56.123\u202FPM", formatFromSkeleton.format(LDT));
|
||||
|
||||
// Format with style
|
||||
DateFormat dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT, Locale.US);
|
||||
assertEquals("", "September 27, 2013 at 7:43\u202FPM", dateTimeFormat.format(LDT));
|
||||
|
||||
DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.US);
|
||||
assertEquals("", "7:43\u202FPM", timeFormat.format(LDT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatConvertedCalendarUsesDefaultTimeZone() {
|
||||
// Save the default time zones
|
||||
TimeZone savedTimeZone = TimeZone.getDefault();
|
||||
java.util.TimeZone jdkSavedTimeZone = java.util.TimeZone.getDefault();
|
||||
// Set to one that we control
|
||||
String timeZoneId = "America/New_York";
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(timeZoneId));
|
||||
java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone(timeZoneId));
|
||||
|
||||
// We check that the calendar from conversion uses the default time zone.
|
||||
DateFormat icuDateFormat = DateFormat.getInstanceForSkeleton("EEEEdMMMMyjmszzzz", Locale.US);
|
||||
String result = icuDateFormat.format(LDT);
|
||||
|
||||
// Restore the default time zones
|
||||
TimeZone.setDefault(savedTimeZone);
|
||||
java.util.TimeZone.setDefault(jdkSavedTimeZone);
|
||||
|
||||
assertEquals("", "Friday, September 27, 2013 at 7:43:56 PM Eastern Daylight Time", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateTimeWithTimeZoneFormatting() {
|
||||
// Formatting with skeleton
|
||||
ZonedDateTime zdt = ZonedDateTime.of(LDT, ZoneId.of("Europe/Paris"));
|
||||
OffsetDateTime odt = OffsetDateTime.of(LDT, ZoneOffset.ofHoursMinutes(5, 30));
|
||||
|
||||
DateFormat formatFromSkeleton = DateFormat.getInstanceForSkeleton("EEEEyMMMMd jmsSSS vvvv", Locale.US);
|
||||
assertEquals("", "Friday, September 27, 2013 at 7:43:56.123\u202FPM Central European Time", formatFromSkeleton.format(zdt));
|
||||
assertEquals("", "Friday, September 27, 2013 at 7:43:56.123\u202FPM GMT+05:30", formatFromSkeleton.format(odt));
|
||||
|
||||
// Format with style
|
||||
DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.FULL, Locale.US);
|
||||
assertEquals("", "7:43:56\u202FPM Central European Summer Time",timeFormat.format(zdt));
|
||||
assertEquals("", "7:43:56\u202FPM GMT+05:30", timeFormat.format(odt));
|
||||
|
||||
DateFormat dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.US);
|
||||
assertEquals("", "Friday, September 27, 2013 at 7:43:56\u202FPM Central European Summer Time", dateTimeFormat.format(zdt));
|
||||
assertEquals("", "Friday, September 27, 2013 at 7:43:56\u202FPM GMT+05:30", dateTimeFormat.format(odt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonGregorianDateFormatting() {
|
||||
// Non-Gregorian as input
|
||||
LocalDate ld = LDT.toLocalDate();
|
||||
HijrahDate hd = HijrahDate.from(ld);
|
||||
JapaneseDate jd = JapaneseDate.from(ld);
|
||||
MinguoDate md = MinguoDate.from(ld);
|
||||
ThaiBuddhistDate td = ThaiBuddhistDate.from(ld);
|
||||
|
||||
DateFormat formatFromSkeleton = DateFormat.getInstanceForSkeleton("EEEEGGGyMMMMd", Locale.US);
|
||||
String expected = "Friday, September 27, 2013 AD";
|
||||
assertEquals("", expected, formatFromSkeleton.format(hd));
|
||||
assertEquals("", expected, formatFromSkeleton.format(jd));
|
||||
assertEquals("", expected, formatFromSkeleton.format(md));
|
||||
assertEquals("", expected, formatFromSkeleton.format(td));
|
||||
|
||||
// Non-Gregorian as formatting calendar
|
||||
String[] expectedPerCalendar = {
|
||||
"buddhist", "September 27, 2556 BE",
|
||||
"chinese", "Eighth Month 23, 2013(gui-si)",
|
||||
"hebrew", "23 Tishri 5774 AM",
|
||||
"indian", "Asvina 5, 1935 Saka",
|
||||
"islamic", "Dhuʻl-Qiʻdah 22, 1434 AH",
|
||||
"japanese", "September 27, 25 Heisei",
|
||||
"persian", "Mehr 5, 1392 AP",
|
||||
"roc", "September 27, 102 Minguo",
|
||||
};
|
||||
String skeleton = "GGGGyMMMMd";
|
||||
for (int i = 0; i < expectedPerCalendar.length; i++) {
|
||||
Locale locale = Locale.forLanguageTag("en-u-ca-" + expectedPerCalendar[i++]);
|
||||
formatFromSkeleton = DateFormat.getInstanceForSkeleton(skeleton, locale);
|
||||
assertEquals("", expectedPerCalendar[i], formatFromSkeleton.format(LDT));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstantAndClockFormatting() {
|
||||
DateFormat formatFromSkeleton = DateFormat.getInstanceForSkeleton("yMMMMd jmsSSSvvvv", Locale.US);
|
||||
|
||||
Instant instant = LDT.toInstant(ZoneOffset.UTC);
|
||||
assertEquals("", "September 27, 2013 at 7:43:56.123 PM Greenwich Mean Time", formatFromSkeleton.format(instant));
|
||||
|
||||
Clock clock = Clock.fixed(instant, ZoneId.of("America/Los_Angeles"));
|
||||
assertEquals("", "September 27, 2013 at 12:43:56.123 PM Pacific Time", formatFromSkeleton.format(clock));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageFormat() {
|
||||
Locale locale = Locale.FRENCH;
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
arguments.put("expDate", LDT);
|
||||
|
||||
// Make sure that the type detection works, we don't pass a type for the formatter
|
||||
MessageFormat mf = new MessageFormat("Your card expires on {expDate}", locale);
|
||||
assertEquals("", "Your card expires on 27/09/2013 19:43", mf.format(arguments));
|
||||
|
||||
// Now we specify that the placeholder is a date, make sure that the style & skeleton are honored.
|
||||
mf = new MessageFormat("Your card expires on {expDate, date}", locale);
|
||||
assertEquals("", "Your card expires on 27 sept. 2013", mf.format(arguments));
|
||||
|
||||
mf = new MessageFormat("Your card expires on {expDate, date, FULL}", locale);
|
||||
assertEquals("", "Your card expires on vendredi 27 septembre 2013", mf.format(arguments));
|
||||
|
||||
mf = new MessageFormat("Your card expires on {expDate, date, ::EEEyMMMd}", locale);
|
||||
assertEquals("", "Your card expires on ven. 27 sept. 2013", mf.format(arguments));
|
||||
|
||||
// MessageFormatter (MF2)
|
||||
|
||||
MessageFormatter.Builder mf2Builder = MessageFormatter.builder()
|
||||
.setLocale(locale);
|
||||
|
||||
MessageFormatter mf2 = mf2Builder.setPattern("(mf2) Your card expires on {$expDate}").build();
|
||||
assertEquals("", "(mf2) Your card expires on 27/09/2013 19:43", mf2.formatToString(arguments));
|
||||
|
||||
mf2 = mf2Builder.setPattern("(mf2) Your card expires on {$expDate :date}").build();
|
||||
assertEquals("", "(mf2) Your card expires on 27/09/2013", mf2.formatToString(arguments));
|
||||
|
||||
mf2 = mf2Builder.setPattern("(mf2) Your card expires on {$expDate :datetime dateStyle=long}").build();
|
||||
assertEquals("", "(mf2) Your card expires on 27 septembre 2013", mf2.formatToString(arguments));
|
||||
|
||||
mf2 = mf2Builder.setPattern("(mf2) Your card expires on {$expDate :date icu:skeleton=EEEyMMMd}").build();
|
||||
assertEquals("", "(mf2) Your card expires on ven. 27 sept. 2013", mf2.formatToString(arguments));
|
||||
|
||||
// Test several java.time types
|
||||
// We don't care much about the string result, as we test that somewhere else.
|
||||
// We only want to make sure that MessageFormat(ter) recognizes the types.
|
||||
|
||||
String expectedMf1Result = "Your card expires on ven. 27 sept. 2013";
|
||||
String expectedMf2Result = "(mf2) " + expectedMf1Result;
|
||||
// LocalDate
|
||||
arguments.put("expDate", LDT.toLocalDate());
|
||||
assertEquals("", expectedMf1Result, mf.format(arguments));
|
||||
assertEquals("", expectedMf2Result, mf2.formatToString(arguments));
|
||||
// ZonedDateTime
|
||||
arguments.put("expDate", LDT.atZone(ZoneId.of("Europe/Paris")));
|
||||
assertEquals("", expectedMf1Result, mf.format(arguments));
|
||||
assertEquals("", expectedMf2Result, mf2.formatToString(arguments));
|
||||
// OffsetDateTime
|
||||
arguments.put("expDate", LDT.atOffset(ZoneOffset.ofHours(2)));
|
||||
assertEquals("", expectedMf1Result, mf.format(arguments));
|
||||
assertEquals("", expectedMf2Result, mf2.formatToString(arguments));
|
||||
// Instant
|
||||
Instant instant = LDT.toInstant(ZoneOffset.UTC);
|
||||
arguments.put("expDate", instant);
|
||||
assertEquals("", expectedMf1Result, mf.format(arguments));
|
||||
assertEquals("", expectedMf2Result, mf2.formatToString(arguments));
|
||||
// Clock
|
||||
arguments.put("expDate", Clock.fixed(instant, ZoneId.of("Europe/Paris")));
|
||||
assertEquals("", expectedMf1Result, mf.format(arguments));
|
||||
assertEquals("", expectedMf2Result, mf2.formatToString(arguments));
|
||||
|
||||
// Test that both JDK and ICU Calendar are recognized as types.
|
||||
arguments.put("expDate", new java.util.GregorianCalendar(2013, 8, 27));
|
||||
// We don't test MessageFormat (MF1) with a java.util.Calendar
|
||||
// because it throws. The ICU DateFormat does not support it.
|
||||
// I filed https://unicode-org.atlassian.net/browse/ICU-22852
|
||||
// MF2 converts the JDK Calendar to an ICU Calendar, so it works.
|
||||
assertEquals("", expectedMf2Result, mf2.formatToString(arguments));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateIntervalFormat() {
|
||||
Locale locale = Locale.FRENCH;
|
||||
String intervalSkeleton = "dMMMMy";
|
||||
LocalDate from = LocalDate.of(2024, Month.SEPTEMBER, 17);
|
||||
LocalDate to = LocalDate.of(2024, Month.SEPTEMBER, 23);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
result.setLength(0);
|
||||
DateIntervalFormat di = DateIntervalFormat.getInstance(intervalSkeleton, locale);
|
||||
assertEquals("", "17–23 septembre 2024",
|
||||
di.format(from, to, result, new FieldPosition(0)).toString());
|
||||
|
||||
to = LocalDate.of(2024, Month.OCTOBER, 3);
|
||||
result.setLength(0);
|
||||
di = DateIntervalFormat.getInstance(intervalSkeleton, locale);
|
||||
assertEquals("", "17 septembre – 3 octobre 2024",
|
||||
di.format(from, to, result, new FieldPosition(0)).toString());
|
||||
|
||||
// LocalDateTime. Date + time difference, same day, different times
|
||||
|
||||
LocalDateTime fromDt = LocalDateTime.of(2024, Month.SEPTEMBER, 17, 9, 30, 0);
|
||||
LocalDateTime toDt = LocalDateTime.of(2024, Month.SEPTEMBER, 17, 18, 0, 0);
|
||||
|
||||
result.setLength(0);
|
||||
di = DateIntervalFormat.getInstance("dMMMMy jm", locale);
|
||||
assertEquals("", "17 septembre 2024, 09:30 – 18:00",
|
||||
di.format(fromDt, toDt, result, new FieldPosition(0)).toString());
|
||||
|
||||
// LocalDateTime. Time difference, same day
|
||||
|
||||
LocalTime fromT = LocalTime.of(9, 30, 0);
|
||||
LocalTime toT = LocalTime.of(18, 0, 0);
|
||||
|
||||
result.setLength(0);
|
||||
di = DateIntervalFormat.getInstance("jm", locale);
|
||||
assertEquals("", "09:30 – 18:00",
|
||||
di.format(fromT, toT, result, new FieldPosition(0)).toString());
|
||||
|
||||
// Non-Gregorian output
|
||||
|
||||
di = DateIntervalFormat.getInstance(intervalSkeleton, Locale.forLanguageTag("fr-u-ca-hebrew"));
|
||||
result.setLength(0);
|
||||
assertEquals("", "14 éloul – 1 tichri 5785 A. M.",
|
||||
di.format(from, to, result, new FieldPosition(0)).toString());
|
||||
|
||||
di = DateIntervalFormat.getInstance(intervalSkeleton, Locale.forLanguageTag("fr-u-ca-coptic"));
|
||||
result.setLength(0);
|
||||
assertEquals("", "7 tout – 23 tout 1741 ap. D.",
|
||||
di.format(from, to, result, new FieldPosition(0)).toString());
|
||||
|
||||
di = DateIntervalFormat.getInstance(intervalSkeleton, Locale.forLanguageTag("fr-u-ca-japanese"));
|
||||
result.setLength(0);
|
||||
assertEquals("", "17 septembre – 3 octobre 6 Reiwa",
|
||||
di.format(from, to, result, new FieldPosition(0)).toString());
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue