Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Julian days tick over at noon #73

Open
tysen opened this issue Jan 13, 2024 · 3 comments
Open

Julian days tick over at noon #73

tysen opened this issue Jan 13, 2024 · 3 comments
Labels
enhancement New feature or request therefor

Comments

@tysen
Copy link

tysen commented Jan 13, 2024

Hi, thanks for this crate. Any plans to handle the half day discrepancy caused by not considering the time of day? The documentation claims that Julian day 0 began at midnight, which is contrary to my understanding (that is, I understand that it began at noon UT on January 1, 4713 BC, proleptic Julian calendar).

I could handle this within my application but it seems better to handle it within this crate. I may be able to help with the implementation but I thought first to ask whether this was a conscious design decision.

@jwodder
Copy link
Owner

jwodder commented Jan 13, 2024

I am aware that the "technically correct" definition of Julian day numbers has them start at noon. I believe my thinking for the behavior of this crate went as follows:

  • For reasons mostly involving simplicity, focus on core functionality, and a distrust of floating-point, this crate only works with dates without time-of-day components (not counting the small number of functions & methods that use timestamps1). Thus, all conversions are between 24-hour periods like "2024-01-13" and JDNs.

  • Thus, given a timeless date like 2024-01-13 as input, the possible JDN outputs are:

    • A pair of integers, one the JDN for AM, one for PM.
      • This would be incredibly awkward to work with.
    • A floating-point number or fractional type equal to some integer plus a half.
      • This would just make converting back from non-integral JDNs complicated, as the date outputs would all need time-of-day components.
    • A single integer corresponding to the "PM JDN" for the date
      • This is what I went with.
      • I could have sworn that, while researching JDNs for this crate, I encountered references to this behavior being some sort of standard/conventional practice when working solely with whole JDNs, but I can't find such a reference again now. However, this practice is implied by most JDN conversion formulae (such as on Wikipedia), which equate JDN 0 to the whole of the date January 1, 4713 BC, Julian calendar, rather than to just the moment at noon.
  • Thus, each date corresponds to one JDN and vice versa, and because dates tick over at midnight, JDNs have to tick over at midnight.

  • For consistency with the date-JDN conversion methods, the timestamp-JDN methods also tick over at midnight, as I felt that following the strict rules for JDNs here would lead to user confusion when compared against the rest of the crate.

So:

  • If you're just working with dates without time-of-day, I think it makes sense for each date to map to one JDN and vice versa, and this results in JDNs ticking over at midnight because dates tick over at midnight.
    • Do you actually need this behavior changed? If so, what would you have it be instead?
    • Admittedly, I could probably add a note in the documentation about the deviation from the strict definition of JDNs.
  • I could probably add variants of the timestamp functions that tick over at noon instead of midnight. Would that solve whatever problem you're having?

Footnotes

  1. That is, Calendar::now(), Calendar::at_system_time(), Calendar::at_unix_time(), system2jdn(), unix2jdn(), and jdn2unix().

@jwodder jwodder added the question User requests information label Jan 13, 2024
@tysen
Copy link
Author

tysen commented Jan 13, 2024

All of my interaction w/ Julian dates has been in astronomy, wherein the 12 hour difference is relevant. Certainly, the whole noon thing is awkward (hence the move to MJD). I understand that there are other uses for the Julian day numbering system for which this discrepancy is not relevant.

My goal is predict the sky position (right ascension / declination) of various solar system objects (the planets, the sun, and Earth's moon) at specific points in the future at specific places on the planet. I have a separate tool to achieve this, and it takes as its input Julian dates. So I would like to convert from chrono::DateTime<Utc> to Julian dates. The 12 hours matter for most of these objects, and especially for the moon.

Here are some preliminary ideas for how to support this in this crate (any subset of these would help):

Rename julian::Date to julian::Day

The thought here is to use the distinction noted here between "Date" and "Day" where the former is the instant in time (including the fractional amount since the last noon) and the latter is only the integral part of this value.

Assume a 00:00:00 UTC timestamp for conversion to julian::Date and julian::Day from chrono::NaiveDate

That is:

// chrono::NaiveDate uses the Gregorian calendar, and a year 0, so this is November 24, 4714 BC
let start = chrono::NaiveDate::from_ymd_opt(-4713, 11, 24).unwrap();
let date: julian::Date = start.into(); // would be same for `julian::Day`
assert_eq(-1, date.julian_day_number()); // not 0

I think these are the least astonishing assumptions (midnight in UTC) to make for a chrono::NaiveDate.

Add a Date and/or Day ::modified_julian_day_number fn

In my opinion, MJD is the appropriate value to support if ticking over at midnight is a primary design goal.

Define a new julian::Date

Date could have an impl of From<DateTime<Utc>> and perhaps a Date::instant fn (maybe a better name exists) returning an f64 which is the (noon-based) Julian day number with the fractional (since noon) day included.

@jwodder jwodder added enhancement New feature or request therefor and removed question User requests information labels Jan 15, 2024
@jwodder
Copy link
Owner

jwodder commented Feb 17, 2024

I've been thinking about this on & off, and while I'm still unsure whether I want to do this or even when I might get around to doing it, if I were to support JDN rollover at noon, I'd do something like the following, just adding functionality without breaking anything:

  • Add a Time type that wraps a Duration and limits it to fewer than 86400 seconds (taking a page out of Unix timestamps' book and pretending leap seconds don't exist)

  • Add a DateTime type that combines a Date and a Time, including exposing (almost) all the same method as Date does, plus stuff like hour(), minute(), and second()

    • Note that implementing a DateLike-trait for sharing methods between Date and DateTime is a no-go, as then the Date methods would lose the precious constness I worked so hard on.
  • Instead of a julian_day_number() method, give DateTime a fractional_jd(&self) -> FractionalJD (Names are WIP) method, where FractionalJD stores an integer Julian day number — with rollover at noon — plus a Duration since noon

    • FractionalJD can then have an as_f64(&self) -> f64 method or similar for converting to a float
  • There will also have to be various methods for constructing a DateTime:

    • Calendar::now(), but returning a DateTime
    • Give a Calendar a YMDHMS tuple or structure
    • Give a Calendar a FractionalJD
    • From a Unix timestamp (and also going from a FractionalJD to Unix)
    • From a SystemTime
    • Combining a Date and a Time
    • etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request therefor
Projects
None yet
Development

No branches or pull requests

2 participants