mirror of
https://github.com/wassname/fullcalendar.git
synced 2026-09-11 12:10:23 +08:00
fixed a bunch of moment/timezone issues
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
"version": "2.0.0-beta2",
|
||||
"devDependencies": {
|
||||
"moment": "2.6.0",
|
||||
"moment-timezone": "~0.0.6",
|
||||
"jquery": "~1.10.2",
|
||||
"jquery-ui": "~1.10.3",
|
||||
"jquery-simulate-ext": "~1.3.0",
|
||||
|
||||
+7
-2
@@ -103,12 +103,17 @@ function Calendar(element, instanceOptions) {
|
||||
|
||||
if (options.timezone === 'local') {
|
||||
mom = fc.moment.apply(null, arguments);
|
||||
|
||||
// Force the moment to be local, because fc.moment doesn't guarantee it.
|
||||
if (mom.hasTime()) { // don't give ambiguously-timed moments a local zone
|
||||
mom.local();
|
||||
}
|
||||
}
|
||||
else if (options.timezone === 'UTC') {
|
||||
mom = fc.moment.utc.apply(null, arguments);
|
||||
mom = fc.moment.utc.apply(null, arguments); // process as UTC
|
||||
}
|
||||
else {
|
||||
mom = fc.moment.parseZone.apply(null, arguments);
|
||||
mom = fc.moment.parseZone.apply(null, arguments); // let the input decide the zone
|
||||
}
|
||||
|
||||
mom._lang = langData;
|
||||
|
||||
+126
-89
@@ -6,99 +6,119 @@ var ambigTimeOrZoneRegex = /^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d
|
||||
// Creating
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
// Creates a moment in the local timezone, similar to the vanilla moment(...) constructor,
|
||||
// but with extra features:
|
||||
// - ambiguous times
|
||||
// - enhanced formatting
|
||||
// Creates a new moment, similar to the vanilla moment(...) constructor, but with
|
||||
// extra features (ambiguous time, enhanced formatting). When gived an existing moment,
|
||||
// it will function as a clone (and retain the zone of the moment). Anything else will
|
||||
// result in a moment in the local zone.
|
||||
fc.moment = function() {
|
||||
return makeMoment(arguments);
|
||||
};
|
||||
|
||||
// Sames as fc.moment, but creates a moment in the UTC timezone.
|
||||
// Sames as fc.moment, but forces the resulting moment to be in the UTC timezone.
|
||||
fc.moment.utc = function() {
|
||||
return makeMoment(arguments, true);
|
||||
var mom = makeMoment(arguments, true);
|
||||
|
||||
// Force it into UTC because makeMoment doesn't guarantee it.
|
||||
if (mom.hasTime()) { // don't give ambiguously-timed moments a UTC zone
|
||||
mom.utc();
|
||||
}
|
||||
|
||||
return mom;
|
||||
};
|
||||
|
||||
// Creates a moment and preserves the timezone offset of the ISO8601 string,
|
||||
// allowing for ambigous timezones. If the string is not an ISO8601 string,
|
||||
// the moment is processed in UTC-mode (a departure from moment's method).
|
||||
// Same as fc.moment, but when given an ISO8601 string, the timezone offset is preserved.
|
||||
// ISO8601 strings with no timezone offset will become ambiguously zoned.
|
||||
fc.moment.parseZone = function() {
|
||||
return makeMoment(arguments, true, true);
|
||||
};
|
||||
|
||||
// when parseZone==true, if can't figure it out, fall back to parseUTC
|
||||
function makeMoment(args, parseUTC, parseZone) {
|
||||
// Builds an FCMoment from args. When given an existing moment, it clones. When given a native
|
||||
// Date, or called with no arguments (the current time), the resulting moment will be local.
|
||||
// Anything else needs to be "parsed" (a string or an array), and will be affected by:
|
||||
// parseAsUTC - if there is no zone information, should we parse the input in UTC?
|
||||
// parseZone - if there is zone information, should we force the zone of the moment?
|
||||
function makeMoment(args, parseAsUTC, parseZone) {
|
||||
var input = args[0];
|
||||
var isSingleString = args.length == 1 && typeof input === 'string';
|
||||
var isAmbigTime = false;
|
||||
var isAmbigZone = false;
|
||||
var isAmbigTime;
|
||||
var isAmbigZone;
|
||||
var ambigMatch;
|
||||
var mom;
|
||||
|
||||
if (isSingleString) {
|
||||
if (ambigDateOfMonthRegex.test(input)) {
|
||||
// accept strings like '2014-05', but convert to the first of the month
|
||||
input += '-01';
|
||||
args = [ input ]; // for when pass it on to moment's constructor
|
||||
isAmbigTime = true;
|
||||
isAmbigZone = true;
|
||||
}
|
||||
else if ((ambigMatch = ambigTimeOrZoneRegex.exec(input))) {
|
||||
isAmbigTime = !ambigMatch[5]; // no time part?
|
||||
isAmbigZone = true;
|
||||
}
|
||||
}
|
||||
else if ($.isArray(input)) {
|
||||
// arrays have no timezone information, so assume ambiguous zone
|
||||
isAmbigZone = true;
|
||||
}
|
||||
|
||||
// instantiate a vanilla moment
|
||||
if (parseUTC || parseZone || isAmbigTime) {
|
||||
mom = moment.utc.apply(moment, args);
|
||||
}
|
||||
else {
|
||||
mom = moment.apply(null, args);
|
||||
}
|
||||
var output; // an object with fields for the new FCMoment object
|
||||
|
||||
if (moment.isMoment(input)) {
|
||||
transferAmbigs(input, mom);
|
||||
}
|
||||
output = moment.apply(null, args); // clone it
|
||||
|
||||
if (isAmbigTime) {
|
||||
mom._ambigTime = true;
|
||||
mom._ambigZone = true; // if ambiguous time, also ambiguous timezone offset
|
||||
// the ambig properties have not been preserved in the clone, so reassign them
|
||||
if (input._ambigTime) {
|
||||
output._ambigTime = true;
|
||||
}
|
||||
if (input._ambigZone) {
|
||||
output._ambigZone = true;
|
||||
}
|
||||
}
|
||||
else if (isNativeDate(input) || input === undefined) {
|
||||
output = moment.apply(null, args); // will be local
|
||||
}
|
||||
else { // "parsing" is required
|
||||
isAmbigTime = false;
|
||||
isAmbigZone = false;
|
||||
|
||||
if (parseZone) {
|
||||
if (isAmbigZone) {
|
||||
mom._ambigZone = true;
|
||||
if (isSingleString) {
|
||||
if (ambigDateOfMonthRegex.test(input)) {
|
||||
// accept strings like '2014-05', but convert to the first of the month
|
||||
input += '-01';
|
||||
args = [ input ]; // for when we pass it on to moment's constructor
|
||||
isAmbigTime = true;
|
||||
isAmbigZone = true;
|
||||
}
|
||||
else if ((ambigMatch = ambigTimeOrZoneRegex.exec(input))) {
|
||||
isAmbigTime = !ambigMatch[5]; // no time part?
|
||||
isAmbigZone = true;
|
||||
}
|
||||
}
|
||||
else if (isSingleString) {
|
||||
mom.zone(input); // if fails, will set it to 0, which it already was
|
||||
else if ($.isArray(input)) {
|
||||
// arrays have no timezone information, so assume ambiguous zone
|
||||
isAmbigZone = true;
|
||||
}
|
||||
else if (isNativeDate(input) || input === undefined) {
|
||||
// native Date object?
|
||||
// specified with no arguments?
|
||||
// then consider the moment to be local
|
||||
mom.local();
|
||||
// otherwise, probably a string with a format
|
||||
|
||||
if (parseAsUTC) {
|
||||
output = moment.utc.apply(moment, args);
|
||||
}
|
||||
else {
|
||||
output = moment.apply(null, args);
|
||||
}
|
||||
|
||||
if (isAmbigTime) {
|
||||
output._ambigTime = true;
|
||||
output._ambigZone = true; // ambiguous time always means ambiguous zone
|
||||
}
|
||||
else if (parseZone) { // let's record the inputted zone somehow
|
||||
if (isAmbigZone) {
|
||||
output._ambigZone = true;
|
||||
}
|
||||
else if (isSingleString) {
|
||||
output.zone(input); // if not a valid zone, will assign UTC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new FCMoment(mom);
|
||||
return new FCMoment(output);
|
||||
}
|
||||
|
||||
// our subclass of Moment.
|
||||
// accepts an object with the internal Moment properties that should be copied over to
|
||||
// this object (most likely another Moment object).
|
||||
function FCMoment(config) {
|
||||
extend(this, config);
|
||||
// Our subclass of Moment.
|
||||
// Accepts an object with the internal Moment properties that should be copied over to
|
||||
// `this` object (most likely another Moment object). The values in this data must not
|
||||
// be referenced by anything else (two moments sharing a Date object for example).
|
||||
function FCMoment(internalData) {
|
||||
extend(this, internalData);
|
||||
}
|
||||
|
||||
// chain the prototype to Moment's
|
||||
// Chain the prototype to Moment's
|
||||
FCMoment.prototype = createObject(moment.fn);
|
||||
|
||||
// we need this because Moment's implementation will not copy over the ambig flags
|
||||
// We need this because Moment's implementation won't create an FCMoment,
|
||||
// nor will it copy over the ambig flags.
|
||||
FCMoment.prototype.clone = function() {
|
||||
return makeMoment([ this ]);
|
||||
};
|
||||
@@ -175,11 +195,9 @@ FCMoment.prototype.hasTime = function() {
|
||||
// timezone offset when .format() is called.
|
||||
FCMoment.prototype.stripZone = function() {
|
||||
var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array
|
||||
var wasAmbigTime = this._ambigTime;
|
||||
|
||||
// set the internal UTC flag
|
||||
moment.fn.utc.call(this); // call the original method, because we don't want to affect _ambigZone
|
||||
|
||||
this._ambigZone = true;
|
||||
moment.fn.utc.call(this); // set the internal UTC flag
|
||||
|
||||
this.year(a[0]) // TODO: find a way to do this in one shot
|
||||
.month(a[1])
|
||||
@@ -189,6 +207,15 @@ FCMoment.prototype.stripZone = function() {
|
||||
.seconds(a[5])
|
||||
.milliseconds(a[6]);
|
||||
|
||||
if (wasAmbigTime) {
|
||||
// the above call to .utc()/.zone() unfortunately clears the ambig flags, so reassign
|
||||
this._ambigTime = true;
|
||||
}
|
||||
|
||||
// Mark the zone as ambiguous. This needs to happen after the .utc() call, which calls .zone(), which
|
||||
// clears all ambig flags. Same concept with the .year/month/date calls in the case of moment-timezone.
|
||||
this._ambigZone = true;
|
||||
|
||||
return this; // for chaining
|
||||
};
|
||||
|
||||
@@ -199,23 +226,50 @@ FCMoment.prototype.hasZone = function() {
|
||||
|
||||
// this method implicitly marks a zone
|
||||
FCMoment.prototype.zone = function(tzo) {
|
||||
|
||||
if (tzo != null) {
|
||||
// FYI, the delete statements need to be before the .zone() call or else chaos ensues
|
||||
// for reasons I don't understand.
|
||||
delete this._ambigTime;
|
||||
delete this._ambigZone;
|
||||
}
|
||||
|
||||
return moment.fn.zone.apply(this, arguments);
|
||||
};
|
||||
|
||||
// this method implicitly marks a zone.
|
||||
// we don't need this, because .local internally calls .zone, but we don't want to depend on that.
|
||||
// this method implicitly marks a zone
|
||||
FCMoment.prototype.local = function() {
|
||||
var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array
|
||||
var wasAmbigZone = this._ambigZone;
|
||||
|
||||
// will happen anyway via .local()/.zone(), but don't want to rely on internal implementation
|
||||
delete this._ambigTime;
|
||||
delete this._ambigZone;
|
||||
return moment.fn.local.apply(this, arguments);
|
||||
|
||||
moment.fn.local.apply(this, arguments);
|
||||
|
||||
if (wasAmbigZone) {
|
||||
// If the moment was ambiguously zoned, the date fields were stored as UTC.
|
||||
// We want to preserve these, but in local time.
|
||||
this.year(a[0]) // TODO: find a way to do this in one shot
|
||||
.month(a[1])
|
||||
.date(a[2])
|
||||
.hours(a[3])
|
||||
.minutes(a[4])
|
||||
.seconds(a[5])
|
||||
.milliseconds(a[6]);
|
||||
}
|
||||
|
||||
return this; // for chaining
|
||||
};
|
||||
|
||||
// this method implicitly marks a zone.
|
||||
// we don't need this, because .utc internally calls .zone, but we don't want to depend on that.
|
||||
// this method implicitly marks a zone
|
||||
FCMoment.prototype.utc = function() {
|
||||
|
||||
// will happen anyway via .local()/.zone(), but don't want to rely on internal implementation
|
||||
delete this._ambigTime;
|
||||
delete this._ambigZone;
|
||||
|
||||
return moment.fn.utc.apply(this, arguments);
|
||||
};
|
||||
|
||||
@@ -272,23 +326,6 @@ $.each([
|
||||
// Misc Internals
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
// transfers our internal _ambig properties from one moment to another
|
||||
function transferAmbigs(src, dest) {
|
||||
if (src._ambigTime) {
|
||||
dest._ambigTime = true;
|
||||
}
|
||||
else if (dest._ambigTime) {
|
||||
delete dest._ambigTime;
|
||||
}
|
||||
|
||||
if (src._ambigZone) {
|
||||
dest._ambigZone = true;
|
||||
}
|
||||
else if (dest._ambigZone) {
|
||||
delete dest._ambigZone;
|
||||
}
|
||||
}
|
||||
|
||||
// given an array of moment-like inputs, return a parallel array w/ moments similarly ambiguated.
|
||||
// for example, of one moment has ambig time, but not others, all moments will have their time stripped.
|
||||
function commonlyAmbiguate(inputs) {
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@ function createObject(proto) {
|
||||
return new f();
|
||||
}
|
||||
|
||||
// copy specifically-owned (non-protoype) properties of `b` onto `a`
|
||||
// Copies specifically-owned (non-protoype) properties of `b` onto `a`.
|
||||
// FYI, $.extend would copy *all* properties of `b` onto `a`.
|
||||
function extend(a, b) {
|
||||
for (var i in b) {
|
||||
if (b.hasOwnProperty(i)) {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<link href='../build/out/fullcalendar.css' rel='stylesheet' />
|
||||
<link href='../build/out/fullcalendar.print.css' rel='stylesheet' media='print' />
|
||||
<script src='../lib/moment/moment.js'></script>
|
||||
<script src='../lib/moment-timezone/moment-timezone.js'></script>
|
||||
<script src='../lib/jquery/jquery.js'></script>
|
||||
<script src='../lib/jquery-ui/ui/jquery-ui.js'></script>
|
||||
<script src='../build/out/fullcalendar.js'></script>
|
||||
<script>
|
||||
|
||||
//moment-timezone-data
|
||||
moment.tz.add({
|
||||
"zones": {
|
||||
"CET": [
|
||||
"1 C-Eur CE%sT"
|
||||
],
|
||||
"Etc/GMT": [
|
||||
"0 - GMT"
|
||||
],
|
||||
"Etc/UTC": [
|
||||
"0 - UTC"
|
||||
],
|
||||
"Europe/Berlin": [
|
||||
"0:53:28 - LMT 1893_3 0:53:28",
|
||||
"1 C-Eur CE%sT 1945_4_24_2 2",
|
||||
"1 SovietZone CE%sT 1946 1",
|
||||
"1 Germany CE%sT 1980 1",
|
||||
"1 EU CE%sT"
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"C-Eur": [
|
||||
"1916 1916 3 30 7 23 0 1 S",
|
||||
"1916 1916 9 1 7 1 0 0",
|
||||
"1917 1918 3 15 1 2 2 1 S",
|
||||
"1917 1918 8 15 1 2 2 0",
|
||||
"1940 1940 3 1 7 2 2 1 S",
|
||||
"1942 1942 10 2 7 2 2 0",
|
||||
"1943 1943 2 29 7 2 2 1 S",
|
||||
"1943 1943 9 4 7 2 2 0",
|
||||
"1944 1945 3 1 1 2 2 1 S",
|
||||
"1944 1944 9 2 7 2 2 0",
|
||||
"1945 1945 8 16 7 2 2 0",
|
||||
"1977 1980 3 1 0 2 2 1 S",
|
||||
"1977 1977 8 0 8 2 2 0",
|
||||
"1978 1978 9 1 7 2 2 0",
|
||||
"1979 1995 8 0 8 2 2 0",
|
||||
"1981 9999 2 0 8 2 2 1 S",
|
||||
"1996 9999 9 0 8 2 2 0"
|
||||
],
|
||||
"SovietZone": [
|
||||
"1945 1945 4 24 7 2 0 2 M",
|
||||
"1945 1945 8 24 7 3 0 1 S",
|
||||
"1945 1945 10 18 7 2 2 0"
|
||||
],
|
||||
"Germany": [
|
||||
"1946 1946 3 14 7 2 2 1 S",
|
||||
"1946 1946 9 7 7 2 2 0",
|
||||
"1947 1949 9 1 0 2 2 0",
|
||||
"1947 1947 3 6 7 3 2 1 S",
|
||||
"1947 1947 4 11 7 2 2 2 M",
|
||||
"1947 1947 5 29 7 3 0 1 S",
|
||||
"1948 1948 3 18 7 2 2 1 S",
|
||||
"1949 1949 3 10 7 2 2 1 S"
|
||||
],
|
||||
"EU": [
|
||||
"1977 1980 3 1 0 1 1 1 S",
|
||||
"1977 1977 8 0 8 1 1 0",
|
||||
"1978 1978 9 1 7 1 1 0",
|
||||
"1979 1995 8 0 8 1 1 0",
|
||||
"1981 9999 2 0 8 1 1 1 S",
|
||||
"1996 9999 9 0 8 1 1 0"
|
||||
]
|
||||
},
|
||||
"links": {}
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#calendar').fullCalendar({
|
||||
editable: true,
|
||||
timezone: 'Europe/Berlin',
|
||||
events: [
|
||||
{
|
||||
title: "event1",
|
||||
start: moment.parseZone('2014-05-30T10:00:00+02:00').tz('Europe/Berlin')
|
||||
}
|
||||
],
|
||||
eventRender: function(event, el) {
|
||||
// render the timezone offset below the event title
|
||||
if (event.start.hasZone()) {
|
||||
el.find('.fc-event-title').after(
|
||||
$('<div class="tzo"/>').text(event.start.format('Z'))
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
<style>
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#calendar {
|
||||
width: 900px;
|
||||
margin: 40px auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id='calendar'></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user