Module icu_datetime::neo_marker

source ·
Expand description

Temporary module for neo formatter markers.

§Examples

§Alignment

By default, datetimes are formatted for a variable-width context. You can give a hint that the strings will be displayed in a column-like context, which will coerce numerics to be padded with zeros.

use icu::calendar::Date;
use icu::calendar::Gregorian;
use icu::datetime::neo::TypedNeoFormatter;
use icu::datetime::neo_marker::NeoYearMonthDayMarker;
use icu::datetime::neo_skeleton::Alignment;
use icu::datetime::neo_skeleton::NeoSkeletonLength;
use icu::locale::locale;
use writeable::assert_try_writeable_eq;

let plain_formatter = TypedNeoFormatter::<Gregorian, _>::try_new(
    &locale!("en-US").into(),
    NeoYearMonthDayMarker::with_length(NeoSkeletonLength::Short),
)
.unwrap();

let column_formatter = TypedNeoFormatter::<Gregorian, _>::try_new(
    &locale!("en-US").into(),
    NeoYearMonthDayMarker::with_length(NeoSkeletonLength::Short)
        .with_alignment(Alignment::Column),
)
.unwrap();

// By default, en-US does not pad the month and day with zeros.
assert_try_writeable_eq!(
    plain_formatter
        .format(&Date::try_new_gregorian(2025, 1, 1).unwrap()),
    "1/1/25"
);

// The column alignment option hints that they should be padded.
assert_try_writeable_eq!(
    column_formatter
        .format(&Date::try_new_gregorian(2025, 1, 1).unwrap()),
    "01/01/25"
);

§Year Style

The precision of the rendered year can be adjusted using the YearStyle option.

use icu::calendar::Date;
use icu::calendar::Gregorian;
use icu::datetime::neo::TypedNeoFormatter;
use icu::datetime::neo_marker::NeoYearMonthDayMarker;
use icu::datetime::neo_skeleton::NeoSkeletonLength;
use icu::datetime::neo_skeleton::YearStyle;
use icu::locale::locale;
use writeable::assert_try_writeable_eq;

let formatter = TypedNeoFormatter::<Gregorian, _>::try_new(
    &locale!("en-US").into(),
    NeoYearMonthDayMarker::with_length(NeoSkeletonLength::Short)
        .with_year_style(YearStyle::Auto),
)
.unwrap();

// Era displayed when needed for disambiguation,
// such as years before year 0 and small year numbers:
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(-1000, 1, 1).unwrap()),
    "1/1/1001 BC"
);
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(77, 1, 1).unwrap()),
    "1/1/77 AD"
);
// Era elided for modern years:
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(1900, 1, 1).unwrap()),
    "1/1/1900"
);
// Era and century both elided for nearby years:
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(2025, 1, 1).unwrap()),
    "1/1/25"
);

let formatter = TypedNeoFormatter::<Gregorian, _>::try_new(
    &locale!("en-US").into(),
    NeoYearMonthDayMarker::with_length(NeoSkeletonLength::Short)
        .with_year_style(YearStyle::Full),
)
.unwrap();

// Era still displayed in cases with ambiguity:
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(-1000, 1, 1).unwrap()),
    "1/1/1001 BC"
);
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(77, 1, 1).unwrap()),
    "1/1/77 AD"
);
// Era elided for modern years:
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(1900, 1, 1).unwrap()),
    "1/1/1900"
);
// But now we always get a full-precision year:
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(2025, 1, 1).unwrap()),
    "1/1/2025"
);

let formatter = TypedNeoFormatter::<Gregorian, _>::try_new(
    &locale!("en-US").into(),
    NeoYearMonthDayMarker::with_length(NeoSkeletonLength::Short)
        .with_year_style(YearStyle::Always),
)
.unwrap();

// Era still displayed in cases with ambiguity:
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(-1000, 1, 1).unwrap()),
    "1/1/1001 BC"
);
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(77, 1, 1).unwrap()),
    "1/1/77 AD"
);
// But now it is shown even on modern years:
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(1900, 1, 1).unwrap()),
    "1/1/1900 AD"
);
assert_try_writeable_eq!(
    formatter.format(&Date::try_new_gregorian(2025, 1, 1).unwrap()),
    "1/1/2025 AD"
);

§Hour Cycle

Hours can be switched between 12-hour and 24-hour time via the u-hc locale keyword.

use icu::calendar::Time;
use icu::datetime::neo::TypedNeoFormatter;
use icu::datetime::neo_marker::NeoHourMinuteMarker;
use icu::datetime::neo_skeleton::NeoSkeletonLength;
use icu::locale::locale;
use writeable::assert_try_writeable_eq;

// By default, en-US uses 12-hour time and fr-FR uses 24-hour time,
// but we can set overrides.

let formatter = TypedNeoFormatter::<(), _>::try_new(
    &locale!("en-US-u-hc-h12").into(),
    NeoHourMinuteMarker::with_length(NeoSkeletonLength::Short),
)
.unwrap();
assert_try_writeable_eq!(
    formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
    "4:12 PM"
);

let formatter = TypedNeoFormatter::<(), _>::try_new(
    &locale!("en-US-u-hc-h23").into(),
    NeoHourMinuteMarker::with_length(NeoSkeletonLength::Short),
)
.unwrap();
assert_try_writeable_eq!(
    formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
    "16:12"
);

let formatter = TypedNeoFormatter::<(), _>::try_new(
    &locale!("fr-FR-u-hc-h12").into(),
    NeoHourMinuteMarker::with_length(NeoSkeletonLength::Short),
)
.unwrap();
assert_try_writeable_eq!(
    formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
    "4:12 PM"
);

let formatter = TypedNeoFormatter::<(), _>::try_new(
    &locale!("fr-FR-u-hc-h23").into(),
    NeoHourMinuteMarker::with_length(NeoSkeletonLength::Short),
)
.unwrap();
assert_try_writeable_eq!(
    formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
    "16:12"
);

Hour cycles h11 and h24 are supported, too:

use icu::calendar::Time;
use icu::datetime::neo::TypedNeoFormatter;
use icu::datetime::neo_marker::NeoHourMinuteMarker;
use icu::datetime::neo_skeleton::NeoSkeletonLength;
use icu::locale::locale;
use writeable::assert_try_writeable_eq;

let formatter = TypedNeoFormatter::<(), _>::try_new(
    &locale!("und-u-hc-h11").into(),
    NeoHourMinuteMarker::with_length(NeoSkeletonLength::Short),
)
.unwrap();
assert_try_writeable_eq!(
    formatter.format(&Time::try_new(0, 0, 0, 0).unwrap()),
    "0:00 AM"
);

let formatter = TypedNeoFormatter::<(), _>::try_new(
    &locale!("und-u-hc-h24").into(),
    NeoHourMinuteMarker::with_length(NeoSkeletonLength::Short),
)
.unwrap();
assert_try_writeable_eq!(
    formatter.format(&Time::try_new(0, 0, 0, 0).unwrap()),
    "24:00"
);

§Fractional Second Digits

Times can be displayed with a custom number of fractional digits from 0-9:

use icu::calendar::Gregorian;
use icu::calendar::Time;
use icu::datetime::neo::TypedNeoFormatter;
use icu::datetime::neo_marker::NeoHourMinuteSecondMarker;
use icu::datetime::neo_skeleton::FractionalSecondDigits;
use icu::datetime::neo_skeleton::NeoSkeletonLength;
use icu::locale::locale;
use writeable::assert_try_writeable_eq;

let formatter = TypedNeoFormatter::<(), _>::try_new(
    &locale!("en-US").into(),
    NeoHourMinuteSecondMarker::with_length(NeoSkeletonLength::Short)
        .with_fractional_second_digits(FractionalSecondDigits::F2),
)
.unwrap();

assert_try_writeable_eq!(
    formatter.format(&Time::try_new(16, 12, 20, 543200000).unwrap()),
    "4:12:20.54 PM"
);

§Time Zone Formatting

Here, we configure a NeoFormatter to format with generic non-location short, which falls back to the offset when unavailable (see NeoTimeZoneGenericMarker).

use icu::calendar::{Date, Time};
use icu::timezone::{TimeZoneInfo, UtcOffset, TimeZoneIdMapper, TimeZoneBcp47Id};
use icu::datetime::neo::TypedNeoFormatter;
use icu::datetime::neo_marker::NeoTimeZoneGenericMarker;
use icu::datetime::neo_skeleton::NeoSkeletonLength;
use icu::datetime::DateTimeWriteError;
use icu::locale::locale;
use tinystr::tinystr;
use writeable::assert_try_writeable_eq;

// Set up the time zone. Note: the inputs here are
//   1. The offset
//   2. The IANA time zone ID
//   3. A date and time (for non-location name resolution)
//   4. Note: we do not need the zone variant because of `load_generic_*()`

// Set up the time zone ID mapper
let mapper = TimeZoneIdMapper::new();

// Set up the formatter
let mut tzf = TypedNeoFormatter::<(), _>::try_new(
    &locale!("en").into(),
    NeoTimeZoneGenericMarker::with_length(NeoSkeletonLength::Short),
)
.unwrap();

// "uschi" - has symbol data for generic_non_location_short
let time_zone = TimeZoneInfo::from_id_and_offset(
    mapper.as_borrowed().iana_to_bcp47("America/Chicago"),
    UtcOffset::from_eighths_of_hour(-5 * 8),
)
.at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::midnight()));
assert_try_writeable_eq!(
    tzf.format(&time_zone),
    "CT"
);

// "ushnl" - has time zone override symbol data for generic_non_location_short
let time_zone = TimeZoneInfo::from_id_and_offset(
    mapper.as_borrowed().iana_to_bcp47("Pacific/Honolulu"),
    UtcOffset::from_eighths_of_hour(-10 * 8),
)
.at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::midnight()));
assert_try_writeable_eq!(
    tzf.format(&time_zone),
    "HST"
);

// Mis-spelling of "America/Chicago" results in a fallback to GMT format
let time_zone = TimeZoneInfo::from_id_and_offset(
    mapper.as_borrowed().iana_to_bcp47("America/Chigagou"),
    UtcOffset::from_eighths_of_hour(-5 * 8),
)
.at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::midnight()));
assert_try_writeable_eq!(
    tzf.format(&time_zone),
    "GMT-5"
);

Structs§

Enums§

Traits§

Type Aliases§