Julian Day Number

  • Thread starter Thread starter Todd Carnes
  • Start date Start date
T

Todd Carnes

Is there a function in C# that will convert a date into a Julian Day
Number or do I have to write my own?

Todd
 
Is there a function in C# that will convert a date into a Julian Day
Number or do I have to write my own?

There are a variety of date classes around such as "JulianCalendar" but I'm
not sure if any do what you want. I've got some very old (but accurate) C
routines however that likely do precisely what you're after (generically
written, well-documented and accurate). The formula was written by a
well-known physicist many years ago and it's quite impressive in fact (don't
know how he came up with it). I can post it here if you want and you can
convert it to C# with little effort. Just let me know.
 
Larry said:
There are a variety of date classes around such as "JulianCalendar" but I'm
not sure if any do what you want. I've got some very old (but accurate) C
routines however that likely do precisely what you're after (generically
written, well-documented and accurate). The formula was written by a
well-known physicist many years ago and it's quite impressive in fact (don't
know how he came up with it). I can post it here if you want and you can
convert it to C# with little effort. Just let me know.

Thank you. It's very kind of you to offer. I don't mind doing the
conversion. I just wondered if a method already existed that I could
use. I was just trying to avoid re-inventing the wheel. :)

Todd
 
Thank you. It's very kind of you to offer. I don't mind doing the
conversion. I just wondered if a method already existed that I could
use. I was just trying to avoid re-inventing the wheel. :)

I agree that it's better to rely on something that already exists if you can find it. If not then here's the code which I haven't used since the early 1990s. Note that I once tested it with every date since 4713 B.C. (see comments) and it worked fine. The conversion to C# should be minimal. Good luck.

////////////////////////////////////////////////////////////////////////////
// Name : jdn.c
//
// Description:
// Routines for processing dates in the format JDN (Julian Day Number).
// Based on formulae originally posted by Tom Van Flandern / Washington,
// DC / (e-mail address removed) in the UseNet newsgroup sci.astro.
// Reposted 14 May 1991 in FidoNet C Echo conference by Paul Schlyter
// (Stockholm). Minor corrections, added JDN to julian, and recast into
// C by Raymond Gardner Englewood, Colorado.
//
// These routines convert Gregorian and Julian calendar dates to and
// from Julian Day Numbers. Julian Day Numbers (JDN) are used by
// astronomers as a date/time measure independent of calendars and
// convenient for computing the elapsed time between dates. The JDN
// for any date/time is the number of days (including fractional days)
// elapsed since noon, Jan 1, 4713 BC. Julian Day Numbers were originated
// by Joseph Scaliger in 1582 and named after his father Julius, not
// after Julius Caesar. They are not related to the Julian calendar.
// For dates from Jan 1, 4713 BC thru Feb 12, 32766 AD, "YMDToJDN()"
// will give the JDN for noon on that date. "JDNToYMD()" will compute
// the year, month, and day from the JDN. Years BC are given (and
// returned) as negative numbers. Note that there is no year 0 BC; the
// day before Jan 1, 1 AD is Dec 31, 1 BC. Also note that 1 BC, 5 BC,
// etc. are leap years.
//
// Pope Gregory XIII decreed that the Julian calendar would end on
// Oct 4, 1582 AD and that the next day would be Oct 15, 1582 in the
// Gregorian Calendar. The only other change is that centesimal years
// (years ending in 00) would no longer be leap years unless divisible
// by 400. Britain and its possessions and colonies continued to use
// the Julian calendar up until Sep 2, 1752, when the next day became
// Sep 14, 1752 in the Gregorian Calendar. These routines can be
// compiled to use either convention. By default, the British convention
// will be used. Simply #define PAPAL to use Pope Gregory's convention.
//
// Each routine takes, as its last argument, a flag to indicate whether
// to use the Julian or Gregorian calendar convention. If this flag is
// negative, the routines decide based on the date itself, using the
// changeover date described in the preceding paragraph. If the flag is
// zero, Gregorian conventions will be used, and if the flag is
// positive, Julian conventions will be used. Consult each function
// in the module for details.
//
// Development Environment:
// MSC V6.00, DOS 3.3
//
// Target Environment:
// Any (ANSI compatible)
//
// Change Control:
// Date Ver Who Ref Description
// Mar 17, 92 01.00.00 LS N/A Creation
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////
#ifdef PAPAL // Pope Gregory XIII's decree ...
#define LASTJULDATE 15821004L // Last day to use Julian calendar
#define LASTJULJDN 2299160L // JDN equivalent of above
#else // British-American usage ...
#define LASTJULDATE 17520902L // last day to use Julian calendar
#define LASTJULJDN 2361221L // JDN equivalent of above
#endif

////////////////////////////////////////////////////////////////////////////
// typedefs
////////////////////////////////////////////////////////////////////////////
typedef long JDN; // Julian Day Number

////////////////////////////////////////////////////////////////////////////
// Name:
// YMDToJDN
//
// Description:
// As described in the module header, converts a date in the range
// Jan 1, 4713 BC to Feb 12, 32766 to its Julian Day Number (JDN).
// This function is the converse of "JDNToYMD()" defined elsewhere in
// the module. Note that no error checking is conducted by the function
// to ensure that the given date is valid.
//
// Input:
// iYear - Year in the range -4713 to 32766. Note that year 0 is
// invalid as described in the module header.
// iMonth - Month in the range 1 to 12
// iDay - Day of the month
// iJulian - As described in the module header, set to 1 if the Julian
// calendar is to be used, 0 if the Gregorian calendar is to
// be used, or -1 if the function decides this based on the
// date itself (again, see the module header for details).
// Note that most of the time, you will likely pass -1. Thus,
// the date you pass is assumed to be a Gregorian date if
// it falls past the Julian==>Gregorian changeover date
// described in the module header. Otherwise, if it falls on
// or before this changeover date, the date is assumed to be a
// Julian date.
//
// Output:
// None
//
// Return:
// The Julian Day Number for the given date.
//
// Comments:
// Note that the date you pass must be valid or erroneous results may
// occur. Thus, besides being a valid date (i.e., the year is not zero,
// the month is in the range 1-12, the day is in the range 1-31 and
// valid for the given month, etc.), care must be taken for those dates
// that fall inbetween the Julian==>Gregorian changeover dates described
// in the module header. Thus, for instance, since Britain and its
// possessions and colonies continued to use the Julian calendar up until
// Sep 2, 1752, where the next day became Sep 14, 1752, it makes little
// sense to pass a date inbetween this range (non-inclusive) if this date
// is intended to be a Gregorian date. It is a valid date however if
// the date is considered to be a Julian date (assumes no changeover
// to the Gregorian calendar for that date). The value returned by the
// function therefore varies according to the "iJulian" flag, returning
// the correct JDN regardless of the date (for its date type, either
// Julian or Gregorian).
//
////////////////////////////////////////////////////////////////////////////
extern JDN YMDToJDN(int iYear, int iMonth, int iDay, int iJulian)
{
long lJDN;

if (iJulian < 0) // Set Julian flag if auto set
iJulian = (((iYear * 100L) + iMonth) * 100 + iDay <= LASTJULDATE);

if (iYear < 0) // Adjust BC year
iYear++;

if (iJulian)
{
lJDN = 367L * iYear - 7 * (iYear + 5001L + (iMonth - 9) / 7) / 4
+ 275 * iMonth / 9 + iDay + 1729777L;
}
else
{
lJDN = (long)(iDay - 32076)
+ 1461L * (iYear + 4800L + (iMonth - 14) / 12) / 4
+ 367 * (iMonth - 2 - (iMonth - 14) / 12 * 12) / 12
- 3 * ((iYear + 4900L + (iMonth - 14) / 12) / 100) / 4
+ 1; // correction by rdg
}

return lJDN;
}

////////////////////////////////////////////////////////////////////////////
// Name:
// JDNToYMD
//
// Description:
// As described in the module header, converts a Julian Day Number (JDN)
// in the range Jan 1, 4713 BC to Feb 12, 32766 to its equivalent year,
// month and day. This function is the converse of "YMDToJDN()" defined
// elsewhere in the module. Note that no error checking is conducted by
// the function to ensure that the given JDN is valid..
//
// Input:
// lJDN - Julian Day Number in the range Jan 1, 4713 BC to
// Feb 12, 32766.
// iJulian - As described in the module header, set to 1 if the Julian
// calendar is to be used, 0 if the Gregorian calendar is to
// be used, or -1 if the function decides this based on the
// date itself (again, see the module header for details).
// Note that most of the time, you will likely pass -1. Thus,
// the date you pass is assumed to be a Gregorian date if
// it falls past the Julian==>Gregorian changeover date
// described in the module header. Otherwise, if it falls on
// or before this changeover date, the date is assumed to be a
// Julian date.
//
// Output:
// piYear - Ignored if NULL. Otherwise, receives the year equivalent of
// the given JDN in the range -4713 to 32766. Note that year 0
// is invalid so this is NEVER returned.
// piMonth - Ignored if NULL. Otherwise, receives the month equivalent
// of the given JDN in the range 1 to 12.
// piDay - Ignored if NULL. Otherwise, receives the day of the month
// equivalent of the given JDN in the range 1 to 31
// (appropriate for the given month).
//
// Return:
// None
//
// Comments:
//
////////////////////////////////////////////////////////////////////////////
extern void JDNToYMD(JDN lJDN, int *piYear, int *piMonth, int *piDay, int iJulian)
{
long x, z, m, d, y;
long lDaysPer400Years = 146097L;
long lFudgedDaysPer4000Years = 1460970L + 31;

if (iJulian < 0) // Set Julian flag if auto set
{
iJulian = (lJDN <= LASTJULJDN);
}

x = lJDN + 68569L;
if (iJulian)
{
x += 38;
lDaysPer400Years = 146100L;
lFudgedDaysPer4000Years = 1461000L + 1;
}
z = 4 * x / lDaysPer400Years;
x = x - (lDaysPer400Years * z + 3) / 4;
y = 4000 * (x + 1) / lFudgedDaysPer4000Years;
x = x - 1461 * y / 4 + 31;
m = 80 * x / 2447;
d = x - 2447 * m / 80;
x = m / 11;
m = m + 2 - 12 * x;
y = 100 * (z - 49) + y + x;

if (piYear)
{
*piYear = (int)y;
if (*piYear <= 0) // Adjust BC years */
{
(*piYear)--;
}
}

if (piMonth)
{
*piMonth = (int)m;
}

if (piDay)
{
*piDay = (int)d;
}
}
 
Peter said:
Depending on what exactly you mean by "Julian Day Number"
(unfortunately, phrases like "Julian date", "Julian day number", etc.
are sometimes used in a non-canonical way), you may be able to get the
JulianCalendar in .NET to do what you want.

Otherwise, I think you'll have to write your own.

Pete

Well in astrophysics "Julian Day Number" and "Julian Date" have seperate
and distinct meanings. I was using the term in an astrophysics context,
but I guess shouldn't have assumed everyone would know what I was
talking about.

An example of what I'm looking for is if I enter the date 02 Feb 1989, I
want the number 2447559.5 returned. I do NOT want a conversion to the
Julian calendar.

Thank you for your suggestion though, I'll check it out. :)

Todd
 
Larry said:
I agree that it's better to rely on something that already exists if you
can find it. If not then here's the code which I haven't used since the
early 1990s. Note that I once tested it with every date since 4713 B.C.
(see comments) and it worked fine. The conversion to C# should be
minimal. Good luck.

Thank you very much. :)

Todd
 
Larry said:
Thank you very much. :)

No problem. Apparently you require the fractional parts of a day however.
These routines deal with whole days only but it's how the formula was
originally developed (by Dr. Van Flandern who wrote it).
 
Larry said:
No problem. Apparently you require the fractional parts of a day however.
These routines deal with whole days only but it's how the formula was
originally developed (by Dr. Van Flandern who wrote it).

That's ok. Including the fractional part of the day is a trivial
modification.

As I read your code, I recognized both Tom Van Flandern's and Paul
Schlyter's names. (In fact, I was just reading one of Dr. Van Flandern's
papers regarding algorithms for computing ephemerides earlier today.)
So, I have faith the code you gave me will do just what I'm looking for.

It's funny really. Van Flandern's paper is what prompted me to ask the
question to begin with. He gave all the info I needed except for the
Julian Day conversion.

Thanks again. :)

Todd
 
// These routines can be
// compiled to use either convention. By default, the British convention
// will be used. Simply #define PAPAL to use Pope Gregory's convention.

The interesting part: this is locale dependent, and the British and the
Papal switch dates are not enough.
Pretty much every country did it's own switching independently.
(ending with Greece in 1923)
http://en.wikipedia.org/wiki/Gregorian_calendar

Ok, it is really trivia, does not affect much the coding decision,
but just shows how messy things can be to get right :-)
 
but just shows how messy things can be to get right :-)

Agreed. In fact, while hardly an exercise in particle physics, working out
an algorithm is still a non-trivial exercise. Looking at his code, I'm still
impressed with it after all these years.
 
Todd said:
Well in astrophysics "Julian Day Number" and "Julian Date" have
seperate and distinct meanings.

Do you mean this:

http://en.wikipedia.org/wiki/Julian_day

<<
The Julian date (JD) is the interval of time in days and fractions of a
day, since January 1, 4713 BC Greenwich noon, Julian proleptic calendar.
Then you can probably write a function that uses the calculations given
there.
 
Todd said:
Well in astrophysics "Julian Day Number" and "Julian Date" have seperate
and distinct meanings. I was using the term in an astrophysics context,
but I guess shouldn't have assumed everyone would know what I was
talking about.

An example of what I'm looking for is if I enter the date 02 Feb 1989, I
want the number 2447559.5 returned. I do NOT want a conversion to the
Julian calendar.

I saw some really complex code posted.

But it really should be simple - there should be a linear
correlation between .NET time and JD.

Some experimentation ended up with:

private const double TICKS_PER_DAY = 24*60*60*10000000L;
//private static readonly double OFFSET = 2400000 - (new
DateTime(1858, 11, 16, 12, 0, 0)).Ticks/TICKS_PER_DAY;
private static readonly double OFFSET = 2400000 - (new
DateTime(1858, 11, 16, 12, 0, 0)).ToUniversalTime().Ticks/TICKS_PER_DAY;
public static double DT2JD(DateTime dt)
{
return dt.Ticks/TICKS_PER_DAY + OFFSET;
}

Arne
 
Michael said:
Actually "Julian Day Number" and "Julian Date" are the same thing (a
number on the order of 2,400,000 for dates near the present, potentially
including a fractional part for time of day) and go back to a system
devised by Julius Scaliger a few centuries ago.

The "Julian Calendar" of Julius Caesar is unrelated.

Sadly, in the database world, some people use "Julian date" for any kind
of day number. I would call that a "scalar date" (date expressed as a
single number).

When I say Julian date, I mean just that. The date on the Julian
Calendar (vice the Gregorian Calendar). That's certainly nothing like
the Julian Day Number of my original question. Nor is it what you are
talking about.

Having said that, I have run into something similar to what you are
talking about. When I was in the military, We used to have what we
called a "Julian Date" that was a different animal all together.

In the military a "Julian Date" is formed by combining the last digit of
the year with the number of the day in the year. By that I mean, 31 Jan
2009 would be 9031, 01 Feb 2009 would be 9032, etc... then you'd end up
at 9365 and 01 Jan 2010 would be 0001. However, the military is the
*only* place I've ever seen this dating system used.

Calendars can be (and often are) quite confusing at times. :)

Todd
 
Back
Top