Guides Localization Date and Timezone

Date and Timezone

Elemental provides a powerful and reactive system for handling date and time localization, built upon the robust dayjs library. This guide will walk you through setting up your application's localization environment, working with dates using the LmnDate class, and formatting them for display with our custom pipes. All functionalities discussed here are available from @elemental/common, unless specified otherwise.

Core Concepts

Understanding these core concepts is key to effectively using Elemental's date and time features:

  • Locales (LmnLocale): A locale defines language-specific formatting conventions, including number formats, date representations, and day/month names. Locales are typically represented by a language code and an optional region/country code (e.g., 'en' for English, 'en-US' for US English, 'fr-CA' for Canadian French). Elemental provides an extensive list of supported locales.
  • Languages (LmnLanguage): While locales handle formatting, the language setting (e.g., 'en', 'it') is primarily used by the translation engine and influences language-specific aspects of dayjs (like month/day names if not fully specified by the locale data).
  • Date Formats (LmnDateFormatPreset): Elemental defines standard date and time format presets for 'short', 'medium', 'long', and 'full' lengths. These presets dictate how dates, times, and datetimes are structured for each locale.
  • Timezones (LmnTimezone): Timezones are identified using TZ database names (e.g., 'America/New_York', 'Europe/Rome', 'UTC'). Elemental allows for both global timezone settings and instance-specific timezone handling.
  • LmnDate Class: This is your primary tool for all date and time operations. It's a reactive wrapper around a dayjs object, internally managing all dates in UTC and handling timezone conversions transparently for display and manipulation.
  • Date Pipes (lmnDate, lmnTime, lmnDatetime, lmnTimezone): Angular pipes for easily formatting LmnDate objects directly within your templates, respecting the current locale and timezone settings.

Setting Up Your Localization Environment

Properly configuring your application's localization environment is the first step. This typically involves setting default values for locale, language, and timezone during your application's bootstrap process.

Setting the Default Locale

The locale dictates how dates, numbers, and other locale-sensitive information are formatted.

setLocale(locale: LmnLocale, localeSpec?: LmnLocaleSpecification, dateFormatSpec?: LmnDateFormatPreset): Promise<void>

  • Use setLocale to define the application-wide default locale. This function will also attempt to dynamically load the corresponding dayjs locale module.
  • You can optionally provide a custom LmnLocaleSpecification to fine-tune dayjs behavior for that locale (e.g., first day of the week, custom month names).
  • You can also provide a LmnDateFormatPreset to define entirely custom date/time formats for this locale, which will override any built-in formats.
  • This updates the currentLocale(): Signal<LmnLocale>, which many formatting functions and pipes depend on.
// Example: app.config.ts
import { ApplicationConfig } from '@angular/core';
import { setLocale } from '@elemental/common';
// Assuming you might also set language and timezone
import { setLanguage, setTimezone } from '@elemental/common';

export function initializeLocalization(): Promise<void> {
  setLanguage('en'); // Set base language for translations
  setTimezone('America/New_York'); // Set default operating timezone
  return setLocale('en-US'); // Set default locale for formatting
}

export const appConfig: ApplicationConfig = {
  providers: [
    // ... other providers
    {
      provide: APP_INITIALIZER,
      useFactory: () => initializeLocalization,
      multi: true,
    },
  ],
};

Setting the Default Language

While setLocale handles formatting, setLanguage influences the language used by dayjs for names (if not overridden by LmnLocaleSpecification) and is crucial for the Translations engine.

setLanguage(language: LmnLanguage): void

  • Updates the currentLanguage(): Signal<LmnLanguage>.
import { setLanguage } from '@elemental/common';

// Typically called once during app initialization
setLanguage('fr'); 

Setting the Default Timezone

Define a default timezone for your application. All LmnDate formatting will default to this timezone unless explicitly overridden.

setTimezone(timezone: LmnTimezone): void

  • Updates the currentTimezone(): Signal<LmnTimezone>.
import { setTimezone } from '@elemental/common';

// Typically called once during app initialization
setTimezone('Europe/Paris'); 

Customizing Date Formats for a Locale

If the default date formats for a locale aren't suitable, you can provide a complete custom LmnDateFormatPreset.

setDateFormatCustom(locale: LmnLocale, dateFormatPreset?: LmnDateFormatPreset): void

  • This custom format will be used whenever getFormatByLocale(locale) or currentFormat() (if locale is the current locale) is called, taking precedence over built-in formats.
import { setDateFormatCustom, LmnDateFormatPreset, LmnLocale } from '@elemental/common';

const myCustomGermanFormats: LmnDateFormatPreset = {
  short: {
    datetime: 'DD.MM.YY HH:mm',
    date: 'DD.MM.YY',
    time: 'HH:mm',
    // ... other lengths and parts (year, month, day, etc.)
  },
  // ... medium, long, full definitions
};

setDateFormatCustom('de', myCustomGermanFormats);

// If currentLocale() is 'de', currentFormat() will now return myCustomGermanFormats.

Working with Dates: The LmnDate Class

The LmnDate class is the cornerstone for handling dates and times in Elemental. It provides a rich, reactive API for parsing, manipulating, formatting, and querying dates.

For a complete list of all methods and their detailed parameters, please refer to the LmnDate API documentation.

Introduction to LmnDate

  • Reactive Wrapper: LmnDate instances are built around Angular Signals, making them inherently reactive to changes in global locale/timezone settings or their own internal state.
  • Internal UTC: All date and time values within an LmnDate object are stored and managed in UTC. Timezone conversions are applied during formatting or when specific timezone-aware methods are called.

Creating LmnDate Instances

You can create LmnDate objects from various sources:

  • Current Date/Time: new LmnDate() creates an instance with the current timestamp.

  • From String, Number, or JS Date:

    const dateFromISO = new LmnDate('2023-10-26T10:00:00Z');
    const dateFromTimestamp = new LmnDate(1698314400000); // Milliseconds since epoch
    const dateFromJsDate = new LmnDate(new Date());
  • Parsing with Specific Format and Timezone: If you have a date string in a non-standard format or a specific timezone, provide those details:

    // Date string is in 'America/Denver' time
    const dateFromString = new LmnDate('10/26/2023 12:30 PM', 'MM/DD/YYYY hh:mm A', 'America/Denver'); 
    console.log(dateFromString.value()); // Will show the UTC equivalent
  • Instance-Specific Locale: You can also create an LmnDate with its own locale, which will override the global currentLocale() for that specific instance's formatting methods:

    const dateInFrench = new LmnDate(undefined, undefined, undefined, undefined, 'fr-CA');
    console.log(dateInFrench.format({ as: 'date', length: 'full' })); // Formats as per 'fr-CA'
  • Cloning: Create a copy with const clonedDate = myLmnDate.clone();.

  • Validation: Check if a date was parsed correctly with myLmnDate.isValid().

Basic Formatting with LmnDate

LmnDate instances expose several computed Signals for common formatting needs. These automatically use the instance's effective locale (instance-specific or global currentLocale()) and the global currentTimezone().

  • Reactive Properties:

    • myLmnDate.value(): Returns the date as an ISO 8601 string in UTC (e.g., "2023-10-26T17:00:00.000Z").
    • myLmnDate.date(): Formatted date string (e.g., "10/26/2023").
    • myLmnDate.time(): Formatted time string (e.g., "01:00 PM").
    • myLmnDate.datetime(): Formatted date and time string (e.g., "10/26/2023 01:00 PM").
    • Similar properties exist for fullDate(), fullTime(), fullDatetime(), and UTC-specific versions like dateUTC().
  • .format(options?: LmnDateFormatOptions) Method: For more control over formatting, use the .format() method:

    const myDate = new LmnDate();
    
    // Default short date format for current locale/timezone
    console.log(myDate.format({ as: 'date' })); 
    
    // Full date format
    console.log(myDate.format({ as: 'date', length: 'full' })); 
    
    // Custom format string, specific timezone
    console.log(myDate.format({ format: 'YYYY-MM-DD HH:mm (dddd)', timezone: 'Europe/Berlin' })); 

Common Date Manipulations

Modify dates easily:

  • Adding/Subtracting Time:

    const today = new LmnDate();
    const nextWeek = today.clone().add(7, 'days');
    const lastMonth = today.clone().subtract(1, 'months');
  • Start/End of a Time Unit:

    const startOfThisMonth = new LmnDate().startOf('month');
    const endOfToday = new LmnDate().endOf('day');

Comparisons and Queries

LmnDate offers a comprehensive set of methods for comparing dates (e.g., isBefore(), isSameOrAfter(), isBetween()) and querying information like isLeapYear().

const date1 = new LmnDate('2024-01-15');
const date2 = new LmnDate('2024-02-20');

if (date1.isBefore(date2)) {
  console.log('Date 1 is earlier.');
}

Human-Readable Time (Relative Time)

Display time relative to now or another date:

  • myLmnDate.getHumanizedTimeRelativeToNow(hideSuffix?: boolean): E.g., "in 5 minutes", "2 hours ago".
  • myLmnDate.getHumanizedTimeRelativeToDate(otherDate: LmnDate, hideSuffix?: boolean).

These respect the current locale for phrasing.

Displaying Dates and Times in Templates (Pipes)

Elemental UI provides convenient Angular pipes for formatting LmnDate objects directly in your component templates. These pipes automatically respect the global currentLocale() and currentTimezone() settings but can also be customized.

Refer to the individual pipes API documentation for detailed options.

  • lmnDate Pipe: Formats the LmnDate as a date string.

    <!-- Assumes myEventDate is an LmnDate instance in your component -->
    <p>Event Date: {{ (myEventDate | lmnDate)() }}</p>                            <!-- Default short date -->
    <p>Full Date: {{ (myEventDate | lmnDate: { length: 'full' })() }}</p>
    <p>Custom: {{ (myEventDate | lmnDate: { format: 'DD/MM/YY', timezone: 'UTC' })() }}</p>

    Detailed documentation

  • lmnTime Pipe: Formats the LmnDate as a time string.

    <p>Event Time: {{ (myEventDate | lmnTime)() }}</p>                            <!-- Default short time -->
    <p>Detailed Time: {{ (myEventDate | lmnTime: { length: 'medium', timezone: 'America/New_York' })() }}</p>

    Detailed documentation

  • lmnDatetime Pipe: Formats the LmnDate as a combined date and time string.

    <p>Timestamp: {{ (myEventDate | lmnDatetime: { length: 'long' })() }}</p>

    Detailed documentation

Working with Timezones

Proper timezone handling is critical for many applications.

  • Global Default: As shown in setup, setTimezone() establishes the application's default timezone, which currentTimezone() reflects. Pipes and LmnDate formatting methods use this default if no specific timezone is provided.

  • Displaying Timezone Information:

    • currentTimezoneLabel(): Signal<string>: A computed Signal providing the full, DST-aware label of the current default timezone (e.g., (GMT -04:00) America/New_York).

    • getTimezoneLabel(timezone: LmnTimezone, referenceDate?: LmnDate): string: Programmatically get the DST-aware label for any timezone, optionally using a specific LmnDate as the reference for DST calculation.

    • lmnTimezone Pipe: Displays the DST-aware label for a given LmnDate and timezone.

      <!-- Uses global currentTimezone() for referenceDate's timezone label -->
      <p>Event Timezone: {{ (myEventDate | lmnTimezone)() }}</p> 
      
      <!-- Shows label for a specific timezone, considering myEventDate for DST -->
      <p>In London: {{ (myEventDate | lmnTimezone: 'Europe/London')() }}</p>
  • Formatting in Specific Timezones: Both LmnDate.format() and the date/time pipes accept a timezone option to render the date as it would appear in that target timezone.

Locale-Specific Date Utilities

For building UIs like custom calendars or date pickers, you might need locale-specific names for days and months. These utilities respect the currentLocale().

  • getWeekdays(localOrder?: boolean): string[]
  • getWeekdaysShort(localOrder?: boolean): string[]
  • getWeekdaysMin(localOrder?: boolean): string[]
  • getWeekdaysInitials(localOrder?: boolean): string[]
  • getMonths(): string[]
  • getMonthsShort(): string[]
import { getMonths, getWeekdaysShort, currentLocale, setLocale } from '@elemental/common';

await setLocale('es-ES'); // Set to Spanish
console.log(getMonths()); // Outputs: ['enero', 'febrero', ...]
console.log(getWeekdaysShort(true)); // Outputs weekday names starting with the locale's first day of week

Common Workflows

  1. Application Initialization:
    • In app.config.ts or an APP_INITIALIZER, call setLanguage(), setLocale(), and setTimezone() to establish application-wide defaults.
    • Optionally use setDateFormatCustom() if default formats for certain locales need overriding.
  2. Displaying a Server-Provided UTC Timestamp:
    • Receive a timestamp or ISO string (assumed to be UTC).
    • Create new LmnDate(timestampOrIsoString).
    • Use {{ (myLmnDate | lmnDatetime)() }} in the template. It will automatically display in the user's currentTimezone() and currentLocale() format.
  3. Date Input from User:
    • User inputs a date string, possibly with a specific format.
    • Create new LmnDate(userInputString, 'MM/DD/YYYY', userSelectedTimezone). Use strict: true for reliable parsing if the format is fixed.
  4. Calculating Future Dates:
    • const expiryDate = new LmnDate().add(30, 'days');
    • Display with {{ (expiryDate | lmnDate: { length: 'long' })() }}.

This comprehensive date and time localization system in Elemental provides the flexibility and reactivity needed for modern global applications. Remember to consult the specific API documentation for LmnDate and related utilities for exhaustive details on all available methods and options.

For representing and formatting periods of time (e.g., "2 hours 30 minutes", "3 days"), Elemental provides the LmnDuration class and lmnDuration pipe. See the Durations Localization Guide for more details.