sproutcore 1.0.1042 → 1.0.1043
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION.yml +3 -3
- data/doc_templates/sproutcore/publish.js +2 -1
- data/frameworks/sproutcore/apps/tests/english.lproj/main_page.css +8 -3
- data/frameworks/sproutcore/apps/tests/english.lproj/main_page.js +5 -4
- data/frameworks/sproutcore/frameworks/animation/core.js +80 -30
- data/frameworks/sproutcore/frameworks/animation/tests/core.js +26 -1
- data/frameworks/sproutcore/frameworks/datastore/system/store.js +47 -4
- data/frameworks/sproutcore/frameworks/desktop/english.lproj/toolbar.css +0 -1
- data/frameworks/sproutcore/frameworks/foundation/mixins/button.js +9 -2
- data/frameworks/sproutcore/frameworks/foundation/system/datetime.js +331 -122
- data/frameworks/sproutcore/frameworks/foundation/system/root_responder.js +92 -1
- data/frameworks/sproutcore/frameworks/foundation/tests/mixins/button/ui.js +68 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/system/datetime.js +191 -90
- data/frameworks/sproutcore/frameworks/mobile/system/root_responder.js +0 -97
- data/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +1 -0
- data/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/observable.js +6 -0
- data/frameworks/sproutcore/themes/standard_theme/Source/SproutCore Theme Buttons.psd +0 -0
- data/lib/sproutcore/builders/minify.rb +6 -7
- data/lib/sproutcore/models/target.rb +8 -8
- data/lib/sproutcore/rack/builder.rb +10 -1
- data/lib/sproutcore/tools/build.rb +21 -16
- metadata +4 -2
@@ -152,11 +152,34 @@ SC.Scanner = SC.Object.extend(
|
|
152
152
|
A class representation of a date and time. It's basically a wrapper around
|
153
153
|
the Date javascript object, KVO friendly and with common date/time
|
154
154
|
manipulation methods.
|
155
|
+
|
156
|
+
This object differs from the standard JS Date object, however, in that it
|
157
|
+
supports time zones other than UTC and that local to the machine on which
|
158
|
+
it is running. Any time zone can be specified when creating an SC.DateTime
|
159
|
+
object, e.g
|
160
|
+
|
161
|
+
// Creates a DateTime representing 5am in Washington, DC and 10am in London
|
162
|
+
var d = SC.DateTime.create({ hour: 5, timezone: 300 }); // -5 hours from UTC
|
163
|
+
var e = SC.DateTime.create({ hour: 10, timezone: 0 }); // same time, specified in UTC
|
164
|
+
|
165
|
+
and it is true that d.isEqual(e).
|
166
|
+
|
167
|
+
The time zone specified upon creation is permanent, and any calls to get() on that
|
168
|
+
instance will return values expressed in that time zone. So,
|
169
|
+
|
170
|
+
d.get('hour') returns 5.
|
171
|
+
e.get('hour') returns 10.
|
172
|
+
|
173
|
+
but
|
174
|
+
|
175
|
+
d.get('milliseconds') === e.get('milliseconds') is true, since they are technically the same position in time.
|
155
176
|
|
156
177
|
@extends SC.Object
|
157
178
|
@extends SC.Freezable
|
158
179
|
@extends SC.Copyable
|
159
180
|
@author Martin Ottenwaelter
|
181
|
+
@author Jonathan Lewis
|
182
|
+
@author Josh Holt
|
160
183
|
@since SproutCore 1.0
|
161
184
|
*/
|
162
185
|
SC.DateTime = SC.Object.extend(SC.Freezable, SC.Copyable,
|
@@ -171,8 +194,10 @@ SC.DateTime = SC.Object.extend(SC.Freezable, SC.Copyable,
|
|
171
194
|
*/
|
172
195
|
_ms: 0,
|
173
196
|
|
174
|
-
/**
|
197
|
+
/** @read-only
|
175
198
|
The offset, in minutes, between UTC and the object's timezone.
|
199
|
+
All calls to get() will use this time zone to translate date/time
|
200
|
+
values into the zone specified here.
|
176
201
|
|
177
202
|
@property
|
178
203
|
@type {Integer}
|
@@ -196,11 +221,23 @@ SC.DateTime = SC.Object.extend(SC.Freezable, SC.Copyable,
|
|
196
221
|
|
197
222
|
(Parts copied from the Ruby On Rails documentation)
|
198
223
|
|
224
|
+
If a time zone is passed in the options hash, all dates and times are assumed
|
225
|
+
to be local to it, and the returned DateTime instance has that time zone. If
|
226
|
+
none is passed, it defaults to SC.DateTime.timezone.
|
227
|
+
|
228
|
+
Note that passing only a time zone does not affect the actual milliseconds since
|
229
|
+
Jan 1, 1970, only the time zone in which it is expressed when displayed.
|
230
|
+
|
199
231
|
@see SC.DateTime#create for the list of options you can pass
|
200
232
|
@returns {DateTime} copy of receiver
|
201
233
|
*/
|
202
|
-
adjust: function(options) {
|
203
|
-
|
234
|
+
adjust: function(options, resetCascadingly) {
|
235
|
+
var timezone;
|
236
|
+
|
237
|
+
options = options ? SC.clone(options) : {};
|
238
|
+
timezone = (options.timezone !== undefined) ? options.timezone : (this.timezone !== undefined) ? this.timezone : 0;
|
239
|
+
|
240
|
+
return this.constructor._adjust(options, this._ms, timezone, resetCascadingly)._createFromCurrentState();
|
204
241
|
},
|
205
242
|
|
206
243
|
/**
|
@@ -212,7 +249,7 @@ SC.DateTime = SC.Object.extend(SC.Freezable, SC.Copyable,
|
|
212
249
|
@returns {DateTime} copy of the receiver
|
213
250
|
*/
|
214
251
|
advance: function(options) {
|
215
|
-
|
252
|
+
return this.constructor._advance(options, this._ms, this.timezone)._createFromCurrentState();
|
216
253
|
},
|
217
254
|
|
218
255
|
/**
|
@@ -271,7 +308,7 @@ SC.DateTime = SC.Object.extend(SC.Freezable, SC.Copyable,
|
|
271
308
|
starting with the first Sunday as the first
|
272
309
|
day of the first week (00..53)
|
273
310
|
- %W - Week number of the current year,
|
274
|
-
starting with the first Monday as the first
|
311
|
+
starting with the first Monday as the first
|
275
312
|
day of the first week (00..53)
|
276
313
|
- %w - Day of the week (Sunday is 0, 0..6)
|
277
314
|
- %x - Preferred representation for the date alone, no time
|
@@ -344,12 +381,16 @@ SC.DateTime = SC.Object.extend(SC.Freezable, SC.Copyable,
|
|
344
381
|
|
345
382
|
If you don't pass any argument, the target timezone is assumed to be 0,
|
346
383
|
ie UTC.
|
384
|
+
|
385
|
+
Note that this method does not change the underlying position in time,
|
386
|
+
but only the time zone in which it is displayed. In other words, the underlying
|
387
|
+
number of milliseconds since Jan 1, 1970 does not change.
|
347
388
|
|
348
389
|
@return {DateTime}
|
349
390
|
*/
|
350
391
|
toTimezone: function(timezone) {
|
351
392
|
if (timezone === undefined) timezone = 0;
|
352
|
-
return this.advance({ timezone: timezone-this.timezone });
|
393
|
+
return this.advance({ timezone: timezone - this.timezone });
|
353
394
|
}
|
354
395
|
|
355
396
|
});
|
@@ -424,6 +465,14 @@ SC.DateTime.mixin(SC.Comparable,
|
|
424
465
|
The unique internal Date object used to make computations. Better
|
425
466
|
performance is obtained by having only one Date object for the whole
|
426
467
|
application and manipulating it with setTime() and getTime().
|
468
|
+
|
469
|
+
Note that since this is used for internal calculations across many
|
470
|
+
DateTime instances, it is not guaranteed to store the date/time that
|
471
|
+
any one DateTime instance represents. So it might be that
|
472
|
+
|
473
|
+
this._date.getTime() !== this._ms
|
474
|
+
|
475
|
+
Be sure to set it before using for internal calculations if necessary.
|
427
476
|
|
428
477
|
@property
|
429
478
|
@type {Date}
|
@@ -477,106 +526,239 @@ SC.DateTime.mixin(SC.Comparable,
|
|
477
526
|
_DT_CACHE_MAX_LENGTH: 1000,
|
478
527
|
|
479
528
|
/** @private
|
480
|
-
|
529
|
+
Both args are optional, but will only overwrite _date and _tz if defined.
|
530
|
+
This method does not affect the DateTime instance's actual time, but simply
|
531
|
+
initializes the one _date instance to a time relevant for a calculation.
|
532
|
+
(this._date is just a resource optimization)
|
533
|
+
|
534
|
+
This is mainly used as a way to store a recursion starting state during
|
535
|
+
internal calculations.
|
536
|
+
|
537
|
+
'milliseconds' is time since Jan 1, 1970.
|
538
|
+
'timezone' is the current time zone we want to be working in internally.
|
539
|
+
|
540
|
+
Returns a hash of the previous milliseconds and time zone in case they
|
541
|
+
are wanted for later restoration.
|
481
542
|
*/
|
482
|
-
|
483
|
-
|
543
|
+
_setCalcState: function(ms, timezone) {
|
544
|
+
var previous = {
|
545
|
+
milliseconds: this._date.getTime(),
|
546
|
+
timezone: this._tz
|
547
|
+
};
|
548
|
+
|
549
|
+
if (ms !== undefined) this._date.setTime(ms);
|
484
550
|
if (timezone !== undefined) this._tz = timezone;
|
551
|
+
|
552
|
+
return previous;
|
485
553
|
},
|
486
|
-
|
554
|
+
|
555
|
+
/**
|
556
|
+
By this time, any time zone setting on 'hash' will be ignored.
|
557
|
+
'timezone' will be used, or the last this._tz.
|
558
|
+
*/
|
559
|
+
_setCalcStateFromHash: function(hash, timezone) {
|
560
|
+
var tz = (timezone !== undefined) ? timezone : this._tz; // use the last-known time zone if necessary
|
561
|
+
var ms = this._toMilliseconds(hash, this._ms, tz); // convert the hash (local to specified time zone) to milliseconds (in UTC)
|
562
|
+
return this._setCalcState(ms, tz); // now call the one we really wanted
|
563
|
+
},
|
564
|
+
|
487
565
|
/** @private
|
488
566
|
@see SC.DateTime#unknownProperty
|
489
567
|
*/
|
490
568
|
_get: function(key, start, timezone) {
|
569
|
+
var ms, tz, doy, m, y, firstDayOfWeek, dayOfWeek, dayOfYear, prefix, suffix;
|
570
|
+
var currentWeekday, targetWeekday;
|
491
571
|
var d = this._date;
|
492
|
-
|
493
|
-
|
494
|
-
//
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
572
|
+
var originalTime, v = null;
|
573
|
+
|
574
|
+
// Set up an absolute date/time using the given milliseconds since Jan 1, 1970.
|
575
|
+
// Only do it if we're given a time value, though, otherwise we want to use the
|
576
|
+
// last one we had because this _get() method is recursive.
|
577
|
+
//
|
578
|
+
// Note that because these private time calc methods are recursive, and because all DateTime instances
|
579
|
+
// share an internal this._date and this._tz state for doing calculations, methods
|
580
|
+
// that modify this._date or this._tz should restore the last state before exiting
|
581
|
+
// to avoid obscure calculation bugs. So we save the original state here, and restore
|
582
|
+
// it before returning at the end.
|
583
|
+
originalTime = this._setCalcState(start, timezone); // save so we can restore it to how it was before we got here
|
584
|
+
|
585
|
+
// Check this first because it is an absolute value -- no tweaks necessary when calling for milliseconds
|
586
|
+
if (key === 'milliseconds') {
|
587
|
+
v = d.getTime();
|
506
588
|
}
|
507
|
-
|
508
|
-
|
509
|
-
if (key === 'isLeapYear') {
|
510
|
-
var y = this._get('year');
|
511
|
-
return (y%4 === 0 && y%100 !== 0) || y%400 === 0;
|
589
|
+
else if (key === 'timezone') {
|
590
|
+
v = this._tz;
|
512
591
|
}
|
513
592
|
|
514
|
-
//
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
593
|
+
// 'nextWeekday' or 'lastWeekday'.
|
594
|
+
// We want to do this calculation in local time, before shifting UTC below.
|
595
|
+
if (v === null) {
|
596
|
+
prefix = key.slice(0, 4);
|
597
|
+
suffix = key.slice(4);
|
598
|
+
if (prefix === 'last' || prefix === 'next') {
|
599
|
+
currentWeekday = d.getDay();
|
600
|
+
targetWeekday = this._englishDayNames.indexOf(suffix);
|
601
|
+
if (targetWeekday >= 0) {
|
602
|
+
var delta = targetWeekday - currentWeekday;
|
603
|
+
if (prefix === 'last' && delta >= 0) delta -= 7;
|
604
|
+
if (prefix === 'next' && delta < 0) delta += 7;
|
605
|
+
this._advance({ day: delta });
|
606
|
+
v = this._createFromCurrentState();
|
607
|
+
}
|
526
608
|
}
|
527
609
|
}
|
528
610
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
doy += this._adjust({month: m})._get('daysInMonth');
|
611
|
+
if (v === null) {
|
612
|
+
// need to adjust for alternate display time zone.
|
613
|
+
// Before calculating, we need to get everything into a common time zone to
|
614
|
+
// negate the effects of local machine time (so we can use all the 'getUTC...() methods on Date).
|
615
|
+
if (timezone !== undefined) {
|
616
|
+
this._setCalcState(d.getTime() - (timezone * 60000), 0); // make this instance's time zone the new UTC temporarily
|
536
617
|
}
|
537
|
-
d.setTime(ms); // restore time
|
538
|
-
return doy;
|
539
|
-
}
|
540
618
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
619
|
+
// simple keys
|
620
|
+
switch (key) {
|
621
|
+
case 'year':
|
622
|
+
v = d.getUTCFullYear(); //TODO: investigate why some libraries do getFullYear().toString() or getFullYear()+""
|
623
|
+
break;
|
624
|
+
case 'month':
|
625
|
+
v = d.getUTCMonth()+1; // January is 0 in JavaScript
|
626
|
+
break;
|
627
|
+
case 'day':
|
628
|
+
v = d.getUTCDate();
|
629
|
+
break;
|
630
|
+
case 'dayOfWeek':
|
631
|
+
v = d.getUTCDay();
|
632
|
+
break;
|
633
|
+
case 'hour':
|
634
|
+
v = d.getUTCHours();
|
635
|
+
break;
|
636
|
+
case 'minute':
|
637
|
+
v = d.getUTCMinutes();
|
638
|
+
break;
|
639
|
+
case 'second':
|
640
|
+
v = d.getUTCSeconds();
|
641
|
+
break;
|
642
|
+
case 'millisecond':
|
643
|
+
v = d.getUTCMilliseconds();
|
644
|
+
break;
|
645
|
+
}
|
646
|
+
|
647
|
+
// isLeapYear
|
648
|
+
if ((v === null) && (key === 'isLeapYear')) {
|
649
|
+
y = this._get('year');
|
650
|
+
v = (y%4 === 0 && y%100 !== 0) || y%400 === 0;
|
651
|
+
}
|
652
|
+
|
653
|
+
// daysInMonth
|
654
|
+
if ((v === null) && (key === 'daysInMonth')) {
|
655
|
+
switch (this._get('month')) {
|
656
|
+
case 4:
|
657
|
+
case 6:
|
658
|
+
case 9:
|
659
|
+
case 11:
|
660
|
+
v = 30;
|
661
|
+
break;
|
662
|
+
case 2:
|
663
|
+
v = this._get('isLeapYear') ? 29 : 28;
|
664
|
+
break;
|
665
|
+
default:
|
666
|
+
v = 31;
|
667
|
+
break;
|
668
|
+
}
|
669
|
+
}
|
553
670
|
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
671
|
+
// dayOfYear
|
672
|
+
if ((v === null) && (key === 'dayOfYear')) {
|
673
|
+
ms = d.getTime(); // save time
|
674
|
+
doy = this._get('day');
|
675
|
+
this._setCalcStateFromHash({ day: 1 });
|
676
|
+
for (m = this._get('month') - 1; m > 0; m--) {
|
677
|
+
this._setCalcStateFromHash({ month: m });
|
678
|
+
doy += this._get('daysInMonth');
|
679
|
+
}
|
680
|
+
d.setTime(ms); // restore time
|
681
|
+
v = doy;
|
682
|
+
}
|
683
|
+
|
684
|
+
// week, week0 or week1
|
685
|
+
if ((v === null) && (key.slice(0, 4) === 'week')) {
|
686
|
+
// firstDayOfWeek should be 0 (Sunday) or 1 (Monday)
|
687
|
+
firstDayOfWeek = key.length === 4 ? 1 : parseInt(key.slice('4'), 10);
|
688
|
+
dayOfWeek = this._get('dayOfWeek');
|
689
|
+
dayOfYear = this._get('dayOfYear') - 1;
|
690
|
+
if (firstDayOfWeek === 0) {
|
691
|
+
v = parseInt((dayOfYear - dayOfWeek + 7) / 7, 10);
|
692
|
+
}
|
693
|
+
else {
|
694
|
+
v = parseInt((dayOfYear - (dayOfWeek - 1 + 7) % 7 + 7) / 7, 10);
|
695
|
+
}
|
566
696
|
}
|
567
697
|
}
|
568
698
|
|
569
|
-
|
699
|
+
// restore the internal calculation state in case someone else was in the
|
700
|
+
// middle of a calculation (we might be recursing).
|
701
|
+
this._setCalcState(originalTime.milliseconds, originalTime.timezone);
|
702
|
+
|
703
|
+
return v;
|
704
|
+
},
|
705
|
+
|
706
|
+
/**
|
707
|
+
Sets the internal calculation state to something specified.
|
708
|
+
*/
|
709
|
+
_adjust: function(options, start, timezone, resetCascadingly) {
|
710
|
+
var opts = options ? SC.clone(options) : {};
|
711
|
+
var ms = this._toMilliseconds(options, start, timezone, resetCascadingly);
|
712
|
+
this._setCalcState(ms, timezone);
|
713
|
+
return this; // for chaining
|
570
714
|
},
|
571
715
|
|
572
716
|
/** @private
|
573
|
-
@see SC.DateTime#
|
717
|
+
@see SC.DateTime#advance
|
574
718
|
*/
|
575
|
-
|
719
|
+
_advance: function(options, start, timezone) {
|
576
720
|
var opts = options ? SC.clone(options) : {};
|
721
|
+
var tz;
|
722
|
+
|
723
|
+
for (var key in opts) {
|
724
|
+
opts[key] += this._get(key, start, timezone);
|
725
|
+
}
|
577
726
|
|
727
|
+
// The time zone can be advanced by a delta as well, so try to use the
|
728
|
+
// new value if there is one.
|
729
|
+
tz = (opts.timezone !== undefined) ? opts.timezone : timezone; // watch out for zero, which is acceptable as a time zone
|
730
|
+
|
731
|
+
return this._adjust(opts, start, tz, NO);
|
732
|
+
},
|
733
|
+
|
734
|
+
/* @private
|
735
|
+
Converts a standard date/time options hash to an integer representing that position
|
736
|
+
in time relative to Jan 1, 1970
|
737
|
+
*/
|
738
|
+
_toMilliseconds: function(options, start, timezone, resetCascadingly) {
|
739
|
+
var opts = options ? SC.clone(options) : {};
|
578
740
|
var d = this._date;
|
579
|
-
|
741
|
+
var previousMilliseconds = d.getTime(); // rather than create a new Date object, we'll reuse the instance we have for calculations, then restore it
|
742
|
+
var ms, tz;
|
743
|
+
|
744
|
+
// Initialize our internal for-calculations Date object to our current date/time.
|
745
|
+
// Note that this object was created in the local machine time zone, so when we set
|
746
|
+
// its params later, it will be assuming these values to be in the same time zone as it is.
|
747
|
+
// It's ok for start to be null, in which case we'll just keep whatever we had in 'd' before.
|
748
|
+
if (!SC.none(start)) {
|
749
|
+
d.setTime(start); // using milliseconds here specifies an absolute location in time, regardless of time zone, so that's nice
|
750
|
+
}
|
751
|
+
|
752
|
+
// We have to get all time expressions, both in 'options' (assume to be in time zone 'timezone')
|
753
|
+
// and in 'd', to the same time zone before we can any calculations correctly. So because the Date object provides
|
754
|
+
// a suite of UTC getters and setters, we'll temporarily redefine 'timezone' as our new
|
755
|
+
// 'UTC', so we don't have to worry about local machine time. We do this by subtracting
|
756
|
+
// milliseconds for the time zone offset. Then we'll do all our calculations, then convert
|
757
|
+
// it back to real UTC.
|
758
|
+
|
759
|
+
// (Zero time zone is considered a valid value.)
|
760
|
+
tz = (timezone !== undefined) ? timezone : (this.timezone !== undefined) ? this.timezone : 0;
|
761
|
+
d.setTime(d.getTime() - (tz * 60000)); // redefine 'UTC' to establish a new local absolute so we can use all the 'getUTC...()' Date methods
|
580
762
|
|
581
763
|
// the time options (hour, minute, sec, millisecond)
|
582
764
|
// reset cascadingly (see documentation)
|
@@ -593,33 +775,36 @@ SC.DateTime.mixin(SC.Comparable,
|
|
593
775
|
opts.millisecond = 0;
|
594
776
|
}
|
595
777
|
}
|
596
|
-
|
597
|
-
if (!SC.none(opts.year)) d.setFullYear(opts.year);
|
598
|
-
if (!SC.none(opts.month)) d.setMonth(opts.month-1); // January is 0 in JavaScript
|
599
|
-
if (!SC.none(opts.day)) d.setDate(opts.day);
|
600
|
-
if (!SC.none(opts.hour)) d.setHours(opts.hour);
|
601
|
-
if (!SC.none(opts.minute)) d.setMinutes(opts.minute);
|
602
|
-
if (!SC.none(opts.second)) d.setSeconds(opts.second);
|
603
|
-
if (!SC.none(opts.millisecond)) d.setMilliseconds(opts.millisecond);
|
604
|
-
if (!SC.none(opts.timezone)) this._tz = opts.timezone;
|
605
778
|
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
if (
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
for
|
779
|
+
// Get the current values for any not provided in the options hash.
|
780
|
+
// Since everything is in 'UTC' now, use the UTC accessors. We do this because,
|
781
|
+
// according to javascript Date spec, you have to set year, month, and day together
|
782
|
+
// if you're setting any one of them. So we'll use the provided Date.UTC() method
|
783
|
+
// to get milliseconds, and we need to get any missing values first...
|
784
|
+
if (SC.none(opts.year)) opts.year = d.getUTCFullYear();
|
785
|
+
if (SC.none(opts.month)) opts.month = d.getUTCMonth() + 1; // January is 0 in JavaScript
|
786
|
+
if (SC.none(opts.day)) opts.day = d.getUTCDate();
|
787
|
+
if (SC.none(opts.hour)) opts.hour = d.getUTCHours();
|
788
|
+
if (SC.none(opts.minute)) opts.minute = d.getUTCMinutes();
|
789
|
+
if (SC.none(opts.second)) opts.second = d.getUTCSeconds();
|
790
|
+
if (SC.none(opts.millisecond)) opts.millisecond = d.getUTCMilliseconds();
|
791
|
+
|
792
|
+
// Ask the JS Date to calculate milliseconds for us (still in redefined UTC). It
|
793
|
+
// is best to set them all together because, for example, a day value means different things
|
794
|
+
// to the JS Date object depending on which month or year it is. It can now handle that stuff
|
795
|
+
// internally as it's made to do.
|
796
|
+
ms = Date.UTC(opts.year, opts.month - 1, opts.day, opts.hour, opts.minute, opts.second, opts.millisecond);
|
797
|
+
|
798
|
+
// Now that we've done all our calculations in a common time zone, add back the offset
|
799
|
+
// to move back to real UTC.
|
800
|
+
d.setTime(ms + (tz * 60000));
|
801
|
+
ms = d.getTime(); // now get the corrected milliseconds value
|
621
802
|
|
622
|
-
|
803
|
+
// Restore what was there previously before leaving in case someone called this method
|
804
|
+
// in the middle of another calculation.
|
805
|
+
d.setTime(previousMilliseconds);
|
806
|
+
|
807
|
+
return ms;
|
623
808
|
},
|
624
809
|
|
625
810
|
/**
|
@@ -647,31 +832,41 @@ SC.DateTime.mixin(SC.Comparable,
|
|
647
832
|
*/
|
648
833
|
create: function() {
|
649
834
|
var arg = arguments.length === 0 ? {} : arguments[0];
|
835
|
+
var timezone;
|
650
836
|
|
837
|
+
// if simply milliseconds since Jan 1, 1970 are given, just use those
|
651
838
|
if (SC.typeOf(arg) === SC.T_NUMBER) {
|
652
|
-
arg = { milliseconds: arg
|
653
|
-
} else if (SC.none(arg.timezone)) {
|
654
|
-
arg.timezone = this.timezone;
|
839
|
+
arg = { milliseconds: arg };
|
655
840
|
}
|
656
|
-
|
841
|
+
|
842
|
+
// Default to local machine time zone if none is given
|
843
|
+
timezone = (arg.timezone !== undefined) ? arg.timezone : this.timezone;
|
844
|
+
if (timezone === undefined) timezone = 0;
|
845
|
+
|
846
|
+
// Desired case: create with milliseconds if we have them.
|
847
|
+
// If we don't, convert what we have to milliseconds and recurse.
|
657
848
|
if (!SC.none(arg.milliseconds)) {
|
849
|
+
|
658
850
|
// quick implementation of a FIFO set for the cache
|
659
|
-
var key = 'nu'+arg.milliseconds+
|
851
|
+
var key = 'nu' + arg.milliseconds + timezone, cache = this._dt_cache;
|
660
852
|
var ret = cache[key];
|
661
853
|
if (!ret) {
|
662
854
|
var previousKey, idx = this._dt_cache_index, C = this;
|
663
|
-
ret = cache[key] = new C([{_ms: arg.milliseconds, timezone:
|
855
|
+
ret = cache[key] = new C([{ _ms: arg.milliseconds, timezone: timezone }]);
|
664
856
|
idx = this._dt_cache_index = (idx + 1) % this._DT_CACHE_MAX_LENGTH;
|
665
857
|
previousKey = cache[idx];
|
666
858
|
if (previousKey !== undefined && cache[previousKey]) delete cache[previousKey];
|
667
859
|
cache[idx] = key;
|
668
860
|
}
|
669
861
|
return ret;
|
670
|
-
}
|
862
|
+
}
|
863
|
+
// otherwise, convert what we have to milliseconds and try again
|
864
|
+
else {
|
671
865
|
var now = new Date();
|
672
|
-
|
673
|
-
|
674
|
-
|
866
|
+
|
867
|
+
return this.create({ // recursive call with new arguments
|
868
|
+
milliseconds: this._toMilliseconds(arg, now.getTime(), timezone, arg.resetCascadingly),
|
869
|
+
timezone: timezone
|
675
870
|
});
|
676
871
|
}
|
677
872
|
|
@@ -775,9 +970,19 @@ SC.DateTime.mixin(SC.Comparable,
|
|
775
970
|
},
|
776
971
|
|
777
972
|
/** @private
|
778
|
-
@see SC.DateTime#
|
973
|
+
@see SC.DateTime#_toFormattedString
|
779
974
|
*/
|
780
|
-
__toFormattedString: function(part) {
|
975
|
+
__toFormattedString: function(part, start, timezone) {
|
976
|
+
var hour, offset;
|
977
|
+
|
978
|
+
// Note: all calls to _get() here should include only one
|
979
|
+
// argument, since _get() is built for recursion and behaves differently
|
980
|
+
// if arguments 2 and 3 are included.
|
981
|
+
//
|
982
|
+
// This method is simply a helper for this._toFormattedString() (one underscore);
|
983
|
+
// this is only called from there, and _toFormattedString() has already
|
984
|
+
// set up the appropriate internal date/time/timezone state for it.
|
985
|
+
|
781
986
|
switch(part[1]) {
|
782
987
|
case 'a': return this.abbreviatedDayNames[this._get('dayOfWeek')];
|
783
988
|
case 'A': return this.dayNames[this._get('dayOfWeek')];
|
@@ -788,10 +993,10 @@ SC.DateTime.mixin(SC.Comparable,
|
|
788
993
|
case 'h': return this._get('hour');
|
789
994
|
case 'H': return this._pad(this._get('hour'));
|
790
995
|
case 'i':
|
791
|
-
|
996
|
+
hour = this._get('hour');
|
792
997
|
return (hour === 12 || hour === 0) ? 12 : (hour + 12) % 12;
|
793
998
|
case 'I':
|
794
|
-
|
999
|
+
hour = this._get('hour');
|
795
1000
|
return this._pad((hour === 12 || hour === 0) ? 12 : (hour + 12) % 12);
|
796
1001
|
case 'j': return this._pad(this._get('dayOfYear'), 3);
|
797
1002
|
case 'm': return this._pad(this._get('month'));
|
@@ -807,7 +1012,7 @@ SC.DateTime.mixin(SC.Comparable,
|
|
807
1012
|
case 'y': return this._pad(this._get('year') % 100);
|
808
1013
|
case 'Y': return this._get('year');
|
809
1014
|
case 'Z':
|
810
|
-
|
1015
|
+
offset = -1 * timezone;
|
811
1016
|
return (offset >= 0 ? '+' : '-')
|
812
1017
|
+ this._pad(parseInt(Math.abs(offset)/60, 10))
|
813
1018
|
+ ':'
|
@@ -820,11 +1025,15 @@ SC.DateTime.mixin(SC.Comparable,
|
|
820
1025
|
@see SC.DateTime#toFormattedString
|
821
1026
|
*/
|
822
1027
|
_toFormattedString: function(format, start, timezone) {
|
823
|
-
this._setState(start, timezone);
|
824
|
-
|
825
1028
|
var that = this;
|
1029
|
+
var tz = (timezone !== undefined) ? timezone : (this.timezone !== undefined) ? this.timezone : 0;
|
1030
|
+
|
1031
|
+
// need to move into local time zone for these calculations
|
1032
|
+
this._setCalcState(start - (timezone * 60000), 0); // so simulate a shifted 'UTC' time
|
1033
|
+
|
826
1034
|
return format.replace(/\%([aAbBcdHIjmMpSUWwxXyYZ\%])/g, function() {
|
827
|
-
|
1035
|
+
var v = that.__toFormattedString.call(that, arguments, start, timezone);
|
1036
|
+
return v;
|
828
1037
|
});
|
829
1038
|
},
|
830
1039
|
|