Luxon: Cannot assign luxon DateTime when it's created from ISO string

212 views Asked by At

When I create a luxon DateTime object from the fromISO method, seems like it cannot be assigned to DateTime properly.

e.g.

let datetime: DateTime = DateTime.fromISO(isoString);

does not compile with the error:

Type 'DateTime<true> | DateTime<false>' is not assignable to type 'DateTime<boolean>'.
  Type 'DateTime<true>' is missing the following properties from type 'DateTime<boolean>': isWeekend, localWeekday, localWeekNumber, localWeekYear, weeksInLocalWeekYear

What is a proper way to construct DateTime from ISO string?

2

There are 2 answers

2
jsejcksn On BEST ANSWER

This is a somewhat complicated one. I'll provide some explanation below, but — in short — you can avoid the issue either by:

  • not using an explicit annotation, and instead simply rely on TypeScript's inference when initializing your variable:

    TS Playground

    import { DateTime } from "luxon";
    
    const isoString = "2017-05-15T08:30:00";
    let dt = DateTime.fromISO(isoString);
    //  ^? let dt: DateTime<true> | DateTime<false>
    
  • or you can use the exported type alias DateTimeMaybeValid to annotate the instance:

    TS Playground

    import { DateTime, type DateTimeMaybeValid } from "luxon";
    
    const isoString = "2017-05-15T08:30:00";
    let dt: DateTimeMaybeValid = DateTime.fromISO(isoString);
    //  ^? let dt: DateTime<true> | DateTime<false>
    

More:

Luxon has a concept of validity. By default, DateTimes fail silently instead of throwing exceptions, and the validity information is stored on the DateTime instances. Here's an example:

<script type="module">

import { DateTime } from "https://cdn.jsdelivr.net/npm/[email protected]/build/es6/luxon.js";

for (const input of ["2017-05-15T08:30:00", "tomorrow"]) {
  const dt = DateTime.fromISO(input);
  const { isValid, invalidExplanation, invalidReason } = dt;
  const text = dt.toString();
  console.log({ input, isValid, invalidReason, invalidExplanation, text });
}

</script>

However, Luxon can be configured to throw in cases where an invalid DateTime would be produced:

<script type="module">

import { DateTime, Settings } from "https://cdn.jsdelivr.net/npm/[email protected]/build/es6/luxon.js";

Settings.throwOnInvalid = true;

for (const input of ["2017-05-15T08:30:00", "tomorrow"]) {
  try {
    const dt = DateTime.fromISO(input);
    const text = dt.toString();
    console.log({ input, valid: true, text });
  } catch (cause) {
    console.log({ input, valid: false });
    console.error(cause);
  }
}

</script>

In the type system, DateTime is generic in an attempt to encode the validity state so that the compiler can use it to narrow:

TS Playground

for (const input of ["2017-05-15T08:30:00", "tomorrow"]) {
  const dt = DateTime.fromISO(input);
  if (dt.isValid) {
    dt.invalidReason
    // ^? (property) DateTime<true>.invalidReason: null
    dt.invalidExplanation
    // ^? (property) DateTime<true>.invalidExplanation: null
  } else {
    dt.invalidReason
    // ^? (property) DateTime<false>.invalidReason: string
    dt.invalidExplanation
    // ^? (property) DateTime<false>.invalidExplanation: string | null
  }
}

Note: You can inform the TypeScript compiler that you enabled the exception-throwing option (above): 1, 2

0
Jip W. On

You can check separately whether the parsing succeeded. As an example:

let datetime = DateTime.fromISO(isoString); // type: DateTime<true> | DateTime<false>

// check if parsing succeeded
if (!datetime.isValid) {
  // cancel the operation because the date time is invalid
  return
}

const validTime = datetime; // type: DateTime<true>

I'm not sure when this was changed, I can not find it in the changelog or the documentation of luxon. If someone knows where this change is from then I'd love to know

edit: there's a lot more information in the answer of @jsejcksn