001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
015: * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
016: * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
017: * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
018: * Copyright (C) 2004 Joey Gibson <joey@joeygibson.com>
019: * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
020: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
021: * Copyright (C) 2006 Thomas E Enebo <enebo@acm.org>
022: * Copyright (C) 2006 Ola Bini <ola.bini@ki.se>
023: * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
024: *
025: * Alternatively, the contents of this file may be used under the terms of
026: * either of the GNU General Public License Version 2 or later (the "GPL"),
027: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
028: * in which case the provisions of the GPL or the LGPL are applicable instead
029: * of those above. If you wish to allow use of your version of this file only
030: * under the terms of either the GPL or the LGPL, and not to allow others to
031: * use your version of this file under the terms of the CPL, indicate your
032: * decision by deleting the provisions above and replace them with the notice
033: * and other provisions required by the GPL or the LGPL. If you do not delete
034: * the provisions above, a recipient may use your version of this file under
035: * the terms of any one of the CPL, the GPL or the LGPL.
036: ***** END LICENSE BLOCK *****/package org.jruby;
037:
038: import java.text.SimpleDateFormat;
039: import java.util.Calendar;
040: import java.util.Date;
041: import java.util.GregorianCalendar;
042: import java.util.Locale;
043: import java.util.TimeZone;
044:
045: import org.jruby.runtime.Arity;
046: import org.jruby.runtime.Block;
047: import org.jruby.runtime.CallbackFactory;
048: import org.jruby.runtime.MethodIndex;
049: import org.jruby.runtime.ObjectAllocator;
050: import org.jruby.runtime.ThreadContext;
051: import org.jruby.runtime.builtin.IRubyObject;
052: import org.jruby.util.RubyDateFormat;
053: import org.jruby.util.ByteList;
054:
055: /** The Time class.
056: *
057: * @author chadfowler, jpetersen
058: */
059: public class RubyTime extends RubyObject {
060: public static final String UTC = "UTC";
061: private Calendar cal;
062: private long usec;
063:
064: private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
065: "-", Locale.US);
066:
067: public static TimeZone getLocalTimeZone(Ruby runtime) {
068: // TODO: cache the RubyString "TZ" so it doesn't need to be recreated for each call?
069: RubyString tzVar = runtime.newString("TZ");
070: RubyHash h = ((RubyHash) runtime.getObject().getConstant("ENV"));
071: IRubyObject tz = h.aref(tzVar);
072: if (tz == null || !(tz instanceof RubyString)) {
073: return TimeZone.getDefault();
074: } else {
075: return TimeZone.getTimeZone(tz.toString());
076: }
077: }
078:
079: public RubyTime(Ruby runtime, RubyClass rubyClass) {
080: super (runtime, rubyClass);
081: }
082:
083: public RubyTime(Ruby runtime, RubyClass rubyClass, Calendar cal) {
084: super (runtime, rubyClass);
085: this .cal = cal;
086: }
087:
088: private static ObjectAllocator TIME_ALLOCATOR = new ObjectAllocator() {
089: public IRubyObject allocate(Ruby runtime, RubyClass klass) {
090: RubyTime instance = new RubyTime(runtime, klass);
091: GregorianCalendar cal = new GregorianCalendar();
092: cal.setTime(new Date());
093: instance.setJavaCalendar(cal);
094: return instance;
095: }
096: };
097:
098: public static RubyClass createTimeClass(Ruby runtime) {
099: RubyClass timeClass = runtime.defineClass("Time", runtime
100: .getObject(), TIME_ALLOCATOR);
101: CallbackFactory callbackFactory = runtime
102: .callbackFactory(RubyTime.class);
103: RubyClass timeMetaClass = timeClass.getMetaClass();
104:
105: timeClass.includeModule(runtime.getModule("Comparable"));
106:
107: timeMetaClass.defineAlias("now", "new");
108: timeMetaClass.defineFastMethod("at", callbackFactory
109: .getFastOptSingletonMethod("new_at"));
110: timeMetaClass.defineFastMethod("local", callbackFactory
111: .getFastOptSingletonMethod("new_local"));
112: timeMetaClass.defineFastMethod("mktime", callbackFactory
113: .getFastOptSingletonMethod("new_local"));
114: timeMetaClass.defineFastMethod("utc", callbackFactory
115: .getFastOptSingletonMethod("new_utc"));
116: timeMetaClass.defineFastMethod("gm", callbackFactory
117: .getFastOptSingletonMethod("new_utc"));
118: timeMetaClass.defineMethod("_load", callbackFactory
119: .getSingletonMethod("s_load", RubyKernel.IRUBY_OBJECT));
120:
121: // To override Comparable with faster String ones
122: timeClass.defineFastMethod(">=", callbackFactory.getFastMethod(
123: "op_ge", RubyKernel.IRUBY_OBJECT));
124: timeClass.defineFastMethod(">", callbackFactory.getFastMethod(
125: "op_gt", RubyKernel.IRUBY_OBJECT));
126: timeClass.defineFastMethod("<=", callbackFactory.getFastMethod(
127: "op_le", RubyKernel.IRUBY_OBJECT));
128: timeClass.defineFastMethod("<", callbackFactory.getFastMethod(
129: "op_lt", RubyKernel.IRUBY_OBJECT));
130:
131: timeClass.defineFastMethod("===", callbackFactory
132: .getFastMethod("same2", RubyKernel.IRUBY_OBJECT));
133: timeClass.defineFastMethod("+", callbackFactory.getFastMethod(
134: "op_plus", RubyKernel.IRUBY_OBJECT));
135: timeClass.defineFastMethod("-", callbackFactory.getFastMethod(
136: "op_minus", RubyKernel.IRUBY_OBJECT));
137: timeClass.defineFastMethod("<=>", callbackFactory
138: .getFastMethod("op_cmp", RubyKernel.IRUBY_OBJECT));
139: timeClass.defineFastMethod("eql?", callbackFactory
140: .getFastMethod("eql_p", RubyKernel.IRUBY_OBJECT));
141: timeClass.defineFastMethod("asctime", callbackFactory
142: .getFastMethod("asctime"));
143: timeClass.defineFastMethod("mday", callbackFactory
144: .getFastMethod("mday"));
145: timeClass.defineAlias("day", "mday");
146: timeClass.defineAlias("ctime", "asctime");
147: timeClass.defineFastMethod("sec", callbackFactory
148: .getFastMethod("sec"));
149: timeClass.defineFastMethod("min", callbackFactory
150: .getFastMethod("min"));
151: timeClass.defineFastMethod("hour", callbackFactory
152: .getFastMethod("hour"));
153: timeClass.defineFastMethod("month", callbackFactory
154: .getFastMethod("month"));
155: timeClass.defineAlias("mon", "month");
156: timeClass.defineFastMethod("year", callbackFactory
157: .getFastMethod("year"));
158: timeClass.defineFastMethod("wday", callbackFactory
159: .getFastMethod("wday"));
160: timeClass.defineFastMethod("yday", callbackFactory
161: .getFastMethod("yday"));
162: timeClass.defineFastMethod("isdst", callbackFactory
163: .getFastMethod("isdst"));
164: timeClass.defineAlias("dst?", "isdst");
165: timeClass.defineFastMethod("zone", callbackFactory
166: .getFastMethod("zone"));
167: timeClass.defineFastMethod("to_a", callbackFactory
168: .getFastMethod("to_a"));
169: timeClass.defineFastMethod("to_f", callbackFactory
170: .getFastMethod("to_f"));
171: timeClass.defineFastMethod("succ", callbackFactory
172: .getFastMethod("succ"));
173: timeClass.defineFastMethod("to_i", callbackFactory
174: .getFastMethod("to_i"));
175: timeClass.defineFastMethod("to_s", callbackFactory
176: .getFastMethod("to_s"));
177: timeClass.defineFastMethod("inspect", callbackFactory
178: .getFastMethod("inspect"));
179: timeClass.defineFastMethod("strftime", callbackFactory
180: .getFastMethod("strftime", IRubyObject.class));
181: timeClass.defineFastMethod("usec", callbackFactory
182: .getFastMethod("usec"));
183: timeClass.defineAlias("tv_usec", "usec");
184: timeClass.defineAlias("tv_sec", "to_i");
185: timeClass.defineFastMethod("gmtime", callbackFactory
186: .getFastMethod("gmtime"));
187: timeClass.defineAlias("utc", "gmtime");
188: timeClass.defineFastMethod("gmt?", callbackFactory
189: .getFastMethod("gmt"));
190: timeClass.defineAlias("utc?", "gmt?");
191: timeClass.defineAlias("gmtime?", "gmt?");
192: timeClass.defineFastMethod("localtime", callbackFactory
193: .getFastMethod("localtime"));
194: timeClass.defineFastMethod("hash", callbackFactory
195: .getFastMethod("hash"));
196: timeClass.defineMethod("initialize", callbackFactory
197: .getOptMethod("initialize"));
198: timeClass.defineFastMethod("initialize_copy", callbackFactory
199: .getFastMethod("initialize_copy", IRubyObject.class));
200: timeClass.defineMethod("_dump", callbackFactory
201: .getOptMethod("dump"));
202: timeClass.defineFastMethod("gmt_offset", callbackFactory
203: .getFastMethod("gmt_offset"));
204: timeClass.defineAlias("gmtoff", "gmt_offset");
205: timeClass.defineAlias("utc_offset", "gmt_offset");
206: timeClass.defineFastMethod("getgm", callbackFactory
207: .getFastMethod("getgm"));
208: timeClass.defineFastMethod("getlocal", callbackFactory
209: .getFastMethod("getlocal"));
210: timeClass.defineAlias("getutc", "getgm");
211:
212: return timeClass;
213: }
214:
215: public void setUSec(long usec) {
216: this .usec = usec;
217: }
218:
219: public long getUSec() {
220: return usec;
221: }
222:
223: public void updateCal(Calendar calendar) {
224: calendar.setTimeZone(cal.getTimeZone());
225: calendar.setTimeInMillis(getTimeInMillis());
226: }
227:
228: protected long getTimeInMillis() {
229: return cal.getTimeInMillis(); // For JDK 1.4 we can use "cal.getTimeInMillis()"
230: }
231:
232: public static RubyTime newTime(Ruby runtime, long milliseconds) {
233: Calendar cal = Calendar.getInstance();
234: RubyTime time = new RubyTime(runtime, runtime.getClass("Time"),
235: cal);
236:
237: cal.setTimeInMillis(milliseconds);
238:
239: return time;
240: }
241:
242: public static RubyTime newTime(Ruby runtime, Calendar cal) {
243: RubyTime time = new RubyTime(runtime, runtime.getClass("Time"),
244: cal);
245:
246: return time;
247: }
248:
249: public IRubyObject initialize_copy(IRubyObject original) {
250: if (!(original instanceof RubyTime)) {
251: throw getRuntime().newTypeError(
252: "Expecting an instance of class Time");
253: }
254:
255: RubyTime originalTime = (RubyTime) original;
256:
257: cal = (Calendar) (originalTime.cal.clone());
258: usec = originalTime.usec;
259:
260: return this ;
261: }
262:
263: public RubyTime succ() {
264: Calendar newCal = (Calendar) cal.clone();
265: newCal.add(Calendar.SECOND, 1);
266: return newTime(getRuntime(), newCal);
267: }
268:
269: public RubyTime gmtime() {
270: cal.setTimeZone(TimeZone.getTimeZone(UTC));
271: return this ;
272: }
273:
274: public RubyTime localtime() {
275: long dump = cal.getTimeInMillis();
276: cal = Calendar.getInstance(getLocalTimeZone(getRuntime()));
277: cal.setTimeInMillis(dump);
278: return this ;
279: }
280:
281: public RubyBoolean gmt() {
282: return getRuntime().newBoolean(
283: cal.getTimeZone().getID().equals(UTC));
284: }
285:
286: public RubyTime getgm() {
287: Calendar newCal = (Calendar) cal.clone();
288: newCal.setTimeZone(TimeZone.getTimeZone(UTC));
289: return newTime(getRuntime(), newCal);
290: }
291:
292: public RubyTime getlocal() {
293: Calendar newCal = (Calendar) cal.clone();
294: newCal.setTimeZone(getLocalTimeZone(getRuntime()));
295: return newTime(getRuntime(), newCal);
296: }
297:
298: public RubyString strftime(IRubyObject format) {
299: final RubyDateFormat rubyDateFormat = new RubyDateFormat("-",
300: Locale.US);
301: rubyDateFormat.setCalendar(cal);
302: rubyDateFormat.applyPattern(format.toString());
303: String result = rubyDateFormat.format(cal.getTime());
304:
305: return getRuntime().newString(result);
306: }
307:
308: public IRubyObject op_ge(IRubyObject other) {
309: if (other instanceof RubyTime) {
310: return getRuntime().newBoolean(cmp((RubyTime) other) >= 0);
311: }
312:
313: return RubyComparable.op_ge(this , other);
314: }
315:
316: public IRubyObject op_gt(IRubyObject other) {
317: if (other instanceof RubyTime) {
318: return getRuntime().newBoolean(cmp((RubyTime) other) > 0);
319: }
320:
321: return RubyComparable.op_gt(this , other);
322: }
323:
324: public IRubyObject op_le(IRubyObject other) {
325: if (other instanceof RubyTime) {
326: return getRuntime().newBoolean(cmp((RubyTime) other) <= 0);
327: }
328:
329: return RubyComparable.op_le(this , other);
330: }
331:
332: public IRubyObject op_lt(IRubyObject other) {
333: if (other instanceof RubyTime) {
334: return getRuntime().newBoolean(cmp((RubyTime) other) < 0);
335: }
336:
337: return RubyComparable.op_lt(this , other);
338: }
339:
340: private int cmp(RubyTime other) {
341: long millis = getTimeInMillis();
342: long millis_other = other.getTimeInMillis();
343: long usec_other = other.usec;
344:
345: if (millis > millis_other
346: || (millis == millis_other && usec > usec_other)) {
347: return 1;
348: } else if (millis < millis_other
349: || (millis == millis_other && usec < usec_other)) {
350: return -1;
351: }
352:
353: return 0;
354: }
355:
356: public IRubyObject op_plus(IRubyObject other) {
357: long time = getTimeInMillis();
358:
359: if (other instanceof RubyTime) {
360: throw getRuntime().newTypeError("time + time ?");
361: }
362: long adjustment = (long) (RubyNumeric.num2dbl(other) * 1000000);
363: int micro = (int) (adjustment % 1000);
364: adjustment = adjustment / 1000;
365:
366: time += adjustment;
367:
368: RubyTime newTime = new RubyTime(getRuntime(), getMetaClass());
369: newTime.cal = Calendar.getInstance();
370: newTime.cal.setTimeZone(cal.getTimeZone());
371: newTime.cal.setTime(new Date(time));
372: newTime.setUSec(micro);
373:
374: return newTime;
375: }
376:
377: public IRubyObject op_minus(IRubyObject other) {
378: long time = getTimeInMillis();
379:
380: if (other instanceof RubyTime) {
381: time -= ((RubyTime) other).getTimeInMillis();
382:
383: return RubyFloat.newFloat(getRuntime(), time * 10e-4);
384: }
385: long adjustment = (long) (((RubyNumeric) other)
386: .getDoubleValue() * 1000000);
387: int micro = (int) (adjustment % 1000);
388: adjustment = adjustment / 1000;
389:
390: time -= adjustment;
391:
392: RubyTime newTime = new RubyTime(getRuntime(), getMetaClass());
393: newTime.cal = Calendar.getInstance();
394: newTime.cal.setTimeZone(cal.getTimeZone());
395: newTime.cal.setTime(new Date(time));
396: newTime.setUSec(micro);
397:
398: return newTime;
399: }
400:
401: public IRubyObject same2(IRubyObject other) {
402: return (RubyNumeric.fix2int(callMethod(getRuntime()
403: .getCurrentContext(), MethodIndex.OP_SPACESHIP, "<=>",
404: other)) == 0) ? getRuntime().getTrue() : getRuntime()
405: .getFalse();
406: }
407:
408: public IRubyObject op_cmp(IRubyObject other) {
409: if (other.isNil()) {
410: return other;
411: }
412:
413: if (other instanceof RubyTime) {
414: return getRuntime().newFixnum(cmp((RubyTime) other));
415: }
416:
417: long millis = getTimeInMillis();
418:
419: if (other instanceof RubyNumeric) {
420: if (other instanceof RubyFloat
421: || other instanceof RubyBignum) {
422: double time = millis / 1000.0;
423:
424: double time_other = ((RubyNumeric) other)
425: .getDoubleValue();
426:
427: if (time > time_other) {
428: return RubyFixnum.one(getRuntime());
429: } else if (time < time_other) {
430: return RubyFixnum.minus_one(getRuntime());
431: }
432:
433: return RubyFixnum.zero(getRuntime());
434: }
435: long millis_other = RubyNumeric.num2long(other) * 1000;
436:
437: if (millis > millis_other
438: || (millis == millis_other && usec > 0)) {
439: return RubyFixnum.one(getRuntime());
440: } else if (millis < millis_other
441: || (millis == millis_other && usec < 0)) {
442: return RubyFixnum.minus_one(getRuntime());
443: }
444:
445: return RubyFixnum.zero(getRuntime());
446: }
447: return getRuntime().getNil();
448: }
449:
450: public IRubyObject eql_p(IRubyObject other) {
451: if (other instanceof RubyTime) {
452: RubyTime otherTime = (RubyTime) other;
453: return (usec == otherTime.usec && getTimeInMillis() == otherTime
454: .getTimeInMillis()) ? getRuntime().getTrue()
455: : getRuntime().getFalse();
456: }
457: return getRuntime().getFalse();
458: }
459:
460: public RubyString asctime() {
461: simpleDateFormat.setCalendar(cal);
462: if (cal.get(Calendar.DAY_OF_MONTH) < 10) {
463: simpleDateFormat.applyPattern("EEE MMM d HH:mm:ss yyyy");
464: } else {
465: simpleDateFormat.applyPattern("EEE MMM dd HH:mm:ss yyyy");
466: }
467: String result = simpleDateFormat.format(cal.getTime());
468:
469: return getRuntime().newString(result);
470: }
471:
472: public IRubyObject to_s() {
473: simpleDateFormat.setCalendar(cal);
474: simpleDateFormat.applyPattern("EEE MMM dd HH:mm:ss z yyyy");
475: String result = simpleDateFormat.format(cal.getTime());
476:
477: return getRuntime().newString(result);
478: }
479:
480: public RubyArray to_a() {
481: return getRuntime().newArrayNoCopy(
482: new IRubyObject[] { sec(), min(), hour(), mday(),
483: month(), year(), wday(), yday(), isdst(),
484: zone() });
485: }
486:
487: public RubyFloat to_f() {
488: long time = getTimeInMillis();
489: time = time * 1000 + usec;
490: return RubyFloat.newFloat(getRuntime(), time / 1000000.0);
491: }
492:
493: public RubyInteger to_i() {
494: return getRuntime().newFixnum(getTimeInMillis() / 1000);
495: }
496:
497: public RubyInteger usec() {
498: return getRuntime().newFixnum(microseconds());
499: }
500:
501: public void setMicroseconds(long mic) {
502: long millis = getTimeInMillis() % 1000;
503: long withoutMillis = getTimeInMillis() - millis;
504: withoutMillis += (mic / 1000);
505: cal.setTimeInMillis(withoutMillis);
506: usec = mic % 1000;
507: }
508:
509: public long microseconds() {
510: return getTimeInMillis() % 1000 * 1000 + usec;
511: }
512:
513: public RubyInteger sec() {
514: return getRuntime().newFixnum(cal.get(Calendar.SECOND));
515: }
516:
517: public RubyInteger min() {
518: return getRuntime().newFixnum(cal.get(Calendar.MINUTE));
519: }
520:
521: public RubyInteger hour() {
522: return getRuntime().newFixnum(cal.get(Calendar.HOUR_OF_DAY));
523: }
524:
525: public RubyInteger mday() {
526: return getRuntime().newFixnum(cal.get(Calendar.DAY_OF_MONTH));
527: }
528:
529: public RubyInteger month() {
530: return getRuntime().newFixnum(cal.get(Calendar.MONTH) + 1);
531: }
532:
533: public RubyInteger year() {
534: return getRuntime().newFixnum(cal.get(Calendar.YEAR));
535: }
536:
537: public RubyInteger wday() {
538: return getRuntime()
539: .newFixnum(cal.get(Calendar.DAY_OF_WEEK) - 1);
540: }
541:
542: public RubyInteger yday() {
543: return getRuntime().newFixnum(cal.get(Calendar.DAY_OF_YEAR));
544: }
545:
546: public RubyInteger gmt_offset() {
547: return getRuntime().newFixnum(
548: (int) (cal.get(Calendar.ZONE_OFFSET) / 1000));
549: }
550:
551: public RubyBoolean isdst() {
552: return getRuntime().newBoolean(
553: cal.getTimeZone().inDaylightTime(cal.getTime()));
554: }
555:
556: public RubyString zone() {
557: return getRuntime().newString(
558: cal.getTimeZone().getDisplayName(
559: cal.get(Calendar.DST_OFFSET) != 0,
560: TimeZone.SHORT));
561: }
562:
563: public void setJavaCalendar(Calendar cal) {
564: this .cal = cal;
565: }
566:
567: public Calendar getJavaCalendar() {
568: return this .cal;
569: }
570:
571: public Date getJavaDate() {
572: return this .cal.getTime();
573: }
574:
575: public RubyFixnum hash() {
576: // modified to match how hash is calculated in 1.8.2
577: return getRuntime()
578: .newFixnum(
579: (int) (((cal.getTimeInMillis() / 1000) ^ microseconds()) << 1) >> 1);
580: }
581:
582: public RubyString dump(final IRubyObject[] args, Block unusedBlock) {
583: if (args.length > 1) {
584: throw getRuntime().newArgumentError(0, 1);
585: }
586:
587: RubyString str = (RubyString) mdump(new IRubyObject[] { this });
588: str.setInstanceVariables(this .getInstanceVariables());
589: return str;
590: }
591:
592: public RubyObject mdump(final IRubyObject[] args) {
593: RubyTime obj = (RubyTime) args[0];
594: Calendar calendar = obj.gmtime().cal;
595: byte dumpValue[] = new byte[8];
596: int pe = 0x1 << 31 | (calendar.get(Calendar.YEAR) - 1900) << 14
597: | calendar.get(Calendar.MONTH) << 10
598: | calendar.get(Calendar.DAY_OF_MONTH) << 5
599: | calendar.get(Calendar.HOUR_OF_DAY);
600: int se = calendar.get(Calendar.MINUTE) << 26
601: | calendar.get(Calendar.SECOND) << 20
602: | calendar.get(Calendar.MILLISECOND);
603:
604: for (int i = 0; i < 4; i++) {
605: dumpValue[i] = (byte) (pe & 0xFF);
606: pe >>>= 8;
607: }
608: for (int i = 4; i < 8; i++) {
609: dumpValue[i] = (byte) (se & 0xFF);
610: se >>>= 8;
611: }
612: return RubyString.newString(obj.getRuntime(), new ByteList(
613: dumpValue, false));
614: }
615:
616: public IRubyObject initialize(IRubyObject[] args, Block block) {
617: return this ;
618: }
619:
620: /* Time class methods */
621:
622: public static IRubyObject s_new(IRubyObject recv,
623: IRubyObject[] args, Block block) {
624: Ruby runtime = recv.getRuntime();
625: RubyTime time = new RubyTime(runtime, (RubyClass) recv);
626: GregorianCalendar cal = new GregorianCalendar();
627: cal.setTime(new Date());
628: time.setJavaCalendar(cal);
629: time.callInit(args, block);
630: return time;
631: }
632:
633: public static IRubyObject new_at(IRubyObject recv,
634: IRubyObject[] args) {
635: Ruby runtime = recv.getRuntime();
636: int len = Arity.checkArgumentCount(runtime, args, 1, 2);
637:
638: Calendar cal = Calendar.getInstance();
639: RubyTime time = new RubyTime(runtime, (RubyClass) recv, cal);
640:
641: if (args[0] instanceof RubyTime) {
642: RubyTime other = (RubyTime) args[0];
643: other.updateCal(cal);
644: time.setUSec(other.getUSec());
645: } else {
646: long seconds = RubyNumeric.num2long(args[0]);
647: long millisecs = 0;
648: long microsecs = 0;
649: if (len > 1) {
650: long tmp = RubyNumeric.num2long(args[1]);
651: millisecs = tmp / 1000;
652: microsecs = tmp % 1000;
653: } else {
654: // In the case of two arguments, MRI will discard the portion of
655: // the first argument after a decimal point (i.e., "floor").
656: // However in the case of a single argument, any portion after
657: // the decimal point is honored.
658: if (args[0] instanceof RubyFloat) {
659: double dbl = ((RubyFloat) args[0]).getDoubleValue();
660: long micro = (long) ((dbl - seconds) * 1000000);
661: millisecs = micro / 1000;
662: microsecs = micro % 1000;
663: }
664: }
665: time.setUSec(microsecs);
666: cal.setTimeInMillis(seconds * 1000 + millisecs);
667: }
668:
669: time.callInit(IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
670:
671: return time;
672: }
673:
674: public static RubyTime new_local(IRubyObject recv,
675: IRubyObject[] args) {
676: return createTime(recv, args, false);
677: }
678:
679: public static RubyTime new_utc(IRubyObject recv, IRubyObject[] args) {
680: return createTime(recv, args, true);
681: }
682:
683: public static RubyTime s_load(IRubyObject recv, IRubyObject from,
684: Block block) {
685: return s_mload(recv,
686: (RubyTime) (((RubyClass) recv).allocate()), from);
687: }
688:
689: protected static RubyTime s_mload(IRubyObject recv, RubyTime time,
690: IRubyObject from) {
691: Ruby runtime = recv.getRuntime();
692: Calendar calendar = Calendar.getInstance();
693: calendar.clear();
694: calendar.setTimeZone(TimeZone.getTimeZone(RubyTime.UTC));
695: byte[] fromAsBytes = null;
696: fromAsBytes = from.convertToString().getBytes();
697: if (fromAsBytes.length != 8) {
698: throw runtime.newTypeError("marshaled time format differ");
699: }
700: int p = 0;
701: int s = 0;
702: for (int i = 0; i < 4; i++) {
703: p |= ((int) fromAsBytes[i] & 0xFF) << (8 * i);
704: }
705: for (int i = 4; i < 8; i++) {
706: s |= ((int) fromAsBytes[i] & 0xFF) << (8 * (i - 4));
707: }
708: if ((p & (1 << 31)) == 0) {
709: calendar.setTimeInMillis(p * 1000L + s);
710: } else {
711: p &= ~(1 << 31);
712: calendar.set(Calendar.YEAR, ((p >>> 14) & 0xFFFF) + 1900);
713: calendar.set(Calendar.MONTH, ((p >>> 10) & 0xF));
714: calendar.set(Calendar.DAY_OF_MONTH, ((p >>> 5) & 0x1F));
715: calendar.set(Calendar.HOUR_OF_DAY, (p & 0x1F));
716: calendar.set(Calendar.MINUTE, ((s >>> 26) & 0x3F));
717: calendar.set(Calendar.SECOND, ((s >>> 20) & 0x3F));
718: calendar.set(Calendar.MILLISECOND, (s & 0xFFFFF));
719: }
720: time.setJavaCalendar(calendar);
721: return time;
722: }
723:
724: private static final String[] months = { "jan", "feb", "mar",
725: "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov",
726: "dec" };
727: private static final long[] time_min = { 1, 0, 0, 0, 0 };
728: private static final long[] time_max = { 31, 23, 59, 60,
729: Long.MAX_VALUE };
730:
731: private static RubyTime createTime(IRubyObject recv,
732: IRubyObject[] args, boolean gmt) {
733: Ruby runtime = recv.getRuntime();
734: int len = 6;
735:
736: if (args.length == 10) {
737: args = new IRubyObject[] { args[5], args[4], args[3],
738: args[2], args[1], args[0] };
739: } else {
740: // MRI accepts additional wday argument which appears to be ignored.
741: len = Arity.checkArgumentCount(runtime, args, 1, 8);
742: }
743: ThreadContext tc = runtime.getCurrentContext();
744:
745: if (args[0] instanceof RubyString) {
746: args[0] = RubyNumeric.str2inum(runtime,
747: (RubyString) args[0], 10, false);
748: }
749:
750: int year = (int) RubyNumeric.num2long(args[0]);
751: int month = 0;
752:
753: if (len > 1) {
754: if (!args[1].isNil()) {
755: if (args[1] instanceof RubyString) {
756: month = -1;
757: for (int i = 0; i < 12; i++) {
758: if (months[i].equalsIgnoreCase(args[1]
759: .toString())) {
760: month = i;
761: }
762: }
763: if (month == -1) {
764: try {
765: month = Integer
766: .parseInt(args[1].toString()) - 1;
767: } catch (NumberFormatException nfExcptn) {
768: throw runtime
769: .newArgumentError("Argument out of range.");
770: }
771: }
772: } else {
773: month = (int) RubyNumeric.num2long(args[1]) - 1;
774: }
775: }
776: if (0 > month || month > 11) {
777: throw runtime
778: .newArgumentError("Argument out of range.");
779: }
780: }
781:
782: int[] int_args = { 1, 0, 0, 0, 0 };
783:
784: for (int i = 0; len > i + 2; i++) {
785: if (!args[i + 2].isNil()) {
786: if (!(args[i + 2] instanceof RubyNumeric)) {
787: args[i + 2] = args[i + 2].callMethod(tc, "to_i");
788: }
789: int_args[i] = (int) RubyNumeric.num2long(args[i + 2]);
790: if (time_min[i] > int_args[i]
791: || int_args[i] > time_max[i]) {
792: throw runtime
793: .newArgumentError("Argument out of range.");
794: }
795: }
796: }
797:
798: if (year < 100)
799: year += 2000;
800:
801: Calendar cal;
802: if (gmt) {
803: cal = Calendar.getInstance(TimeZone
804: .getTimeZone(RubyTime.UTC));
805: } else {
806: cal = Calendar.getInstance(RubyTime
807: .getLocalTimeZone(runtime));
808: }
809: cal.set(year, month, int_args[0], int_args[1], int_args[2],
810: int_args[3]);
811: cal.set(Calendar.MILLISECOND, int_args[4] / 1000);
812:
813: if (cal.getTimeInMillis() / 1000 < -0x80000000) {
814: throw runtime.newArgumentError("time out of range");
815: }
816:
817: RubyTime time = new RubyTime(runtime, (RubyClass) recv, cal);
818:
819: time.setUSec(int_args[4] % 1000);
820: time.callInit(IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
821:
822: return time;
823: }
824: }
|