~daggerstab/stellarium/telescope-control-servo-cat

« back to all changes in this revision

Viewing changes to src/astrolib/DateOps.cpp

  • Committer: xalioth
  • Date: 2002-07-12 17:35:08 UTC
  • Revision ID: vcs-imports@canonical.com-20020712173508-pgzrcpff6pka8qcf
Initial revision

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************\
 
2
 * DateOps.cpp
 
3
 *
 
4
 * DateOps contains misc. time and date operations
 
5
 *
 
6
 * author: mark huss (mark@mhuss.com)
 
7
 * Based on Bill Gray's open-source code at projectpluto.com
 
8
 *
 
9
\*****************************************************************************/
 
10
 
 
11
#include "DateOps.h"
 
12
 
 
13
#include <string.h>
 
14
 
 
15
/* General calendrical comments:
 
16
 
 
17
This code supports conversions between JD and five calendrical systems:
 
18
Julian,  Gregorian,  Hebrew,  Islamic,  and (French) revolutionary.
 
19
Comments pertaining to specific calendars are found near the code for
 
20
those calendars.
 
21
 
 
22
For each calendar,  there is a "get_(calendar_name)_year_data( )" function,
 
23
used only within this source code. This function takes a particular year
 
24
number,  and computes the JD corresponding to "new years day" (first day of
 
25
the first month) in that calendar in that year.  It also figures out the
 
26
number of days in each month of that year,  returned in the array
 
27
month_data[].  There can be up to 13 months,  because the Hebrew calendar
 
28
(and some others that may someday be added) can include an "intercalary
 
29
month".  If a month doesn't exist,  then the month_data[] entry for it
 
30
will be zero (thus,  in the Gregorian and Julian and Islamic calendars,
 
31
month_data[12] is always zero,  since these calendars have only 12 months.)
 
32
 
 
33
The next level up is the get_calendar_data( ) function,  which (through
 
34
the wonders of a switch statement) can get the JD of New Years Day and
 
35
the array of months for any given year for any calendar.  Above this
 
36
point,  all calendars can be treated in a common way;  one is shielded
 
37
from the oddities of individual calendrical systems.
 
38
 
 
39
Finally,  at the top level,  we reach the only two functions that are
 
40
exported for the rest of the world to use:  dmy_to_day( ) and day_to_dmy( ).
 
41
The first takes a day,  month,  year,  and calendar system.  It calls
 
42
get_calendar_data( ) for the given year,  adds in the days in the months
 
43
intervening New Years Day and the desired month,  and adds in the day
 
44
of the month,  and returns the resulting Julian Day.
 
45
 
 
46
day_to_dmy( ) reverses this process.  It finds an "approximate" year
 
47
corresponding to an input JD,  and calls get_calendar_data( ) for
 
48
that year.  By adding all the month_data[] values for that year,  it
 
49
can also find the JD for the _end_ of that year;  if the input JD is
 
50
outside that range,  it may have to back up a year or add in a year.
 
51
Once it finds "JD of New Years Day < JD < JD of New Years Eve",  it's
 
52
a simple matter to track down which month and day of the month corresponds
 
53
to the input JD.
 
54
*/
 
55
 
 
56
const char* monthNames[12] = {
 
57
      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
58
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 
59
};
 
60
 
 
61
/*
 
62
 *  Begin:  Gregorian and Julian calendars (combined for simplicity)
 
63
 *
 
64
 *  It's common to implement Gregorian/Julian calendar code with the
 
65
 * aid of cryptic formulae,  rather than through simple lookup tables.
 
66
 * For example,  consider this formula from Fliegel and Van Flandern,
 
67
 * to convert Gregorian (D)ay, (M)onth, (Y)ear to JD:
 
68
 *
 
69
 * JD = (1461*(Y+4800+(M-14)/12))/4+(367*(M-2-12*((M-14)/12)))/12
 
70
 *       -(3*((Y+4900+(M-14)/12)/100))/4+D-32075
 
71
 *
 
72
 * The only way to verify that they work is to feed through all possible
 
73
 * cases.  Personally,  I like to be able to look at a chunk of code and
 
74
 * see what it means.  It should resemble the Reformation view of the
 
75
 * Bible:  anyone can read it and witness the truth thereof.
 
76
 */
 
77
 
 
78
//----------------------------------------------------------------------------
 
79
void DateOps::getJulGregYearData(
 
80
    long year, long& daysInYear, MonthDays& md, bool julian )
 
81
{
 
82
  static const MonthDays months =
 
83
                   { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
 
84
 
 
85
  if( year >= 0L ) {
 
86
    daysInYear = year * 365L + year / 4L;
 
87
    if( !julian )
 
88
      daysInYear += -year / 100L + year / 400L;
 
89
  }
 
90
  else {
 
91
   daysInYear = (year * 365L) + (year - 3L) / 4L;
 
92
   if( !julian )
 
93
     daysInYear += -(year - 99L) / 100L + (year - 399L) / 400L;
 
94
  }
 
95
 
 
96
  if( julian )
 
97
    daysInYear -= 2L;
 
98
 
 
99
  memcpy( &md, months, sizeof(MonthDays) );
 
100
 
 
101
  if( !(year % 4)) {
 
102
    if( (year % 100L) || !(year % 400L) || julian ) {
 
103
      md[1] = 29;
 
104
      daysInYear--;
 
105
    }
 
106
  }
 
107
  daysInYear += E_JULIAN_GREGORIAN + 1;
 
108
}
 
109
 
 
110
//----------------------------------------------------------------------------
 
111
int DateOps::getCalendarData(
 
112
    long year, YearEndDays& days, MonthDays& md, int calendar)
 
113
{
 
114
  int rval = 0;
 
115
 
 
116
  memset( &md, 0, sizeof(MonthDays) );
 
117
  switch( calendar)
 
118
  {
 
119
    case T_GREGORIAN:
 
120
    case T_JULIAN:
 
121
        getJulGregYearData( year, days[0], md, (T_JULIAN == calendar) );
 
122
        break;
 
123
#if defined( CALENDARS_OF_THE_WORLD )
 
124
    case T_ISLAMIC:
 
125
        getIslamicYearData( year, days[0], md );
 
126
        break;
 
127
    case T_HEBREW:
 
128
        getHebrewYearData( year, days, md );
 
129
        break;
 
130
    case T_REVOLUTIONARY:
 
131
        getRevolutionaryYearData( year, days, md );
 
132
        break;
 
133
    case T_PERSIAN:
 
134
        if( year >= LOWER_PERSIAN_YEAR && year <= UPPER_PERSIAN_YEAR)
 
135
          getJalaliYearData( year, days, md );
 
136
        else
 
137
          rval = -1;
 
138
        break;
 
139
#endif
 
140
    default:
 
141
        rval = -1;
 
142
        break;
 
143
  }
 
144
  if( 0 == rval ) {
 
145
    //
 
146
    // days[1] = JD of "New Years Eve" + 1;  that is, New Years Day of the
 
147
    // following year.  If you have days[0] <= JD < days[1],  JD is in the
 
148
    // current year.
 
149
    //
 
150
    days[1] = days[0];
 
151
    for( int i=0; i<13; i++ )
 
152
      days[1] += md[i];
 
153
  }
 
154
  return( rval );
 
155
}
 
156
 
 
157
/*
 
158
 * dmy_to_day( )
 
159
 *
 
160
 * just gets calendar data for the current year,  including the JD of New Years
 
161
 * Day for that year.  After that,  all it has to do is add up the days in
 
162
 * intervening months, plus the day of the month, and it's done:
 
163
 */
 
164
 
 
165
// calendar: 0 = gregorian, 1 = julian
 
166
 
 
167
long DateOps::dmyToDay( int day, int month, long year, int calendar )
 
168
{
 
169
  long jd = 0;
 
170
  MonthDays md;
 
171
  YearEndDays yed;
 
172
 
 
173
  if( 0 == getCalendarData( year, yed, md, calendar ) ) {
 
174
    jd = yed[0];
 
175
    for( int i=0; i<(month-1); i++ ) {
 
176
      jd += md[i];
 
177
    }
 
178
    jd += long(day - 1);
 
179
  }
 
180
  return jd;
 
181
}
 
182
 
 
183
/*
 
184
 * day_to_dmy( )
 
185
 *
 
186
 * Estimates the year corresponding to an input JD and calls get_calendar_data();
 
187
 * for that year.  Occasionally,  it will find that the guesstimate was off;
 
188
 * in such cases,  it moves ahead or back a year and tries again.  Once it's
 
189
 * done,  jd - year_ends[0] gives the number of days since New Years Day;
 
190
 * by subtracting month_data[] values,  we quickly determine which month and
 
191
 * day of month we're in.
 
192
 */
 
193
 
 
194
// calendar: 0 = gregorian, 1 = julian
 
195
 
 
196
void DateOps::dayToDmy( long jd, int& day, int& month, long& year, int calendar )
 
197
{
 
198
  day = -1;           /* to signal an error */
 
199
  switch( calendar) {
 
200
  case T_GREGORIAN:
 
201
  case T_JULIAN:
 
202
    year = (jd - E_JULIAN_GREGORIAN) / 365L;
 
203
    break;
 
204
#if defined( CALENDARS_OF_THE_WORLD )
 
205
  case T_HEBREW:
 
206
    year = (jd - E_HEBREW) / 365L;
 
207
    break;
 
208
  case T_ISLAMIC:
 
209
    year = (jd - E_ISLAMIC) / 354L;
 
210
    break;
 
211
  case T_REVOLUTIONARY:
 
212
    year = (jd - E_REVOLUTIONARY) / 365L;
 
213
    break;
 
214
  case T_PERSIAN:
 
215
    year = (jd - JALALI_ZERO) / 365L;
 
216
    if( year < LOWER_PERSIAN_YEAR)
 
217
      year = LOWER_PERSIAN_YEAR;
 
218
    if( year > UPPER_PERSIAN_YEAR)
 
219
      year = UPPER_PERSIAN_YEAR;
 
220
    break;
 
221
#endif
 
222
  default:       /* undefined calendar */
 
223
    return;
 
224
    break;
 
225
  }  // end switch()
 
226
 
 
227
  YearEndDays yed;
 
228
  MonthDays md;
 
229
  do {
 
230
    if( 0 != getCalendarData( year, yed, md, calendar ) )
 
231
      return;
 
232
 
 
233
    if( yed[0] > jd)
 
234
      year--;
 
235
 
 
236
    if( yed[1] <= jd)
 
237
      year++;
 
238
 
 
239
  } while( yed[0] > jd || yed[1] <= jd );
 
240
 
 
241
  long currJd = yed[0];
 
242
  month = -1;
 
243
  for( int i = 0; i < 13; i++) {
 
244
    day = int( jd - currJd );
 
245
    if( day < md[i] ) {
 
246
      month = i + 1;
 
247
      day++;
 
248
      return;
 
249
    }
 
250
    currJd += long( md[i] );
 
251
  }
 
252
  return;
 
253
}
 
254
 
 
255
 
 
256
//----------------------------------------------------------------------------
 
257
// Determine DST start JD (first Sunday in April)
 
258
//
 
259
long DateOps::dstStart(int year)
 
260
{
 
261
  long jdStart = dmyToDay( 1, 4, year, T_GREGORIAN );
 
262
  while ( 6 != (jdStart % 7 ) ) // Sunday
 
263
    jdStart++;
 
264
 
 
265
  return jdStart;
 
266
}
 
267
 
 
268
//----------------------------------------------------------------------------
 
269
// Determine DST end JD (last Sunday in October)
 
270
//
 
271
long DateOps::dstEnd(int year)
 
272
{
 
273
  long jdEnd = dmyToDay( 31, 10, year, T_GREGORIAN );
 
274
  while ( 6 != (jdEnd % 7 ) ) // Sunday
 
275
    jdEnd--;
 
276
 
 
277
  return jdEnd;
 
278
}
 
279
 
 
280
//----------------------------------------------------------------------------