# `Astro.Time`
[🔗](https://github.com/kipcole9/astro/blob/v2.1.0/lib/astro/time.ex#L1)

Time scales, conversions, and calendar utilities for astronomical calculations.

## Moments

A **moment** is the primary internal time representation used throughout
`Astro`. It is a floating-point number of days since the Gregorian epoch
(0000-01-01 00:00:00 UTC). The integer part identifies the calendar day;
the fractional part represents the elapsed fraction of that day since
midnight.

The public API in `Astro` accepts `Date` and `DateTime` parameters and
converts them to moments before delegating to the implementation modules
(`Astro.Solar`, `Astro.Lunar`, `Astro.Solar.SunRiseSet`,
`Astro.Lunar.MoonRiseSet`). Those modules work primarily with moments
and aim to never convert back to `Date` or `DateTime` internally for
performance reasons.

Use `date_time_to_moment/1` to convert a `Date` or `DateTime` to a
moment, and `date_time_from_moment/1` to convert a moment back to a
UTC `DateTime`.

## Time scales

Six time scales appear in astronomical calculations. Each serves a
different purpose.

### Universal Time (UTC)

The civil clock time standard. UTC is kept within one second of mean
solar time at 0° longitude by the occasional insertion of leap seconds.
It is the base time scale for moments: a moment is always in UTC.

See: `universal_from_local/2`, `universal_from_standard/2`,
`universal_from_dynamical/1`.

### Standard Time

UTC adjusted for a named time zone (e.g. `"America/New_York"`),
including any daylight-saving offset. Standard time has discrete zone
boundaries and changes at politically determined transition points.

See: `standard_from_universal/2`, `universal_from_standard/2`.

### Local (Mean Solar) Time

UTC adjusted purely by geographic longitude — what a sundial reads.
The offset is `longitude / 360` of a day, a smooth function of
position with no zone boundaries or daylight-saving rules.

See: `local_from_universal/2`, `universal_from_local/2`.

### Dynamical Time

The uniform time scale used for computing planetary and lunar orbits.
In Astro, "dynamical time" refers to TDB (Barycentric Dynamical
Time) — the time argument expected by JPL ephemerides.

Two representations coexist:

  * **Moment-domain** — `dynamical_from_universal/1` adds ΔT (as a
    fraction of a day) to a UTC moment, producing a dynamical moment
    used by the Meeus-era polynomial series via
    `julian_centuries_from_moment/1`.

  * **Seconds-past-J2000.0** — `dynamical_time_from_moment/1` converts
    a UTC moment to TDB seconds past J2000.0, the scale used directly
    by the SPK kernel. `dynamical_time_to_moment/1` inverts it.

Both representations derive ΔT from the unified `delta_t/1` function.

### Terrestrial Time (TT)

A uniform atomic time scale defined on Earth's geoid, the modern
successor to Ephemeris Time (ET). TT is related to International
Atomic Time (TAI) by a fixed offset: TT = TAI + 32.184 s. For
solar-system calculations at Earth's distance, TT ≈ TDB to within
~1.7 ms, and Astro treats them as identical.

The `DateTime` conversion `utc_date_time_from_dynamical_date_time/1`
delegates to `universal_from_dynamical/1` internally.

### Sidereal Time

Measures Earth's rotation relative to the stars rather than the Sun.
A sidereal day is ~3 minutes 56 seconds shorter than a solar day.
Greenwich Mean Sidereal Time (GMST) is used to convert between
equatorial coordinates and the local horizon; Greenwich Apparent
Sidereal Time (GAST) adds the equation of the equinoxes (nutation
in right ascension).

Functions: `greenwich_mean_sidereal_time/1`,
`local_sidereal_time/2`, `mean_sidereal_from_moment/1`,
`apparent_sidereal_from_moment/1`. See also `Astro.Coordinates.gast/1`.

## Time scale conversion table

The table below shows how to convert from one time scale (row) to
another (column). Each cell describes the conversion algorithm.
A dash (—) marks the identity diagonal.

| From \ To          | Universal (UTC)  | Standard            | Local               | Dynamical           | Terrestrial         | Sidereal              |
|:--------------------|:-----------------|:--------------------|:--------------------|:--------------------|:--------------------|:----------------------|
| **Universal** (UTC) | —                | + zone offset       | + longitude/360     | + ΔT                | + ΔT (≈ dynamical)  | GMST polynomial in UTC |
| **Standard**        | − zone offset    | —                   | − zone + long/360   | − zone + ΔT         | − zone + ΔT         | via UTC then GMST     |
| **Local**           | − longitude/360  | − long/360 + zone   | —                   | − long/360 + ΔT     | − long/360 + ΔT     | via UTC then GMST     |
| **Dynamical**       | − ΔT             | − ΔT + zone         | − ΔT + long/360     | —                   | identity (≈)        | via UTC then GMST     |
| **Terrestrial**     | − ΔT (≈ dyn)     | − ΔT + zone         | − ΔT + long/360     | identity (≈)        | —                   | via UTC then GMST     |
| **Sidereal**        | not invertible†  | not invertible†     | not invertible†     | not invertible†     | not invertible†     | —                     |

**Notes:**

* **zone offset** = UTC offset + DST offset for the named time zone,
  looked up via the configured `TimeZoneDatabase`.
* **longitude/360** = fraction of a day corresponding to the observer's
  geographic longitude (west negative).
* **ΔT** = TT − UTC in seconds, converted to fractional days by
  dividing by 86400. Computed by `delta_t/1`.
* **Terrestrial ≈ Dynamical**: TT and TDB differ by at most ~1.7 ms;
  Astro treats them as identical.
* **† Sidereal → other**: sidereal time is not uniquely invertible
  because multiple UTC instants map to the same sidereal angle within
  a sidereal day. In practice, sidereal time is computed *from* UTC
  for a known date, not converted back.

## ΔT

ΔT (TT − UTC) is the difference between the uniform dynamical time
scale and civil clock time. It varies as Earth's rotation rate changes
due to tidal friction and other geophysical effects. The unified
`delta_t/1` function returns ΔT in seconds for a given decimal year,
drawing on IERS observations (1972–2025), the Meeus biennial table
(1620–1971), and polynomial approximations for earlier and later dates.

## Julian day system

The Julian day system provides a continuous day count independent of
any calendar. It is the standard time-keeping framework in positional
astronomy.

### Julian Day (JD)

A continuous count of days (and fractions) from an epoch set at
Greenwich noon on 1 January 4713 BC (Julian proleptic calendar).
Day boundaries fall at noon, not midnight — JD 2451545.0 corresponds
to 2000-01-01 12:00:00 TT.

Functions: `julian_day_from_date/1`, `date_time_from_julian_days/1`,
`date_from_julian_days/1`.

### J2000.0

The standard astronomical epoch: 2000 January 1.5 TT (Julian Day
2451545.0). Precession angles, nutation series, and ephemeris
polynomials are all referenced to this epoch. Dynamical time in
this library is expressed as seconds past J2000.0.

Constant: `j2000/0` (returns the moment for J2000.0).

### Modified Julian Day (MJD)

JD − 2400000.5. This shifts the day boundary from noon to midnight
and produces smaller numbers, convenient for modern dates. MJD 0
corresponds to 1858-11-17 00:00:00 UTC.

Function: `mjd/1`.

### Julian Centuries

A Julian century is exactly 36525 days (100 Julian years of 365.25
days each). Precession and nutation polynomials are evaluated in
Julian centuries from J2000.0. Two conversion paths exist:

  * `julian_centuries_from_julian_day/1` — converts a Julian day
    directly.
  * `julian_centuries_from_moment/1` — converts a UTC moment by
    first applying ΔT to obtain a dynamical moment.
  * `julian_centuries_from_dynamical_time/1` — converts dynamical
    time (seconds past J2000.0) to Julian centuries.

# `days`

```elixir
@type days() :: number()
```

A number of days as a float

# `fraction_of_day`

```elixir
@type fraction_of_day() :: number()
```

A time of day as a float fraction of a day

# `hms`

```elixir
@type hms() :: {Calendar.hour(), Calendar.minute(), Calendar.second()}
```

A tuple of integer hours, integer minutes and integer seconds

# `hours`

```elixir
@type hours() :: number()
```

A number of hours as a float

# `julian_centuries`

```elixir
@type julian_centuries() :: number()
```

The float number of Julian centuries.

Since there are 365.25 days in a Julian year,
a Julian century has 36,525 days.

# `julian_days`

```elixir
@type julian_days() :: number()
```

A float number of days since the Julian epoch.

The current Julian epoch is defined to have been
noon on January 1, 2000. This epoch is
denoted J2000 and has the exact Julian day
number `2,451,545.0`.

# `minutes`

```elixir
@type minutes() :: number()
```

A number of minutes as a float

# `moment`

```elixir
@type moment() :: number()
```

A moment is a floating point representation of
the fraction of a day.

# `season`

```elixir
@type season() :: Astro.angle()
```

Season expressed as a non-negative number
that is <= 360 representing the sun angle of incidence
(the angle at which the sun hits the earth).

# `seconds`

```elixir
@type seconds() :: number()
```

A number of seconds as a float

# `time`

```elixir
@type time() :: number()
```

A time is a floating point number of
days since 0000-01-01 including the fractional
part of a day.

# `zone_name`

```elixir
@type zone_name() :: binary()
```

A time zone name as a string

# `ajd`

# `apparent_sidereal_from_moment`

# `date_from_julian_days`

Returns the date for a given Julian day number.

The Julian day is rounded to the nearest integer before
conversion. This is suitable when only the calendar date is
needed, without time-of-day information.

### Arguments

* `julian_day` is a Julian day number (float or integer).

### Returns

* `{:ok, date}` — a `t:Date.t/0`.

### Examples

    iex> Astro.Time.date_from_julian_days(2458822.5)
    {:ok, ~D[2019-12-05]}

# `date_time_from_date_and_minutes`

```elixir
@spec date_time_from_date_and_minutes(minutes(), Calendar.date()) ::
  {:ok, Calendar.datetime()}
```

Returns a UTC datetime by combining a date with a float number
of minutes since midnight.

### Arguments

* `minutes` is a float number of minutes since midnight.

* `date` is any `t:Calendar.date/0`.

### Returns

* `{:ok, datetime}` — a `t:DateTime.t/0` in UTC.

### Examples

    iex> Astro.Time.date_time_from_date_and_minutes(720.0, ~D[2024-06-21])
    {:ok, ~U[2024-06-21 12:00:00Z]}

# `date_time_from_julian_days`

```elixir
@spec date_time_from_julian_days(julian_days()) :: {:ok, Calendar.datetime()}
```

Returns a UTC datetime for a given Julian day number.

### Arguments

* `julian_day` is a Julian day number as a `float`.

### Returns

* `{:ok, datetime}` — a `t:DateTime.t/0` in the UTC time zone.

### Examples

    iex> Astro.Time.date_time_from_julian_days(2458822.5)
    {:ok, ~U[2019-12-05 00:00:00Z]}

    iex> Astro.Time.date_time_from_julian_days(2451545.0)
    {:ok, ~U[2000-01-01 12:00:00Z]}

# `date_time_from_moment`

```elixir
@spec date_time_from_moment(moment()) :: {:ok, DateTime.t()}
```

Returns a UTC `DateTime` for a given moment.

A moment is by definition in the UTC timezone, so the returned
`DateTime` always has `time_zone: "Etc/UTC"`. Microsecond
precision is preserved.

### Arguments

* `moment` is a float representation of a UTC datetime where
  the integer part is the number of Gregorian days since
  0000-01-01 and the fractional part is the fraction of a day
  since midnight.

### Returns

* `{:ok, datetime}` — a `t:DateTime.t/0` in UTC with
  microsecond precision.

### Examples

    iex> Astro.Time.date_time_from_moment(740047.5)
    {:ok, ~U[2026-03-07 12:00:00.000000Z]}

    iex> Astro.Time.date_time_from_moment(740047.0)
    {:ok, ~U[2026-03-07 00:00:00.000000Z]}

    iex> Astro.Time.date_time_from_moment(740047.999999)
    {:ok, ~U[2026-03-07 23:59:59.913599Z]}

# `date_time_in_requested_zone`

Returns a `DateTime` shifted to the requested time zone.

Converts a UTC `DateTime` to the time zone specified in `options`.
When the time zone is `:utc`, the datetime is returned unchanged.
When `:default`, the time zone is resolved from the location using
`TzWorld` (or a custom `:time_zone_resolver`). When a time zone
name string is given, the datetime is shifted to that zone.

### Arguments

* `utc_event_time` is a UTC `DateTime`.

* `location` is a `Geo.PointZ` struct with `{longitude, latitude, altitude}`
  coordinates. Used only when `:time_zone` is `:default`.

* `options` is a map containing:
  * `:time_zone` — `:utc`, `:default`, or a time zone name string.
  * `:time_zone_database` — the time zone database module
    (e.g. Tz.TimeZoneDatabase).
  * `:time_zone_resolver` — (optional) a 1-arity function
    `(%Geo.Point{}) → {:ok, String.t()}` for custom zone resolution.

### Returns

* `{:ok, datetime}` — the `DateTime` in the requested time zone.
* `{:error, reason}` — if the time zone cannot be resolved or shifted.

### Examples

    iex> utc = ~U[2024-06-21 12:00:00Z]
    iex> location = %Geo.PointZ{coordinates: {0.0, 51.5, 0.0}}
    iex> options = %{time_zone: :utc, time_zone_database: Tz.TimeZoneDatabase}
    iex> Astro.Time.date_time_in_requested_zone(utc, location, options)
    {:ok, ~U[2024-06-21 12:00:00Z]}

# `date_time_to_moment`

```elixir
@spec date_time_to_moment(Calendar.date() | Calendar.datetime()) :: moment()
```

Returns a moment for a given date or datetime.

A moment is a float where the integer part is the number of Gregorian
days since 0000-01-01 and the fractional part is the fraction of a day
since midnight. Moments are always in UTC.

When given a `DateTime`, it is first shifted to UTC before conversion.
When given a `Date`, returns the integer Gregorian day number
(midnight UTC).

### Arguments

* `date_or_datetime` is any `t:Calendar.date/0` or
  `t:Calendar.datetime/0`.

### Returns

* A moment as a `float` (or integer for `Date` inputs).

### Examples

    iex> Astro.Time.date_time_to_moment(~U[2026-03-07 12:00:00Z])
    740047.5

    iex> Astro.Time.date_time_to_moment(~D[2026-03-07])
    740047

# `delta_t`

```elixir
@spec delta_t(float()) :: float()
```

Returns ΔT (TT − UTC) in seconds for the given decimal year.

ΔT is the difference between Terrestrial Time (TT) and Universal
Time (UTC). It varies over time as Earth's rotation rate changes.

Uses the best available data for each era:
- **1972–2025**: IERS-observed annual values with linear interpolation
- **1620–1971**: Meeus biennial lookup table with interpolation
- **Pre-1620 and post-2025**: Polynomial approximations

### Arguments

* `year` is a decimal year (e.g., 2024.5 for mid-2024)

### Returns

* ΔT in seconds as a `float`.

### Examples

    iex> Astro.Time.delta_t(2000.0)
    63.83

    iex> Float.round(Astro.Time.delta_t(2024.5), 2)
    69.24

# `dynamical_from_universal`

```elixir
@spec dynamical_from_universal(time()) :: time()
```

Returns the dynamical moment for a given universal (UTC) moment.

Adds ΔT (converted to a fraction of a day) to the UTC moment,
producing a dynamical moment suitable for evaluating Meeus-era
polynomial series via `julian_centuries_from_moment/1`.

### Arguments

* `t` is a moment (float Gregorian days since 0000-01-01) in UTC.

### Returns

* A moment in the dynamical time scale (float Gregorian days).

### Examples

    iex> t = Astro.Time.date_time_to_moment(~U[2000-01-01 12:00:00Z])
    iex> dt = Astro.Time.dynamical_from_universal(t)
    iex> Float.round(dt - t, 6)
    0.000739

# `dynamical_time_from_moment`

```elixir
@spec dynamical_time_from_moment(float()) :: float()
```

Returns dynamical time (TDB seconds past J2000.0) for a given
UTC moment.

Applies a date-dependent ΔT via `delta_t/1` to convert the UTC
moment to TDB, the time scale expected by the JPL SPK ephemeris
kernel.

### Arguments

* `moment` is a moment (float Gregorian days since 0000-01-01)
  in UTC.

### Returns

* Dynamical time as a `float` (TDB seconds past J2000.0).

### Examples

    iex> t = Astro.Time.date_time_to_moment(~U[2000-01-01 12:00:00Z])
    iex> dt = Astro.Time.dynamical_time_from_moment(t)
    iex> Float.round(dt, 1)
    63.8

# `dynamical_time_to_moment`

```elixir
@spec dynamical_time_to_moment(float()) :: float()
```

Returns a UTC moment for a given dynamical time (TDB seconds past
J2000.0).

This is the inverse of `dynamical_time_from_moment/1`. Subtracts a
date-dependent ΔT to recover the UTC moment.

### Arguments

* `dynamical_time` is TDB seconds past J2000.0 (float).

### Returns

* A moment (float Gregorian days since 0000-01-01) in UTC.

### Examples

    iex> t = Astro.Time.date_time_to_moment(~U[2000-01-01 12:00:00Z])
    iex> dt = Astro.Time.dynamical_time_from_moment(t)
    iex> round_trip = Astro.Time.dynamical_time_to_moment(dt)
    iex> Float.round(abs(round_trip - t) * 86400, 3)
    0.0

# `greenwich_mean_sidereal_time`
*since 0.11.0* 

```elixir
@spec greenwich_mean_sidereal_time(Calendar.datetime()) :: moment()
```

Returns the Greenwich Mean Sidereal Time (GMST) in degrees for a
given datetime.

GMST measures Earth's rotation relative to the stars. It is the
hour angle of the mean vernal equinox at the Greenwich meridian.

### Arguments

* `date_time` is any `t:Calendar.datetime/0`. If not already in UTC,
  it is converted to UTC before computation.

### Returns

* GMST in degrees (float, typically 0–360).

### Examples

    iex> gmst = Astro.Time.greenwich_mean_sidereal_time(~U[2000-01-01 12:00:00Z])
    iex> Float.round(gmst, 4)
    280.7273

# `hours_and_date_to_date_time`

```elixir
@spec hours_and_date_to_date_time(hours(), Calendar.date()) ::
  {:ok, Calendar.datetime()}
```

Returns a UTC datetime by combining a date with a float number
of hours since midnight.

### Arguments

* `time_of_day` is a float number of hours since midnight
  (e.g. 13.5 for 1:30 PM).

* `date` is any `t:Calendar.date/0`.

### Returns

* `{:ok, datetime}` — a `t:DateTime.t/0` in UTC.

### Examples

    iex> {:ok, dt} = Astro.Time.hours_and_date_to_date_time(12.0, ~D[2024-06-21])
    iex> DateTime.truncate(dt, :second)
    ~U[2024-06-21 12:00:00Z]

# `hours_to_days`

```elixir
@spec hours_to_days(hours()) :: days()
```

Returns the number of days for a given number of hours.

### Arguments

* `hours` is a number of hours.

### Returns

* The equivalent number of days as a `float`.

### Examples

    iex> Astro.Time.hours_to_days(48)
    2.0

    iex> Astro.Time.hours_to_days(6)
    0.25

# `hours_to_hms`

```elixir
@spec hours_to_hms(hours()) :: hms()
```

Returns an `{hours, minutes, seconds}` tuple for a given float
number of hours since midnight.

### Arguments

* `time_of_day` is a float number of hours since midnight.

### Returns

* A `{hour, minute, second}` tuple. Fractional seconds are
  truncated.

### Examples

    iex> Astro.Time.hours_to_hms(0.0)
    {0, 0, 0}

    iex> Astro.Time.hours_to_hms(23.999)
    {23, 59, 56}

    iex> Astro.Time.hours_to_hms(15.456)
    {15, 27, 21}

# `j2000`

Returns the J2000.0 epoch as a moment (Gregorian days since
0000-01-01).

J2000.0 is 2000 January 1.5 TT (noon on January 1, 2000). This
is the standard reference epoch for precession, nutation, and
ephemeris polynomials.

### Returns

* The J2000.0 moment as a `float` (730485.5).

### Examples

    iex> Astro.Time.j2000()
    730485.5

# `julian_centuries_from_dynamical_time`

```elixir
@spec julian_centuries_from_dynamical_time(float()) :: float()
```

Returns Julian centuries from J2000.0 for a given dynamical time.

A pure arithmetic conversion: divides TDB seconds by the number
of seconds in a Julian century (36525 × 86400).

### Arguments

* `dynamical_time` is TDB seconds past J2000.0 (float).

### Returns

* Julian centuries from J2000.0 as a `float`.

### Examples

    iex> Astro.Time.julian_centuries_from_dynamical_time(0.0)
    0.0

    iex> Astro.Time.julian_centuries_from_dynamical_time(36525.0 * 86400.0)
    1.0

# `julian_centuries_from_julian_day`

Returns Julian centuries from J2000.0 for a given Julian day.

### Arguments

* `julian_day` is a Julian day number (float) such as returned
  from `julian_day_from_date/1`.

### Returns

* Julian centuries from J2000.0 as a `float`.

### Examples

    iex> Astro.Time.julian_centuries_from_julian_day(2451545.0)
    0.0

    iex> Float.round(Astro.Time.julian_centuries_from_julian_day(2451545.0 + 36525.0), 1)
    1.0

# `julian_centuries_from_moment`

Returns Julian centuries from J2000.0 for a given UTC moment.

First converts the UTC moment to a dynamical moment (by adding ΔT),
then computes Julian centuries from J2000.0. This is the time
argument expected by Meeus-era polynomial series for precession,
nutation, and orbital elements.

### Arguments

* `t` is a moment (float Gregorian days since 0000-01-01) in UTC.

### Returns

* Julian centuries from J2000.0 as a `float`.

### Examples

    iex> t = Astro.Time.date_time_to_moment(~U[2000-01-01 12:00:00Z])
    iex> Float.round(Astro.Time.julian_centuries_from_moment(t), 6)
    0.0

# `julian_day_from_date`

```elixir
@spec julian_day_from_date(Calendar.date()) :: julian_days()
```

Returns the astronomical Julian day number for a given date.

### Arguments

* `date` is any `t:Calendar.date/0`.

### Returns

* The Julian day as a `float`. The `.5` fractional part
  reflects the Julian day convention of starting at noon.

### Examples

    iex> Astro.Time.julian_day_from_date(~D[2019-12-05])
    2458822.5

    iex> Astro.Time.julian_day_from_date(~D[2000-01-01])
    2451544.5

# `julian_day_from_julian_centuries`

```elixir
@spec julian_day_from_julian_centuries(julian_centuries()) :: julian_days()
```

Returns the Julian day for a given number of Julian centuries
from J2000.0.

This is the inverse of `julian_centuries_from_julian_day/1`.

### Arguments

* `julian_centuries` is a float number of Julian centuries from
  J2000.0.

### Returns

* The Julian day as a `float`.

### Examples

    iex> Astro.Time.julian_day_from_julian_centuries(0.0)
    2451545.0

    iex> Astro.Time.julian_day_from_julian_centuries(1.0)
    2488070.0

# `local_from_universal`

```elixir
@spec local_from_universal(time(), Geo.PointZ.t()) :: time()
```

Returns the local mean solar time for a given universal (UTC) moment
and location.

Local mean solar time is UTC plus an offset derived purely from
geographic longitude (`longitude / 360` of a day). This is distinct
from standard time, which uses named time zone boundaries and
daylight-saving rules.

### Arguments

* `t` is a moment (float Gregorian days since 0000-01-01) in UTC.

* `location` is a `Geo.PointZ` with `{longitude, latitude, altitude}`.

### Returns

* A moment in local mean solar time (float Gregorian days).

### Examples

    iex> location = %Geo.PointZ{coordinates: {-90.0, 40.0, 0.0}}
    iex> t = 740047.5
    iex> Astro.Time.local_from_universal(t, location)
    740047.25

# `local_mean_time_offset`

Returns the Local Mean Time offset in seconds for a given location and time zone.

The offset is the difference between Local Mean Time at the given
longitude and Standard Time in effect for the given time zone.
Local Mean Time is the solar time at a specific longitude, while
Standard Time is the civil time for the time zone.

### Arguments

* `location` is a `Geo.PointZ` struct with `{longitude, latitude, altitude}`
  coordinates.

* `gregorian_seconds` is the number of seconds since the Gregorian
  epoch (0000-01-01 00:00:00).

* `time_zone` is a time zone name string (e.g. `"Etc/UTC"`).

### Returns

* A `float` representing the offset in seconds between Local Mean Time
  and Standard Time.

### Examples

    iex> location = %Geo.PointZ{coordinates: {0.0, 51.5, 0.0}}
    iex> gregorian_seconds = 63_871_632_000
    iex> Astro.Time.local_mean_time_offset(location, gregorian_seconds, "Etc/UTC")
    0.0

# `local_sidereal_time`
*since 0.11.0* 

```elixir
@spec local_sidereal_time(Astro.location(), Calendar.datetime()) :: moment()
```

Returns the local sidereal time in degrees for a given location
and datetime.

Local sidereal time is GMST plus the observer's geographic
longitude.

### Arguments

* `location` is any `t:Astro.location/0` (a `Geo.Point`,
  `Geo.PointZ`, or `{longitude, latitude}` tuple).

* `date_time` is any `t:Calendar.datetime/0`.

### Returns

* Local sidereal time in degrees (float).

### Examples

    iex> lst = Astro.Time.local_sidereal_time({0.0, 51.5}, ~U[2000-01-01 12:00:00Z])
    iex> Float.round(lst, 4)
    280.7273

# `mean_sidereal_from_moment`

# `mjd`

```elixir
@spec mjd(Calendar.date()) :: julian_days()
```

Returns the Modified Julian Day (MJD) for a given date.

MJD is JD − 2400000.5, shifting the day boundary from noon to
midnight and producing smaller numbers. MJD 0 corresponds to
1858-11-17 00:00:00 UTC.

### Arguments

* `date` is any `t:Calendar.date/0`.

### Returns

* The Modified Julian Day as a `float`.

### Examples

    iex> Astro.Time.mjd(~D[2019-12-05])
    58822.0

# `offset_for_zone`

Returns the time zone offset as a fraction of a day for a given
instant and time zone.

### Arguments

* `gregorian_seconds` is the number of seconds since the Gregorian
  epoch (0000-01-01 00:00:00).

* `time_zone` is a time zone name string (e.g. `"Europe/London"`).

* `time_zone_database` is the time zone database module (defaults
  to `Calendar.get_time_zone_database()`).

### Returns

* The total offset (UTC offset + DST) as a fraction of a day
  (float).

* `:ambiguous_time` if the instant falls in a DST overlap.

* `:no_such_time_or_zone` if the zone is unknown or the instant
  falls in a DST gap.

### Examples

    iex> t = Date.to_gregorian_days(~D[2021-08-01]) * (60 * 60 * 24)
    iex> Astro.Time.offset_for_zone(t, "Europe/London")
    {:ok, 0.041666666666666664}

# `offset_from_longitude`

```elixir
@spec offset_from_longitude(Geo.PointZ.t() | Astro.longitude()) :: moment()
```

Returns the local mean solar time offset from UTC as a fraction
of a day for a given longitude.

The offset is `longitude / 360` of a day. West longitudes
(negative) produce negative offsets, east longitudes produce
positive offsets.

### Arguments

* `longitude` is either a `Geo.PointZ` struct or a numeric
  longitude in degrees (west negative, east positive).

### Returns

* The offset as a fraction of a day (float). For example, −90°
  returns −0.25 (6 hours behind UTC).

### Examples

    iex> Astro.Time.offset_from_longitude(-90.0)
    -0.25

    iex> Astro.Time.offset_from_longitude(180.0)
    0.5

# `seconds_to_hms`

```elixir
@spec seconds_to_hms(fraction_of_day()) :: hms()
```

Returns an `{hours, minutes, seconds}` tuple for a given number
of seconds since midnight.

### Arguments

* `time_of_day` is a number of seconds since midnight.

### Returns

* A `{hour, minute, second}` tuple. Fractional seconds are
  truncated.

### Examples

    iex> Astro.Time.seconds_to_hms(0.0)
    {0, 0, 0}

    iex> Astro.Time.seconds_to_hms(3214)
    {0, 53, 34}

    iex> Astro.Time.seconds_to_hms(10_000)
    {2, 46, 39}

# `standard_from_universal`

```elixir
@spec standard_from_universal(time(), zone_name() | number()) :: time()
```

Returns the standard time moment for a given universal (UTC) moment
and time zone name or time zone offset.

Standard time is UTC adjusted by the named time zone's UTC offset
and any daylight-saving offset in effect at the given instant.

### Arguments

* `t` is a moment (float Gregorian days since 0000-01-01) in UTC.

* `zone_name` is either a time zone name string
  (e.g. `"America/New_York"`) or a numeric offset in fractional days.

### Returns

* A moment in standard time (float Gregorian days).

### Examples

    iex> t = Astro.Time.date_time_to_moment(~U[2024-01-15 12:00:00Z])
    iex> Astro.Time.standard_from_universal(t, "Australia/Sydney")
    739265.9200462963

    iex> t = Astro.Time.date_time_to_moment(~U[2024-01-15 12:00:00Z])
    iex> standard = Astro.Time.standard_from_universal(t, 0.25)
    iex> standard - t
    0.25

# `universal_from_dynamical`

```elixir
@spec universal_from_dynamical(time()) :: time()
```

Returns the universal (UTC) moment for a given dynamical moment.

Subtracts ΔT (converted to a fraction of a day) from a dynamical
moment, recovering the corresponding UTC moment.

### Arguments

* `t` is a moment (float Gregorian days since 0000-01-01) in
  dynamical time.

### Returns

* A moment in the UTC time scale (float Gregorian days).

### Examples

    iex> t = Astro.Time.date_time_to_moment(~U[2000-01-01 12:00:00Z])
    iex> dyn = Astro.Time.dynamical_from_universal(t)
    iex> Astro.Time.universal_from_dynamical(dyn)
    730485.5

# `universal_from_local`

```elixir
@spec universal_from_local(time(), Geo.PointZ.t()) :: time()
```

Returns the universal (UTC) moment for a given local mean solar
time moment and location.

Subtracts the longitude-based offset (`longitude / 360` of a day)
from the local time to recover UTC. This is the inverse of
`local_from_universal/2`.

### Arguments

* `t` is a moment (float Gregorian days since 0000-01-01) in
  local mean solar time.

* `location` is a `Geo.PointZ` with `{longitude, latitude, altitude}`.

### Returns

* A moment in UTC (float Gregorian days).

### Examples

    iex> location = %Geo.PointZ{coordinates: {-90.0, 40.0, 0.0}}
    iex> local_t = 740047.25
    iex> Astro.Time.universal_from_local(local_t, location)
    740047.5

# `universal_from_standard`

```elixir
@spec universal_from_standard(time(), zone_name() | number()) :: time()
```

Returns the universal (UTC) moment for a given standard time moment
and time zone.

Subtracts the time zone offset (UTC offset + DST) from the standard
time moment to recover UTC. This is the inverse of
`standard_from_universal/2`.

### Arguments

* `t` is a moment (float Gregorian days since 0000-01-01) in
  standard time.

* `zone_name` is either a time zone name string
  (e.g. `"America/New_York"`) or a numeric offset in fractional days.

### Returns

* A moment in UTC (float Gregorian days).

### Examples

    iex> t = 740047.75
    iex> utc = Astro.Time.universal_from_standard(t, 0.25)
    iex> t - utc
    0.25

# `utc_date_time_from_dynamical_date_time`

```elixir
@spec utc_date_time_from_dynamical_date_time(Calendar.datetime()) ::
  {:ok, Calendar.datetime()}
```

Returns a UTC datetime for a given dynamical time datetime.

Converts a `DateTime` whose clock reading is in dynamical time
(TDB ≈ TT) to the corresponding UTC `DateTime` by subtracting
ΔT. Internally delegates to `universal_from_dynamical/1`.

### Arguments

* `datetime` is a `DateTime` whose clock reading is in dynamical
  time (TDB seconds, equivalently Terrestrial Time).

### Returns

* `{:ok, utc_datetime}` — the corresponding UTC `DateTime`.

### Examples

    iex> {:ok, dyn} = Astro.Time.date_time_from_julian_days(2451545.0)
    iex> {:ok, utc} = Astro.Time.utc_date_time_from_dynamical_date_time(dyn)
    iex> DateTime.truncate(utc, :second)
    ~U[2000-01-01 11:58:56Z]

---

*Consult [api-reference.md](api-reference.md) for complete listing*
