tsackton / taelgar

0 stars 0 forks source link

Support DR and CY dates #12

Open msackton opened 9 months ago

msackton commented 9 months ago

It should be possible to define dates in either DR or CY terms. Additionally, output strings should be able to output in either DR or CY or both.

So a timeline of dwarven events, defined in CY terms, should be able to output in DR terms in a timeline of Sembaran events. Because there is no "pre-DR" era, events with negative DR years need to be considered, either by showing them with a specific static text (i.e. "before Drankor") or just showing them in negative DR terms or as CY years

tsackton commented 9 months ago

I've been thinking about this a bit. At least in Python, there are huge advantages to working with date objects instead of trying to roll our own.

My proposed structure is that the internal date objects are represented as days since the start of time, that is days since 1/1/1 CY. At least in Python, it is just one line of code to convert from any input date to this, as long as 1/1/1 in the input date is defined in terms of days since start of time. It is also just one line of code to convert from days back, against assuming 1/1/1 is defined.

I think the best course of action is to explicitly not support display of "BCE"/negative dates; if you as for a date prior to 1/1/1 of the calendar era you are requesting, you get back "before XXX (YYYY(-MM-DD) CY)".

This would make display a little bit trickier since we need to track output display calendar era, but would make a lot of internal date operations easier presumably, since you are just comparing integers.

tsackton commented 9 months ago

Linking this to #11, I think I would propose something like this. Rough draft, so can be improved on I'm sure.

  1. internally, all dates get run through a function that a) converts to days since 1/1/1 CY; b) sets an output era; c) sets an uncertainty (which we assume is symmetric)
  2. all calculations happen with an exact integer number of days
  3. to output a date or date range, first calculate back to appropriate calendar era, and then round based on uncertainty.

I would propose uncertainty be ymd, and can be optionally set either as a specific date (e.g., born: { date: 1200, error: 50y }), or perhaps set in displayDefaults.

Age calculations are assumed to use the largest error, so if you have born: { date: 1200, error: 50y }; died: 1423, you'd report age rounded as something like ((days % 365) % 50), +1 if remainder > 25.

You could implicitly set the error of plain years to 1y, e.g. (days % 365), +1 if remainder > (365/2). Could even implicitly set error of plain months to 1m.

Ideally, the output function would also control error, so that output is adjusted for error. Probably you do something like: error <= 1 month, output MM-YYYY error >1 and <=6 month, output around MM-YYYY error >6 month <= 1.5 years, output YYYY error >1.5 years, output around YYYY (with YYYY rounded)

But this could be fiddled with.

msackton commented 9 months ago

@tsackton one question is whether date objects assume a leap year. Maybe we don’t care but the length of time between two dates is likely wrong in some edge cases.

tsackton commented 9 months ago

I think it shouldn't matter for length of time calculations. E.g., for any date Y, if there are Z leap years between Y and start of time, you get days = actual days + Z. But then when you convert back, those extra Z days get absorbed back by leap years. As long as the first step is always to convert to days treating current date as an idealized Georgian calendar, it shouldn't matter.

I believe the biggest issue would be having display dates of Feb 29th, which canonically doesn't exist.

However, given that we don't have to deal with leap years, it would actually be fairly simple to just do the parsing manually, without use date objects, since it is just integer modulo math. That would also allow a calendar with different months, e.g. if we assume the dwarven calendar doesn't use exactly the earth calendar month/day setup.

msackton commented 9 months ago

Right, storing a date as a number of days since the creation of time is a good, simple way to handle things when (a) the date of the start of time is known and (b) the year is exactly 365 days long.

The other nice thing about doing the parsing manually is we could use the fantasy calendar JSON, which defines the lengths of each month and the display names of each month, so that if we decided we wanted to tweak the days per month or rename the months everything still works.

To tweak your proposal a bit:

(a) a date is defined as ~YYYY, YYYY, ~YYYY, YYYY, YYYY-MM, YYYY-MM-DD, or { date: [YYYY|YYYY-MM|YYYY-MM-DD], error: n[yd] } (b) a date of ~YYYY is the same as { date: YYYY-07-01, error: 5y } (c) a date of ~~YYYY is the same as { date: YYYY-07-01, error: 50y }
(d) a date of ~~~YYYY is the same as { date: YYYY-07-01, error: 500y }
(e) a date of YYYY is the same as { date: YYYY-07-01, error: 182d } (f) a date of YYYY-MM is the same as { date: YYYY-MM-15, error: (middle day)d } where middle day is the middle day of the month (g) a date of YYYY-MM-DD is the same as {date: YYYY-MM-DD, error: 0d }

We define errors as only in years or days to keep the logic simple, as I think it is quite rare to have the need for month-based errors.

In general, the start date for comparisions is the date minus the error days, and the end date is the date plus the error days. Need to tweak where we round / add one to make it work, too lazy to figure that out right now.

When displaying a date: Calculate the start and end dates. 1) If the day/month/year are the same, display the date as a full date 2) If the month/year are the same, display the date as "Month, year" 3) if the months are different and the year is the same 3a) if the months within 1 of the middle, display as circa middle month year 3b) if the months are both <=4, display as "early year" 3c) if the months are both >4 <=8, display as "mid year" 3d) if the months are both > 8, display as "late year" 3e) else display as "year" 4) need to think about how to handle multi-years, we basically want to show "circa year" or "1500s" or "1550s" or "circa 1000" 5) might want to consider if the error is over some specific value simply not showing a date, i.e. showing it as "unknown" or "uncertain"

Still thinking about lengths but your basic idea makes sense.

For output, I would propose:

etc I believe we only output dates via format strings at this point so pretty straightforward. The "default" output is DR.