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