Skip to content

Commit 721e0ff

Browse files
authored
feat(calendar): support token dateformats
This PR adds support to use token strings as a date formatter. It's still possible to provide functions as before. The base idea is adopted from https://blog.stevenlevithan.com/archives/javascript-date-format Now you can use something like ```javascript formatter: { date: '"Intergalactic Date:" D.M.YYYY' } ``` Usable Tokens (i compared popular big date libs like momentjs, date-fns, luxon and dayjs to make sure to basically match the same syntax) |Description|Token|Resultset| |-|-|-| |Day of Week|d|0-6| |Day of Week (2 char text)|dd|Su - Sa| |Day of Week (3 char text)|ddd|Sun - Sat| |Day of Week (Full text)|dddd|Sunday - Saturday| |Day of Month |D|1 to 31| |Day of Month (Leading zero)|DD|01-31| |Day suffix|S|st,nd,rd,th| |Month |M|1 to 12| |Month (Leading zero)|MM|01-12| |Month (3 char text)|MMM|Jan-Dec| |Month (Full text)|MMMM|January-December| |Year|YYYY or Y|2022| |Year (2 Digits)|YY|22| |Week|w|1-53| |Week (Leading zero)|ww|01-53| |Hour (12h Format)|h|1-12| |Hour (12h Format, Leading zero)|hh|01-12| |Hour (24h Format)|H|0-23| |Hour (24h Format, Leading zero)|HH|00-23| |Minute|m|0-59| |Minute (Leading zero)|mm|00-59| |Second|s|0-59| |Second (Leading zero)|ss|00-59| |AM/PM|A|AM-PM| |AM/PM (lowercase)|a|am-pm| |Custom Text|Wrap in double or single quotes|Your text without quotes| I removed the `ampm` setting, because it is now controllable via the `A` or `a` token.
1 parent b4551f6 commit 721e0ff

File tree

1 file changed

+77
-62
lines changed

1 file changed

+77
-62
lines changed

src/definitions/modules/calendar.js

Lines changed: 77 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ $.fn.calendar = function(parameters) {
322322
var headerDate = isYear || isMonth ? new Date(year, 0, 1) :
323323
isDay ? new Date(year, month, 1) : new Date(year, month, day, hour, minute);
324324
var headerText = $('<span/>').addClass(className.link).appendTo(cell);
325-
headerText.text(formatter.header(headerDate, mode, settings));
325+
headerText.text(module.helper.dateFormat(formatter[mode+'Header'], headerDate));
326326
var newMode = isMonth ? (settings.disableYear ? 'day' : 'year') :
327327
isDay ? (settings.disableMonth ? 'year' : 'month') : 'day';
328328
headerText.data(metadata.mode, newMode);
@@ -370,7 +370,7 @@ $.fn.calendar = function(parameters) {
370370
isHour ? new Date(year, month, day, i) : new Date(year, month, day, hour, i * settings.minTimeGap);
371371
var cellText = isYear ? i :
372372
isMonth ? settings.text.monthsShort[i] : isDay ? cellDate.getDate() :
373-
formatter.time(cellDate, settings, true);
373+
module.helper.dateFormat(formatter.cellTime,cellDate);
374374
cell = $('<td/>').addClass(className.cell).appendTo(row);
375375
cell.text(cellText);
376376
cell.data(metadata.date, cellDate);
@@ -651,7 +651,7 @@ $.fn.calendar = function(parameters) {
651651
$container.removeClass(className.active);
652652
if (settings.formatInput) {
653653
var date = module.get.date();
654-
var text = formatter.datetime(date, settings);
654+
var text = module.helper.dateFormat(formatter[settings.type], date);
655655
$input.val(text);
656656
}
657657
if(selectionComplete){
@@ -722,6 +722,9 @@ $.fn.calendar = function(parameters) {
722722
return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
723723
}();
724724
},
725+
formattedDate: function(format, date) {
726+
return module.helper.dateFormat(format || formatter[settings.type], date || module.get.date());
727+
},
725728
date: function () {
726729
return module.helper.sanitiseDate($module.data(metadata.date)) || null;
727730
},
@@ -815,7 +818,7 @@ $.fn.calendar = function(parameters) {
815818
date = module.helper.dateInRange(date);
816819

817820
var mode = module.get.mode();
818-
var text = formatter.datetime(date, settings);
821+
var text = module.helper.dateFormat(formatter[settings.type],date);
819822

820823
if (fireChange && settings.onBeforeChange.call(element, date, text, mode) === false) {
821824
return false;
@@ -986,6 +989,60 @@ $.fn.calendar = function(parameters) {
986989
},
987990

988991
helper: {
992+
dateFormat: function(format,date) {
993+
if (!(date instanceof Date)) {
994+
return '';
995+
}
996+
if(typeof format === 'function') {
997+
return format.call(module, date, settings);
998+
}
999+
1000+
var D = date.getDate(),
1001+
M = date.getMonth(),
1002+
Y = date.getFullYear(),
1003+
d = date.getDay(),
1004+
H = date.getHours(),
1005+
m = date.getMinutes(),
1006+
s = date.getSeconds(),
1007+
w = module.get.weekOfYear(Y,M,D+1-settings.firstDayOfWeek),
1008+
h = H % 12 || 12,
1009+
a = H < 12 ? settings.text.am.toLowerCase() : settings.text.pm.toLowerCase(),
1010+
tokens = {
1011+
D: D,
1012+
DD: ('0'+D).slice(-2),
1013+
M: M + 1,
1014+
MM: ('0'+(M+1)).slice(-2),
1015+
MMM: settings.text.monthsShort[M],
1016+
MMMM: settings.text.months[M],
1017+
Y: Y,
1018+
YY: String(Y).slice(2),
1019+
YYYY: Y,
1020+
d: d,
1021+
dd: settings.text.dayNamesShort[d].slice(0,2),
1022+
ddd: settings.text.dayNamesShort[d],
1023+
dddd: settings.text.dayNames[d],
1024+
h: h,
1025+
hh: ('0'+h).slice(-2),
1026+
H: H,
1027+
HH: ('0'+H).slice(-2),
1028+
m: m,
1029+
mm: ('0'+m).slice(-2),
1030+
s: s,
1031+
ss: ('0'+s).slice(-2),
1032+
a: a,
1033+
A: a.toUpperCase(),
1034+
S: ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : (D % 100 - D % 10 !== 10) * D % 10],
1035+
w: w,
1036+
ww: ('0'+w).slice(-2)
1037+
}
1038+
;
1039+
return format.replace(settings.regExp.token, function (match) {
1040+
if (match in tokens) {
1041+
return tokens[match];
1042+
}
1043+
return match.slice(1, match.length - 1);
1044+
});
1045+
},
9891046
isDisabled: function(date, mode) {
9901047
return (mode === 'day' || mode === 'month' || mode === 'year' || mode === 'hour') && (((mode === 'day' && settings.disabledDaysOfWeek.indexOf(date.getDay()) !== -1) || settings.disabledDates.some(function(d){
9911048
if(typeof d === 'string') {
@@ -1436,15 +1493,14 @@ $.fn.calendar.settings = {
14361493
constantHeight : true, // add rows to shorter months to keep day calendar height consistent (6 rows)
14371494
today : false, // show a 'today/now' button at the bottom of the calendar
14381495
closable : true, // close the popup after selecting a date/time
1439-
monthFirst : true, // month before day when parsing/converting date from/to text
1496+
monthFirst : true, // month before day when parsing date from text
14401497
touchReadonly : true, // set input to readonly on touch devices
14411498
inline : false, // create the calendar inline instead of inside a popup
14421499
on : null, // when to show the popup (defaults to 'focus' for input, 'click' for others)
14431500
initialDate : null, // date to display initially when no date is selected (null = now)
14441501
startMode : false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day')
14451502
minDate : null, // minimum date/time that can be selected, dates/times before are disabled
14461503
maxDate : null, // maximum date/time that can be selected, dates/times after are disabled
1447-
ampm : true, // show am/pm in time mode
14481504
disableYear : false, // disable year selection mode
14491505
disableMonth : false, // disable month selection mode
14501506
disableMinute : false, // disable minute selection mode
@@ -1454,7 +1510,7 @@ $.fn.calendar.settings = {
14541510
multiMonth : 1, // show multiple months when in 'day' mode
14551511
monthOffset : 0, // position current month by offset when multimonth > 1
14561512
minTimeGap : 5,
1457-
showWeekNumbers : null, // show Number of Week at the very first column of a dayView
1513+
showWeekNumbers : false, // show Number of Week at the very first column of a dayView
14581514
disabledHours : [], // specific hour(s) which won't be selectable and contain additional information.
14591515
disabledDates : [], // specific day(s) which won't be selectable and contain additional information.
14601516
disabledDaysOfWeek : [], // day(s) which won't be selectable(s) (0 = Sunday)
@@ -1474,6 +1530,8 @@ $.fn.calendar.settings = {
14741530

14751531
text: {
14761532
days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
1533+
dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
1534+
dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
14771535
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
14781536
monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
14791537
today: 'Today',
@@ -1484,67 +1542,23 @@ $.fn.calendar.settings = {
14841542
},
14851543

14861544
formatter: {
1487-
header: function (date, mode, settings) {
1488-
return mode === 'year' ? settings.formatter.yearHeader(date, settings) :
1489-
mode === 'month' ? settings.formatter.monthHeader(date, settings) :
1490-
mode === 'day' ? settings.formatter.dayHeader(date, settings) :
1491-
mode === 'hour' ? settings.formatter.hourHeader(date, settings) :
1492-
settings.formatter.minuteHeader(date, settings);
1493-
},
14941545
yearHeader: function (date, settings) {
14951546
var decadeYear = Math.ceil(date.getFullYear() / 10) * 10;
14961547
return (decadeYear - 9) + ' - ' + (decadeYear + 2);
14971548
},
1498-
monthHeader: function (date, settings) {
1499-
return date.getFullYear();
1500-
},
1501-
dayHeader: function (date, settings) {
1502-
var month = settings.text.months[date.getMonth()];
1503-
var year = date.getFullYear();
1504-
return month + ' ' + year;
1505-
},
1506-
hourHeader: function (date, settings) {
1507-
return settings.formatter.date(date, settings);
1508-
},
1509-
minuteHeader: function (date, settings) {
1510-
return settings.formatter.date(date, settings);
1511-
},
1549+
monthHeader: 'YYYY',
1550+
dayHeader: 'MMMM YYYY',
1551+
hourHeader: 'MMMM D, YYYY',
1552+
minuteHeader: 'MMMM D, YYYY',
15121553
dayColumnHeader: function (day, settings) {
15131554
return settings.text.days[day];
15141555
},
1515-
datetime: function (date, settings) {
1516-
if (!date) {
1517-
return '';
1518-
}
1519-
var day = settings.type === 'time' ? '' : settings.formatter.date(date, settings);
1520-
var time = settings.type.indexOf('time') < 0 ? '' : settings.formatter.time(date, settings, false);
1521-
var separator = settings.type === 'datetime' ? ' ' : '';
1522-
return day + separator + time;
1523-
},
1524-
date: function (date, settings) {
1525-
if (!date) {
1526-
return '';
1527-
}
1528-
var day = date.getDate();
1529-
var month = settings.text.months[date.getMonth()];
1530-
var year = date.getFullYear();
1531-
return settings.type === 'year' ? year :
1532-
settings.type === 'month' ? month + ' ' + year :
1533-
(settings.monthFirst ? month + ' ' + day : day + ' ' + month) + ', ' + year;
1534-
},
1535-
time: function (date, settings, forCalendar) {
1536-
if (!date) {
1537-
return '';
1538-
}
1539-
var hour = date.getHours();
1540-
var minute = date.getMinutes();
1541-
var ampm = '';
1542-
if (settings.ampm) {
1543-
ampm = ' ' + (hour < 12 ? settings.text.am : settings.text.pm);
1544-
hour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
1545-
}
1546-
return hour + ':' + (minute < 10 ? '0' : '') + minute + ampm;
1547-
},
1556+
datetime: 'MMMM D, YYYY h:mm A',
1557+
date: 'MMMM D, YYYY',
1558+
time: 'h:mm A',
1559+
cellTime: 'h:mm A',
1560+
month: 'MMMM YYYY',
1561+
year: 'YYYY',
15481562
today: function (settings) {
15491563
return settings.type === 'date' ? settings.text.today : settings.text.now;
15501564
},
@@ -1813,7 +1827,8 @@ $.fn.calendar.settings = {
18131827

18141828
regExp: {
18151829
dateWords: /[^A-Za-z\u00C0-\u024F]+/g,
1816-
dateNumbers: /[^\d:]+/g
1830+
dateNumbers: /[^\d:]+/g,
1831+
token: /d{1,4}|D{1,2}|M{1,4}|YY(?:YY)?|([Hhmsw])\1?|[SAaY]|"[^"]*"|'[^']*'/g
18171832
},
18181833

18191834
error: {

0 commit comments

Comments
 (0)