001/* 002 * Zmanim Java API 003 * Copyright (C) 2004-2023 Eliyahu Hershfeld 004 * 005 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General 006 * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) 007 * any later version. 008 * 009 * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied 010 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 011 * details. 012 * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to 013 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA, 014 * or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html 015 */ 016package com.kosherjava.zmanim.util; 017 018import java.text.SimpleDateFormat; 019import java.util.Comparator; 020import java.util.Date; 021import java.util.TimeZone; 022 023/** 024 * A wrapper class for a astronomical times / <em>zmanim</em> that is mostly intended to allow sorting collections of astronomical times. 025 * It has fields for both date/time and duration based <em>zmanim</em>, name / labels as well as a longer description or explanation of a 026 * <em>zman</em>. 027 * 028 * Here is an example of various ways of sorting <em>zmanim</em>. 029 * <p>First create the Calendar for the location you would like to calculate: 030 * 031 * <pre style="background: #FEF0C9; display: inline-block;"> 032 * String locationName = "Lakewood, NJ"; 033 * double latitude = 40.0828; // Lakewood, NJ 034 * double longitude = -74.2094; // Lakewood, NJ 035 * double elevation = 20; // optional elevation correction in Meters 036 * // the String parameter in getTimeZone() has to be a valid timezone listed in {@link java.util.TimeZone#getAvailableIDs()} 037 * TimeZone timeZone = TimeZone.getTimeZone("America/New_York"); 038 * GeoLocation location = new GeoLocation(locationName, latitude, longitude, elevation, timeZone); 039 * ComplexZmanimCalendar czc = new ComplexZmanimCalendar(location); 040 * Zman sunset = new Zman(czc.getSunset(), "Sunset"); 041 * Zman shaah16 = new Zman(czc.getShaahZmanis16Point1Degrees(), "Shaah zmanis 16.1"); 042 * Zman sunrise = new Zman(czc.getSunrise(), "Sunrise"); 043 * Zman shaah = new Zman(czc.getShaahZmanisGra(), "Shaah zmanis GRA"); 044 * ArrayList<Zman> zl = new ArrayList<Zman>(); 045 * zl.add(sunset); 046 * zl.add(shaah16); 047 * zl.add(sunrise); 048 * zl.add(shaah); 049 * //will sort sunset, shaah 1.6, sunrise, shaah GRA 050 * System.out.println(zl); 051 * Collections.sort(zl, Zman.DATE_ORDER); 052 * // will sort sunrise, sunset, shaah, shaah 1.6 (the last 2 are not in any specific order) 053 * Collections.sort(zl, Zman.DURATION_ORDER); 054 * // will sort sunrise, sunset (the first 2 are not in any specific order), shaah GRA, shaah 1.6 055 * Collections.sort(zl, Zman.NAME_ORDER); 056 * // will sort shaah 1.6, shaah GRA, sunrise, sunset 057 * </pre> 058 * 059 * @author © Eliyahu Hershfeld 2007-2023 060 * @todo Add secondary sorting. As of now the {@code Comparator}s in this class do not sort by secondary order. This means that when sorting a 061 * {@link java.util.Collection} of <em>zmanim</em> and using the {@link #DATE_ORDER} {@code Comparator} will have the duration based <em>zmanim</em> 062 * at the end, but they will not be sorted by duration. This should be N/A for label based sorting. 063 */ 064public class Zman { 065 /** 066 * The name / label of the <em>zman</em>. 067 */ 068 private String label; 069 070 /** 071 * The {@link Date} of the <em>zman</em> 072 */ 073 private Date zman; 074 075 /** 076 * The duration if the <em>zman</em> is a {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour} (or the various 077 * <em>shaah zmanis</em> base times such as {@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() <em>shaah Zmanis GRA</em>} or 078 * {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() <em>shaah Zmanis 16.1°</em>}). 079 */ 080 private long duration; 081 082 /** 083 * A longer description or explanation of a <em>zman</em>. 084 */ 085 private String description; 086 087 /** 088 * The location information of the <em>zman</em>. 089 */ 090 private GeoLocation geoLocation; 091 092 /** 093 * The constructor setting a {@link Date} based <em>zman</em> and a label. In most cases you will likely want to call 094 * {@link #Zman(Date, GeoLocation, String)} that also sets the location. 095 * @param date the Date of the <em>zman</em>. 096 * @param label the label of the <em>zman</em> such as "<em>Sof Zman Krias Shema GRA</em>". 097 * @see #Zman(Date, GeoLocation, String) 098 */ 099 public Zman(Date date, String label) { 100 this(date, null, label); 101 } 102 103 /** 104 * The constructor setting a {@link Date} based <em>zman</em> and a label. In most cases you will likely want to call 105 * {@link #Zman(Date, GeoLocation, String)} that also sets the geo location. 106 * @param date the Date of the <em>zman</em>. 107 * @param geoLocation the {@link GeoLocation} of the <em>zman</em>. 108 * @param label the label of the <em>zman</em> such as "<em>Sof Zman Krias Shema GRA</em>". 109 */ 110 public Zman(Date date, GeoLocation geoLocation, String label) { 111 this.zman = date; 112 this.geoLocation = geoLocation; 113 this.label = label; 114 } 115 116 /** 117 * The constructor setting a duration based <em>zman</em> such as 118 * {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour} (or the various <em>shaah zmanis</em> times such as 119 * {@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() <em>shaah zmanis GRA</em>} or 120 * {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() <em>shaah Zmanis 16.1°</em>}) and label. 121 * @param duration a duration based <em>zman</em> such as ({@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour()} 122 * @param label the label of the <em>zman</em> such as "<em>Shaah Zmanis GRA</em>". 123 * @see #Zman(Date, String) 124 */ 125 public Zman(long duration, String label) { 126 this.label = label; 127 this.duration = duration; 128 } 129 130 /** 131 * Returns the {@code Date} based <em>zman</em>. 132 * @return the <em>zman</em>. 133 * @see #setZman(Date) 134 */ 135 public Date getZman() { 136 return this.zman; 137 } 138 139 /** 140 * Sets a {@code Date} based <em>zman</em>. 141 * @param date a {@code Date} based <em>zman</em> 142 * @see #getZman() 143 */ 144 public void setZman(Date date) { 145 this.zman = date; 146 } 147 148 /** 149 * Returns the {link TimeZone} of the <em>zman</em>. 150 * @return the time zone 151 */ 152 public GeoLocation getGeoLocation() { 153 return geoLocation; 154 } 155 156 /** 157 * Sets the {@code GeoLocation} of the <em>zman</em>. 158 * @param geoLocation the {@code GeoLocation} of the <em>zman</em>. 159 */ 160 public void setGeoLocation(GeoLocation geoLocation) { 161 this.geoLocation = geoLocation; 162 } 163 164 /** 165 * Returns a duration based <em>zman</em> such as {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour} 166 * (or the various <em>shaah zmanis</em> times such as {@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() <em>shaah zmanis GRA</em>} 167 * or {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() <em>shaah zmanis 16.1°</em>}). 168 * @return the duration based <em>zman</em>. 169 * @see #setDuration(long) 170 */ 171 public long getDuration() { 172 return this.duration; 173 } 174 175 /** 176 * Sets a duration based <em>zman</em> such as {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour} 177 * (or the various <em>shaah zmanis</em> times as {@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() <em>shaah zmanis GRA</em>} or 178 * {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() <em>shaah zmanis 16.1°</em>}). 179 * @param duration duration based <em>zman</em> such as {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour()}. 180 * @see #getDuration() 181 */ 182 public void setDuration(long duration) { 183 this.duration = duration; 184 } 185 186 /** 187 * Returns the name / label of the <em>zman</em> such as "<em>Sof Zman Krias Shema GRA</em>". There are no automatically set labels 188 * and you must set them using {@link #setLabel(String)}. 189 * @return the name/label of the <em>zman</em>. 190 * @see #setLabel(String) 191 */ 192 public String getLabel() { 193 return this.label; 194 } 195 196 /** 197 * Sets the the name / label of the <em>zman</em> such as "<em>Sof Zman Krias Shema GRA</em>". 198 * @param label the name / label to set for the <em>zman</em>. 199 * @see #getLabel() 200 */ 201 public void setLabel(String label) { 202 this.label = label; 203 } 204 205 /** 206 * Returns the longer description or explanation of a <em>zman</em>. There is no default value for this and it must be set using 207 * {@link #setDescription(String)} 208 * @return the description or explanation of a <em>zman</em>. 209 * @see #setDescription(String) 210 */ 211 public String getDescription() { 212 return this.description; 213 } 214 215 /** 216 * Sets the longer description or explanation of a <em>zman</em>. 217 * @param description 218 * the <em>zman</em> description to set. 219 * @see #getDescription() 220 */ 221 public void setDescription(String description) { 222 this.description = description; 223 } 224 225 /** 226 * A {@link Comparator} that will compare and sort <em>zmanim</em> by date/time order. Compares its two arguments by the zman's date/time 227 * order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater 228 * than the second. 229 * Please note that this class will handle cases where either the {@code Zman} is a null or {@link #getZman()} returns a null. 230 */ 231 public static final Comparator<Zman> DATE_ORDER = new Comparator<Zman>() { 232 public int compare(Zman zman1, Zman zman2) { 233 long firstTime = (zman1 == null || zman1.getZman() == null) ? Long.MAX_VALUE : zman1.getZman().getTime(); 234 long secondTime = (zman2 == null || zman2.getZman() == null) ? Long.MAX_VALUE : zman2.getZman().getTime(); 235 return Long.valueOf(firstTime).compareTo(Long.valueOf(secondTime)); 236 } 237 }; 238 239 /** 240 * A {@link Comparator} that will compare and sort zmanim by zmanim label order. Compares its two arguments by the zmanim label 241 * name order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater 242 * than the second. 243 * Please note that this class will will sort cases where either the {@code Zman} is a null or {@link #label} returns a null 244 * as empty {@code String}s. 245 */ 246 public static final Comparator<Zman> NAME_ORDER = new Comparator<Zman>() { 247 public int compare(Zman zman1, Zman zman2) { 248 String firstLabel = (zman1 == null || zman1.getLabel() == null) ? "" : zman1.getLabel(); 249 String secondLabel = (zman2 == null || zman2.getLabel() == null) ? "" : zman2.getLabel(); 250 return firstLabel.compareTo(secondLabel); 251 } 252 }; 253 254 255 /** 256 * A {@link Comparator} that will compare and sort duration based <em>zmanim</em> such as 257 * {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour} (or the various <em>shaah zmanis</em> times 258 * such as <em>{@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() shaah zmanis GRA}</em> or 259 * {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() <em>shaah zmanis 16.1°</em>}). Returns a negative 260 * integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second. 261 * Please note that this class will will sort cases where {@code Zman} is a null. 262 */ 263 public static final Comparator<Zman> DURATION_ORDER = new Comparator<Zman>() { 264 public int compare(Zman zman1, Zman zman2) { 265 long firstDuration = zman1 == null ? Long.MAX_VALUE : zman1.getDuration(); 266 long secondDuration = zman2 == null ? Long.MAX_VALUE : zman2.getDuration(); 267 return firstDuration == secondDuration ? 0 : firstDuration > secondDuration ? 1 : -1; 268 } 269 }; 270 271 /** 272 * A method that returns an XML formatted <code>String</code> representing the serialized <code>Object</code>. Very 273 * similar to the toString method but the return value is in an xml format. The format currently used (subject to 274 * change) is: 275 * 276 * <pre> 277 * <Zman> 278 * <Label>Sof Zman Krias Shema GRA</Label> 279 * <Zman>1969-02-08T09:37:56.820</Zman> 280 * <TimeZone> 281 * <TimezoneName>America/Montreal</TimezoneName> 282 * <TimeZoneDisplayName>Eastern Standard Time</TimeZoneDisplayName> 283 * <TimezoneGMTOffset>-5</TimezoneGMTOffset> 284 * <TimezoneDSTOffset>1</TimezoneDSTOffset> 285 * </TimeZone> 286 * <Duration>0</Duration> 287 * <Description>Sof Zman Krias Shema GRA is 3 sha'os zmaniyos calculated from sunrise to sunset.</Description> 288 * </Zman> 289 * </pre> 290 * @return The XML formatted <code>String</code>. 291 */ 292 public String toXML() { 293 SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); 294 StringBuilder sb = new StringBuilder(); 295 sb.append("<Zman>\n"); 296 sb.append("\t<Label>").append(getLabel()).append("</Label>\n"); 297 sb.append("\t<Zman>").append(getZman() == null ? "": formatter.format(getZman())).append("</Zman>\n"); 298 sb.append("\t" + getGeoLocation().toXML().replaceAll("\n", "\n\t")); 299 sb.append("\n\t<Duration>").append(getDuration()).append("</Duration>\n"); 300 sb.append("\t<Description>").append(getDescription()).append("</Description>\n"); 301 sb.append("</Zman>"); 302 return sb.toString(); 303 } 304 305 /** 306 * @see java.lang.Object#toString() 307 */ 308 public String toString() { 309 StringBuilder sb = new StringBuilder(); 310 sb.append("\nLabel:\t").append(this.getLabel()); 311 sb.append("\nZman:\t").append(getZman()); 312 sb.append("\nGeoLocation:\t").append(getGeoLocation().toString().replaceAll("\n", "\n\t")); 313 sb.append("\nDuration:\t").append(getDuration()); 314 sb.append("\nDescription:\t").append(getDescription()); 315 return sb.toString(); 316 } 317}