001: // serverDate.java
002: // -------------------------------------------
003: // (C) by Michael Peter Christen; mc@anomic.de
004: // (C) by by Bjoern 'Fuchs' Krombholz; fox.box@gmail.com
005: // first published on http://www.anomic.de
006: // Frankfurt, Germany, 2005, 2007
007: // last major change: 14.03.2005
008: //
009: // This program is free software; you can redistribute it and/or modify
010: // it under the terms of the GNU General Public License as published by
011: // the Free Software Foundation; either version 2 of the License, or
012: // (at your option) any later version.
013: //
014: // This program is distributed in the hope that it will be useful,
015: // but WITHOUT ANY WARRANTY; without even the implied warranty of
016: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017: // GNU General Public License for more details.
018: //
019: // You should have received a copy of the GNU General Public License
020: // along with this program; if not, write to the Free Software
021: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: //
023: // Using this software in any meaning (reading, learning, copying, compiling,
024: // running) means that you agree that the Author(s) is (are) not responsible
025: // for cost, loss of data or any harm that may be caused directly or indirectly
026: // by usage of this softare or this documentation. The usage of this software
027: // is on your own risk. The installation and usage (starting/running) of this
028: // software may allow other people or application to access your computer and
029: // any attached devices and is highly dependent on the configuration of the
030: // software which must be done by the user of the software; the author(s) is
031: // (are) also not responsible for proper configuration and usage of the
032: // software, even if provoked by documentation provided together with
033: // the software.
034: //
035: // Any changes to this file according to the GPL as documented in the file
036: // gpl.txt aside this file in the shipment you received can be done to the
037: // lines that follows this copyright notice here, but changes must not be
038: // done inside the copyright notive above. A re-distribution must contain
039: // the intact and unchanged copyright notice.
040: // Contributions and changes to the program code must be marked as such.
041:
042: // this class is needed to replace the slow java built-in date method by a faster version
043:
044: package de.anomic.server;
045:
046: import java.text.DateFormat;
047: import java.text.ParseException;
048: import java.text.SimpleDateFormat;
049: import java.util.Calendar;
050: import java.util.Date;
051: import java.util.Locale;
052: import java.util.NoSuchElementException;
053: import java.util.StringTokenizer;
054: import java.util.TimeZone;
055:
056: import de.anomic.server.logging.serverLog;
057:
058: public final class serverDate {
059:
060: /** minimal date format without time information (fixed width: 8) */
061: public static final String PATTERN_SHORT_DAY = "yyyyMMdd";
062: /** minimal date format (fixed width: 14) */
063: public static final String PATTERN_SHORT_SECOND = "yyyyMMddHHmmss";
064: /** minimal date format including milliseconds (fixed width: 17) */
065: public static final String PATTERN_SHORT_MILSEC = "yyyyMMddHHmmssSSS";
066:
067: /** default HTTP 1.1 header date format pattern */
068: public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
069: /** date pattern used in older HTTP implementations */
070: public static final String PATTERN_ANSIC = "EEE MMM d HH:mm:ss yyyy";
071: /** date pattern used in older HTTP implementations */
072: public static final String PATTERN_RFC1036 = "EEEE, dd-MMM-yy HH:mm:ss zzz";
073:
074: /** pattern for a W3C datetime variant of a non-localized ISO8601 date */
075: public static final String PATTERN_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
076:
077: /** predefined GMT TimeZone object */
078: private static final TimeZone TZ_GMT = TimeZone.getTimeZone("GMT");
079:
080: /** predefined non-localized Calendar object for generic GMT dates */
081: private static final Calendar CAL_GMT = Calendar.getInstance(
082: TZ_GMT, Locale.US);
083:
084: /** Date formatter/parser for minimal yyyyMMdd pattern */
085: public static final SimpleDateFormat FORMAT_SHORT_DAY = new SimpleDateFormat(
086: PATTERN_SHORT_DAY);
087: /** Date formatter/parser for minimal yyyyMMddHHmmss pattern */
088: public static final SimpleDateFormat FORMAT_SHORT_SECOND = new SimpleDateFormat(
089: PATTERN_SHORT_SECOND);
090: /** Date formatter/parser for minimal yyyyMMddHHmmssSSS pattern */
091: public static final SimpleDateFormat FORMAT_SHORT_MILSEC = new SimpleDateFormat(
092: PATTERN_SHORT_MILSEC);
093:
094: /** Date formatter/non-sloppy parser for W3C datetime (ISO8601) in GMT/UTC */
095: public static final SimpleDateFormat FORMAT_ISO8601 = new SimpleDateFormat(
096: PATTERN_ISO8601);
097:
098: /** Date formatter/parser for standard compliant HTTP header dates (RFC 1123) */
099: public static final SimpleDateFormat FORMAT_RFC1123 = new SimpleDateFormat(
100: PATTERN_RFC1123, Locale.US);
101:
102: /**
103: * RFC 2616 requires that HTTP clients are able to parse all 3 different
104: * formats. All times MUST be in GMT/UTC, but ...
105: */
106: public static SimpleDateFormat[] FORMATS_HTTP = new SimpleDateFormat[] {
107: // RFC 1123/822 (Standard) "Mon, 12 Nov 2007 10:11:12 GMT"
108: FORMAT_RFC1123,
109: // RFC 1036/850 (old) "Monday, 12-Nov-07 10:11:12 GMT"
110: new SimpleDateFormat(PATTERN_RFC1036, Locale.US),
111: // ANSI C asctime() "Mon Nov 12 10:11:12 2007"
112: new SimpleDateFormat(PATTERN_ANSIC, Locale.US), };
113:
114: /** Initialization of static formats */
115: static {
116: // 2-digit dates are automatically parsed by SimpleDateFormat,
117: // we need to detect the real year by adding 1900 or 2000 to
118: // the year value starting with 1970
119: CAL_GMT.setTimeInMillis(0);
120:
121: for (int i = 0; i < serverDate.FORMATS_HTTP.length; i++) {
122: SimpleDateFormat f = serverDate.FORMATS_HTTP[i];
123: f.setTimeZone(TZ_GMT);
124: f.set2DigitYearStart(CAL_GMT.getTime());
125: }
126:
127: // we want GMT times on the SHORT formats as well as they don't support any timezone
128: FORMAT_SHORT_DAY.setTimeZone(TZ_GMT);
129: FORMAT_SHORT_SECOND.setTimeZone(TZ_GMT);
130: FORMAT_SHORT_MILSEC.setTimeZone(TZ_GMT);
131: FORMAT_ISO8601.setTimeZone(TZ_GMT);
132: }
133:
134: /**
135: * Parse a HTTP string representation of a date into a Date instance.
136: * @param s The date String to parse.
137: * @return The Date instance if successful, <code>null</code> otherwise.
138: */
139: public static Date parseHTTPDate(String s) {
140: s = s.trim();
141: if ((s == null) || (s.length() < 9))
142: return null;
143:
144: for (int i = 0; i < FORMATS_HTTP.length; i++) {
145: try {
146: return parse(FORMATS_HTTP[i], s);
147: } catch (ParseException e) {
148: // on ParseException try again with next parser
149: }
150: }
151:
152: // the method didn't return a Date, so we got an illegal String
153: serverLog.logSevere("HTTPC-header", "DATE ERROR (Parse): " + s);
154: return null;
155: }
156:
157: /**
158: * Creates a String representation of a Date using the format defined
159: * in ISO8601/W3C datetime
160: * The result will be in UTC/GMT, e.g. "2007-12-19T10:20:30Z".
161: *
162: * @param date The Date instance to transform.
163: * @return A fixed width (20 chars) ISO8601 date String.
164: */
165: public static String formatISO8601(Date date) {
166: return format(FORMAT_ISO8601, date);
167: }
168:
169: /**
170: * Parse dates as defined in {@linkplain http://www.w3.org/TR/NOTE-datetime}.
171: * This format (also specified in ISO8601) allows different "precisions".
172: * The following lower precision versions for the complete date
173: * "2007-12-19T10:20:30.567+0300" are allowed:<br>
174: * "2007"<br>
175: * "2007-12"<br>
176: * "2007-12-19"<br>
177: * "2007-12-19T10:20+0300<br>
178: * "2007-12-19T10:20:30+0300<br>
179: * "2007-12-19T10:20:30.567+0300<br>
180: * Additionally a timezone offset of "+0000" can be substituted as "Z".<br>
181: * Parsing is done in a fuzzy way. If there is an illegal character somewhere in
182: * the String, the date parsed so far will be returned, e.g. the input
183: * "2007-12-19FOO" would return a date that represents "2007-12-19".
184: *
185: * @param s
186: * @return
187: * @throws ParseException
188: */
189: public static Date parseISO8601(String s) throws ParseException {
190: Calendar cal = Calendar.getInstance(TZ_GMT, Locale.US);
191: cal.clear();
192:
193: // split 2007-12-19T10:20:30.789+0500 into its parts
194: // correct: yyyy['-'MM['-'dd['T'HH':'MM[':'ss['.'SSS]]('Z'|ZZZZZ)]]]
195: StringTokenizer t = new StringTokenizer(s, "-T:.Z+", true);
196: if (s == null || t.countTokens() == 0)
197: throw new ParseException("parseISO8601: Cannot parse '" + s
198: + "'", 0);
199:
200: try {
201: // year
202: cal.set(Calendar.YEAR, Integer.parseInt(t.nextToken()));
203: // month
204: if (t.nextToken().equals("-")) {
205: cal.set(Calendar.MONTH,
206: Integer.parseInt(t.nextToken()) - 1);
207: } else {
208: return cal.getTime();
209: }
210: // day
211: if (t.nextToken().equals("-")) {
212: cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(t
213: .nextToken()));
214: } else {
215: return cal.getTime();
216: }
217: // The standard says: if there is an hour there has to be a minute and a
218: // timezone token, too.
219: // hour
220: if (t.nextToken().equals("T")) {
221: int hour = Integer.parseInt(t.nextToken());
222: // no error, got hours
223: int min = 0;
224: int sec = 0;
225: int msec = 0;
226: if (t.nextToken().equals(":")) {
227: min = Integer.parseInt(t.nextToken());
228: // no error, got minutes
229: // need TZ or seconds
230: String token = t.nextToken();
231: if (token.equals(":")) {
232: sec = Integer.parseInt(t.nextToken());
233: // need millisecs or TZ
234: token = t.nextToken();
235: if (token.equals(".")) {
236: msec = Integer.parseInt(t.nextToken());
237: // need TZ
238: token = t.nextToken();
239: }
240: }
241:
242: // check for TZ data
243: int offset;
244: if (token.equals("Z")) {
245: offset = 0;
246: } else {
247: int sign = 0;
248: if (token.equals("+")) {
249: sign = 1;
250: } else if (token.equals("-")) {
251: sign = -1;
252: } else {
253: // no legal TZ offset found
254: return cal.getTime();
255: }
256: offset = sign * Integer.parseInt(t.nextToken())
257: * 10 * 3600;
258: }
259: cal.set(Calendar.ZONE_OFFSET, offset);
260: }
261: cal.set(Calendar.HOUR_OF_DAY, hour);
262: cal.set(Calendar.MINUTE, min);
263: cal.set(Calendar.SECOND, sec);
264: cal.set(Calendar.MILLISECOND, msec);
265: }
266: } catch (NoSuchElementException e) {
267: // ignore this as it is perfectly fine to have non-complete date in this format
268: } catch (Exception e) {
269: // catch all Exceptions and return what we parsed so far
270: serverLog.logInfo("SERVER",
271: "parseISO8601: DATE ERROR with: '" + s
272: + "' got so far: '" + cal.toString());
273: }
274:
275: // in case we couldn't even parse a year
276: if (!cal.isSet(Calendar.YEAR))
277: throw new ParseException("parseISO8601: Cannot parse '" + s
278: + "'", 0);
279:
280: return cal.getTime();
281: }
282:
283: /**
284: * Note: The short day format doesn't include any timezone information. This method
285: * transforms the date into the GMT/UTC timezone. Example: If the local system time is,
286: * 2007-12-18 01:15:00 +0200, then the resulting String will be "2007-12-17".
287: * In case you need a format with a timezon offset, use {@link #formatShortDay(TimeZone)}
288: * @return a String representation of the current system date in GMT using the
289: * short day format, e.g. "20071218".
290: */
291: public static String formatShortDay() {
292: return format(FORMAT_SHORT_DAY, new Date());
293: }
294:
295: /**
296: * @see #formatShortDay()
297: * @param date the Date to transform
298: */
299: public static String formatShortDay(Date date) {
300: return format(FORMAT_SHORT_DAY, date);
301: }
302:
303: /**
304: * This should only be used, if you need a short date String that needs to be aligned to
305: * a special timezone other than GMT/UTC. Be aware that a receiver won't be able to
306: * recreate the original Date without additional timezone information.
307: * @see #formatShortDay()
308: * @param date the Date to transform
309: * @param tz a TimeZone the resulting date String should be aligned to.
310: */
311: public static String formatShortDay(Date date, TimeZone tz) {
312: return format(FORMAT_SHORT_DAY, date, tz);
313: }
314:
315: /**
316: * Parse a String representation of a Date in short day format assuming the date
317: * is aligned to the GMT/UTC timezone. An example for such a date string is "20071218".
318: * @see #formatShortDay()
319: * @throws ParseException The exception is thrown if an error occured during while parsing
320: * the String.
321: */
322: public static Date parseShortDay(String timeString)
323: throws ParseException {
324: return parse(FORMAT_SHORT_DAY, timeString);
325: }
326:
327: /**
328: * Returns the current date in short second format which is a fixed width (14 chars)
329: * String including the date and the time like "20071218233510". The result is in GMT/UTC.
330: * @see #formatShortDay()
331: */
332: public static String formatShortSecond() {
333: return formatShortSecond(new Date());
334: }
335:
336: /**
337: * Identical to {@link #formatShortDay(Date)}, but for short second format.
338: */
339: public static String formatShortSecond(Date date) {
340: return format(FORMAT_SHORT_SECOND, date);
341: }
342:
343: /**
344: * Identical to {@link #formatShortDay(Date, TimeZone)}, but for short second format.
345: */
346: public static String formatShortSecond(Date date, TimeZone tz) {
347: return format(FORMAT_SHORT_SECOND, date, tz);
348: }
349:
350: //TODO check the following 2 parse methods for correct use (GMT vs. different timezone)
351: /**
352: * Like {@link #parseShortDay(String)}, but for the "short second" format which is short date
353: * plus a 6 digit day time value, like "20071218233510". The String should be in GMT/UTC to
354: * get a correct Date.
355: */
356: public static Date parseShortSecond(String timeString)
357: throws ParseException {
358: return parse(FORMAT_SHORT_SECOND, timeString);
359: }
360:
361: /**
362: * Like {@link #parseShortSecond(String)} using additional timezone information provided in an
363: * offset String, like "+0100" for CET.
364: */
365: public static Date parseShortSecond(String remoteTimeString,
366: String remoteUTCOffset) {
367: // FIXME: This method returns an incorrect date, check callers!
368: // ex: de.anomic.server.serverDate.parseShortSecond("20070101120000", "+0200").toGMTString()
369: // => 1 Jan 2007 13:00:00 GMT
370: if (remoteTimeString == null || remoteTimeString.length() == 0) {
371: return new Date();
372: }
373: if (remoteUTCOffset == null || remoteUTCOffset.length() == 0) {
374: return new Date();
375: }
376: try {
377: return new Date(
378: parse(FORMAT_SHORT_SECOND, remoteTimeString)
379: .getTime()
380: - serverDate.UTCDiff()
381: + serverDate.UTCDiff(remoteUTCOffset));
382: } catch (java.text.ParseException e) {
383: serverLog.logFinest("parseUniversalDate", e.getMessage()
384: + ", remoteTimeString=[" + remoteTimeString + "]");
385: return new Date();
386: } catch (java.lang.NumberFormatException e) {
387: serverLog.logFinest("parseUniversalDate", e.getMessage()
388: + ", remoteTimeString=[" + remoteTimeString + "]");
389: return new Date();
390: }
391: }
392:
393: /**
394: * Format a time inteval in milliseconds into a String of the form
395: * X 'day'['s'] HH':'mm
396: */
397: public static String formatInterval(long millis) {
398: try {
399: long mins = millis / 60000;
400:
401: StringBuffer uptime = new StringBuffer();
402:
403: int uptimeDays = (int) (Math.floor(mins / 1440));
404: int uptimeHours = (int) (Math.floor(mins / 60) % 24);
405: int uptimeMins = (int) mins % 60;
406:
407: uptime.append(uptimeDays).append(
408: ((uptimeDays == 1) ? " day " : " days ")).append(
409: (uptimeHours < 10) ? "0" : "").append(uptimeHours)
410: .append(":").append((uptimeMins < 10) ? "0" : "")
411: .append(uptimeMins);
412:
413: return uptime.toString();
414: } catch (Exception e) {
415: return "unknown";
416: }
417: }
418:
419: /** called by all public format...(..., TimeZone) methods */
420: private static String format(SimpleDateFormat format, Date date,
421: TimeZone tz) {
422: TimeZone bakTZ = format.getTimeZone();
423: String result;
424:
425: synchronized (format) {
426: format.setTimeZone(tz == null ? TZ_GMT : tz);
427: result = format.format(date);
428: format.setTimeZone(bakTZ);
429: }
430:
431: return result;
432: }
433:
434: /** called by all public format...(...) methods */
435: private static String format(SimpleDateFormat format, Date date) {
436: synchronized (format) {
437: return format.format(date);
438: }
439: }
440:
441: /** calles by all public parse...(...) methods */
442: private static Date parse(SimpleDateFormat format, String dateString)
443: throws ParseException {
444: synchronized (format) {
445: return format.parse(dateString);
446: }
447: }
448:
449: // statics
450: public final static long secondMillis = 1000;
451: public final static long minuteMillis = 60 * secondMillis;
452: public final static long hourMillis = 60 * minuteMillis;
453: public final static long dayMillis = 24 * hourMillis;
454: public final static long normalyearMillis = 365 * dayMillis;
455: public final static long leapyearMillis = 366 * dayMillis;
456: public final static int january = 31, normalfebruary = 28,
457: leapfebruary = 29, march = 31, april = 30, may = 31,
458: june = 30, july = 31, august = 31, september = 30,
459: october = 31, november = 30, december = 31;
460: public final static int[] dimnormal = { january, normalfebruary,
461: march, april, may, june, july, august, september, october,
462: november, december };
463: public final static int[] dimleap = { january, leapfebruary, march,
464: april, may, june, july, august, september, october,
465: november, december };
466: public final static String[] wkday = { "Mon", "Tue", "Wed", "Thu",
467: "Fri", "Sat", "Sun" };
468: //private final static String[] month = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
469:
470: // find out time zone and DST offset
471: private static Calendar this Calendar = Calendar.getInstance();
472:
473: // pre-calculation of time tables
474: private final static long[] dimnormalacc, dimleapacc;
475: private static long[] utimeyearsacc;
476: static {
477: long millis = 0;
478: utimeyearsacc = new long[67];
479: for (int i = 0; i < 67; i++) {
480: utimeyearsacc[i] = millis;
481: millis += ((i & 3) == 0) ? leapyearMillis
482: : normalyearMillis;
483: }
484: millis = 0;
485: dimnormalacc = new long[12];
486: for (int i = 0; i < 12; i++) {
487: dimnormalacc[i] = millis;
488: millis += (dayMillis * dimnormal[i]);
489: }
490: millis = 0;
491: dimleapacc = new long[12];
492: for (int i = 0; i < 12; i++) {
493: dimleapacc[i] = millis;
494: millis += (dayMillis * dimleap[i]);
495: }
496: }
497:
498: // class variables
499: private int milliseconds, seconds, minutes, hours, days, months,
500: years; // years since 1970
501: private int dow; // day-of-week
502: private long utime;
503:
504: public static String UTCDiffString() {
505: // we express the UTC Difference in 5 digits:
506: // SHHMM
507: // S ::= '+'|'-'
508: // HH ::= '00'|'01'|'02'|'03'|'04'|'05'|'06'|'07'|'08'|'09'|'10'|'11'|'12'
509: // MM ::= '00'|'15'|'30'|'45'
510: // since there are some places on earth where there is a time shift of half an hour
511: // we need too show also the minutes of the time shift
512: // Examples: http://www.timeanddate.com/library/abbreviations/timezones/
513: long offsetHours = UTCDiff();
514: int om = Math.abs((int) (offsetHours / minuteMillis)) % 60;
515: int oh = Math.abs((int) (offsetHours / hourMillis));
516: String diff = Integer.toString(om);
517: if (diff.length() < 2)
518: diff = "0" + diff;
519: diff = Integer.toString(oh) + diff;
520: if (diff.length() < 4)
521: diff = "0" + diff;
522: if (offsetHours >= 0) {
523: return "+" + diff;
524: } else {
525: return "-" + diff;
526: }
527: }
528:
529: public static long UTCDiff() {
530: // DST_OFFSET is dependent on the time of the Calendar, so it has to be updated
531: // to get the correct current offset
532: this Calendar.setTimeInMillis(System.currentTimeMillis());
533: long zoneOffsetHours = this Calendar.get(Calendar.ZONE_OFFSET);
534: long DSTOffsetHours = this Calendar.get(Calendar.DST_OFFSET);
535: return zoneOffsetHours + DSTOffsetHours;
536: }
537:
538: public static long UTCDiff(String diffString) {
539: if (diffString.length() != 5)
540: throw new IllegalArgumentException(
541: "UTC String malformed (wrong size):" + diffString);
542: boolean ahead = true;
543: if (diffString.charAt(0) == '+')
544: ahead = true;
545: else if (diffString.charAt(0) == '-')
546: ahead = false;
547: else
548: throw new IllegalArgumentException(
549: "UTC String malformed (wrong sign):" + diffString);
550: long oh = Long.parseLong(diffString.substring(1, 3));
551: long om = Long.parseLong(diffString.substring(3));
552: return ((ahead) ? (long) 1 : (long) -1)
553: * (oh * hourMillis + om * minuteMillis);
554: }
555:
556: public static long correctedUTCTime() {
557: return System.currentTimeMillis() - UTCDiff();
558: }
559:
560: public serverDate() {
561: this (System.currentTimeMillis());
562: }
563:
564: public serverDate(long utime) {
565: // set the time as the difference, measured in milliseconds,
566: // between the current time and midnight, January 1, 1970 UTC/GMT
567: this .utime = utime;
568: dow = (int) (((utime / dayMillis) + 3) % 7);
569: years = (int) (utime / normalyearMillis); // a guess
570: if (utime < utimeyearsacc[years])
571: years--; // the correction
572: long remain = utime - utimeyearsacc[years];
573: months = (int) (remain / (29 * dayMillis)); // a guess
574: if (months > 11)
575: months = 11;
576: if ((years & 3) == 0) {
577: if (remain < dimleapacc[months])
578: months--; // correction
579: remain = remain - dimleapacc[months];
580: } else {
581: if (remain < dimnormalacc[months])
582: months--; // correction
583: remain = remain - dimnormalacc[months];
584: }
585: days = (int) (remain / dayMillis);
586: remain = remain % dayMillis;
587: hours = (int) (remain / hourMillis);
588: remain = remain % hourMillis;
589: minutes = (int) (remain / minuteMillis);
590: remain = remain % minuteMillis;
591: seconds = (int) (remain / secondMillis);
592: remain = remain % secondMillis;
593: milliseconds = (int) remain;
594: }
595:
596: private void calcUTime() {
597: this .utime = utimeyearsacc[years] + dimleapacc[months - 1]
598: + dayMillis * (days - 1) + hourMillis * hours
599: + minuteMillis * minutes + secondMillis * seconds
600: + milliseconds;
601: this .dow = (int) (((utime / dayMillis) + 3) % 7);
602: }
603:
604: public serverDate(String datestring)
605: throws java.text.ParseException {
606: // parse a date string; othervise throw a java.text.ParseException
607: if ((datestring.length() == 14) || (datestring.length() == 17)) {
608: // parse a ShortString
609: try {
610: years = Integer.parseInt(datestring.substring(0, 4)) - 1970;
611: } catch (NumberFormatException e) {
612: throw new java.text.ParseException("serverDate '"
613: + datestring + "' wrong year", 0);
614: }
615: if (years < 0)
616: throw new java.text.ParseException("serverDate '"
617: + datestring + "' wrong year", 0);
618: try {
619: months = Integer.parseInt(datestring.substring(4, 6)) - 1;
620: } catch (NumberFormatException e) {
621: throw new java.text.ParseException("serverDate '"
622: + datestring + "' wrong month", 4);
623: }
624: if ((months < 0) || (months > 11))
625: throw new java.text.ParseException("serverDate '"
626: + datestring + "' wrong month", 4);
627: try {
628: days = Integer.parseInt(datestring.substring(6, 8)) - 1;
629: } catch (NumberFormatException e) {
630: throw new java.text.ParseException("serverDate '"
631: + datestring + "' wrong day", 6);
632: }
633: if ((days < 0) || (days > 30))
634: throw new java.text.ParseException("serverDate '"
635: + datestring + "' wrong day", 6);
636: try {
637: hours = Integer.parseInt(datestring.substring(8, 10));
638: } catch (NumberFormatException e) {
639: throw new java.text.ParseException("serverDate '"
640: + datestring + "' wrong hour", 8);
641: }
642: if ((hours < 0) || (hours > 23))
643: throw new java.text.ParseException("serverDate '"
644: + datestring + "' wrong hour", 8);
645: try {
646: minutes = Integer
647: .parseInt(datestring.substring(10, 12));
648: } catch (NumberFormatException e) {
649: throw new java.text.ParseException("serverDate '"
650: + datestring + "' wrong minute", 10);
651: }
652: if ((minutes < 0) || (minutes > 59))
653: throw new java.text.ParseException("serverDate '"
654: + datestring + "' wrong minute", 10);
655: try {
656: seconds = Integer
657: .parseInt(datestring.substring(12, 14));
658: } catch (NumberFormatException e) {
659: throw new java.text.ParseException("serverDate '"
660: + datestring + "' wrong second", 12);
661: }
662: if ((seconds < 0) || (seconds > 59))
663: throw new java.text.ParseException("serverDate '"
664: + datestring + "' wrong second", 12);
665: if (datestring.length() == 17) {
666: try {
667: milliseconds = Integer.parseInt(datestring
668: .substring(14, 17));
669: } catch (NumberFormatException e) {
670: throw new java.text.ParseException("serverDate '"
671: + datestring + "' wrong millisecond", 14);
672: }
673: } else {
674: milliseconds = 0;
675: }
676: if ((milliseconds < 0) || (milliseconds > 999))
677: throw new java.text.ParseException("serverDate '"
678: + datestring + "' wrong millisecond", 14);
679: calcUTime();
680: return;
681: }
682: throw new java.text.ParseException("serverDate '" + datestring
683: + "' format unknown", 0);
684: }
685:
686: public String toString() {
687: return "utime=" + utime + ", year=" + (years + 1970)
688: + ", month=" + (months + 1) + ", day=" + (days + 1)
689: + ", hour=" + hours + ", minute=" + minutes
690: + ", second=" + seconds + ", millis=" + milliseconds
691: + ", day-of-week=" + wkday[dow];
692: }
693:
694: public String toShortString(boolean millis) {
695: // returns a "yyyyMMddHHmmssSSS"
696: byte[] result = new byte[(millis) ? 17 : 14];
697: int x = 1970 + years;
698: result[0] = (byte) (48 + (x / 1000));
699: x = x % 1000;
700: result[1] = (byte) (48 + (x / 100));
701: x = x % 100;
702: result[2] = (byte) (48 + (x / 10));
703: x = x % 10;
704: result[3] = (byte) (48 + x);
705: x = months + 1;
706: result[4] = (byte) (48 + (x / 10));
707: result[5] = (byte) (48 + (x % 10));
708: x = days + 1;
709: result[6] = (byte) (48 + (x / 10));
710: result[7] = (byte) (48 + (x % 10));
711: result[8] = (byte) (48 + (hours / 10));
712: result[9] = (byte) (48 + (hours % 10));
713: result[10] = (byte) (48 + (minutes / 10));
714: result[11] = (byte) (48 + (minutes % 10));
715: result[12] = (byte) (48 + (seconds / 10));
716: result[13] = (byte) (48 + (seconds % 10));
717: if (millis) {
718: x = milliseconds;
719: result[14] = (byte) (48 + (x / 100));
720: x = x % 100;
721: result[15] = (byte) (48 + (x / 10));
722: x = x % 10;
723: result[16] = (byte) (48 + x);
724: }
725: return new String(result);
726: }
727:
728: public static long remainingTime(long start, long due, long minimum) {
729: if (due < 0)
730: return -1;
731: long r = due + start - System.currentTimeMillis();
732: if (r <= 0)
733: return minimum;
734: else
735: return r;
736: }
737:
738: public static void main(String[] args) {
739: //System.out.println("kelondroDate is (" + new kelondroDate().toString() + ")");
740: System.out.println("offset is " + (UTCDiff() / 1000 / 60 / 60)
741: + " hours, javaDate is " + new Date()
742: + ", correctedDate is " + new Date(correctedUTCTime()));
743: System.out.println("serverDate : "
744: + new serverDate().toShortString(false));
745: System.out.println(" javaDate : " + formatShortSecond());
746: System.out.println("serverDate : "
747: + new serverDate().toString());
748: System.out.println(" JavaDate : "
749: + DateFormat.getDateInstance().format(new Date()));
750: System.out.println("serverDate0: "
751: + new serverDate(0).toShortString(false));
752: System.out.println(" JavaDate0: "
753: + FORMAT_SHORT_SECOND.format(new Date(0)));
754: System.out.println("serverDate0: "
755: + new serverDate(0).toString());
756: System.out.println(" JavaDate0: "
757: + DateFormat.getDateInstance().format(new Date(0)));
758: // parse test
759: try {
760: System.out.println("serverDate re-parse short: "
761: + new serverDate(new serverDate()
762: .toShortString(false)).toShortString(true));
763: System.out.println("serverDate re-parse long : "
764: + new serverDate(new serverDate()
765: .toShortString(true)).toShortString(true));
766: } catch (java.text.ParseException e) {
767: System.out.println("Parse Exception: " + e.getMessage()
768: + ", pos " + e.getErrorOffset());
769: }
770: //String testresult;
771: int cycles = 10000;
772: long start;
773:
774: String[] testresult = new String[10000];
775: start = System.currentTimeMillis();
776: for (int i = 0; i < cycles; i++)
777: testresult[i] = new serverDate().toShortString(false);
778: System.out.println("time for " + cycles
779: + " calls to serverDate:"
780: + (System.currentTimeMillis() - start)
781: + " milliseconds");
782:
783: start = System.currentTimeMillis();
784: for (int i = 0; i < cycles; i++)
785: testresult[i] = FORMAT_SHORT_SECOND.format(new Date());
786: System.out.println("time for " + cycles
787: + " calls to javaDate:"
788: + (System.currentTimeMillis() - start)
789: + " milliseconds");
790: }
791: }
|