001    /*
002     * Zmanim Java API
003     * Copyright (C) 2004-2007 Eliyahu Hershfeld
004     * 
005     * This program is free software; you can redistribute it and/or modify it under the terms of the
006     * GNU General Public License as published by the Free Software Foundation; either version 2 of the
007     * License, or (at your option) any later version.
008     * 
009     * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
010     * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011     * General Public License for more details.
012     * 
013     * You should have received a copy of the GNU General Public License along with this program; if
014     * not, write to the Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA
015     * 02111-1307, USA or connect to: http://www.fsf.org/copyleft/gpl.html
016     */
017    package net.sourceforge.zmanim.util;
018    
019    import java.text.DecimalFormat;
020    import java.util.Date;
021    import java.util.Calendar;
022    import java.text.SimpleDateFormat;
023    
024    /**
025     * A class used to format non {@link java.util.Date} times generated by the
026     * Zmanim package. For example the
027     * {@link net.sourceforge.zmanim.AstronomicalCalendar#getTemporalHour()} returns
028     * the length of the hour in milliseconds. This class can format this time.
029     * 
030     * @author © Eliyahu Hershfeld 2004 - 2007
031     * @version 0.9.0
032     */
033    public class ZmanimFormatter {
034            private boolean prependZeroHours;
035    
036            private boolean useSeconds;
037    
038            private boolean useMillis;
039    
040            boolean useDecimal;
041    
042            private static DecimalFormat minuteSecondNF = new DecimalFormat("00");;
043    
044            private DecimalFormat hourNF;
045    
046            private static DecimalFormat milliNF = new DecimalFormat("000");
047    
048            private SimpleDateFormat dateFormat;
049    
050            // private DecimalFormat decimalNF;
051    
052            /**
053             * Format using hours, minutes, seconds and milliseconds using the xsd:time
054             * format. This format will return 00.00.00.0 when formatting 0.
055             */
056            public static final int SEXAGESIMAL_XSD_FORMAT = 0;
057    
058            private int timeFormat = SEXAGESIMAL_XSD_FORMAT;
059    
060            /**
061             * Format using standard decimal format with 5 positions after the decimal.
062             */
063            public static final int DECIMAL_FORMAT = 1;
064    
065            /** Format using hours and minutes. */
066            public static final int SEXAGESIMAL_FORMAT = 2;
067    
068            /** Format using hours, minutes and seconds. */
069            public static final int SEXAGESIMAL_SECONDS_FORMAT = 3;
070    
071            /** Format using hours, minutes, seconds and milliseconds. */
072            public static final int SEXAGESIMAL_MILLIS_FORMAT = 4;
073    
074            /**
075             * Format using the XSD Duration format. This is in the format of
076             * PT1H6M7.869S (P for period (duration), T for time, H, M and S indicate
077             * hours, minutes and seconds.
078             */
079            public static final int XSD_DURATION_FORMAT = 5;
080    
081            public ZmanimFormatter() {
082                    this(0, new SimpleDateFormat("h:mm:ss"));
083            }
084    
085            /**
086             * ZmanimFormatter constructor using a formatter
087             * 
088             * @param format
089             *            int The formatting style to use. Using
090             *            ZmanimFormatter.SEXAGESIMAL_SECONDS_FORMAT will format the
091             *            time time of 90*60*1000 + 1 as 1:30:00
092             */
093            public ZmanimFormatter(int format, SimpleDateFormat dateFormat) {
094                    String hourFormat = "0";
095                    if (prependZeroHours) {
096                            hourFormat = "00";
097                    }
098                    hourNF = new DecimalFormat(hourFormat);
099                    // decimalNF = new DecimalFormat("0.0####");
100                    setTimeFormat(format);
101                    this.setDateFormat(dateFormat);
102            }
103    
104            /**
105             * Sets the format to use for formatting.
106             * 
107             * @param format
108             *            int the format constant to use.
109             */
110            public void setTimeFormat(int format) {
111                    timeFormat = format;
112                    switch (format) {
113                    case SEXAGESIMAL_XSD_FORMAT:
114                            setSettings(true, true, true);
115                            break;
116                    case SEXAGESIMAL_FORMAT:
117                            setSettings(false, false, false);
118                            break;
119                    case SEXAGESIMAL_SECONDS_FORMAT:
120                            setSettings(false, true, false);
121                            break;
122                    case SEXAGESIMAL_MILLIS_FORMAT:
123                            setSettings(false, true, true);
124                            break;
125                    case DECIMAL_FORMAT:
126                    default:
127                            useDecimal = true;
128                    }
129            }
130    
131            public void setDateFormat(SimpleDateFormat sdf) {
132                    dateFormat = sdf;
133            }
134    
135            public SimpleDateFormat getDateFormat() {
136                    return dateFormat;
137            }
138    
139            private void setSettings(boolean prependZeroHours, boolean useSeconds,
140                            boolean useMillis) {
141                    this.prependZeroHours = prependZeroHours;
142                    this.useSeconds = useSeconds;
143                    this.useMillis = useMillis;
144            }
145    
146            /**
147             * A method that formats milliseconds into a time format.
148             * 
149             * @param milliseconds
150             *            The time in milliseconds.
151             * @return String The formatted <code>String</code>
152             */
153            public String format(double milliseconds) {
154                    return format((int) milliseconds);
155            }
156    
157            /**
158             * A method that formats milliseconds into a time format.
159             * 
160             * @param millis
161             *            The time in milliseconds.
162             * @return String The formatted <code>String</code>
163             */
164            public String format(int millis) {
165                    return format(new Time(millis));
166            }
167    
168            /**
169             * A method that formats {@link Time}objects.
170             * 
171             * @param time
172             *            The time <code>Object</code> to be formatted.
173             * @return String The formatted <code>String</code>
174             */
175            public String format(Time time) {
176                    if (timeFormat == XSD_DURATION_FORMAT) {
177                            return formatXSDDurationTime(time);
178                    }
179                    StringBuffer sb = new StringBuffer();
180                    sb.append(hourNF.format(time.getHours()));
181                    sb.append(":");
182                    sb.append(minuteSecondNF.format(time.getMinutes()));
183                    if (useSeconds) {
184                            sb.append(":");
185                            sb.append(minuteSecondNF.format(time.getSeconds()));
186                    }
187                    if (useMillis) {
188                            sb.append(".");
189                            sb.append(milliNF.format(time.getMilliseconds()));
190                    }
191                    return sb.toString();
192            }
193    
194            /**
195             * Formats a date using this classe's {@link #getDateFormat() date format}.
196             * 
197             * @param dateTime
198             *            the date to format
199             * @param calendar
200             *            the {@link java.util.Calendar Calendar} used to help format
201             *            based on the Calendar's DST and other settings.
202             * @return the formatted String
203             */
204            public String formatDateTime(Date dateTime, Calendar calendar) {
205                    dateFormat.setCalendar(calendar);
206                    if (this.dateFormat.toPattern().equals("yyyy-MM-dd'T'HH:mm:ss")) {
207                            return getXSDateTime(dateTime, calendar);
208                    } else {
209                            return dateFormat.format(dateTime);
210                    }
211    
212            }
213    
214            /**
215             * The date:date-time function returns the current date and time as a
216             * date/time string. The date/time string that's returned must be a string
217             * in the format defined as the lexical representation of xs:dateTime in <a
218             * href="http://www.w3.org/TR/xmlschema11-2/#dateTime">[3.3.8 dateTime]</a>
219             * of <a href="http://www.w3.org/TR/xmlschema11-2/">[XML Schema 1.1 Part 2:
220             * Datatypes]</a>. The date/time format is basically CCYY-MM-DDThh:mm:ss,
221             * although implementers should consult <a
222             * href="http://www.w3.org/TR/xmlschema11-2/">[XML Schema 1.1 Part 2:
223             * Datatypes]</a> and <a href="http://www.iso.ch/markete/8601.pdf">[ISO
224             * 8601]</a> for details. The date/time string format must include a time
225             * zone, either a Z to indicate Coordinated Universal Time or a + or -
226             * followed by the difference between the difference from UTC represented as
227             * hh:mm.
228             */
229            public String getXSDateTime(Date dateTime, Calendar cal) {
230                    // Calendar cal = Calendar.getInstance();
231                    // Date datetime = cal.getTime();
232                    // Format for date and time.
233                    String xsdDateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss";
234                    /*
235                     * if (xmlDateFormat == null || xmlDateFormat.trim().equals("")) {
236                     * xmlDateFormat = xsdDateTimeFormat; }
237                     */
238                    SimpleDateFormat dateFormat = new SimpleDateFormat(xsdDateTimeFormat);
239    
240                    StringBuffer buff = new StringBuffer(dateFormat.format(dateTime));
241                    // Must also include offset from UTF.
242                    // Get the offset (in milliseconds).
243                    int offset = cal.get(Calendar.ZONE_OFFSET)
244                                    + cal.get(Calendar.DST_OFFSET);
245                    // If there is no offset, we have "Coordinated
246                    // Universal Time."
247                    if (offset == 0)
248                            buff.append("Z");
249                    else {
250                            // Convert milliseconds to hours and minutes
251                            int hrs = offset / (60 * 60 * 1000);
252                            // In a few cases, the time zone may be +/-hh:30.
253                            int min = offset % (60 * 60 * 1000);
254                            char posneg = hrs < 0 ? '-' : '+';
255                            buff.append(posneg + formatDigits(hrs) + ':' + formatDigits(min));
256                    }
257                    return buff.toString();
258            }
259    
260            /**
261             * Represent the hours and minutes with two-digit strings.
262             * 
263             * @param digits hours or minutes.
264             * @return two-digit String representation of hrs or minutes.
265             */
266            private static String formatDigits(int digits) {
267                    String dd = String.valueOf(Math.abs(digits));
268                    return dd.length() == 1 ? '0' + dd : dd;
269            }
270    
271            /**
272             * This returns the xml representation of an xsd:duration object.
273             * 
274             * @param millis the duration in milliseconds
275             * @return the xsd:duration formatted String
276             */
277            public String formatXSDDurationTime(long millis) {
278                    return formatXSDDurationTime(new Time(millis));
279            }
280    
281            /**
282             * This returns the xml representation of an xsd:duration object.
283             * 
284             * @param time the duration as a Time object 
285             * @return the xsd:duration formatted String
286             */
287            public String formatXSDDurationTime(Time time) {
288                    StringBuffer duration = new StringBuffer();
289    
290                    duration.append("P");
291    
292                    if (time.getHours() != 0 || time.getMinutes() != 0
293                                    || time.getSeconds() != 0 || time.getMilliseconds() != 0) {
294                            duration.append("T");
295    
296                            if (time.getHours() != 0)
297                                    duration.append(time.getHours() + "H");
298    
299                            if (time.getMinutes() != 0)
300                                    duration.append(time.getMinutes() + "M");
301    
302                            if (time.getSeconds() != 0 || time.getMilliseconds() != 0) {
303                                    duration.append(time.getSeconds() + "."
304                                                    + milliNF.format(time.getMilliseconds()));
305                                    duration.append("S");
306                            }
307                            if (duration.length() == 1) // zero seconds
308                                    duration.append("T0S");
309                            if (time.isNegative())
310                                    duration.insert(0, "-");
311                    }
312                    return duration.toString();
313            }
314    }