arbre-app / read-gedcom

A modern Gedcom parser written in Typescript
https://docs.arbre.app/read-gedcom
MIT License
15 stars 4 forks source link

converting from a gedcom date to a typescript date #2

Closed lschierer closed 2 years ago

lschierer commented 2 years ago

In the API docs, I see the toJsDate function, but I am having trouble understanding how to call it. Can you add an example on extracting when an event occurred from a gedcom file, and reformatting to match the (for example) the user's locale for display?

FlorianCassayre commented 2 years ago

Hello, thanks for the question. I think the documentation is confusing if not incorrect, sorry about that.

This example shows where dates can appear in a Gedcom file and how one can extract them. Now, Gedcom dates can take various forms (range of dates, uncertainty, etc.), this is why the method valueAsDate will return a ValueDate. In practice, this type will be most of the type an instance of ValueDatePunctual, which you can check by querying the field isDatePunctual and implies that your object has a field date of type ValuePartDate. You can pass this date object to the method toJsDate (note that it expects a ValuePartDateYear but it should actually be ValuePartDate; though both reduce to the exact same type). This method will attempt to convert this date into a JavaScript standard date object.

Here is a complete example:

import { toJsDate } from 'read-gedcom';

const dates: (Date | null)[] = gedcom
  .getIndividualRecord('@I1@')
  .getEventBirth()
  .getDate() // 24 MAY 1819
  .valueAsDate() // { date: { day: 24, month: 5, year: 1819, calendar: { isGregorian: true, ... }, ... }, ... }
  .map(d => {
    if(d && d.isDatePunctual) {
      const datePart = d.date; // { day: 24, month: 5, year: 1819, calendar: { isGregorian: true, ... }, ... }
      return toJsDate(datePart); // Date | null
    } else {
      return null;
    }
  });

Note that these dates are always expressed in the UTC-0 timezone, not in your local timezone. You may for example use the following methods to access this object correctly:

const date: Date = ...; // toJsDate

console.log(date.toISOString()); // 1819-05-24T00:00:00.000Z

console.log(date.getUTCFullYear()); // 1819
console.log(date.getUTCMonth()); // 4 (in JS months start at 0)
console.log(date.getUTCDate()); // 24

I'll leave the ticket open until I fix the documentation. Feel free to ask more questions in the meantime.

lschierer commented 2 years ago

using read-gedcom@0.3.1 and transpiling using Hugo 0.101.0, I am getting "Uncaught ReferenceError: toJsDate is not defined" in my browser's javascript console. I am able to successfully console.log the gedcom.getIndividualRecord('@I1@').toString()

if ((typeof(this.myGedId) !== 'undefined') && (this.myGedId !== '')) {
      console.log(`in birthday, GedId is ${this.myGedId}`);
      const i = g.getIndividualRecord(`${this.myGedId}`);
      console.log('individual is ' + i.toString());
      this.myBirthday = g.getIndividualRecord(`${this.myGedId}`)
        .getEventBirth()
        .getDate()
        .valueAsDate()
        .map(d => {
          if(d.isDatePunctual) {
            const datePart = d.date;
            return toJsDate(datePart);
          } else {
            console.log('birthday is complicated');
            return null;
          }
        });
    } 
lschierer commented 2 years ago

npm ls | grep -i type ├── @babel/preset-typescript@7.17.12 ├── @fortawesome/fontawesome-common-types@0.3.0 ├── @types/alpinejs@3.7.0 ├── @types/isomorphic-fetch@0.0.36 ├── @types/jest@26.0.24 ├── @types/node@10.17.60 ├── typescript@4.7.4

FlorianCassayre commented 2 years ago

0.3.1 is the right version; but I should have mentioned that you must import toJsDate in order to use it:

import { readGedcom, toJsDate } from 'read-gedcom';

(or if you are transpiling to CJS:)

const { readGedcom, toJsDate } = require('read-gedcom');
lschierer commented 2 years ago

The final code ended up being

import { readGedcom, toJsDate, ValueDatePunctual } from 'read-gedcom';

      this.myBirthday = g.getIndividualRecord(`${this.myGedId}`)
        .getEventBirth()
        .getDate()
        .valueAsDate()
        .map(d => {
          if(d && d.isDatePunctual) {
            const datePart = (d as ValueDatePunctual).date;
            return toJsDate(datePart);
          } else {
            return null;
          }
        });

thanks for your help!!!

FlorianCassayre commented 2 years ago

Great! You're welcome.

This d as ValueDatePunctual appears unnecessary to me; in fact the library is designed to avoid type ascriptions at all costs. Here isDatePunctual === true implies date !== undefined and the compiler should be able to pick that up. In fact I just tested it locally without the ascription, so you should be able to remove it in your code too.

Anyway don't hesitate if you have more questions. I am striving to make this library better, and feedback/suggestions/contributions are welcome.

lschierer commented 2 years ago

it is probably a function of my tsconfig, as I have noticed that npm run build is far more strict than the hugo transpiler,

cat tsconfig.json 
{
  "compilerOptions": {
    "target": "es2020",
    "module": "es2020",
    "lib": ["ES2020", "DOM"],
    "moduleResolution": "Node",
    "experimentalDecorators": true,
    "declaration": true,
    "useDefineForClassFields": false,
    "strict": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "noEmit": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noImplicitReturns": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "alwaysStrict": true,
    "noFallthroughCasesInSwitch": false,
    "strictPropertyInitialization": false,
    "typeRoots": [
      "./node_modules/@types"
    ]
  },
  "exclude": [
    "node_modules",
    "cdk.out",
        "infrastructure/cdk.out"
  ]
}
FlorianCassayre commented 2 years ago

I see, thanks. With this config the example I provided does not compile because the option strictNullChecks is enabled (I have forgotten a null check), but the said type ascription is still optional. In my opinion it's a good thing to leave this option on, I should enable it on this repo too.