source: trunk/SRC/Calendar/julday.pro @ 136

Last change on this file since 136 was 136, checked in by pinsard, 18 years ago

some improvements and corrections in some .pro file according to
aspell and idldoc log file

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.3 KB
Line 
1;+
2;
3; @file_comments
4; Calculate the Julian Day Number for a given month, day, and year.
5; This is the inverse of the library function CALDAT.
6; See also caldat, the inverse of this function.
7;
8; @categories Calendar
9;
10; @param MONTH {in}{required}
11; Number of the desired month (1 = January, ..., 12 = December).
12; Can be scalar or array
13;
14; @param DAY {in}{required}
15; Number of day of the month.Can be scalar or array
16;
17; @param YEARin {in}{required}
18; Number of the desired year. Year parameters must be valid
19; values from the civil calendar.  Years B.C.E. are represented
20; as negative integers.  Years in the common era are represented
21; as positive integers.  In particular, note that there is no
22; year 0 in the civil calendar.  1 B.C.E. (-1) is followed by
23; 1 C.E. (1). Can be scalar or array
24;
25; @param HOUR {in}{optional}
26; Number of the hour of the day. Can be scalar or array
27;
28; @param MINUTE {in}{optional}
29; Number of the minute of the hour. Can be scalar or array
30;
31; @param SECOND {in}{optional}
32; Number of the second of the minute. Can be scalar or array
33;
34; @restrictions
35; The Result will have the same dimensions as the smallest array, or
36; will be a scalar if all arguments are scalars.
37;
38; @keyword NDAYSPM {default=30}
39; for using a calendar with fixed number of days per months.
40;
41; @returns
42; the Julian Day Number (which begins at noon) of the specified calendar date.
43; If Hour, Minute, and Second are not specified, then the result will be a
44; long integer, otherwise the result is a double precision floating point
45; number.
46;
47; @uses cm_4cal
48;
49; @restrictions
50; Accuracy using IEEE double precision numbers is approximately
51; 1/10000th of a second, with higher accuracy for smaller (earlier)
52; Julian dates.
53;
54; @history Translated from "Numerical Recipes in C", by William H. Press,
55; Brian P. Flannery, Saul A. Teukolsky, and William T. Vetterling.
56; Cambridge University Press, 1988 (second printing).
57;
58; AB, September, 1988
59; DMS, April, 1995, Added time of day.
60;
61; Eric Guilyardi, June 1999
62; Added keyword ndayspm for fixed number of days per months
63; + change to accept year 0
64;
65; Sebastien Masson, Aug. 2003
66; fix bug for negative and large values of month values
67; eg. julday(349,1,1970)
68;
69; CT, April 2000, Now accepts vectors or scalars.
70;
71; @version $Id$
72;-
73;
74function JULDAY, MONTH, DAY, YEARin, Hour, Minute, Second, NDAYSPM = ndayspm
75;------------------------------------------------------------
76@cm_4cal
77;------------------------------------------------------------
78
79  COMPILE_OPT idl2
80
81  ON_ERROR, 2                   ; Return to caller if errors
82
83  IF n_elements(key_caltype) EQ 0 THEN key_caltype = 'greg'
84  if keyword_set(ndayspm) then key_caltype = '360d'
85;
86
87  YEAR = long(yearin)
88  zero = where(year EQ 0, cnt)
89  IF cnt NE 0 THEN YEAR[zero] = 654321L
90;
91  CASE key_caltype OF
92    'greg':BEGIN
93
94
95; Gregorian Calender was adopted on Oct. 15, 1582
96; skipping from Oct. 4, 1582 to Oct. 15, 1582
97      GREG = 2299171L           ; incorrect Julian day for Oct. 25, 1582
98
99; Process the input, if all are missing, use todays date.
100      NP = n_params()
101      IF (np EQ 0) THEN RETURN, SYSTIME(/JULIAN)
102      IF (np LT 3) THEN MESSAGE, 'Incorrect number of arguments.'
103
104; Find the dimensions of the Result:
105;  1. Find all of the input arguments that are arrays (ignore scalars)
106;  2. Out of the arrays, find the smallest number of elements
107;  3. Find the dimensions of the smallest array
108
109; Step 1: find all array arguments
110      nDims = [SIZE(month, /N_DIMENSIONS), SIZE(day, /N_DIMENSIONS), $
111               SIZE(year, /N_DIMENSIONS), SIZE(hour, /N_DIMENSIONS), $
112               SIZE(minute, /N_DIMENSIONS), SIZE(second, /N_DIMENSIONS)]
113      arrays = WHERE(nDims GE 1)
114
115      nJulian = 1L              ; assume everything is a scalar
116      IF (arrays[0] GE 0) THEN BEGIN
117                                ; Step 2: find the smallest number of elements
118        nElement = [N_ELEMENTS(month), N_ELEMENTS(day), $
119                    N_ELEMENTS(year), N_ELEMENTS(hour), $
120                    N_ELEMENTS(minute), N_ELEMENTS(second)]
121        nJulian = MIN(nElement[arrays], whichVar)
122                                ; step 3: find dimensions of the smallest array
123        CASE arrays[whichVar] OF
124          0: julianDims = SIZE(month, /DIMENSIONS)
125          1: julianDims = SIZE(day, /DIMENSIONS)
126          2: julianDims = SIZE(year, /DIMENSIONS)
127          3: julianDims = SIZE(hour, /DIMENSIONS)
128          4: julianDims = SIZE(minute, /DIMENSIONS)
129          5: julianDims = SIZE(second, /DIMENSIONS)
130        ENDCASE
131      ENDIF
132
133      d_Second = 0d             ; defaults
134      d_Minute = 0d
135      d_Hour = 0d
136; convert all Arguments to appropriate array size & type
137      SWITCH np OF              ; use switch so we fall thru all arguments...
138        6: d_Second = (SIZE(second, /N_DIMENSIONS) GT 0) ? $
139                      second[0:nJulian-1] : second
140        5: d_Minute = (SIZE(minute, /N_DIMENSIONS) GT 0) ? $
141                      minute[0:nJulian-1] : minute
142        4: d_Hour = (SIZE(hour, /N_DIMENSIONS) GT 0) ? $
143                    hour[0:nJulian-1] : hour
144        3: BEGIN                ; convert m,d,y to type LONG
145          L_MONTH = (SIZE(month, /N_DIMENSIONS) GT 0) ? $
146                    LONG(month[0:nJulian-1]) : LONG(month)
147          L_DAY = (SIZE(day, /N_DIMENSIONS) GT 0) ? $
148                  LONG(day[0:nJulian-1]) : LONG(day)
149          L_YEAR = (SIZE(year, /N_DIMENSIONS) GT 0) ? $
150                   LONG(year[0:nJulian-1]) : LONG(year)
151        END
152      ENDSWITCH
153
154
155      min_calendar = -4716
156      max_calendar = 5000000
157      minn = MIN(l_year, MAX = maxx)
158      IF (minn LT min_calendar) OR (maxx GT max_calendar) THEN MESSAGE, $
159        'Value of Julian date is out of allowed range.'
160; change to accept year 0
161; if (MAX(L_YEAR eq 0) NE 0) then message, $
162; 'There is no year zero in the civil calendar.'
163;
164; by seb Aug 2003
165
166      tochange = where(L_MONTH LT 0)
167      IF tochange[0] NE -1 THEN BEGIN
168        L_YEAR[tochange] = L_YEAR[tochange]+L_MONTH[tochange]/12-1
169        L_MONTH[tochange] =  12 + L_MONTH[tochange] MOD 12
170      ENDIF
171
172      tochange = where(L_MONTH GT 12)
173      IF tochange[0] NE -1 THEN BEGIN
174        L_YEAR[tochange] = L_YEAR[tochange]+L_MONTH[tochange]/12
175        L_MONTH[tochange] =  L_MONTH[tochange] MOD 12
176      ENDIF
177; by seb Aug 2003 - end
178;
179;
180      bc = (L_YEAR LT 0)
181      L_YEAR = TEMPORARY(L_YEAR) + TEMPORARY(bc)
182      inJanFeb = (L_MONTH LE 2)
183      JY = L_YEAR - inJanFeb
184      JM = L_MONTH + (1b + 12b*TEMPORARY(inJanFeb))
185
186      JUL = floor(365.25d * JY) + floor(30.6001d*TEMPORARY(JM)) + L_DAY + 1720995L
187
188; Test whether to change to Gregorian Calendar.
189      IF (MIN(JUL) GE GREG) THEN BEGIN ; change all dates
190        JA = long(0.01d * TEMPORARY(JY))
191        JUL = TEMPORARY(JUL) + 2L - JA + long(0.25d * JA)
192      ENDIF ELSE BEGIN
193        gregChange = WHERE(JUL ge GREG, ngreg)
194        IF (ngreg GT 0) THEN BEGIN
195          JA = long(0.01d * JY[gregChange])
196          JUL[gregChange] = JUL[gregChange] + 2L - JA + long(0.25d * JA)
197        ENDIF
198      ENDELSE
199
200
201; hour,minute,second?
202      IF (np GT 3) THEN BEGIN   ; yes, compute the fractional Julian date
203; Add a small offset so we get the hours, minutes, & seconds back correctly
204; if we convert the Julian dates back. This offset is proportional to the
205; Julian date, so small dates (a long, long time ago) will be "more" accurate.
206        eps = (MACHAR(/DOUBLE)).eps
207        eps = eps*ABS(jul) > eps
208; For Hours, divide by 24, then subtract 0.5, in case we have unsigned integers.
209        jul = TEMPORARY(JUL) + ( (TEMPORARY(d_Hour)/24d - 0.5d) + $
210                                 TEMPORARY(d_Minute)/1440d + TEMPORARY(d_Second)/86400d + eps )
211      ENDIF
212
213; check to see if we need to reform vector to array of correct dimensions
214      IF (N_ELEMENTS(julianDims) GT 1) THEN $
215        JUL = REFORM(TEMPORARY(JUL), julianDims)
216
217      RETURN, jul
218
219    END
220    '360d':BEGIN
221;
222; Fixed number of days per month (default=30) :
223;
224      IF keyword_set(ndayspm) THEN BEGIN
225        IF ndayspm EQ 1 THEN ndayspm = 30
226      ENDIF ELSE ndayspm = 30
227
228      L_MONTH = LONG(MONTH)
229      L_DAY = LONG(DAY)
230      L_YEAR = LONG(YEAR)
231
232      neg = where(L_YEAR LT 0)
233      IF neg[0] NE -1 THEN L_YEAR[neg] =  L_YEAR[neg]+1
234
235      JUL = ((L_YEAR-1)*12 + (L_MONTH-1))* ndayspm + L_DAY
236      if n_elements(Hour) + n_elements(Minute) + n_elements(Second) eq 0 then $
237        return, JUL
238      if n_elements(Hour) eq 0 then Hour = 0
239      if n_elements(Minute) eq 0 then Minute = 0
240      if n_elements(Second) eq 0 then Second = 0
241     
242      IF Hour+Minute+Second EQ 0 THEN return, JUL ELSE $
243        return, JUL + (Hour / 24.0d0) + (Minute/1440.0d0) + (Second / 86400.0d0)
244
245    END
246    'noleap':BEGIN
247
248      L_MONTH = LONG(MONTH)
249      L_DAY = LONG(DAY)
250      L_YEAR = LONG(YEAR)
251;
252      tochange = where(L_MONTH LT 0)
253      IF tochange[0] NE -1 THEN BEGIN
254        L_YEAR[tochange] = L_YEAR[tochange]+L_MONTH[tochange]/12-1
255        L_MONTH[tochange] =  12 + L_MONTH[tochange] MOD 12
256      ENDIF
257;
258      tochange = where(L_MONTH GT 12)
259      IF tochange[0] NE -1 THEN BEGIN
260        L_YEAR[tochange] = L_YEAR[tochange]+L_MONTH[tochange]/12
261        L_MONTH[tochange] =  L_MONTH[tochange] MOD 12
262      ENDIF
263;
264      L_YEAR =  L_YEAR - 1
265;
266      daysyear = long(total([0, 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30], /cumulative))
267
268      return, 365*L_YEAR + daysyear[L_MONTH] + L_DAY
269
270    END
271    ELSE:return, report('only 3 types of calendar are accepted: greg, 360d and noleap')
272  ENDCASE
273
274END
Note: See TracBrowser for help on using the repository browser.