Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5959123
DiscordianDateTimeFormatter.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
DiscordianDateTimeFormatter.js
View Options
(
function
()
{
/**
* @classdesc DateTimeFormatter for the Discordian calendar.
*
* Provides various methods needed for formatting dates and times. This
* implementation implements the [Discordian calendar](https://en.wikipedia.org/wiki/Discordian_calendar),
* mainly for testing with something very different from the usual Gregorian
* calendar.
*
* Being intended mainly for testing, niceties like i18n and better
* configurability have been omitted.
*
* @class
* @extends mw.widgets.datetime.DateTimeFormatter
*
* @constructor
* @description Create an instance of `mw.widgets.datetime.DiscordianDateTimeFormatter`.
* @param {Object} [config] Configuration options
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
=
function
MwWidgetsDatetimeDiscordianDateTimeFormatter
(
config
)
{
config
=
Object
.
assign
(
{},
config
);
// Parent constructor
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
super
.
call
(
this
,
config
);
};
/* Setup */
OO
.
inheritClass
(
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
,
mw
.
widgets
.
datetime
.
DateTimeFormatter
);
/* Static */
/**
* Default format specifications.
*
* See the `format` parameter in {@link mw.widgets.datetime.DateTimeFormatter}.
*
* @memberof mw.widgets.datetime.DiscordianDateTimeFormatter
* @type {Object.<string,string>}
* @name formats
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
static
.
formats
=
{
'@time'
:
'${hour|0}:${minute|0}:${second|0}'
,
'@date'
:
'$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#}'
,
'@datetime'
:
'$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}'
,
'@default'
:
'$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}'
};
/* Methods */
/**
* Turn a tag into a field specification object.
*
* Additional fields implemented here are:
* - ${year|#}: Year as a number
* - ${season|#}: Season as a number
* - ${season|full}: Season as a string
* - ${day|#}: Day of the month as a number
* - ${day|0}: Day of the month as a number with leading 0
* - ${dow|full}: Day of the week as a string
* - ${hour|#}: Hour as a number
* - ${hour|0}: Hour as a number with leading 0
* - ${minute|#}: Minute as a number
* - ${minute|0}: Minute as a number with leading 0
* - ${second|#}: Second as a number
* - ${second|0}: Second as a number with leading 0
* - ${millisecond|#}: Millisecond as a number
* - ${millisecond|0}: Millisecond as a number, zero-padded to 3 digits
*
* @protected
* @param {string} tag
* @param {string[]} params
* @return {FieldSpecificationObject} Field specification object, or null if the tag+params are unrecognized.
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
getFieldForTag
=
function
(
tag
,
params
)
{
let
spec
=
null
;
switch
(
tag
+
'|'
+
params
[
0
]
)
{
case
'year|#'
:
spec
=
{
component
:
'Year'
,
calendarComponent
:
true
,
type
:
'number'
,
size
:
4
,
zeropad
:
false
};
break
;
case
'season|#'
:
spec
=
{
component
:
'Season'
,
calendarComponent
:
true
,
type
:
'number'
,
size
:
1
,
intercalarySize
:
{
1
:
0
},
zeropad
:
false
};
break
;
case
'season|full'
:
spec
=
{
component
:
'Season'
,
calendarComponent
:
true
,
type
:
'string'
,
intercalarySize
:
{
1
:
0
},
values
:
{
1
:
'Chaos'
,
2
:
'Discord'
,
3
:
'Confusion'
,
4
:
'Bureaucracy'
,
5
:
'The Aftermath'
}
};
break
;
case
'dow|full'
:
spec
=
{
component
:
'DOW'
,
calendarComponent
:
true
,
editable
:
false
,
type
:
'string'
,
intercalarySize
:
{
1
:
0
},
values
:
{
'-1'
:
'N/A'
,
0
:
'Sweetmorn'
,
1
:
'Boomtime'
,
2
:
'Pungenday'
,
3
:
'Prickle-Prickle'
,
4
:
'Setting Orange'
}
};
break
;
case
'day|#'
:
case
'day|0'
:
spec
=
{
component
:
'Day'
,
calendarComponent
:
true
,
type
:
'string'
,
size
:
2
,
intercalarySize
:
{
1
:
13
},
zeropad
:
params
[
0
]
===
'0'
,
formatValue
:
function
(
v
)
{
if
(
v
===
'tib'
)
{
return
'St. Tib\'s Day'
;
}
return
mw
.
widgets
.
datetime
.
DateTimeFormatter
.
prototype
.
formatSpecValue
.
call
(
this
,
v
);
},
parseValue
:
function
(
v
)
{
if
(
/^\s*(st.?\s*)?tib('?s)?(\s*day)?\s*$/i
.
test
(
v
)
)
{
return
'tib'
;
}
return
mw
.
widgets
.
datetime
.
DateTimeFormatter
.
prototype
.
parseSpecValue
.
call
(
this
,
v
);
}
};
break
;
case
'hour|#'
:
case
'hour|0'
:
case
'minute|#'
:
case
'minute|0'
:
case
'second|#'
:
case
'second|0'
:
spec
=
{
component
:
tag
.
charAt
(
0
).
toUpperCase
()
+
tag
.
slice
(
1
),
calendarComponent
:
false
,
type
:
'number'
,
size
:
2
,
zeropad
:
params
[
0
]
===
'0'
};
break
;
case
'millisecond|#'
:
case
'millisecond|0'
:
spec
=
{
component
:
'Millisecond'
,
calendarComponent
:
false
,
type
:
'number'
,
size
:
3
,
zeropad
:
params
[
0
]
===
'0'
};
break
;
default
:
return
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
super
.
prototype
.
getFieldForTag
.
call
(
this
,
tag
,
params
);
}
if
(
spec
)
{
if
(
spec
.
editable
===
undefined
)
{
spec
.
editable
=
true
;
}
if
(
spec
.
component
!==
'Day'
)
{
spec
.
formatValue
=
this
.
formatSpecValue
;
spec
.
parseValue
=
this
.
parseSpecValue
;
}
if
(
spec
.
values
)
{
spec
.
size
=
Math
.
max
.
apply
(
// eslint-disable-next-line no-jquery/no-map-util
null
,
$
.
map
(
spec
.
values
,
(
v
)
=>
v
.
length
)
);
}
}
return
spec
;
};
/**
* Get components from a Date object.
*
* Components are:
* - Year {number}
* - Season {number} 1-5
* - Day {number|string} 1-73 or 'tib'
* - DOW {number} 0-4, or -1 on St. Tib's Day
* - Hour {number} 0-23
* - Minute {number} 0-59
* - Second {number} 0-59
* - Millisecond {number} 0-999
* - intercalary {string} '1' on St. Tib's Day
*
* @param {Date|null} date
* @return {Object} Components
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
getComponentsFromDate
=
function
(
date
)
{
let
ret
,
day
,
month
;
const
monthDays
=
[
0
,
31
,
59
,
90
,
120
,
151
,
181
,
212
,
243
,
273
,
304
,
334
];
if
(
!
(
date
instanceof
Date
)
)
{
date
=
this
.
defaultDate
;
}
if
(
this
.
local
)
{
day
=
date
.
getDate
();
month
=
date
.
getMonth
();
ret
=
{
Year
:
date
.
getFullYear
()
+
1166
,
Hour
:
date
.
getHours
(),
Minute
:
date
.
getMinutes
(),
Second
:
date
.
getSeconds
(),
Millisecond
:
date
.
getMilliseconds
(),
zone
:
date
.
getTimezoneOffset
()
};
}
else
{
day
=
date
.
getUTCDate
();
month
=
date
.
getUTCMonth
();
ret
=
{
Year
:
date
.
getUTCFullYear
()
+
1166
,
Hour
:
date
.
getUTCHours
(),
Minute
:
date
.
getUTCMinutes
(),
Second
:
date
.
getUTCSeconds
(),
Millisecond
:
date
.
getUTCMilliseconds
(),
zone
:
0
};
}
if
(
month
===
1
&&
day
===
29
)
{
ret
.
Season
=
1
;
ret
.
Day
=
'tib'
;
ret
.
DOW
=
-
1
;
ret
.
intercalary
=
'1'
;
}
else
{
day
=
monthDays
[
month
]
+
day
-
1
;
ret
.
Season
=
Math
.
floor
(
day
/
73
)
+
1
;
ret
.
Day
=
(
day
%
73
)
+
1
;
ret
.
DOW
=
day
%
5
;
ret
.
intercalary
=
''
;
}
return
ret
;
};
/**
* @inheritdoc
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
adjustComponent
=
function
(
date
,
component
,
delta
,
mode
)
{
return
this
.
getDateFromComponents
(
this
.
adjustComponentInternal
(
this
.
getComponentsFromDate
(
date
),
component
,
delta
,
mode
)
);
};
/**
* Adjust the components directly.
*
* @private
* @param {Object} components Modified in place
* @param {string} component
* @param {number} delta
* @param {string} mode
* @return {Object} components
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
adjustComponentInternal
=
function
(
components
,
component
,
delta
,
mode
)
{
let
i
,
min
,
max
,
range
,
next
,
preTib
,
postTib
,
wasTib
;
if
(
delta
===
0
)
{
return
components
;
}
switch
(
component
)
{
case
'Year'
:
min
=
1166
;
max
=
11165
;
next
=
null
;
break
;
case
'Season'
:
min
=
1
;
max
=
5
;
next
=
'Year'
;
break
;
case
'Week'
:
if
(
components
.
Day
===
'tib'
)
{
components
.
Day
=
59
;
// Could choose either one...
components
.
Season
=
1
;
}
min
=
1
;
max
=
73
;
next
=
'Season'
;
break
;
case
'Day'
:
min
=
1
;
max
=
73
;
next
=
'Season'
;
break
;
case
'Hour'
:
min
=
0
;
max
=
23
;
next
=
'Day'
;
break
;
case
'Minute'
:
min
=
0
;
max
=
59
;
next
=
'Hour'
;
break
;
case
'Second'
:
min
=
0
;
max
=
59
;
next
=
'Minute'
;
break
;
case
'Millisecond'
:
min
=
0
;
max
=
999
;
next
=
'Second'
;
break
;
default
:
return
components
;
}
switch
(
mode
)
{
case
'overflow'
:
case
'clip'
:
case
'wrap'
:
}
if
(
component
===
'Day'
)
{
i
=
Math
.
abs
(
delta
);
delta
=
delta
<
0
?
-
1
:
1
;
preTib
=
delta
>
0
?
59
:
60
;
postTib
=
delta
>
0
?
60
:
59
;
while
(
i
--
>
0
)
{
if
(
components
.
Day
===
preTib
&&
components
.
Season
===
1
&&
this
.
isLeapYear
(
components
.
Year
)
)
{
components
.
Day
=
'tib'
;
}
else
if
(
components
.
Day
===
'tib'
)
{
components
.
Day
=
postTib
;
components
.
Season
=
1
;
}
else
{
components
.
Day
+=
delta
;
if
(
components
.
Day
<
min
)
{
switch
(
mode
)
{
case
'overflow'
:
components
.
Day
=
max
;
this
.
adjustComponentInternal
(
components
,
'Season'
,
-
1
,
mode
);
break
;
case
'wrap'
:
components
.
Day
=
max
;
break
;
case
'clip'
:
components
.
Day
=
min
;
i
=
0
;
break
;
}
}
if
(
components
.
Day
>
max
)
{
switch
(
mode
)
{
case
'overflow'
:
components
.
Day
=
min
;
this
.
adjustComponentInternal
(
components
,
'Season'
,
1
,
mode
);
break
;
case
'wrap'
:
components
.
Day
=
min
;
break
;
case
'clip'
:
components
.
Day
=
max
;
i
=
0
;
break
;
}
}
}
}
}
else
{
if
(
component
===
'Week'
)
{
component
=
'Day'
;
delta
*=
5
;
}
if
(
components
.
Day
===
'tib'
)
{
components
.
Season
=
1
;
}
switch
(
mode
)
{
case
'overflow'
:
if
(
components
.
Day
===
'tib'
&&
(
component
===
'Season'
||
component
===
'Year'
)
)
{
components
.
Day
=
59
;
// Could choose either one...
wasTib
=
true
;
}
else
{
wasTib
=
false
;
}
i
=
Math
.
abs
(
delta
);
delta
=
delta
<
0
?
-
1
:
1
;
while
(
i
--
>
0
)
{
components
[
component
]
+=
delta
;
if
(
components
[
component
]
<
min
)
{
components
[
component
]
=
max
;
components
=
this
.
adjustComponentInternal
(
components
,
next
,
-
1
,
mode
);
}
if
(
components
[
component
]
>
max
)
{
components
[
component
]
=
min
;
components
=
this
.
adjustComponentInternal
(
components
,
next
,
1
,
mode
);
}
}
if
(
wasTib
&&
components
.
Season
===
1
&&
this
.
isLeapYear
(
components
.
Year
)
)
{
components
.
Day
=
'tib'
;
}
break
;
case
'wrap'
:
range
=
max
-
min
+
1
;
components
[
component
]
+=
delta
;
while
(
components
[
component
]
<
min
)
{
components
[
component
]
+=
range
;
}
while
(
components
[
component
]
>
max
)
{
components
[
component
]
-=
range
;
}
break
;
case
'clip'
:
components
[
component
]
+=
delta
;
if
(
components
[
component
]
<
min
)
{
components
[
component
]
=
min
;
}
if
(
components
[
component
]
>
max
)
{
components
[
component
]
=
max
;
}
break
;
}
if
(
components
.
Day
===
'tib'
&&
(
components
.
Season
!==
1
||
!
this
.
isLeapYear
(
components
.
Year
)
)
)
{
components
.
Day
=
59
;
// Could choose either one...
}
}
return
components
;
};
/**
* @inheritdoc
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
getDateFromComponents
=
function
(
components
)
{
let
month
,
day
;
const
date
=
new
Date
(),
monthDays
=
[
0
,
31
,
59
,
90
,
120
,
151
,
181
,
212
,
243
,
273
,
304
,
334
,
365
];
components
=
Object
.
assign
(
{},
this
.
getComponentsFromDate
(
null
),
components
);
if
(
components
.
Day
===
'tib'
)
{
month
=
1
;
day
=
29
;
}
else
{
const
days
=
components
.
Season
*
73
+
components
.
Day
-
74
;
month
=
0
;
while
(
days
>=
monthDays
[
month
+
1
]
)
{
month
++
;
}
day
=
days
-
monthDays
[
month
]
+
1
;
}
if
(
components
.
zone
)
{
// Can't just use the constructor because that's stupid about ancient years.
date
.
setFullYear
(
components
.
Year
-
1166
,
month
,
day
);
date
.
setHours
(
components
.
Hour
,
components
.
Minute
,
components
.
Second
,
components
.
Millisecond
);
}
else
{
// Date.UTC() is stupid about ancient years too.
date
.
setUTCFullYear
(
components
.
Year
-
1166
,
month
,
day
);
date
.
setUTCHours
(
components
.
Hour
,
components
.
Minute
,
components
.
Second
,
components
.
Millisecond
);
}
return
date
;
};
/**
* Get whether the year is a leap year.
*
* @private
* @param {number} year
* @return {boolean}
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
isLeapYear
=
function
(
year
)
{
year
-=
1166
;
if
(
year
%
4
)
{
return
false
;
}
else
if
(
year
%
100
)
{
return
true
;
}
return
(
year
%
400
)
===
0
;
};
/**
* @inheritdoc
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
getCalendarHeadings
=
function
()
{
return
[
'SM'
,
'BT'
,
'PD'
,
'PP'
,
null
,
'SO'
];
};
/**
* @inheritdoc
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
sameCalendarGrid
=
function
(
date1
,
date2
)
{
const
components1
=
this
.
getComponentsFromDate
(
date1
),
components2
=
this
.
getComponentsFromDate
(
date2
);
return
components1
.
Year
===
components2
.
Year
&&
components1
.
Season
===
components2
.
Season
;
};
/**
* @inheritdoc
*/
mw
.
widgets
.
datetime
.
DiscordianDateTimeFormatter
.
prototype
.
getCalendarData
=
function
(
date
)
{
const
ret
=
{
dayComponent
:
'Day'
,
weekComponent
:
'Week'
,
monthComponent
:
'Season'
},
seasons
=
[
'Chaos'
,
'Discord'
,
'Confusion'
,
'Bureaucracy'
,
'The Aftermath'
],
seasonStart
=
[
0
,
-
3
,
-
1
,
-
4
,
-
2
];
if
(
!
(
date
instanceof
Date
)
)
{
date
=
this
.
defaultDate
;
}
const
components
=
this
.
getComponentsFromDate
(
date
);
components
.
Day
=
1
;
const
season
=
components
.
Season
;
ret
.
header
=
seasons
[
season
-
1
]
+
' '
+
components
.
Year
;
if
(
seasonStart
[
season
-
1
]
)
{
this
.
adjustComponentInternal
(
components
,
'Day'
,
seasonStart
[
season
-
1
],
'overflow'
);
}
ret
.
rows
=
[];
do
{
const
row
=
[];
for
(
let
i
=
0
;
i
<
6
;
i
++
)
{
const
dt
=
this
.
getDateFromComponents
(
components
);
row
[
i
]
=
{
display
:
components
.
Day
===
'tib'
?
'Tib'
:
String
(
components
.
Day
),
date
:
dt
,
extra
:
components
.
Season
<
season
?
'prev'
:
components
.
Season
>
season
?
'next'
:
null
};
this
.
adjustComponentInternal
(
components
,
'Day'
,
1
,
'overflow'
);
if
(
components
.
Day
!==
'tib'
&&
i
===
3
)
{
row
[
++
i
]
=
null
;
}
}
ret
.
rows
.
push
(
row
);
}
while
(
components
.
Season
===
season
);
return
ret
;
};
}()
);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Oct 3, 06:14 (2 w, 4 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ac/3e/84addc85910c8ec73ee800d5962c
Default Alt Text
DiscordianDateTimeFormatter.js (14 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment