001/*
002 * Zmanim Java API
003 * Copyright © 2011 - 2026 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; version 2.1 of the License.
007 *
008 * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied
009 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
010 * details.
011 * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
012 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA,
013 * or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
014 */
015package com.kosherjava.zmanim.hebrewcalendar;
016
017import java.time.format.DateTimeFormatter;
018import java.util.EnumMap;
019
020/**
021 * The HebrewDateFormatter class formats a {@link JewishDate}. The class formats Jewish dates, numbers, <em>Daf Yomi</em>
022 * (<em>Bavli</em> and <em>Yerushalmi</em>), the <em>Omer</em>, <em>Parshas Hashavua</em> (including the special <em>parshiyos</em>
023 * of <em>Shekalim</em>, <em>Zachor</em>, <em>Parah</em> and <em>Hachodesh</em>), <em>Yomim Tovim</em> in Hebrew or Latin chars, and
024 * has various settings. Sample full date output includes (using various options):
025 * <ul>
026 * <li>21 Shevat, 5729</li>
027 * <li>כא שבט תשכט</li>
028 * <li>כ״א שבט ה׳תשכ״ט</li>
029 * <li>כ״א שבט תש״פ or כ״א שבט תש״ף</li>
030 * <li>כ׳ שבט ו׳ אלפים</li>
031 * </ul>
032 * 
033 * @see JewishDate
034 * @see JewishCalendar
035 * @author &copy; Eliyahu Hershfeld 2011 - 2026
036 */
037public class HebrewDateFormatter {
038        
039        /**
040         * See {@link #isHebrewFormat()} and {@link #setHebrewFormat(boolean)}.
041         */
042        private boolean hebrewFormat = false;
043        
044        /**
045         * See {@link #isUseLongHebrewYears()} and {@link #setUseLongHebrewYears(boolean)}.
046         */
047        private boolean useLonghebrewYears = false;
048        
049        /**
050         * See {@link #isUseGershGershayim()} and {@link #setUseGershGershayim(boolean)}.
051         */
052        private boolean useGershGershayim = true;
053        
054        /**
055         * See {@link #isLongWeekFormat()} and {@link #setLongWeekFormat(boolean)}.
056         */
057        private boolean longWeekFormat = true;
058        
059        /**
060         * See {@link #isUseFinalFormLetters()} and {@link #setUseFinalFormLetters(boolean)}.
061         */
062        private boolean useFinalFormLetters = false;
063        
064        /**
065         * The internal DateFormat. See {@link #isLongWeekFormat()} and {@link #setLongWeekFormat(boolean)}.
066         */
067        private DateTimeFormatter weekFormat;
068        
069        /**
070         * List of transliterated parshiyos using the default <em>Ashkenazi</em> pronunciation. For information on the format, see
071         * {@link #getTransliteratedParshiosList()}.
072         * 
073         * @see #getTransliteratedParshiosList()
074         * @see #setTransliteratedParshiosList(EnumMap)
075         * @see #HebrewDateFormatter() where the map is initially set.
076         */
077        private EnumMap<JewishCalendar.Parsha, String> transliteratedParshaMap;
078        
079        /**
080         * An {@link EnumMap} of Hebrew <em>parshiyos</em>. The list includes double and special <em>parshiyos</em> and contains<br>
081         * <code>&rlm;בראשית, נח, לך לך,וירא, חיי שרה,תולדות, ויצא, וישלח,וישב, מקץ, ויגש, ויחי,שמות, וארא, בא, בשלח,יתרו, משפטים, תרומה,תצוה, כי תשא, ויקהל,פקודי, 
082         * ויקרא, צו,שמיני, תזריע, מצרע,אחרי מות, קדושים,אמור, בהר, בחקתי,במדבר, נשא, בהעלתך,שלח לך, קרח, חוקת, בלק,פינחס, מטות, מסעי,דברים, ואתחנן, עקב,ראה, שופטים, כי תצא,כי
083         * תבוא, נצבים, וילך,האזינו, וזאת הברכה,ויקהל פקודי, תזריעמצרע, אחרי מותקדושים, בהר בחקתי,חוקת בלק, מטות מסעי,נצבים וילך,
084         * שקלים,זכור, פרה, החדש,שובה,שירה,הגדול,חזון,נחמו</code>
085         */
086        private final EnumMap<JewishCalendar.Parsha, String> hebrewParshaMap;
087        
088        /**
089         * Default constructor sets the {@link EnumMap}s of Hebrew and default transliterated parshiyos.
090         */
091        public HebrewDateFormatter() {
092                weekFormat = DateTimeFormatter.ofPattern("EEEE");
093                transliteratedParshaMap = new EnumMap<>(JewishCalendar.Parsha.class);
094                transliteratedParshaMap.put(JewishCalendar.Parsha.NONE, "");
095                transliteratedParshaMap.put(JewishCalendar.Parsha.BERESHIS, "Bereshis");
096                transliteratedParshaMap.put(JewishCalendar.Parsha.NOACH, "Noach");
097                transliteratedParshaMap.put(JewishCalendar.Parsha.LECH_LECHA, "Lech Lecha");
098                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYERA, "Vayera");
099                transliteratedParshaMap.put(JewishCalendar.Parsha.CHAYEI_SARA, "Chayei Sara");
100                transliteratedParshaMap.put(JewishCalendar.Parsha.TOLDOS, "Toldos");
101                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYETZEI, "Vayetzei");
102                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYISHLACH, "Vayishlach");
103                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYESHEV, "Vayeshev");
104                transliteratedParshaMap.put(JewishCalendar.Parsha.MIKETZ, "Miketz");
105                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYIGASH, "Vayigash");
106                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYECHI, "Vayechi");
107                transliteratedParshaMap.put(JewishCalendar.Parsha.SHEMOS, "Shemos");
108                transliteratedParshaMap.put(JewishCalendar.Parsha.VAERA, "Vaera");
109                transliteratedParshaMap.put(JewishCalendar.Parsha.BO, "Bo");
110                transliteratedParshaMap.put(JewishCalendar.Parsha.BESHALACH, "Beshalach");
111                transliteratedParshaMap.put(JewishCalendar.Parsha.YISRO, "Yisro");
112                transliteratedParshaMap.put(JewishCalendar.Parsha.MISHPATIM, "Mishpatim");
113                transliteratedParshaMap.put(JewishCalendar.Parsha.TERUMAH, "Terumah");
114                transliteratedParshaMap.put(JewishCalendar.Parsha.TETZAVEH, "Tetzaveh");
115                transliteratedParshaMap.put(JewishCalendar.Parsha.KI_SISA, "Ki Sisa");
116                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYAKHEL, "Vayakhel");
117                transliteratedParshaMap.put(JewishCalendar.Parsha.PEKUDEI, "Pekudei");
118                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYIKRA, "Vayikra");
119                transliteratedParshaMap.put(JewishCalendar.Parsha.TZAV, "Tzav");
120                transliteratedParshaMap.put(JewishCalendar.Parsha.SHMINI, "Shmini");
121                transliteratedParshaMap.put(JewishCalendar.Parsha.TAZRIA, "Tazria");
122                transliteratedParshaMap.put(JewishCalendar.Parsha.METZORA, "Metzora");
123                transliteratedParshaMap.put(JewishCalendar.Parsha.ACHREI_MOS, "Achrei Mos");
124                transliteratedParshaMap.put(JewishCalendar.Parsha.KEDOSHIM, "Kedoshim");
125                transliteratedParshaMap.put(JewishCalendar.Parsha.EMOR, "Emor");
126                transliteratedParshaMap.put(JewishCalendar.Parsha.BEHAR, "Behar");
127                transliteratedParshaMap.put(JewishCalendar.Parsha.BECHUKOSAI, "Bechukosai");
128                transliteratedParshaMap.put(JewishCalendar.Parsha.BAMIDBAR, "Bamidbar");
129                transliteratedParshaMap.put(JewishCalendar.Parsha.NASSO, "Nasso");
130                transliteratedParshaMap.put(JewishCalendar.Parsha.BEHAALOSCHA, "Beha'aloscha");
131                transliteratedParshaMap.put(JewishCalendar.Parsha.SHLACH, "Sh'lach");
132                transliteratedParshaMap.put(JewishCalendar.Parsha.KORACH, "Korach");
133                transliteratedParshaMap.put(JewishCalendar.Parsha.CHUKAS, "Chukas");
134                transliteratedParshaMap.put(JewishCalendar.Parsha.BALAK, "Balak");
135                transliteratedParshaMap.put(JewishCalendar.Parsha.PINCHAS, "Pinchas");
136                transliteratedParshaMap.put(JewishCalendar.Parsha.MATOS, "Matos");
137                transliteratedParshaMap.put(JewishCalendar.Parsha.MASEI, "Masei");
138                transliteratedParshaMap.put(JewishCalendar.Parsha.DEVARIM, "Devarim");
139                transliteratedParshaMap.put(JewishCalendar.Parsha.VAESCHANAN, "Vaeschanan");
140                transliteratedParshaMap.put(JewishCalendar.Parsha.EIKEV, "Eikev");
141                transliteratedParshaMap.put(JewishCalendar.Parsha.REEH, "Re'eh");
142                transliteratedParshaMap.put(JewishCalendar.Parsha.SHOFTIM, "Shoftim");
143                transliteratedParshaMap.put(JewishCalendar.Parsha.KI_SEITZEI, "Ki Seitzei");
144                transliteratedParshaMap.put(JewishCalendar.Parsha.KI_SAVO, "Ki Savo");
145                transliteratedParshaMap.put(JewishCalendar.Parsha.NITZAVIM, "Nitzavim");
146                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYEILECH, "Vayeilech");
147                transliteratedParshaMap.put(JewishCalendar.Parsha.HAAZINU, "Ha'Azinu");
148                transliteratedParshaMap.put(JewishCalendar.Parsha.VZOS_HABERACHA, "Vezos Habracha");
149                transliteratedParshaMap.put(JewishCalendar.Parsha.VAYAKHEL_PEKUDEI, "Vayakhel Pekudei");
150                transliteratedParshaMap.put(JewishCalendar.Parsha.TAZRIA_METZORA, "Tazria Metzora");
151                transliteratedParshaMap.put(JewishCalendar.Parsha.ACHREI_MOS_KEDOSHIM, "Achrei Mos Kedoshim");
152                transliteratedParshaMap.put(JewishCalendar.Parsha.BEHAR_BECHUKOSAI, "Behar Bechukosai");
153                transliteratedParshaMap.put(JewishCalendar.Parsha.CHUKAS_BALAK, "Chukas Balak");
154                transliteratedParshaMap.put(JewishCalendar.Parsha.MATOS_MASEI, "Matos Masei");
155                transliteratedParshaMap.put(JewishCalendar.Parsha.NITZAVIM_VAYEILECH, "Nitzavim Vayeilech");
156                transliteratedParshaMap.put(JewishCalendar.Parsha.SHKALIM, "Shekalim");
157                transliteratedParshaMap.put(JewishCalendar.Parsha.ZACHOR, "Zachor");
158                transliteratedParshaMap.put(JewishCalendar.Parsha.PARA, "Parah");
159                transliteratedParshaMap.put(JewishCalendar.Parsha.HACHODESH, "Hachodesh");
160                transliteratedParshaMap.put(JewishCalendar.Parsha.SHUVA, "Shuva");
161                transliteratedParshaMap.put(JewishCalendar.Parsha.SHIRA, "Shira");
162                transliteratedParshaMap.put(JewishCalendar.Parsha.HAGADOL, "Hagadol");
163                transliteratedParshaMap.put(JewishCalendar.Parsha.CHAZON, "Chazon");
164                transliteratedParshaMap.put(JewishCalendar.Parsha.NACHAMU, "Nachamu");
165                
166                hebrewParshaMap = new EnumMap<>(JewishCalendar.Parsha.class);
167                hebrewParshaMap.put(JewishCalendar.Parsha.NONE, "");
168                hebrewParshaMap.put(JewishCalendar.Parsha.BERESHIS, "בראשית");
169                hebrewParshaMap.put(JewishCalendar.Parsha.NOACH, "נח");
170                hebrewParshaMap.put(JewishCalendar.Parsha.LECH_LECHA, "לך לך");
171                hebrewParshaMap.put(JewishCalendar.Parsha.VAYERA, "וירא");
172                hebrewParshaMap.put(JewishCalendar.Parsha.CHAYEI_SARA, "חיי שרה");
173                hebrewParshaMap.put(JewishCalendar.Parsha.TOLDOS, "תולדות");
174                hebrewParshaMap.put(JewishCalendar.Parsha.VAYETZEI, "ויצא");
175                hebrewParshaMap.put(JewishCalendar.Parsha.VAYISHLACH, "וישלח");
176                hebrewParshaMap.put(JewishCalendar.Parsha.VAYESHEV, "וישב");
177                hebrewParshaMap.put(JewishCalendar.Parsha.MIKETZ, "מקץ");
178                hebrewParshaMap.put(JewishCalendar.Parsha.VAYIGASH, "ויגש");
179                hebrewParshaMap.put(JewishCalendar.Parsha.VAYECHI, "ויחי");
180                hebrewParshaMap.put(JewishCalendar.Parsha.SHEMOS, "שמות");
181                hebrewParshaMap.put(JewishCalendar.Parsha.VAERA, "וארא");
182                hebrewParshaMap.put(JewishCalendar.Parsha.BO, "בא");
183                hebrewParshaMap.put(JewishCalendar.Parsha.BESHALACH, "בשלח");
184                hebrewParshaMap.put(JewishCalendar.Parsha.YISRO, "יתרו");
185                hebrewParshaMap.put(JewishCalendar.Parsha.MISHPATIM, "משפטים");
186                hebrewParshaMap.put(JewishCalendar.Parsha.TERUMAH, "תרומה");
187                hebrewParshaMap.put(JewishCalendar.Parsha.TETZAVEH, "תצוה");
188                hebrewParshaMap.put(JewishCalendar.Parsha.KI_SISA, "כי תשא");
189                hebrewParshaMap.put(JewishCalendar.Parsha.VAYAKHEL, "ויקהל");
190                hebrewParshaMap.put(JewishCalendar.Parsha.PEKUDEI, "פקודי");
191                hebrewParshaMap.put(JewishCalendar.Parsha.VAYIKRA, "ויקרא");
192                hebrewParshaMap.put(JewishCalendar.Parsha.TZAV, "צו");
193                hebrewParshaMap.put(JewishCalendar.Parsha.SHMINI, "שמיני");
194                hebrewParshaMap.put(JewishCalendar.Parsha.TAZRIA, "תזריע");
195                hebrewParshaMap.put(JewishCalendar.Parsha.METZORA, "מצרע");
196                hebrewParshaMap.put(JewishCalendar.Parsha.ACHREI_MOS, "אחרי מות");
197                hebrewParshaMap.put(JewishCalendar.Parsha.KEDOSHIM, "קדושים");
198                hebrewParshaMap.put(JewishCalendar.Parsha.EMOR, "אמור");
199                hebrewParshaMap.put(JewishCalendar.Parsha.BEHAR, "בהר");
200                hebrewParshaMap.put(JewishCalendar.Parsha.BECHUKOSAI, "בחקתי");
201                hebrewParshaMap.put(JewishCalendar.Parsha.BAMIDBAR, "במדבר");
202                hebrewParshaMap.put(JewishCalendar.Parsha.NASSO, "נשא");
203                hebrewParshaMap.put(JewishCalendar.Parsha.BEHAALOSCHA, "בהעלתך");
204                hebrewParshaMap.put(JewishCalendar.Parsha.SHLACH, "שלח לך");
205                hebrewParshaMap.put(JewishCalendar.Parsha.KORACH, "קרח");
206                hebrewParshaMap.put(JewishCalendar.Parsha.CHUKAS, "חוקת");
207                hebrewParshaMap.put(JewishCalendar.Parsha.BALAK, "בלק");
208                hebrewParshaMap.put(JewishCalendar.Parsha.PINCHAS, "פינחס");
209                hebrewParshaMap.put(JewishCalendar.Parsha.MATOS, "מטות");
210                hebrewParshaMap.put(JewishCalendar.Parsha.MASEI, "מסעי");
211                hebrewParshaMap.put(JewishCalendar.Parsha.DEVARIM, "דברים");
212                hebrewParshaMap.put(JewishCalendar.Parsha.VAESCHANAN, "ואתחנן");
213                hebrewParshaMap.put(JewishCalendar.Parsha.EIKEV, "עקב");
214                hebrewParshaMap.put(JewishCalendar.Parsha.REEH, "ראה");
215                hebrewParshaMap.put(JewishCalendar.Parsha.SHOFTIM, "שופטים");
216                hebrewParshaMap.put(JewishCalendar.Parsha.KI_SEITZEI, "כי תצא");
217                hebrewParshaMap.put(JewishCalendar.Parsha.KI_SAVO, "כי תבוא");
218                hebrewParshaMap.put(JewishCalendar.Parsha.NITZAVIM, "נצבים");
219                hebrewParshaMap.put(JewishCalendar.Parsha.VAYEILECH, "וילך");
220                hebrewParshaMap.put(JewishCalendar.Parsha.HAAZINU, "האזינו");
221                hebrewParshaMap.put(JewishCalendar.Parsha.VZOS_HABERACHA, "וזאת הברכה");
222                hebrewParshaMap.put(JewishCalendar.Parsha.VAYAKHEL_PEKUDEI, "ויקהל פקודי");
223                hebrewParshaMap.put(JewishCalendar.Parsha.TAZRIA_METZORA, "תזריע מצרע");
224                hebrewParshaMap.put(JewishCalendar.Parsha.ACHREI_MOS_KEDOSHIM, "אחרי מות קדושים");
225                hebrewParshaMap.put(JewishCalendar.Parsha.BEHAR_BECHUKOSAI, "בהר בחקתי");
226                hebrewParshaMap.put(JewishCalendar.Parsha.CHUKAS_BALAK, "חוקת בלק");
227                hebrewParshaMap.put(JewishCalendar.Parsha.MATOS_MASEI, "מטות מסעי");
228                hebrewParshaMap.put(JewishCalendar.Parsha.NITZAVIM_VAYEILECH, "נצבים וילך");
229                hebrewParshaMap.put(JewishCalendar.Parsha.SHKALIM, "שקלים");
230                hebrewParshaMap.put(JewishCalendar.Parsha.ZACHOR, "זכור");
231                hebrewParshaMap.put(JewishCalendar.Parsha.PARA, "פרה");
232                hebrewParshaMap.put(JewishCalendar.Parsha.HACHODESH, "החדש");
233                hebrewParshaMap.put(JewishCalendar.Parsha.SHUVA, "שובה");
234                hebrewParshaMap.put(JewishCalendar.Parsha.SHIRA, "שירה");
235                hebrewParshaMap.put(JewishCalendar.Parsha.HAGADOL, "הגדול");
236                hebrewParshaMap.put(JewishCalendar.Parsha.CHAZON, "חזון");
237                hebrewParshaMap.put(JewishCalendar.Parsha.NACHAMU, "נחמו");
238        }
239
240        /**
241         * Returns if the {@link #formatDayOfWeek(JewishDate)} will use the long format such as ראשון or short such as א when formatting
242         * the day of week in {@link #isHebrewFormat() Hebrew}.
243         * 
244         * @return the longWeekFormat
245         * @see #setLongWeekFormat(boolean)
246         * @see #formatDayOfWeek(JewishDate)
247         */
248        public boolean isLongWeekFormat() {
249                return longWeekFormat;
250        }
251
252        /**
253         * Setting to control if the {@link #formatDayOfWeek(JewishDate)} will use the long format such as ראשון or short such as א when
254         * formatting the day of week in {@link #isHebrewFormat() Hebrew}.
255         * 
256         * @param longWeekFormat the longWeekFormat to set
257         */
258        public void setLongWeekFormat(boolean longWeekFormat) {
259                this.longWeekFormat = longWeekFormat;
260                if (longWeekFormat) {
261                        weekFormat = DateTimeFormatter.ofPattern("EEEE");
262                } else {
263                        weekFormat = DateTimeFormatter.ofPattern("EEE");
264                }
265        }
266
267        /**
268         * The <a href="https://en.wikipedia.org/wiki/Geresh#Punctuation_mark">gersh</a> character is the ׳ char that is similar to a
269         * single quote and is used in formatting Hebrew numbers.
270         */
271        private static final String GERESH = "׳";
272        
273        /**
274         * The <a href="https://en.wikipedia.org/wiki/Gershayim#Punctuation_mark">gershyim</a> character is the ״ char that is similar
275         * to a double quote and is used in formatting Hebrew numbers.
276         */
277        private static final String GERSHAYIM = "״";
278        
279        /**
280         * Transliterated month names that default to <code>["Nissan", "Iyar", "Sivan", "Tammuz", "Av", "Elul", "Tishrei", "Cheshvan",
281         * "Kislev", "Teves", "Shevat", "Adar", "Adar II", "Adar I" ]</code>.
282         * @see #getTransliteratedMonthList()
283         * @see #setTransliteratedMonthList(String[])
284         */
285        private String[] transliteratedMonths = { "Nissan", "Iyar", "Sivan", "Tammuz", "Av", "Elul", "Tishrei", "Cheshvan",
286                        "Kislev", "Teves", "Shevat", "Adar", "Adar II", "Adar I" };
287        
288        /**
289         * The Hebrew omer prefix charachter. It defaults to ב producing בעומר, but can be set to ל to produce לעומר (or any other prefix).
290         * @see #getHebrewOmerPrefix()
291         * @see #setHebrewOmerPrefix(String)
292         */
293        private String hebrewOmerPrefix = "ב";
294
295        /**
296         * The default value for formatting "Shabbos" (Saturday) when transliterated.
297         * @see #getTransliteratedShabbosDayOfWeek()
298         * @see #setTransliteratedShabbosDayOfWeek(String)
299         */
300        private String transliteratedShabbosDayOfWeek = "Shabbos";
301
302        /**
303         * Returns the day of Shabbos transliterated into Latin chars. The default uses Ashkenazi pronunciation "Shabbos". This can be
304         * overwritten using the {@link #setTransliteratedShabbosDayOfWeek(String)}. It is uesd by {@link #formatDayOfWeek(JewishDate)}.
305         * 
306         * @return the transliteratedShabbos. The default list of months uses Ashkenazi pronunciation "Shabbos".
307         * @see #setTransliteratedShabbosDayOfWeek(String)
308         * @see #formatDayOfWeek(JewishDate)
309         */
310        public String getTransliteratedShabbosDayOfWeek() {
311                return transliteratedShabbosDayOfWeek;
312        }
313
314        /**
315         * Setter to override the default transliterated name of "Shabbos" to alternate spelling such as "Shabbat" used by
316         * the {@link #formatDayOfWeek(JewishDate)}.
317         * 
318         * @param transliteratedShabbos the transliteratedShabbos to set
319         * @see #getTransliteratedShabbosDayOfWeek()
320         * @see #formatDayOfWeek(JewishDate)
321         */
322        public void setTransliteratedShabbosDayOfWeek(String transliteratedShabbos) {
323                this.transliteratedShabbosDayOfWeek = transliteratedShabbos;
324        }
325
326        /**
327         * See {@link #getTransliteratedHolidayList()} and {@link #setTransliteratedHolidayList(String[])}.
328         */
329        private String[] transliteratedHolidays = {"Erev Pesach", "Pesach", "Chol Hamoed Pesach", "Pesach Sheni",
330                        "Erev Shavuos", "Shavuos", "Seventeenth of Tammuz", "Tishah B'Av", "Tu B'Av", "Erev Rosh Hashana",
331                        "Rosh Hashana", "Fast of Gedalyah", "Erev Yom Kippur", "Yom Kippur", "Erev Succos", "Succos",
332                        "Chol Hamoed Succos", "Hoshana Rabbah", "Shemini Atzeres", "Simchas Torah", "Erev Chanukah", "Chanukah",
333                        "Tenth of Teves", "Tu B'Shvat", "Fast of Esther", "Purim", "Shushan Purim", "Purim Katan", "Rosh Chodesh",
334                        "Yom HaShoah", "Yom Hazikaron", "Yom Ha'atzmaut", "Yom Yerushalayim", "Lag B'Omer", "Shushan Purim Katan",
335                        "Isru Chag"};
336
337        /**
338         * Returns the array of <em>Yomim Tovim</em> (holidays) transliterated into Latin chars. This is used by the {@link
339         * #formatYomTov(JewishCalendar)} when formatting the <em>Yom Tov</em> String. The default list of months usesnAshkenazi
340         * pronunciation in typical American English spelling. The default list is currently
341         * <code>["Erev Pesach", "Pesach", "Chol Hamoed Pesach", "Pesach Sheni", "Erev Shavuos", "Shavuos", "Seventeenth of Tammuz",
342         * "Tishah B'Av", "Tu B'Av", "Erev Rosh Hashana", "Rosh Hashana", "Fast of Gedalyah", "Erev Yom Kippur", "Yom Kippur", "Erev
343         * Succos", "Succos", "Chol Hamoed Succos", "Hoshana Rabbah", "Shemini Atzeres", "Simchas Torah", "Erev Chanukah", "Chanukah",
344         * "Tenth of Teves", "Tu B'Shvat", "Fast of Esther", "Purim", "Shushan Purim",m"Purim Katan", "Rosh Chodesh", "Yom HaShoah",
345         * "Yom Hazikaron", "Yom Ha'atzmaut", "Yom Yerushalayim", "Lag B'Omer", "Shushan Purim Katan", "Isru Chag"]</code>.
346         * 
347         * @return the array of transliterated <em>Yomim Tovim</em> (holidays). 
348         * @see #setTransliteratedMonthList(String[])
349         * @see #formatYomTov(JewishCalendar)
350         * @see #isHebrewFormat()
351         */
352        public String[] getTransliteratedHolidayList() {
353                return transliteratedHolidays;
354        }
355
356        /**
357         * Sets the array of <em>Yomim Tovim</em> (holidays) transliterated into Latin chars. This is used by the
358         * {@link #formatYomTov(JewishCalendar)} when formatting the <em>Yom Tov</em> String. The list uses the following order and uses
359         * the spelling as follows. 
360         * <code>["Erev Pesach", "Pesach", "Chol Hamoed Pesach", "Pesach Sheni", "Erev Shavuos", "Shavuos", "Seventeenth of Tammuz",
361         * "Tishah B'Av", "Tu B'Av", "Erev Rosh Hashana", "Rosh Hashana", "Fast of Gedalyah", "Erev Yom Kippur", "Yom Kippur", "Erev
362         * Succos", "Succos", "Chol Hamoed Succos", "Hoshana Rabbah", "Shemini Atzeres", "Simchas Torah", "Erev Chanukah", "Chanukah",
363         * "Tenth of Teves", "Tu B'Shvat", "Fast of Esther", "Purim", "Shushan Purim", "Purim Katan", "Rosh Chodesh", "Yom HaShoah",
364         * "Yom Hazikaron", "Yom Ha'atzmaut", "Yom Yerushalayim", "Lag B'Omer", "Shushan Purim Katan", "Isru Chag"]</code>.
365         * 
366         * @param transliteratedHolidays the transliteratedHolidays to set. Ensure that the sequence exactly matches the list returned
367         *         by the default.
368         */
369        public void setTransliteratedHolidayList(String[] transliteratedHolidays) {
370                this.transliteratedHolidays = transliteratedHolidays;
371        }
372
373        /**
374         * Hebrew <em>Yomim Tovim</em> (holidays) array in the following format.<br>
375         * <code>&rlm;["ערב פסח", "פסח", "חול המועד פסח", "פסח שני", "ערב שבועות", "שבועות", "שבעה עשר בתמוז", "תשעה באב", "ט״ו באב", "ערב ראש השנה", "ראש השנה",
376         * "צום גדליה", "ערב יום כיפור", "יום כיפור", "ערב סוכות", "סוכות", "חול המועד סוכות", "הושענא רבה", "שמיני עצרת", "שמחת תורה", "ערב חנוכה", "חנוכה", "עשרה בטבת",
377         * "ט״ו בשבט", "תענית אסתר", "פורים", "שושן פורים", "פורים קטן", "ראש חודש", "יום השואה", "יום הזיכרון", "יום העצמאות", "יום ירושלים", "ל״ג בעומר", "שושן פורים קטן"]</code>
378         */
379        private final String[] hebrewHolidays = { "ערב פסח", "פסח",
380                        "חול המועד פסח",
381                        "פסח שני", "ערב שבועות",
382                        "שבועות",
383                        "שבעה עשר בתמוז",
384                        "תשעה באב", "ט״ו באב",
385                        "ערב ראש השנה",
386                        "ראש השנה", "צום גדליה",
387                        "ערב יום כיפור",
388                        "יום כיפור", "ערב סוכות",
389                        "סוכות",
390                        "חול המועד סוכות",
391                        "הושענא רבה",
392                        "שמיני עצרת",
393                        "שמחת תורה", "ערב חנוכה",
394                        "חנוכה", "עשרה בטבת",
395                        "ט״ו בשבט", "תענית אסתר",
396                        "פורים", "שושן פורים",
397                        "פורים קטן", "ראש חודש",
398                        "יום השואה",
399                        "יום הזיכרון",
400                        "יום העצמאות",
401                        "יום ירושלים",
402                        "ל״ג בעומר",
403                        "שושן פורים קטן",
404                        "אסרו חג"};
405
406        /** The transliterated <em>tekufa</em> names.*/
407        private final String[] transliteratedTekufaNames = new String[] {"Tishrei", "Teves", "Nissan", "Tammuz"};
408        
409        /** The <em>tekufa</em> names.*/
410        private final String[] tekufaNames = new String[] {"תשרי", "טבת", "ניסן", "תמוז"};
411        
412        /**
413         * Formats the <em>Yom Tov</em> (holiday) in Hebrew or transliterated Latin characters.
414         * 
415         * @param jewishCalendar the JewishCalendar
416         * @return the formatted <em>Yom Tov</em> (holiday) or an empty String if the day is not a <em>Yom Tov</em> (holiday).
417         * @see #isHebrewFormat()
418         */
419        public String formatYomTov(JewishCalendar jewishCalendar) {
420                int index = jewishCalendar.getYomTovIndex();
421                if (index == JewishCalendar.CHANUKAH) {
422                        int dayOfChanukah = jewishCalendar.getDayOfChanukah();
423                        return hebrewFormat ? (formatHebrewNumber(dayOfChanukah) + " " + hebrewHolidays[index])
424                                        : (transliteratedHolidays[index] + " " + dayOfChanukah);
425                }
426                return index == -1 ? "" : hebrewFormat ? hebrewHolidays[index] : transliteratedHolidays[index];
427        }
428
429        /**
430         * Formats a day as Rosh Chodesh in the format of in the format of ראש חודש שבט or Rosh Chodesh Shevat. If it is not Rosh Chodesh,
431         * an empty <code>String</code> will be returned.
432         * @param jewishCalendar the JewishCalendar
433         * @return The formatted <code>String</code> in the format of ראש חודש שבט or Rosh Chodesh Shevat. If it is not Rosh Chodesh, an
434         *         empty <code>String</code> will be returned.
435         */
436        public String formatRoshChodesh(JewishCalendar jewishCalendar) {
437                if (!jewishCalendar.isRoshChodesh()) {
438                        return "";
439                }
440                int month = jewishCalendar.getJewishMonth();
441                if (jewishCalendar.getJewishDayOfMonth() == 30) {
442                        if (month < JewishCalendar.ADAR || (month == JewishCalendar.ADAR && jewishCalendar.isJewishLeapYear())) {
443                                month++;
444                        } else { // roll to Nissan
445                                month = JewishCalendar.NISSAN;
446                        }
447                }
448
449                // This method is only about formatting, so we shouldn't make any changes to the params passed in...
450                jewishCalendar = (JewishCalendar) jewishCalendar.clone();
451                jewishCalendar.setJewishMonth(month);
452                String formattedRoshChodesh = hebrewFormat ? hebrewHolidays[JewishCalendar.ROSH_CHODESH]
453                                : transliteratedHolidays[JewishCalendar.ROSH_CHODESH];
454                formattedRoshChodesh += " " + formatMonth(jewishCalendar);
455                return formattedRoshChodesh;
456        }
457
458        /**
459         * Returns if the formatter is set to use Hebrew formatting in the various formatting methods.
460         * 
461         * @return the hebrewFormat
462         * @see #setHebrewFormat(boolean)
463         * @see #format(JewishDate)
464         * @see #formatDayOfWeek(JewishDate)
465         * @see #formatMonth(JewishDate)
466         * @see #formatOmer(JewishCalendar)
467         * @see #formatYomTov(JewishCalendar)
468         */
469        public boolean isHebrewFormat() {
470                return hebrewFormat;
471        }
472
473        /**
474         * Sets the formatter to format in Hebrew in the various formatting methods.
475         * 
476         * @param hebrewFormat <code>true</code> to format in Hebrew.
477         * @see #isHebrewFormat()
478         * @see #format(JewishDate)
479         * @see #formatDayOfWeek(JewishDate)
480         * @see #formatMonth(JewishDate)
481         * @see #formatOmer(JewishCalendar)
482         * @see #formatYomTov(JewishCalendar)
483         */
484        public void setHebrewFormat(boolean hebrewFormat) {
485                this.hebrewFormat = hebrewFormat;
486        }
487
488        /**
489         * Returns the Hebrew Omer prefix. By default it is the letter ב producing בעומר, but it can be set to ל to produce לעומר (or any
490         * other prefix) using the {@link #setHebrewOmerPrefix(String)}.
491         * 
492         * @return the hebrewOmerPrefix
493         * @see #hebrewOmerPrefix
494         * @see #setHebrewOmerPrefix(String)
495         * @see #formatOmer(JewishCalendar)
496         */
497        public String getHebrewOmerPrefix() {
498                return hebrewOmerPrefix;
499        }
500
501        /**
502         * Method to set the Hebrew Omer prefix. By default it is the letter ב producing בעומר, but it can be set to ל to format it לעומר
503         * (or any other prefix).
504         * @param hebrewOmerPrefix the hebrewOmerPrefix to set. You can set it to ל to produce to לעומר.
505         * @see #hebrewOmerPrefix
506         * @see #getHebrewOmerPrefix()
507         * @see #formatOmer(JewishCalendar)
508         */
509        public void setHebrewOmerPrefix(String hebrewOmerPrefix) {
510                this.hebrewOmerPrefix = hebrewOmerPrefix;
511        }
512        
513        /**
514         * Returns the Hebrew array of months in the order of<br><code>&rlm;["ניסן", "אייר", "סיון", "תמוז", "אב", "אלול", "תשרי", "חשון", "כסלו", "טבת", "שבט", "אדר", 
515         * "אדר ב", "אדר א"]</code>. This list has a length of 14 starting with "ניסן" and ending with 3 variations of Adar -
516         * "אדר", "אדר ב", "אדר א".
517         * @return the array of Hebrew months.
518         * @see #hebrewMonths
519         * @see #setHebrewMonthList(String[])
520         */
521        public String[] getHebrewMonthList() {
522                return hebrewMonths;
523        }
524        
525        /**
526         * Setter method to allow overriding of the default list of Hebrew month names. This allows changing things such as the default
527         * month name of חשון to מרחשון, etc. This list expects a length of 14 starting with "ניסן" and ending with 3 variations of Adar -
528         * "אדר", "אדר ב", "אדר".
529         * 
530         * @param hebrewMonths the array of Hebrew months beginning in "ניסן" and ending in "אדר", "אדר ב", "אדר א"
531         * @see #getHebrewMonthList()
532         */
533        public void setHebrewMonthList(String[] hebrewMonths) {
534                if(hebrewMonths.length !=14) {
535                        throw new IllegalArgumentException("The Hebrew month array must have a length of 14.");
536                }
537                this.hebrewMonths = hebrewMonths;
538        }
539
540        /**
541         * Returns the array of months transliterated into Latin chars. The default list of months uses Ashkenazi pronunciation in
542         * typical American English spelling. This list has a length of 14 with 3 variations for Adar - "Adar", "Adar II", "Adar I".
543         * The array of months beginn in Nissan and end in "Adar", "Adar II", "Adar I". The default list is
544         * <code>["Nissan", "Iyar", "Sivan", "Tammuz", "Av", "Elul", "Tishrei", "Cheshvan", "Kislev", "Teves", "Shevat", "Adar",
545         * "Adar II", "Adar I"]</code>.
546         * 
547         * @return the array of 14 month names beginning in Nissan and ending in "Adar", "Adar II", "Adar I".
548         * @see #setTransliteratedMonthList(String[])
549         */
550        public String[] getTransliteratedMonthList() {
551                return transliteratedMonths;
552        }
553
554        /**
555         * Setter method to allow overriding of the default list of months transliterated into Latin chars. The default list uses
556         * Ashkenazi American English transliteration. The array of 14 transliterated month names begin in "Nissan" and end in the
557         * 3 Adar variations - "Adar", "Adar II", "Adar I". The default list is
558         * <code>["Nissan", "Iyar", "Sivan", "Tammuz", "Av", "Elul", "Tishrei", "Cheshvan", "Kislev", "Teves", "Shevat", "Adar",
559         * "Adar II", "Adar I"]</code>.
560         * 
561         * @param transliteratedMonths the array of 14 month names beginning in Nissan and ending in "Adar", "Adar II", "Adar I".
562         * @see #getTransliteratedMonthList()
563         */
564        public void setTransliteratedMonthList(String[] transliteratedMonths) {
565                if(transliteratedHolidays.length !=14) {
566                        throw new IllegalArgumentException("The transliterated month array must have a length of 14.");
567                }
568                this.transliteratedMonths = transliteratedMonths;
569        }
570
571        /**
572         * List of Hebrew months. The list has* a length of 14 starting with "ניסן" and ending with the 3 variations of Adar -
573         * "אדר", "אדר ב", "אדר א".
574         * 
575         * @see #getHebrewMonthList()
576         * @see #setHebrewMonthList(String[])
577         * @see #formatMonth(JewishDate)
578         */
579        private String[] hebrewMonths = { "ניסן", "אייר",
580                        "סיון", "תמוז", "אב", "אלול",
581                        "תשרי", "חשון", "כסלו",
582                        "טבת", "שבט", "אדר", "אדר ב",
583                        "אדר א" };
584
585        /**
586         * Unicode list of Hebrew days of week in the format of <code>&rlm;["ראשון", "שני", "שלישי", "רביעי", "חמישי", "ששי", "שבת"]</code>
587         */
588        private static final String[] hebrewDaysOfWeek = { "ראשון", "שני", "שלישי", "רביעי", "חמישי", "ששי", "שבת" };
589
590        /**
591         * Formats the day of week. If {@link #isHebrewFormat() Hebrew formatting} is set, it will display in the format ראשון etc. If
592         * Hebrew formatting is not in use it will return it in the format of Sunday etc. There are various formatting options that will
593         * affect the output.
594         * 
595         * @param jewishDate the JewishDate Object
596         * @return the formatted day of week
597         * @see #isHebrewFormat()
598         * @see #isLongWeekFormat()
599         */
600        public String formatDayOfWeek(JewishDate jewishDate) {
601                if (hebrewFormat) {
602                        if (isLongWeekFormat()) {
603                                return hebrewDaysOfWeek[jewishDate.getDayOfWeek() - 1];
604                        } else {
605                                if (jewishDate.getDayOfWeek() == 7) {
606                                        return formatHebrewNumber(300);
607                                } else {
608                                        return formatHebrewNumber(jewishDate.getDayOfWeek());
609                                }
610                        }
611                } else {
612                        if (jewishDate.getDayOfWeek() == 7) {
613                                if (isLongWeekFormat()) {
614                                        return getTransliteratedShabbosDayOfWeek();
615                                } else {
616                                        return getTransliteratedShabbosDayOfWeek().substring(0,3);
617                                }
618                        } else {
619                                return weekFormat.format(jewishDate.getLocalDate());
620                        }
621                }
622        }
623
624        /**
625         * Returns whether the class is set to use the Geresh ׳ and Gershayim ״ in formatting Hebrew dates and numbers. When true and
626         * output would look like כ״א שבט תש״כ (or כ״א שבט תש״ך). When set to false, this output would display as כא שבט תשכ.
627         * 
628         * @return true if set to use the Geresh ׳ and Gershayim ״ in formatting Hebrew dates and numbers.
629         */
630        public boolean isUseGershGershayim() {
631                return useGershGershayim;
632        }
633
634        /**
635         * Sets whether to use the Geresh ׳ and Gershayim ״ in formatting Hebrew dates and numbers. The default value is true and output
636         * would look like כ״א שבט תש״כ (or כ״א שבט תש״ך). When set to false, this output would display as כא שבט תשכ (or כא שבט תשך).
637         * Single digit days or month or years such as כ׳ שבט ו׳ אלפים show the use of the Geresh.
638         * 
639         * @param useGershGershayim set this to false to omit the Geresh ׳ and Gershayim ״ in formatting
640         */
641        public void setUseGershGershayim(boolean useGershGershayim) {
642                this.useGershGershayim = useGershGershayim;
643        }
644
645        /**
646         * Returns whether the class is set to use the מנצפ״ך letters when formatting years ending in 20, 40, 50, 80 and 90 to produce
647         * תש״פ if false or תש״ף if true. Traditionally non-final form letters are used, so the year 5780 would be formatted as תש״פ if
648         * the default false is used here. If this returns true, the format תש״ף would be used.
649         * 
650         * @return true if set to use final form letters when formatting Hebrew years. The default value is false.
651         */
652        public boolean isUseFinalFormLetters() {
653                return useFinalFormLetters;
654        }
655
656        /**
657         * When formatting a Hebrew Year, traditionally years ending in 20, 40, 50, 80 and 90 are formatted using non-final form letters
658         * for example תש״פ for the year 5780. Setting this to true (the default is false) will use the final form letters for מנצפ״ך and
659         * will format the year 5780 as תש״ף.
660         * 
661         * @param useFinalFormLetters Set this to true to use final form letters when formatting Hebrew years.
662         */
663        public void setUseFinalFormLetters(boolean useFinalFormLetters) {
664                this.useFinalFormLetters = useFinalFormLetters;
665        }
666
667        /**
668         * Returns whether the class is set to use the thousands digit when formatting a Hebrew Year. Traditionally the thousands digit
669         * is omitted and output for a year such as 5729 (1969 Gregorian) would be calculated as 729 and formatted as תשכ״ט. When set to
670         * true the long format year such,  as ה׳ תשכ״ט for 5729/1969 is returned.
671         * 
672         * @return true if set to use the thousands digit when formatting Hebrew dates and numbers.
673         */
674        public boolean isUseLongHebrewYears() {
675                return useLonghebrewYears;
676        }
677
678        /**
679         * When formatting a Hebrew Year, traditionally the thousands digit is omitted and output for a year such as 5729 (1969 
680         * Gregorian) would be calculated for 729 and format as תשכ״ט. This method allows setting this to true to return the long format
681         * year such as ה׳ תשכ״ט for 5729/1969.
682         * 
683         * @param useLongHebrewYears Set this to true to use the long formatting
684         */
685        public void setUseLongHebrewYears(boolean useLongHebrewYears) {
686                this.useLonghebrewYears = useLongHebrewYears;
687        }
688        /**
689         * Formats the Jewish date. If the formatter is set to Hebrew, it will format in the form, "day Month year" for example
690         * כ״א שבט תשכ״ט, and the format "21 Shevat, 5729" if not.
691         * 
692         * @param jewishDate the JewishDate to be formatted
693         * @return the formatted date. If the formatter is set to Hebrew, it will format in the form, "day Month year" as כ״א שבט תשכ״ט,
694         *         and "21 Shevat, 5729" if not.
695         */
696        public String format(JewishDate jewishDate) {
697                if (isHebrewFormat()) {
698                        return formatHebrewNumber(jewishDate.getJewishDayOfMonth()) + " " + formatMonth(jewishDate) + " "
699                                        + formatHebrewNumber(jewishDate.getJewishYear());
700                } else {
701                        return jewishDate.getJewishDayOfMonth() + " " + formatMonth(jewishDate) + ", " + jewishDate.getJewishYear();
702                }
703        }
704
705        /**
706         * Returns a string of the current Hebrew month formatted as "אדר ב׳" or "Adar II" depending on how {@link #isHebrewFormat()}
707         * is set.
708         * 
709         * @param jewishDate the JewishDate to format
710         * @return the formatted month name formatted as "אדר ב׳" or "Adar II" depending on how {@link #isHebrewFormat()} is set.
711         * @see #isHebrewFormat()
712         * @see #setHebrewFormat(boolean)
713         * @see #getTransliteratedMonthList()
714         * @see #setTransliteratedMonthList(String[])
715         */
716        public String formatMonth(JewishDate jewishDate) {
717                final int month = jewishDate.getJewishMonth();
718                if (isHebrewFormat()) {
719                        if (jewishDate.isJewishLeapYear() && month == JewishDate.ADAR) {
720                                return hebrewMonths[JewishDate.ADAR_II] + (useGershGershayim ? GERESH : ""); // return Adar I, not Adar in a leap year
721                        } else if (jewishDate.isJewishLeapYear() && month == JewishDate.ADAR_II) {
722                                return hebrewMonths[JewishDate.ADAR] + (useGershGershayim ? GERESH : "");
723                        } else {
724                                return hebrewMonths[month - 1];
725                        }
726                } else {
727                        if (jewishDate.isJewishLeapYear() && month == JewishDate.ADAR) {
728                                return transliteratedMonths[JewishDate.ADAR_II]; // return Adar I, not Adar in a leap year
729                        } else {
730                                return transliteratedMonths[month - 1];
731                        }
732                }
733        }
734
735        /**
736         * Returns a String of the Omer day in the form ל״ג בעומר if Hebrew Format is set, or "Omer X" or "Lag B'Omer" if not. An empty
737         * string if there is no Omer this day.
738         * 
739         * @param jewishCalendar the JewishCalendar to be formatted
740         * @return a String of the Omer day in the form or an empty string if there is no Omer this day. The default formatting has a
741         * ב prefix that would output בעומר, but this can be set via the {@link #setHebrewOmerPrefix(String)} method to use a ל and
742         *         output ל״ג לעומר.
743         * @see #isHebrewFormat()
744         * @see #getHebrewOmerPrefix()
745         * @see #setHebrewOmerPrefix(String)
746         */
747        public String formatOmer(JewishCalendar jewishCalendar) {
748                int omer = jewishCalendar.getDayOfOmer();
749                if (omer == -1) {
750                        return "";
751                }
752                if (hebrewFormat) {
753                        return formatHebrewNumber(omer) + " " + hebrewOmerPrefix + "עומר";
754                } else {
755                        if (omer == 33) { // if Lag B'Omer
756                                return transliteratedHolidays[33];
757                        } else {
758                                return "Omer " + omer;
759                        }
760                }
761        }
762
763        /**
764         * Returns the kviah in the traditional 3 letter Hebrew format where the first letter represents the day of week of Rosh Hashana,
765         * the second letter represents the lengths of Cheshvan and Kislev ({@link JewishDate#SHELAIMIM Shelaimim} , {@link
766         * JewishDate#KESIDRAN Kesidran} or {@link JewishDate#CHASERIM Chaserim}) and the 3rd letter represents the day of week of Pesach.
767         * For example 5729 (1969) would return בשה (Rosh Hashana on Monday, Shelaimim, and Pesach on Thursday), while 5771 (2011) would
768         * return השג (Rosh Hashana on Thursday, Shelaimim, and Pesach on Tuesday).
769         * 
770         * @param jewishYear the Jewish year
771         * @return the Hebrew String such as בשה for 5729 (1969) and השג for 5771 (2011).
772         */
773        public String getFormattedKviah(int jewishYear) {
774                JewishDate jewishDate = new JewishDate(jewishYear, JewishDate.TISHREI, 1); // set date to Rosh Hashana
775                int kviah = jewishDate.getCheshvanKislevKviah();
776                int roshHashanaDayOfWeek = jewishDate.getDayOfWeek();
777                String returnValue = formatHebrewNumber(roshHashanaDayOfWeek);
778                returnValue += (kviah == JewishDate.CHASERIM ? "ח" : kviah == JewishDate.SHELAIMIM ? "ש" : "כ");
779                jewishDate.setJewishDate(jewishYear, JewishDate.NISSAN, 15); // set to Pesach of the given year
780                int pesachDayOfWeek = jewishDate.getDayOfWeek();
781                returnValue += formatHebrewNumber(pesachDayOfWeek);
782                returnValue = returnValue.replaceAll(GERESH, "");// geresh is never used in the kviah format
783                // boolean isLeapYear = JewishDate.isJewishLeapYear(jewishYear);
784                // for efficiency we can avoid the expensive recalculation of the pesach day of week by adding 1 day to Rosh
785                // Hashana for a 353-day year, 2 for a 354-day year, 3 for a 355 or 383-day year, 4 for a 384-day year and 5 for
786                // a 385-day year
787                return returnValue;
788        }
789
790        /**
791         * Formats the <a href="https://en.wikipedia.org/wiki/Daf_Yomi">Daf Yomi</a> Bavli in the format of "עירובין נ״ב" if {@link
792         * #isHebrewFormat()} is set to <code>true</code>, or the transliterated format of "Eruvin 52" if set to <code>false</code>.
793         * @param daf the Daf to be formatted.
794         * @return the formatted daf.
795         */
796        public String formatDafYomiBavli(Daf daf) {
797                if (hebrewFormat) {
798                        return daf.getMasechta() + " " + formatHebrewNumber(daf.getDaf());
799                } else {
800                        return daf.getMasechtaTransliterated() + " " + daf.getDaf();
801                }
802        }
803        
804        /**
805         * Formats the <a href="https://en.wikipedia.org/wiki/Jerusalem_Talmud#Daf_Yomi_Yerushalmi">Daf Yomi Yerushalmi</a> in the format
806         * of "עירובין נ״ב" in {@link #isHebrewFormat()} is set to <code>true</code>, or the transliterated format of "Eruvin 52" if set to
807         * <code>false</code>.
808         * 
809         * @param daf the Daf to be formatted.
810         * @return the formatted daf.
811         */
812        public String formatDafYomiYerushalmi(Daf daf) {
813                if (daf == null) {
814                        if (hebrewFormat) {
815                                return Daf.getYerushalmiMasechtos()[39];
816                        } else {
817                                return Daf.getYerushalmiMasechtosTransliterated()[39];
818                        }
819                }
820                if (hebrewFormat) {                     
821                        return daf.getYerushalmiMasechta() + " " + formatHebrewNumber(daf.getDaf());
822                } else {
823                        return daf.getYerushalmiMasechtaTransliterated() + " " + daf.getDaf();
824                }
825        }
826
827        /**
828         * Returns a Hebrew formatted string of a number. The method can calculate from 0 to 9999.
829         * <ul>
830         * <li>Single digit numbers such as 3, 30 and 100 will be returned with a ׳ (<a
831         * href="http://en.wikipedia.org/wiki/Geresh">Geresh</a>) appended as at the end. For example ג׳, ל׳ and ק׳</li>
832         * <li>multi digit numbers such as 21 and 769 will be returned with a ״ (<a
833         * href="http://en.wikipedia.org/wiki/Gershayim">Gershayim</a>) between the second to last and last letters. For
834         * example כ״א, תשכ״ט</li>
835         * <li>15 and 16 will be returned as ט״ו and ט״ז</li>
836         * <li>Single digit numbers (years assumed) such as 6000 (%1000=0) will be returned as ו׳אלפים</li>
837         * <li>0 will return אפס</li>
838         * </ul>
839         * 
840         * @param number the number to be formatted. It will throw an IllegalArgumentException if the number is &lt; 0 or &gt; 9999.
841         * @return the Hebrew formatted number such as תשכ״ט
842         * @see #isUseFinalFormLetters()
843         * @see #isUseGershGershayim()
844         * @see #isHebrewFormat()
845         * 
846         */
847        public String formatHebrewNumber(int number) {
848                if (number < 0) {
849                        throw new IllegalArgumentException("negative numbers can't be formatted");
850                } else if (number > 9999) {
851                        throw new IllegalArgumentException("numbers > 9999 can't be formatted");
852                }
853
854                String ALAFIM = "אלפים";
855                String EFES = "אפס";
856
857                String[] jHundreds = new String[] { "", "ק", "ר", "ש", "ת", "תק", "תר", "תש", "תת", "תתק" };
858                String[] jTens = new String[] { "", "י", "כ", "ל", "מ", "נ", "ס", "ע", "פ", "צ" };
859                String[] jTenEnds = new String[] { "", "י", "ך", "ל", "ם", "ן", "ס", "ע", "ף", "ץ" };
860                String[] tavTaz = new String[] { "טו", "טז" };
861                String[] jOnes = new String[] { "", "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט" };
862
863                if (number == 0) { // do we really need this? Should it be applicable to a date?
864                        return EFES;
865                }
866                int shortNumber = number % 1000; // discard thousands
867                // next check for all possible single Hebrew digit years
868                boolean singleDigitNumber = (shortNumber < 11 || (shortNumber < 100 && shortNumber % 10 == 0) ||
869                                (shortNumber <= 400 && shortNumber % 100 == 0));
870                int thousands = number / 1000; // get # thousands
871                StringBuilder sb = new StringBuilder();
872                // append thousands to String
873                if (number % 1000 == 0) { // in year is 5000, 4000 etc
874                        sb.append(jOnes[thousands]);
875                        if (isUseGershGershayim()) {
876                                sb.append(GERESH);
877                        }
878                        sb.append(" ");
879                        sb.append(ALAFIM); // add # of thousands plus the word "thousand" (override alafim boolean)
880                        return sb.toString();
881                } else if (useLonghebrewYears && number >= 1000) { // if alafim boolean display thousands
882                        sb.append(jOnes[thousands]);
883                        if (isUseGershGershayim()) {
884                                sb.append(GERESH); // append thousands quote
885                        }
886                        sb.append(" ");
887                }
888                number = number % 1000; // remove 1000s
889                int hundreds = number / 100; // # of hundreds
890                sb.append(jHundreds[hundreds]); // add hundreds to String
891                number = number % 100; // remove 100s
892                if (number == 15) { // special case 15
893                        sb.append(tavTaz[0]);
894                } else if (number == 16) { // special case 16
895                        sb.append(tavTaz[1]);
896                } else {
897                        int tens = number / 10;
898                        if (number % 10 == 0) { // if evenly divisible by 10
899                                if (!singleDigitNumber) {
900                                        if (isUseFinalFormLetters()) {
901                                                sb.append(jTenEnds[tens]); // years like 5780 will end with a final form ף
902                                        } else {
903                                                sb.append(jTens[tens]); // years like 5780 will end with a regular פ
904                                        }
905                                } else {
906                                        sb.append(jTens[tens]); // standard letters so years like 5050 will end with a regular nun
907                                }
908                        } else {
909                                sb.append(jTens[tens]);
910                                number = number % 10;
911                                sb.append(jOnes[number]);
912                        }
913                }
914                if (isUseGershGershayim()) {
915                        if (singleDigitNumber) {
916                                sb.append(GERESH); // append single quote
917                        } else { // append double quote before last digit
918                                sb.insert(sb.length() - 1, GERSHAYIM);
919                        }
920                }
921                return sb.toString();
922        }       
923
924        /**
925         * Returns the map of transliterated parshiyos used by this formatter. This list using the default <em>Ashkenazi</em>
926         * pronunciation. This list can be overridden (for <em>Sephardi</em> English transliteration for example) by setting the
927         * {@link #setTransliteratedParshiosList(EnumMap)}. The list includes double and special <em>parshiyos</em> in the following
928         * order and spelling "<em>Bereshis, Noach, Lech Lecha, Vayera, Chayei Sara, Toldos, Vayetzei, Vayishlach, Vayeshev, Miketz,
929         * Vayigash, Vayechi, Shemos, Vaera, Bo, Beshalach, Yisro, Mishpatim, Terumah, Tetzaveh, Ki Sisa, Vayakhel, Pekudei, Vayikra,
930         * Tzav, Shmini, Tazria, Metzora, Achrei Mos, Kedoshim, Emor, Behar, Bechukosai, Bamidbar, Nasso, Beha'aloscha, Sh'lach,
931         * Korach, Chukas, Balak, Pinchas, Matos, Masei, Devarim, Vaeschanan, Eikev, Re'eh, Shoftim, Ki Seitzei, Ki Savo, Nitzavim,
932         * Vayeilech, Ha'Azinu, Vezos Habracha, Vayakhel Pekudei, Tazria Metzora, Achrei Mos Kedoshim, Behar Bechukosai, Chukas Balak,
933         * Matos Masei, Nitzavim Vayeilech, Shekalim, Zachor, Parah, Hachodesh,Shuva, Shira, Hagadol, Chazon, Nachamu</em>".
934         * 
935         * @return the map of transliterated Parshios
936         * @see #setTransliteratedParshiosList(EnumMap)
937         * @see #formatParsha(JewishCalendar)
938         * @see #formatParsha(JewishCalendar.Parsha)
939         */
940        public EnumMap<JewishCalendar.Parsha, String> getTransliteratedParshiosList() {
941                return transliteratedParshaMap;
942        }
943
944        /**
945         * Setter method to allow overriding of the default list of parshiyos transliterated into Latin chars. The
946         * default uses Ashkenazi American English transliteration.
947         * 
948         * @param transliteratedParshaMap the transliterated Parshios as an EnumMap to set
949         * @see #getTransliteratedParshiosList() for information on the format.
950         */
951        public void setTransliteratedParshiosList(EnumMap<JewishCalendar.Parsha, String> transliteratedParshaMap) {
952                this.transliteratedParshaMap = transliteratedParshaMap;
953        }
954        
955        /**
956         * Returns a String with the name of the current parsha(ios). This method gets the current <em>parsha</em> by calling {@link
957         * JewishCalendar#getParshah()} that does not return a <em>parsha</em> for any non-<em>Shabbos</em> or a <em>Shabbos</em> that
958         * occurs on a <em>Yom Tov</em>, and will return an empty <code>String</code> in those cases. If the class {@link
959         * #isHebrewFormat() is set to format in Hebrew} it will return a <code>String</code> of the current parsha(ios) in Hebrew for
960         * example בראשית or נצבים וילך for a double parsha, or an empty string will be returned if there is not parsha that week. If not set
961         * to Hebrew, it returns a string of the parsha(ios) transliterated into Latin chars. The default uses Ashkenazi pronunciation
962         * in typical American English spelling, for example Bereshis, Nitzavim Vayeilech for a double parsha, An empty string if there
963         * are none.
964         * 
965         * @param jewishCalendar the JewishCalendar Object
966         * @return today's parsha(ios) in Hebrew for example, if the formatter is set to format in Hebrew, returns a string of the current
967         *         parsha(ios) in Hebrew for example בראשית or נצבים וילך, for a double parsha or an empty <code>String</code> if there is
968         *         no parsha that week. If not set to Hebrew, it returns a string of the parsha(ios) transliterated into Latin chars. The
969         *         default uses Ashkenazi pronunciation in typical American English spelling, for example Bereshis, Nitzavim Vayeilech for
970         *         a double parsha, or an empty <code>String</code> if there are none.
971         * @see #formatParsha(JewishCalendar)
972         * @see #isHebrewFormat()
973         * @see JewishCalendar#getParshah()
974         */
975        public String formatParsha(JewishCalendar jewishCalendar) {
976                JewishCalendar.Parsha parsha =  jewishCalendar.getParshah();
977                return formatParsha(parsha);
978        }
979
980        /**
981         * Returns a <code>String</code> with the name of the current parsha(ios). This method overloads {@link
982         * #formatParsha(JewishCalendar)} and unlike that method, it will format the <em>parsha</em> passed to this method regardless of
983         * the day of week. This is the way to format a <em>parsha</em> retrieved from calling
984         * {@link JewishCalendar#getUpcomingParshah()}.
985         *
986         * @param parsha a JewishCalendar.Parsha object
987         * @return today's parsha(ios) in Hebrew for example, if the formatter is set to format in Hebrew, returns a <code>String</code>
988         *         of the current parsha(ios) in Hebrew for example בראשית or נצבים וילך for a double parsha, or an empty <code>String</code>
989         *         if there is no parsha that week. If not set to Hebrew, it returns a string of the parsha(ios) transliterated into
990         *         Latin chars. The default uses Ashkenazi pronunciation in typical American English spelling, for example Bereshis,  or
991         *         Nitzavim Vayeilech for a double parsha, or an empty string if there are none.
992         * @see #formatParsha(JewishCalendar)
993         * @see JewishCalendar#getUpcomingParshah()
994         */
995        public String formatParsha(JewishCalendar.Parsha parsha) {
996                return hebrewFormat ? hebrewParshaMap.get(parsha) : transliteratedParshaMap.get(parsha);
997        }
998        
999        /**
1000         * Returns a String with the name of the current special parsha of Shekalim, Zachor, Parah or Hachodesh or an empty String for a
1001         * non-special parsha. If the formatter is set to format in Hebrew, it returns a string of the current special parsha in Hebrew,
1002         * for example שקלים, זכור, פרה or החדש, or an empty <code>string</code> if the date is not a special parsha. If not set to Hebrew,
1003         * it returns a string of the special parsha transliterated into Latin chars. The default uses Ashkenazi pronunciation in typical
1004         * American English spelling Shekalim, Zachor, Parah or Hachodesh.
1005         * 
1006         * @param jewishCalendar the JewishCalendar Object
1007         * @return today's special parsha. If the formatter is set to format in Hebrew, returns a string of the current special parsha in
1008         *         Hebrew for in the format of שקלים, זכור, פרה or החדש or an empty string if there are none. If not set to Hebrew, it
1009         *         returns a string of the special parsha transliterated into Latin chars. The default uses Ashkenazi pronunciation in
1010         *         typical American English spelling of Shekalim, Zachor, Parah or Hachodesh. An empty string if there are none.
1011         */
1012        public String formatSpecialParsha(JewishCalendar jewishCalendar) {
1013                JewishCalendar.Parsha specialParsha =  jewishCalendar.getSpecialShabbos();
1014                return hebrewFormat ? hebrewParshaMap.get(specialParsha) : transliteratedParshaMap.get(specialParsha);
1015        }
1016        
1017        /**
1018         * Returns a the formatted <em>tekufa</em> name if it is the day of the <em>tekufa</em> event, or an empty {@code String} if it
1019         * is not.
1020         * @param jewishCalendar the {@code JewishCalendar} to format the <em>tekufa</em> name for.
1021         * @return a {@code String} with the name of the upcoming tekufa/season, in the format of "תקופת תשרי" if {@link #isHebrewFormat()}
1022         *         is set to {@code true}, or "Tekufas Tishrei" if set to {@code false} or an empty string on a day without a
1023         *         <em>tekufa</em> event.
1024         */
1025        public String formatTekufaName(JewishCalendar jewishCalendar) {
1026                double INITIAL_TEKUFA_OFFSET = 12.625;  // the number of days Tekufas Tishrei occurs before JEWISH_EPOCH
1027                double days = JewishDate.getJewishCalendarElapsedDays(jewishCalendar.getJewishYear()) + jewishCalendar.getDaysSinceStartOfJewishYear() + INITIAL_TEKUFA_OFFSET - 1;  // total days since first Tekufas Tishrei event
1028
1029                double solarDaysElapsed = days % 365.25;  // total days elapsed since start of solar year
1030                int currentTekufaNumber = (int) (solarDaysElapsed / 91.3125);  // the current quarter of the solar year
1031                double tekufaDaysElapsed = solarDaysElapsed % 91.3125;  // the number of days that have passed since a tekufa event
1032                if (tekufaDaysElapsed > 0 && tekufaDaysElapsed <= 1) {  // if the tekufa happens in the upcoming 24 hours
1033                        return isHebrewFormat() ? "תקופת " + tekufaNames[currentTekufaNumber] : "Tekufas " + transliteratedTekufaNames[currentTekufaNumber];//0 for Tishrei, 1 for Tevet, 2, for Nissan, 3 for Tammuz
1034                } else {
1035                        return "";
1036                }
1037        }
1038}