the Sun is currently in Virgo ♍, 13°7'9" or longitude 56.0762 degrees how does one calculate when exactly the Sun will reach this position again. Since the moon is doing a spin in 29.x days lets use that as example also, "she" is in Gemini ♊, 1°33'23" or 56.1074 degrees , can we calculate when will it reach Gemini 05° 45' 00" or 56.1263 degrees ( a random coordination) Any math or programming language will be just fine even tho I messed with swisseph ( solcross, mooncross) and skyfield and no result ...

  • $\begingroup$ The longitudes in Virgo and Gemini cannot both be 56 degrees. The Sun is approaching the Sep equinox, so the longitude should be close to 180 degrees. (Otherwise, the question is still valid about crossing a specific longitude.) $\endgroup$
    – JohnHoltz
    Sep 5 at 23:30
  • $\begingroup$ thanks john, i was using a python code to do that so i guess its wrong .. :( $\endgroup$
    – dimitri33
    Sep 6 at 0:45
  • 3
    $\begingroup$ At the time of posting, the sun was in Leo, at an ecliptic long of +162 degrees, and the moon was in Taurus at 62 degrees. The sun is only in "virgo" in the astrological sense, which is no longer related to the actual positions of the stars in the sky, $\endgroup$
    – James K
    Sep 6 at 5:59

2 Answers 2


In skyfield, to find the times when something happens, you build a function that takes a Time object, outputs an integer value, and has a step_days attribute, and pass that function to the find_discrete method to find the times when your function transitions from one integer or boolean value to the next during its search period.

In the below script, I create a function ecliptic_longitude_exceeds that returns functions that can be used in the find_discrete method. It's a simple function, that should work for the Sun or the Moon, because we can trust their ecliptic longitudes to be constantly increasing from 0° to 360°. Not as easy to work with planets (they'll backtrack, requiring more code to detect and ignore the 360° to 0° transition) We'll use the year 2023 as our time window.

from typing import Callable

import pytz
import skyfield.searchlib
from skyfield import api, timelib
from skyfield.jpllib import SpiceKernel, ChebyshevPosition
from skyfield.vectorlib import VectorSum
from skyfield.framelib import ecliptic_frame

def ecliptic_longitude_exceeds(
        longitude: float,
        target: str|VectorSum|ChebyshevPosition,
        ephemeris: SpiceKernel) -> Callable[[timelib.Time], int]:
    Creates functions that check whether target ecliptic longitude exceeds
    value at a specified time
    :param longitude: Ecliptic Longitude in decimal degrees
    :param target: Object to be checked
    :param ephemeris: Ephemeris to be used.
    :return: find_discrete-compatible function

    earth = ephemeris['earth']
    target = ephemeris[target] if isinstance(target, str) else target

    def function(time: timelib.Time) -> bool:
        :param time: Time of Observation
        :return: True if ecliptic longitude >= longitude False otherwise
        observation = earth.at(time).observe(target).apparent()
        _, observed_longitude, _ = observation.frame_latlon(ecliptic_frame)
        return observed_longitude.degrees >= longitude

    function.step_days = 60

    return function

def main():

    ephemeris = api.load('de421.bsp')
    ts = api.load.timescale()
    utc = pytz.timezone("UTC")
    start, stop = ts.utc(2023), ts.utc(2024)

    sun_exceeds = ecliptic_longitude_exceeds(
        longitude=56.1074, target="sun", ephemeris=ephemeris  

    times, states = skyfield.searchlib.find_discrete(start, stop, sun_exceeds)

    longitude_times = list(time for time, in_state
                           in zip(times.astimezone(utc), states)
                           if in_state)

    print('\n'.join(str(lt) for lt in longitude_times))

if __name__ == '__main__':

This script returns:

2023-05-17 06:11:22.543808+00:00

Quickly checking in my desktop copy of Stellarium puts the Sun's Ecliptic longitude at that time as 56°06'28.8", which matches up.

  • $\begingroup$ you are amazing it works like a charm <3, i installed Stellarium on my laptop and i will use it as reference .. $\endgroup$
    – dimitri33
    Sep 6 at 23:11
  • $\begingroup$ hey , i tried this for all the planets and as long as they are not in "retrograde" this works perfect .. now the question is can one calculate the same for the "true node" or nodes in general ? i tried moon_nodes and got "unknown SPICE target $\endgroup$
    – dimitri33
    Sep 28 at 17:58

You had the right idea with solcross and mooncross from swisseph. The functions utc_to_jd and jdet_to_utc can help convert from a date+time to a Julian date and back.

This code:

import swisseph as swe
from datetime import datetime, timezone

def jd_from_dt(dt_ut):
    """Converts a datetime in UTC to a Julian date in TT."""
    (jd_tt, jd_ut) = swe.utc_to_jd(dt_ut.year, dt_ut.month, dt_ut.day,
            dt_ut.hour, dt_ut.minute, dt_ut.second)
    return jd_tt

def dt_from_jd(jd_tt):
    """Converts a Julian date in TT to a datetime in UTC."""
    (yr, mo, day, h, m, s) = swe.jdet_to_utc(jd_tt)
    dt_ut = datetime(yr, mo, day, h, m, round(s), tzinfo=timezone.utc)
    return dt_ut

# examples from question

post_jd = jd_from_dt(datetime(2023, 9, 5, 23, tzinfo=timezone.utc))

sun1_lon = 150 + 13 + 7/60 + 9/3600
sun1a_jd = swe.solcross(sun1_lon, post_jd - 30)
sun1b_jd = swe.solcross(sun1_lon, post_jd + 335)
print('Sun at lon={0:.4f}:\n  {1}\n  {2}'.format(
        sun1_lon, dt_from_jd(sun1a_jd), dt_from_jd(sun1b_jd)))

moon1_lon = 60 + 1 + 33/60 + 23/3600
moon1a_jd = swe.mooncross(moon1_lon, post_jd - 3)
moon1b_jd = swe.mooncross(moon1_lon, post_jd + 24)
print('Moon at lon={0:.4f}:\n  {1}\n  {2}'.format(
        moon1_lon, dt_from_jd(moon1a_jd), dt_from_jd(moon1b_jd)))

moon2_lon = 60 + 5 + 45/60
moon2a_jd = swe.mooncross(moon2_lon, post_jd - 3)
moon2b_jd = swe.mooncross(moon2_lon, post_jd + 24)
print('Moon at lon={0:.4f}:\n  {1}\n  {2}'.format(
        moon2_lon, dt_from_jd(moon2a_jd), dt_from_jd(moon2b_jd)))

# example from notovny's answer

jan1_jd = jd_from_dt(datetime(2023, 1, 1, 0, tzinfo=timezone.utc))

sun2_lon = 56.1074
sun2a_jd = swe.solcross(sun2_lon, jan1_jd)
sun2b_jd = swe.solcross(sun2_lon, jan1_jd + 365)
print('Sun at lon={0:.4f}:\n  {1}\n  {2}'.format(
        sun2_lon, dt_from_jd(sun2a_jd), dt_from_jd(sun2b_jd)))

produces this output:

Sun at lon=163.1192:
  2023-09-05 22:54:26+00:00
  2024-09-05 04:39:52+00:00
Moon at lon=61.5564:
  2023-09-05 22:58:23+00:00
  2023-10-03 07:50:07+00:00
Moon at lon=65.7500:
  2023-09-06 06:44:16+00:00
  2023-10-03 15:23:09+00:00
Sun at lon=56.1074:
  2023-05-17 06:11:23+00:00
  2024-05-16 11:57:53+00:00
  • $\begingroup$ thank you Mike, this changes things a little bit <3 $\endgroup$
    – dimitri33
    Sep 7 at 15:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .