001 /*
002 * Zmanim Java API
003 * Copyright (C) 2004-2008 Eliyahu Hershfeld
004 *
005 * This program is free software; you can redistribute it and/or modify it under the terms of the
006 * GNU General Public License as published by the Free Software Foundation; either version 2 of the
007 * License, or (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
010 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011 * General Public License for more details.
012 *
013 * You should have received a copy of the GNU General Public License along with this program; if
014 * not, write to the Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA
015 * 02111-1307, USA or connect to: http://www.fsf.org/copyleft/gpl.html
016 */
017 package net.sourceforge.zmanim;
018
019 import java.util.Calendar;
020 import java.util.Date;
021 import java.util.GregorianCalendar;
022
023 import net.sourceforge.zmanim.util.AstronomicalCalculator;
024 import net.sourceforge.zmanim.util.GeoLocation;
025 import net.sourceforge.zmanim.util.ZmanimFormatter;
026
027 /**
028 * A Java calendar that calculates astronomical time calculations such as
029 * {@link #getSunrise() sunrise} and {@link #getSunset() sunset} times. This
030 * class contains a {@link #getCalendar() Calendar} and can therefore use the
031 * standard Calendar functionality to change dates etc. The calculation engine
032 * used to calculate the astronomical times can be changed to a different
033 * implementation by implementing the {@link AstronomicalCalculator} and setting
034 * it with the {@link #setAstronomicalCalculator(AstronomicalCalculator)}. A
035 * number of different implementations are included in the util package <br />
036 * <b>Note:</b> There are times when the algorithms can't calculate proper
037 * values for sunrise and sunset. This is usually caused by trying to calculate
038 * times for areas either very far North or South, where sunrise / sunset never
039 * happen on that date. This is common when calculating twilight with a deep dip
040 * below the horizon for locations as south of the North Pole as London in the
041 * northern hemisphere. When the calculations encounter this condition a null
042 * will be returned when a <code>{@link java.util.Date}</code> is expected and
043 * {@link Double#NaN} when a double is expected. The reason that
044 * <code>Exception</code>s are not thrown in these cases is because the lack
045 * of a rise/set are not exceptions, but expected in many parts of
046 * the world.
047 *
048 * Here is a simple example of how to use the API to calculate sunrise: <br />
049 * First create the Calendar for the location you would like to calculate:
050 *
051 * <pre>
052 * String locationName = "Lakewood, NJ";
053 * double latitude = 40.0828; //Lakewood, NJ
054 * double longitude = -74.2094; //Lakewood, NJ
055 * double elevation = 20; // optional elevation correction in Meters
056 * //the String parameter in getTimeZone() has to be a valid timezone listed in {@link java.util.TimeZone#getAvailableIDs()}
057 * TimeZone timeZone = TimeZone.getTimeZone("America/New_York");
058 * GeoLocation location = new GeoLocation(locationName, latitude, longitude,
059 * elevation, timeZone);
060 * AstronomicalCalendar ac = new AstronomicalCalendar(location);
061 * </pre>
062 *
063 * To get the time of sunrise, first set the date (if not set, the date will
064 * default to today):
065 *
066 * <pre>
067 * ac.getCalendar().set(Calendar.MONTH, Calendar.FEBRUARY);
068 * ac.getCalendar().set(Calendar.DAY_OF_MONTH, 8);
069 * Date sunrise = ac.getSunrise();
070 * </pre>
071 *
072 *
073 * @author © Eliyahu Hershfeld 2004 - 2008
074 * @version 1.1
075 */
076 public class AstronomicalCalendar implements Cloneable {
077 private static final long serialVersionUID = 1;
078
079 /**
080 * 90° below the vertical. Used for certain calculations.<br />
081 * <b>Note </b>: it is important to note the distinction between this zenith
082 * and the {@link AstronomicalCalculator#adjustZenith adjusted zenith} used
083 * for some solar calculations. This 90 zenith is only used because some
084 * calculations in some subclasses are historically calculated as an offset
085 * in reference to 90.
086 */
087 public static final double GEOMETRIC_ZENITH = 90;
088
089 /**
090 * Default value for Sun's zenith and true rise/set Zenith (used in this
091 * class and subclasses) is the angle that the center of the Sun makes to a
092 * line perpendicular to the Earth's surface. If the Sun were a point and
093 * the Earth were without an atmosphere, true sunset and sunrise would
094 * correspond to a 90° zenith. Because the Sun is not a point, and
095 * because the atmosphere refracts light, this 90° zenith does not, in
096 * fact, correspond to true sunset or sunrise, instead the center of the
097 * Sun's disk must lie just below the horizon for the upper edge to be
098 * obscured. This means that a zenith of just above 90° must be used.
099 * The Sun subtends an angle of 16 minutes of arc (this can be changed via
100 * the {@link #setSunRadius(double)} method , and atmospheric refraction
101 * accounts for 34 minutes or so (this can be changed via the
102 * {@link #setRefraction(double)} method), giving a total of 50 arcminutes.
103 * The total value for ZENITH is 90+(5/6) or 90.8333333° for true
104 * sunrise/sunset.
105 */
106 // public static double ZENITH = GEOMETRIC_ZENITH + 5.0 / 6.0;
107 /** Sun's zenith at civil twilight (96°). */
108 public static final double CIVIL_ZENITH = 96;
109
110 /** Sun's zenith at nautical twilight (102°). */
111 public static final double NAUTICAL_ZENITH = 102;
112
113 /** Sun's zenith at astronomical twilight (108°). */
114 public static final double ASTRONOMICAL_ZENITH = 108;
115
116 /** constant for milliseconds in a minute (60,000) */
117 static final long MINUTE_MILLIS = 60 * 1000;
118
119 /** constant for milliseconds in an hour (3,600,000) */
120 static final long HOUR_MILLIS = MINUTE_MILLIS * 60;
121
122 /**
123 * The Java Calendar encapsulated by this class to track the current date
124 * used by the class
125 */
126 private Calendar calendar;
127
128 private GeoLocation geoLocation;
129
130 private AstronomicalCalculator astronomicalCalculator;
131
132 /**
133 * The getSunrise method Returns a <code>Date</code> representing the
134 * sunrise time. The zenith used for the calculation uses
135 * {@link #GEOMETRIC_ZENITH geometric zenith} of 90°. This is adjusted
136 * by the {@link AstronomicalCalculator} that adds approximately 50/60 of a
137 * degree to account for 34 archminutes of refraction and 16 archminutes for
138 * the sun's radius for a total of
139 * {@link AstronomicalCalculator#adjustZenith 90.83333°}. See
140 * documentation for the specific implementation of the
141 * {@link AstronomicalCalculator} that you are using.
142 *
143 * @return the <code>Date</code> representing the exact sunrise time. If
144 * the calculation can not be computed null will be returned.
145 * @see AstronomicalCalculator#adjustZenith
146 */
147 public Date getSunrise() {
148 double sunrise = getUTCSunrise(GEOMETRIC_ZENITH);
149 if (Double.isNaN(sunrise)) {
150 return null;
151 } else {
152 return getDateFromTime(sunrise);
153 }
154 }
155
156 /**
157 * Method that returns the sunrise without correction for elevation.
158 * Non-sunrise and sunset calculations such as dawn and dusk, depend on the
159 * amount of visible light, something that is not affected by elevation.
160 * This method returns sunrise calculated at sea level. This forms the base
161 * for dawn calculations that are calculated as a dip below the horizon
162 * before sunrise.
163 *
164 * @return the <code>Date</code> representing the exact sea-level sunrise
165 * time. If the calculation can not be computed null will be
166 * returned.
167 * @see AstronomicalCalendar#getSunrise
168 * @see AstronomicalCalendar#getUTCSeaLevelSunrise
169 */
170 public Date getSeaLevelSunrise() {
171 double sunrise = getUTCSeaLevelSunrise(GEOMETRIC_ZENITH);
172 if (Double.isNaN(sunrise)) {
173 return null;
174 } else {
175 return getDateFromTime(sunrise);
176 }
177 }
178
179 /**
180 * A method to return the the beginning of civil twilight (dawn) using a
181 * zenith of {@link #CIVIL_ZENITH 96°}.
182 *
183 * @return The <code>Date</code> of the beginning of civil twilight using
184 * a zenith of 96°. If the calculation can not be computed null
185 * will be returned.
186 * @see #CIVIL_ZENITH
187 */
188 public Date getBeginCivilTwilight() {
189 return getSunriseOffsetByDegrees(CIVIL_ZENITH);
190 }
191
192 /**
193 * A method to return the the beginning of nautical twilight using a zenith
194 * of {@link #NAUTICAL_ZENITH 102°}.
195 *
196 * @return The <code>Date</code> of the beginning of nautical twilight
197 * using a zenith of 102°. If the calculation can not be
198 * computed null will be returned.
199 * @see #NAUTICAL_ZENITH
200 */
201 public Date getBeginNauticalTwilight() {
202 return getSunriseOffsetByDegrees(NAUTICAL_ZENITH);
203 }
204
205 /**
206 * A method that returns the the beginning of astronomical twilight using a
207 * zenith of {@link #ASTRONOMICAL_ZENITH 108°}.
208 *
209 * @return The <code>Date</code> of the beginning of astronomical twilight
210 * using a zenith of 108°. If the calculation can not be
211 * computed null will be returned.
212 * @see #ASTRONOMICAL_ZENITH
213 */
214 public Date getBeginAstronomicalTwilight() {
215 return getSunriseOffsetByDegrees(ASTRONOMICAL_ZENITH);
216 }
217
218 /**
219 * The getSunset method Returns a <code>Date</code> representing the
220 * sunset time. The zenith used for the calculation uses
221 * {@link #GEOMETRIC_ZENITH geometric zenith} of 90°. This is adjusted
222 * by the {@link AstronomicalCalculator} that adds approximately 50/60 of a
223 * degree to account for 34 archminutes of refraction and 16 archminutes for
224 * the sun's radius for a total of
225 * {@link AstronomicalCalculator#adjustZenith 90.83333°}. See
226 * documentation for the specific implementation of the
227 * {@link AstronomicalCalculator} that you are using. Note: In certain cases
228 * the calculates sunset will occur before sunrise. This will typically
229 * happen when a timezone other than the local timezone is used (calculating
230 * Los Angeles sunset using a GMT timezone for example). In this case the
231 * sunset date will be incremented to the following date.
232 *
233 * @return the <code>Date</code> representing the exact sunset time. If
234 * the calculation can not be computed null will be returned. If the
235 * time calculation
236 * @see AstronomicalCalculator#adjustZenith
237 */
238 public Date getSunset() {
239 double sunset = getUTCSunset(GEOMETRIC_ZENITH);
240 if (Double.isNaN(sunset)) {
241 return null;
242 } else {
243 return getAdjustedSunsetDate(getDateFromTime(sunset), getSunrise());
244 }
245 }
246
247 /**
248 * A method that will roll the sunset time forward a day if sunset occurs
249 * before sunrise. This will typically happen when a timezone other than the
250 * local timezone is used (calculating Los Angeles sunset using a GMT
251 * timezone for example). In this case the sunset date will be incremented
252 * to the following date.
253 *
254 * @param sunset
255 * the sunset date to adjust if needed
256 * @param sunrise
257 * the sunrise to compare to the sunset
258 * @return the adjusted sunset date
259 */
260 private Date getAdjustedSunsetDate(Date sunset, Date sunrise) {
261 if (sunset != null && sunrise != null && sunrise.compareTo(sunset) >= 0) {
262 Calendar clonedCalendar = (GregorianCalendar) getCalendar().clone();
263 clonedCalendar.setTime(sunset);
264 clonedCalendar.add(Calendar.DAY_OF_MONTH, 1);
265 return clonedCalendar.getTime();
266 } else {
267 return sunset;
268 }
269 }
270
271 /**
272 * Method that returns the sunset without correction for elevation.
273 * Non-sunrise and sunset calculations such as dawn and dusk, depend on the
274 * amount of visible light, something that is not affected by elevation.
275 * This method returns sunset calculated at sea level. This forms the base
276 * for dusk calculations that are calculated as a dip below the horizon
277 * after sunset.
278 *
279 * @return the <code>Date</code> representing the exact sea-level sunset
280 * time. If the calculation can not be computed null will be
281 * returned.
282 * @see AstronomicalCalendar#getSunset
283 * @see AstronomicalCalendar#getUTCSeaLevelSunset
284 */
285 public Date getSeaLevelSunset() {
286 double sunset = getUTCSeaLevelSunset(GEOMETRIC_ZENITH);
287 if (Double.isNaN(sunset)) {
288 return null;
289 } else {
290 return getAdjustedSunsetDate(getDateFromTime(sunset),
291 getSeaLevelSunrise());
292 }
293 }
294
295 /**
296 * A method to return the the end of civil twilight using a zenith of
297 * {@link #CIVIL_ZENITH 96°}.
298 *
299 * @return The <code>Date</code> of the end of civil twilight using a
300 * zenith of {@link #CIVIL_ZENITH 96°}. If the calculation can
301 * not be computed null will be returned.
302 * @see #CIVIL_ZENITH
303 */
304 public Date getEndCivilTwilight() {
305 return getSunsetOffsetByDegrees(CIVIL_ZENITH);
306 }
307
308 /**
309 * A method to return the the end of nautical twilight using a zenith of
310 * {@link #NAUTICAL_ZENITH 102°}.
311 *
312 * @return The <code>Date</code> of the end of nautical twilight using a
313 * zenith of {@link #NAUTICAL_ZENITH 102°}. If the calculation
314 * can not be computed null will be returned.
315 * @see #NAUTICAL_ZENITH
316 */
317 public Date getEndNauticalTwilight() {
318 return getSunsetOffsetByDegrees(NAUTICAL_ZENITH);
319 }
320
321 /**
322 * A method to return the the end of astronomical twilight using a zenith of
323 * {@link #ASTRONOMICAL_ZENITH 108°}.
324 *
325 * @return The The <code>Date</code> of the end of astronomical twilight
326 * using a zenith of {@link #ASTRONOMICAL_ZENITH 108°}. If the
327 * calculation can not be computed null will be returned.
328 * @see #ASTRONOMICAL_ZENITH
329 */
330 public Date getEndAstronomicalTwilight() {
331 return getSunsetOffsetByDegrees(ASTRONOMICAL_ZENITH);
332 }
333
334 /**
335 * Utility method that returns a date offset by the offset time passed in.
336 * This method casts the offset as a <code>long</code> and calls
337 * {@link #getTimeOffset(Date, long)}.
338 *
339 * @param time
340 * the start time
341 * @param offset
342 * the offset in milliseconds to add to the time
343 * @return the {@link java.util.Date}with the offset added to it
344 */
345 public Date getTimeOffset(Date time, double offset) {
346 return getTimeOffset(time, (long) offset);
347 }
348
349 /**
350 * A utility method to return a date offset by the offset time passed in.
351 *
352 * @param time
353 * the start time
354 * @param offset
355 * the offset in milliseconds to add to the time.
356 * @return the {@link java.util.Date} with the offset in milliseconds added
357 * to it
358 */
359 public Date getTimeOffset(Date time, long offset) {
360 if (time == null || offset == Long.MIN_VALUE) {
361 return null;
362 }
363 return new Date(time.getTime() + offset);
364 }
365
366 /**
367 * A utility method to return the time of an offset by degrees below or
368 * above the horizon of {@link #getSunrise() sunrise}.
369 *
370 * @param offsetZenith
371 * the degrees before {@link #getSunrise()} to use in the
372 * calculation. For time after sunrise use negative numbers.
373 * @return The {@link java.util.Date} of the offset after (or before)
374 * {@link #getSunrise()}. If the calculation can not be computed
375 * null will be returned.
376 */
377 public Date getSunriseOffsetByDegrees(double offsetZenith) {
378 double alos = getUTCSunrise(offsetZenith);
379 if (Double.isNaN(alos)) {
380 return null;
381 } else {
382 return getDateFromTime(alos);
383 }
384 }
385
386 /**
387 * A utility method to return the time of an offset by degrees below or
388 * above the horizon of {@link #getSunset() sunset}.
389 *
390 * @param offsetZenith
391 * the degrees after {@link #getSunset()} to use in the
392 * calculation. For time before sunset use negative numbers.
393 * @return The {@link java.util.Date}of the offset after (or before)
394 * {@link #getSunset()}. If the calculation can not be computed
395 * null will be returned.
396 */
397 public Date getSunsetOffsetByDegrees(double offsetZenith) {
398 double sunset = getUTCSunset(offsetZenith);
399 if (Double.isNaN(sunset)) {
400 return null;
401 } else {
402 return getAdjustedSunsetDate(getDateFromTime(sunset),
403 getSunriseOffsetByDegrees(offsetZenith));
404 }
405 }
406
407 /**
408 * Default constructor will set a default {@link GeoLocation#GeoLocation()},
409 * a default
410 * {@link AstronomicalCalculator#getDefault() AstronomicalCalculator} and
411 * default the calendar to the current date.
412 */
413 public AstronomicalCalendar() {
414 this(new GeoLocation());
415 }
416
417 /**
418 * A constructor that takes in as a parameter geolocation information
419 *
420 * @param geoLocation
421 * The location information used for astronomical calculating sun
422 * times.
423 */
424 public AstronomicalCalendar(GeoLocation geoLocation) {
425 setCalendar(Calendar.getInstance(geoLocation.getTimeZone()));
426 setGeoLocation(geoLocation);// duplicate call
427 setAstronomicalCalculator(AstronomicalCalculator.getDefault());
428 }
429
430 /**
431 * Method that returns the sunrise in UTC time without correction for time
432 * zone offset from GMT and without using daylight savings time.
433 *
434 * @param zenith
435 * the degrees below the horizon. For time after sunrise use
436 * negative numbers.
437 * @return The time in the format: 18.75 for 18:45:00 UTC/GMT. If the
438 * calculation can not be computed {@link Double#NaN} will be
439 * returned.
440 */
441 public double getUTCSunrise(double zenith) {
442 return getAstronomicalCalculator().getUTCSunrise(this, zenith, true);
443 }
444
445 /**
446 * Method that returns the sunrise in UTC time without correction for time
447 * zone offset from GMT and without using daylight savings time. Non-sunrise
448 * and sunset calculations such as dawn and dusk, depend on the amount of
449 * visible light, something that is not affected by elevation. This method
450 * returns UTC sunrise calculated at sea level. This forms the base for dawn
451 * calculations that are calculated as a dip below the horizon before
452 * sunrise.
453 *
454 * @param zenith
455 * the degrees below the horizon. For time after sunrise use
456 * negative numbers.
457 * @return The time in the format: 18.75 for 18:45:00 UTC/GMT. If the
458 * calculation can not be computed {@link Double#NaN} will be
459 * returned.
460 * @see AstronomicalCalendar#getUTCSunrise
461 * @see AstronomicalCalendar#getUTCSeaLevelSunset
462 */
463 public double getUTCSeaLevelSunrise(double zenith) {
464 return getAstronomicalCalculator().getUTCSunrise(this, zenith, false);
465 }
466
467 /**
468 * Method that returns the sunset in UTC time without correction for time
469 * zone offset from GMT and without using daylight savings time.
470 *
471 * @param zenith
472 * the degrees below the horizon. For time after before sunset
473 * use negative numbers.
474 * @return The time in the format: 18.75 for 18:45:00 UTC/GMT. If the
475 * calculation can not be computed {@link Double#NaN} will be
476 * returned.
477 * @see AstronomicalCalendar#getUTCSeaLevelSunset
478 */
479 public double getUTCSunset(double zenith) {
480 return getAstronomicalCalculator().getUTCSunset(this, zenith, true);
481 }
482
483 /**
484 * Method that returns the sunset in UTC time without correction for
485 * elevation, time zone offset from GMT and without using daylight savings
486 * time. Non-sunrise and sunset calculations such as dawn and dusk, depend
487 * on the amount of visible light, something that is not affected by
488 * elevation. This method returns UTC sunset calculated at sea level. This
489 * forms the base for dusk calculations that are calculated as a dip below
490 * the horizon after sunset.
491 *
492 * @param zenith
493 * the degrees below the horizon. For time before sunset use
494 * negative numbers.
495 * @return The time in the format: 18.75 for 18:45:00 UTC/GMT. If the
496 * calculation can not be computed {@link Double#NaN} will be
497 * returned.
498 * @see AstronomicalCalendar#getUTCSunset
499 * @see AstronomicalCalendar#getUTCSeaLevelSunrise
500 */
501 public double getUTCSeaLevelSunset(double zenith) {
502 return getAstronomicalCalculator().getUTCSunset(this, zenith, false);
503 }
504
505 /**
506 * A method that adds time zone offset and daylight savings time to the raw
507 * UTC time.
508 *
509 * @param time
510 * The UTC time to be adjusted.
511 * @return The time adjusted for the time zone offset and daylight savings
512 * time.
513 */
514 private double getOffsetTime(double time) {
515 boolean dst = getCalendar().getTimeZone().inDaylightTime(
516 getCalendar().getTime());
517 double dstOffset = 0;
518 // be nice to Newfies and use a double
519 double gmtOffset = getCalendar().getTimeZone().getRawOffset()
520 / (60 * MINUTE_MILLIS);
521 if (dst) {
522 dstOffset = getCalendar().getTimeZone().getDSTSavings()
523 / (60 * MINUTE_MILLIS);
524 }
525 return time + gmtOffset + dstOffset;
526 }
527
528 /**
529 * Method to return a temporal (solar) hour. The day from sunrise to sunset
530 * is split into 12 equal parts with each one being a temporal hour.
531 *
532 * @return the <code>long</code> millisecond length of a temporal hour. If
533 * the calculation can not be computed {@link Long#MIN_VALUE} will
534 * be returned.
535 */
536 public long getTemporalHour() {
537 return getTemporalHour(getSunrise(), getSunset());
538 }
539
540 /**
541 * Utility method that will allow the calculation of a temporal (solar) hour
542 * based on the sunrise and sunset passed to this method.
543 *
544 * @param sunrise
545 * The start of the day.
546 * @param sunset
547 * The end of the day.
548 * @see #getTemporalHour()
549 * @return the <code>long</code> millisecond length of the temporal hour.
550 * If the calculation can not be computed {@link Long#MIN_VALUE}
551 * will be returned.
552 */
553 public long getTemporalHour(Date sunrise, Date sunset) {
554 if (sunrise == null || sunset == null) {
555 return Long.MIN_VALUE;
556 }
557 return (sunset.getTime() - sunrise.getTime()) / 12;
558 }
559
560 /**
561 * A method that returns sundial or solar noon. It occurs when the Sun is <a
562 * href="http://en.wikipedia.org/wiki/Transit_%28astronomy%29">transitting</a>
563 * the <a
564 * href="http://en.wikipedia.org/wiki/Meridian_%28astronomy%29">celestial
565 * meridian</a>. In this class it is calculated as halfway between sunrise
566 * and sunset, which can be slightly off the real transit time due to the
567 * lengthening or shortening day.
568 *
569 * @return the <code>Date</code> representing Sun's transit. If the
570 * calculation can not be computed null will be returned.
571 */
572 public Date getSunTransit() {
573 return getTimeOffset(getSunrise(), getTemporalHour() * 6);
574 }
575
576 /**
577 * A method that returns a <code>Date</code> from the time passed in
578 *
579 * @param time
580 * The time to be set as the time for the <code>Date</code>.
581 * The time expected is in the format: 18.75 for 6:45:00 PM
582 * @return The Date.
583 */
584 protected Date getDateFromTime(double time) {
585 if (Double.isNaN(time)) {
586 return null;
587 }
588 time = getOffsetTime(time);
589 time = (time + 240) % 24; // the calculators sometimes return a double
590 // that is negative or slightly greater than
591 // 24
592 Calendar cal = new GregorianCalendar();
593 cal.clear();
594 cal.set(Calendar.YEAR, getCalendar().get(Calendar.YEAR));
595 cal.set(Calendar.MONTH, getCalendar().get(Calendar.MONTH));
596 cal
597 .set(Calendar.DAY_OF_MONTH, getCalendar().get(
598 Calendar.DAY_OF_MONTH));
599
600 int hours = (int) time; // cut off minutes
601
602 time -= hours;
603 int minutes = (int) (time *= 60);
604 time -= minutes;
605 int seconds = (int) (time *= 60);
606 time -= seconds; // milliseconds
607
608 cal.set(Calendar.HOUR_OF_DAY, hours);
609 cal.set(Calendar.MINUTE, minutes);
610 cal.set(Calendar.SECOND, seconds);
611 cal.set(Calendar.MILLISECOND, (int) (time * 1000));
612 return cal.getTime();
613 }
614
615 /**
616 * @return an XML formatted representation of the class. It returns the
617 * default output of the {@link net.sourceforge.zmanim.util.ZmanimFormatter#toXML(AstronomicalCalendar) toXML} method.
618 * @see net.sourceforge.zmanim.util.ZmanimFormatter#toXML(AstronomicalCalendar)
619 * @see java.lang.Object#toString()
620 */
621 public String toString() {
622 return ZmanimFormatter.toXML(this);
623 }
624
625 /**
626 * @see java.lang.Object#equals(Object)
627 */
628 public boolean equals(Object object) {
629 if (this == object)
630 return true;
631 if (!(object instanceof AstronomicalCalendar))
632 return false;
633 AstronomicalCalendar aCal = (AstronomicalCalendar) object;
634 return getCalendar().equals(aCal.getCalendar())
635 && getGeoLocation().equals(aCal.getGeoLocation())
636 && getAstronomicalCalculator().equals(
637 aCal.getAstronomicalCalculator());
638 }
639
640 /**
641 * @see java.lang.Object#hashCode()
642 */
643 public int hashCode() {
644 int result = 17;
645 // needed or this and subclasses will return identical hash
646 result = 37 * result + getClass().hashCode();
647 result += 37 * result + getCalendar().hashCode();
648 result += 37 * result + getGeoLocation().hashCode();
649 result += 37 * result + getAstronomicalCalculator().hashCode();
650 return result;
651 }
652
653 /**
654 * A method that returns the currently set {@link GeoLocation} that contains
655 * location information used for the astronomical calculations.
656 *
657 * @return Returns the geoLocation.
658 */
659 public GeoLocation getGeoLocation() {
660 return geoLocation;
661 }
662
663 /**
664 * Set the {@link GeoLocation} to be used for astronomical calculations.
665 *
666 * @param geoLocation
667 * The geoLocation to set.
668 */
669 public void setGeoLocation(GeoLocation geoLocation) {
670 this.geoLocation = geoLocation;
671 // if not set the output will be in the original timezone. The call
672 // below is also in the constructor
673 getCalendar().setTimeZone(geoLocation.getTimeZone());
674 }
675
676 /**
677 * A method to return the current AstronomicalCalculator set.
678 *
679 * @return Returns the astronimicalCalculator.
680 * @see #setAstronomicalCalculator(AstronomicalCalculator)
681 */
682 public AstronomicalCalculator getAstronomicalCalculator() {
683 return astronomicalCalculator;
684 }
685
686 /**
687 * A method to set the {@link AstronomicalCalculator} used for astronomical
688 * calculations. The Zmanim package ships with a number of different
689 * implementations of the <code>abstract</code>
690 * {@link AstronomicalCalculator} based on different algorithms, including
691 * {@link net.sourceforge.zmanim.util.SunTimesCalculator one implementation}
692 * based on the <a href = "http://aa.usno.navy.mil/">US Naval Observatory's</a>
693 * algorithm, and
694 * {@link net.sourceforge.zmanim.util.JSuntimeCalculator another} based on
695 * <a href=""http://noaa.gov">NOAA's</a> algorithm. This allows easy
696 * runtime switching and comparison of different algorithms.
697 *
698 * @param astronomicalCalculator
699 * The astronimicalCalculator to set.
700 */
701 public void setAstronomicalCalculator(
702 AstronomicalCalculator astronomicalCalculator) {
703 this.astronomicalCalculator = astronomicalCalculator;
704 }
705
706 /**
707 * returns the Calendar object encapsulated in this class.
708 *
709 * @return Returns the calendar.
710 */
711 public Calendar getCalendar() {
712 return calendar;
713 }
714
715 /**
716 * @param calendar
717 * The calendar to set.
718 */
719 public void setCalendar(Calendar calendar) {
720 this.calendar = calendar;
721 if (getGeoLocation() != null) {// set the timezone if possible
722 // Always set the Calendar's timezone to match the GeoLocation TimeZone
723 getCalendar().setTimeZone(getGeoLocation().getTimeZone());
724 }
725 }
726
727 /**
728 * A method that creates a <a
729 * href="http://en.wikipedia.org/wiki/Object_copy#Deep_copy">deep copy</a>
730 * of the object. <br />
731 * <b>Note:</b> If the {@link java.util.TimeZone} in the cloned
732 * {@link net.sourceforge.zmanim.util.GeoLocation} will be changed from the
733 * original, it is critical that
734 * {@link net.sourceforge.zmanim.AstronomicalCalendar#getCalendar()}.{@link java.util.Calendar#setTimeZone(TimeZone) setTimeZone(TimeZone)}
735 * be called in order for the AstronomicalCalendar to output times in the
736 * expected offset after being cloned.
737 *
738 * @see java.lang.Object#clone()
739 * @since 1.1
740 */
741 public Object clone() {
742 AstronomicalCalendar clone = null;
743 try {
744 clone = (AstronomicalCalendar) super.clone();
745 } catch (CloneNotSupportedException cnse) {
746 System.out
747 .print("Required by the compiler. Should never be reached since we implement clone()");
748 }
749 clone.setGeoLocation((GeoLocation) getGeoLocation().clone());
750 clone.setCalendar((Calendar) getCalendar().clone());
751 clone
752 .setAstronomicalCalculator((AstronomicalCalculator) getAstronomicalCalculator()
753 .clone());
754 return clone;
755 }
756 }