Skip to content

Commit

Permalink
Implement date ranges & conversion (WiP)
Browse files Browse the repository at this point in the history
  • Loading branch information
khoidt committed Feb 28, 2024
1 parent 90edf1b commit 65b2541
Show file tree
Hide file tree
Showing 4 changed files with 390 additions and 188 deletions.
128 changes: 2 additions & 126 deletions src/chronology/domain/Date.ts
Original file line number Diff line number Diff line change
@@ -1,132 +1,8 @@
import { MesopotamianDateDto } from 'fragmentarium/domain/FragmentDtos'
import _ from 'lodash'
import { romanize } from 'romans'
import { MesopotamianDateBase } from 'chronology/domain/DateBase'
import { MesopotamianDateString } from 'chronology/domain/DateString'

export class MesopotamianDate extends MesopotamianDateBase {
export class MesopotamianDate extends MesopotamianDateString {
static fromJson(dateJson: MesopotamianDateDto): MesopotamianDate {
return new MesopotamianDate({ ...dateJson })
}

toString(): string {
const dayMonthYear = this.dayMonthYearToString().join('.')
const dateTail = `${this.kingEponymOrEraToString()}${this.ur3CalendarToString()}${this.modernDateToString()}`
return [dayMonthYear, dateTail]
.filter((string) => !_.isEmpty(string))
.join(' ')
}

private modernDateToString(): string {
const julianDate = this.toModernDate('Julian')
const gregorianDate = this.toModernDate('Gregorian')
return julianDate &&
gregorianDate &&
gregorianDate.replace('PGC', 'PJC') !== julianDate
? ` (${[julianDate, gregorianDate].join(' | ')})`
: julianDate
? ` (${julianDate})`
: ''
}

private dayMonthYearToString(): string[] {
const fields = ['day', 'month', 'year']
const emptyParams = fields.map((field) => {
const { isBroken, isUncertain, value } = this[field]
return !isBroken && !isUncertain && _.isEmpty(value)
})
if (!emptyParams.includes(false)) {
return []
}
return fields.map((field) =>
this.datePartToString(field as 'year' | 'day' | 'month')
)
}

private parameterToString(
field: 'year' | 'day' | 'month',
element?: string
): string {
element =
!_.isEmpty(element) && typeof element == 'string'
? element
: !_.isEmpty(this[field].value)
? this[field].value
: '∅'
return this.brokenAndUncertainToString(field, element)
}

private brokenAndUncertainToString(
field: 'year' | 'day' | 'month',
element: string
): string {
const { isBroken, isUncertain, value } = this[field]
let brokenIntercalary = ''
if (isBroken && !value) {
element = 'x'
brokenIntercalary =
field === 'month' && this.month.isIntercalary ? '²' : ''
}
return this.getBrokenAndUncertainString({
element,
brokenIntercalary,
isBroken,
isUncertain,
})
}

private getBrokenAndUncertainString({
element,
brokenIntercalary = '',
isBroken,
isUncertain,
}: {
element: string
brokenIntercalary?: string
isBroken?: boolean
isUncertain?: boolean
}): string {
return `${isBroken ? '[' : ''}${element}${
isBroken ? ']' + brokenIntercalary : ''
}${isUncertain ? '?' : ''}`
}

datePartToString(part: 'year' | 'month' | 'day'): string {
if (part === 'month') {
const month = Number(this.month.value)
? romanize(Number(this.month.value))
: this.month.value
const intercalary = this.month.isIntercalary ? '²' : ''
return this.parameterToString('month', month + intercalary)
}
return this.parameterToString(part)
}

private eponymToString(): string {
return `${this.getBrokenAndUncertainString({
element: this?.eponym?.name ?? '',
...this?.eponym,
})} (${this?.eponym?.phase} eponym)`
}

private kingToString(): string {
return this.getBrokenAndUncertainString({
element: this.king?.name ?? '',
...this?.king,
})
}

kingEponymOrEraToString(): string {
if (this.isSeleucidEra) {
return 'SE'
} else if (this.isAssyrianDate && this.eponym?.name) {
return this.eponymToString()
} else if (this.king?.name) {
return this.kingToString()
}
return ''
}

ur3CalendarToString(): string {
return this.ur3Calendar ? `, ${this.ur3Calendar} calendar` : ''
}
}
112 changes: 74 additions & 38 deletions src/chronology/domain/DateBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Eponym } from 'chronology/ui/DateEditor/Eponyms'
import DateConverter from 'chronology/domain/DateConverter'
import data from 'chronology/domain/dateConverterData.json'
import _ from 'lodash'
import DateRange from './DateRange'

export interface DateField {
value: string
Expand Down Expand Up @@ -32,6 +33,13 @@ interface DateProps {
calendar: 'Julian' | 'Gregorian'
}

export enum DateType {
seleucidDate = 'seleucidDate',
nabonassarEraDate = 'nabonassarEraDate',
assyrianDate = 'assyrianDate',
kingDate = 'kingDate',
}

export enum Ur3Calendar {
ADAB = 'Adab',
GIRSU = 'Girsu',
Expand All @@ -54,6 +62,7 @@ export class MesopotamianDateBase {
isSeleucidEra?: boolean
isAssyrianDate?: boolean
ur3Calendar?: Ur3Calendar
range?: DateRange

constructor({
year,
Expand Down Expand Up @@ -82,10 +91,15 @@ export class MesopotamianDateBase {
this.isSeleucidEra = isSeleucidEra
this.isAssyrianDate = isAssyrianDate
this.ur3Calendar = ur3Calendar
if (this.getEmptyOrBrokenFields().length > 0 && this.getType() !== null) {
this.range = DateRange.getRangeFromPartialDate(this)
console.log('!! Range:', this.range, this.range.toDateString())
}
}

private isSeleucidEraApplicable(year: number): boolean {
return !!this.isSeleucidEra && year > 0
private isSeleucidEraApplicable(year?: number | string): boolean {
year = typeof year === 'number' ? year : parseInt(year ?? '')
return !!this.isSeleucidEra && !isNaN(year) && year > 0
}

private isNabonassarEraApplicable(): boolean {
Expand All @@ -103,44 +117,48 @@ export class MesopotamianDateBase {
return !!this.king?.date
}

toModernDate(calendar: 'Julian' | 'Gregorian' = 'Julian'): string {
const dateProps = {
...this.getDateApproximation(),
calendar,
}
let julianDate = ''
if (this.isSeleucidEraApplicable(dateProps.year)) {
julianDate = this.seleucidToModernDate(dateProps)
getType(): DateType | null {
if (this?.year?.value && this.isSeleucidEraApplicable(this?.year?.value)) {
return DateType.seleucidDate
} else if (this.isNabonassarEraApplicable()) {
julianDate = this.getNabonassarEraDate(dateProps)
return DateType.nabonassarEraDate
} else if (this.isAssyrianDateApplicable()) {
julianDate = this.getAssyrianDate({ calendar: 'Julian' })
return DateType.assyrianDate
} else if (this.isKingDateApplicable()) {
julianDate = this.kingToModernDate({ ...dateProps, calendar: 'Julian' })
return DateType.kingDate
}
return julianDate
return null
}

private getNabonassarEraDate({
year,
month,
day,
isApproximate,
calendar,
}: DateProps): string {
return this.nabonassarEraToModernDate({
year: year > 0 ? year : 1,
month,
day,
isApproximate,
toModernDate(calendar: 'Julian' | 'Gregorian' = 'Julian'): string {
const type = this.getType()
if (type === null) {
return ''
}
const dateProps = {
...this.getDateApproximation(),
calendar,
})
}
const { year } = dateProps
return {
seleucidDate: () => this.seleucidToModernDate(dateProps),
nabonassarEraDate: () =>
this.nabonassarEraToModernDate({
...dateProps,
year: year > 0 ? year : 1,
}),
assyrianDate: () => this.getAssyrianDate({ calendar: 'Julian' }),
kingDate: () =>
this.kingToModernDate({ ...dateProps, calendar: 'Julian' }),
}[type]()
}

private getAssyrianDate({
calendar = 'Julian',
}: Pick<DateProps, 'calendar'>): string {
return `ca. ${this.eponym?.date} BCE ${calendarToAbbreviation(calendar)}`
// ToDo: Continue here
// Calculate years in range if year is missing
}

private getDateApproximation(): {
Expand All @@ -152,19 +170,28 @@ export class MesopotamianDateBase {
const year = parseInt(this.year.value)
const month = parseInt(this.month.value)
const day = parseInt(this.day.value)
// ToDo: Change this to ranges
// Implement `getDateRangeFromPartialDate`
// And use it.
// If possible, try to adjust to exact ranges
// that differ from scenario to scenario
const isApproximate = this.isApproximate()
// ToDo: Change this
return {
year: isNaN(year) ? -1 : year,
month: isNaN(month) ? 1 : month,
day: isNaN(day) ? 1 : day,
isApproximate: this.isApproximate(),
isApproximate,
}
}

getEmptyOrBrokenFields(): Array<'year' | 'month' | 'day'> {
const fields: Array<'year' | 'month' | 'day'> = ['year', 'month', 'day']
return fields
.map((field) => {
if (isNaN(parseInt(this[field].value)) || this[field].isBroken) {
return field
}
return null
})
.filter((field) => !!field) as Array<'year' | 'month' | 'day'>
}

private isApproximate(): boolean {
return [
_.some(
Expand Down Expand Up @@ -193,6 +220,8 @@ export class MesopotamianDateBase {
isApproximate,
calendar,
}: DateProps): string {
// ToDo: Continue here
// Use converter to compute earliest and latest dates in range
const converter = new DateConverter()
converter.setToSeBabylonianDate(year, month, day)
return this.insertDateApproximation(
Expand All @@ -208,12 +237,11 @@ export class MesopotamianDateBase {
isApproximate,
calendar,
}: DateProps): string {
const kingName = Object.keys(data.rulerToBrinkmanKings).find(
(key) => data.rulerToBrinkmanKings[key] === this.king?.orderGlobal
)
if (kingName) {
if (this.kingName) {
// ToDo: Continue here
// Use converter to compute earliest and latest dates in range
const converter = new DateConverter()
converter.setToMesopotamianDate(kingName, year, month, day)
converter.setToMesopotamianDate(this.kingName, year, month, day)
return this.insertDateApproximation(
converter.toDateString(calendar),
isApproximate
Expand All @@ -222,6 +250,12 @@ export class MesopotamianDateBase {
return ''
}

get kingName(): string | undefined {
return Object.keys(data.rulerToBrinkmanKings).find(
(key) => data.rulerToBrinkmanKings[key] === this.king?.orderGlobal
)
}

private kingToModernDate({
year,
calendar = 'Julian',
Expand All @@ -234,6 +268,8 @@ export class MesopotamianDateBase {
: this.king?.date && !['', '?'].includes(this.king?.date)
? `ca. ${this.king?.date} BCE ${calendarToAbbreviation(calendar)}`
: ''
// ToDo: Continue here
// Calculate years in range if year is missing
}

private insertDateApproximation(
Expand Down
Loading

0 comments on commit 65b2541

Please sign in to comment.