Reputation: 4426
I would like to use the moment.calendar() option without the time... so instead of "Last Tuesday at 5pm" I want "Last Tuesday". Does anybody know whether moment has a solution for that by now? I found this fiddle http://jsfiddle.net/nawxZ/, which apparently shows a solution for that, but I can't see how this is supposed to work? thanks carl
function log(str) {
$('body').append('<p>' + str + '</p>');
}
log(moment().calendar());
log(moment().calendar(true));
Upvotes: 41
Views: 38552
Reputation: 1675
Assuming moment.js
locales won't change, I wrote a simple wrapper function around moment().calendar()
for the following languages that I use:
en, ar, bg, cs, da, de, el, es, et, fi, fr-ca, fr, he, hr, hu, hy-am, it, ja, kk, ko, lt, nl, nb, pl, pt, ro, ru, sk, sl, sr, sv, th, tr, uk, vi, zh-cn, zh-tw
You can add new or remove existing cases inside the switch statement based on your needs and it should still work just fine.
const timeRegExp = /[0-9:,]/g;
const dateRegExp = /[0-9.-/]/g;
const removeLastNWords = (str, n, separator) => {
return `${str.split(' ').splice(0, n).join(separator)}`;
}
const removeDigitsAndTimeChars = (str) => {
return `${str.replace(timeRegExp, '').trimEnd()}`;
}
const removeLastNWordsStripped = (str, n, separator) => {
return removeDigitsAndTimeChars(removeLastNWords(str, n, separator));
}
const calendarNoTime = (inp, strict) => {
const originalCalendarDate = moment(inp, strict).calendar();
const parts = originalCalendarDate.split(' ');
/**
* Most languages use the 'L' format for calendar: { sameElse },
* these have parts of length 1. zh-cn does not use a space to separate
* the time from the date, so remove it if present otherwise keep
* the 'L' format. If `cn` not needed, simply return parts[0].
* `sr/sl` uses spaces to separate day, month, year.
*
*/
if (parts.length === 1 || dateRegExp.test(parts[0])) {
return parts[0].includes(':') ? removeDigitsAndTimeChars(parts[0]) : parts.join(' ');
}
switch (moment.locale()) {
/**
* These languages have a preposition with a length of < 3
* before the time. Find that preposition and keep the words
* till that preposition. If `ru` not needed,
* use removeLastNWords(originalCalendarDate, prepositionIndex, ' ')
*
*/
case 'bg':
case 'cs':
case 'de':
case 'es':
case 'en':
case 'fr':
case 'fr-ca':
case 'hr':
case 'nl':
case 'pl':
case 'pt':
case 'ro':
case 'ru':
case 'sk':
case 'sl':
case 'sr':
case 'uk':
const prepositionIndex = parts.findIndex((part, i) => i !== 0 && part.length < 3);
return removeLastNWordsStripped(originalCalendarDate, prepositionIndex, ' ');
/**
* Less strict than the above, these languages put their preposition at
* next to last index inside the `parts` array to every wording.
*
*/
case 'da':
case 'fi':
case 'it':
case 'kk':
case 'nb':
case 'th':
case 'vi':
return removeLastNWords(originalCalendarDate, parts.length - 2, ' ');
// Uses [dddd, XX:XX] format, remove digits and special characters
case 'et':
case 'ja':
case 'ko':
case 'lt':
case 'sv':
case 'zh-tw':
case 'zh-cn':
return removeDigitsAndTimeChars(originalCalendarDate);
// Same as above expect the preposition is at parts.length - 3
case 'el':
return removeLastNWords(originalCalendarDate, parts.length - 3, ' ');
// Same as above + [dddd LT] format for some days
case 'hy-am':
return parts.length < 3 ? parts[0] : removeLastNWords(originalCalendarDate, parts.length - 3, ' ');
// Same as above, preposition is at parts.length - 2
case 'tr':
return parts.length < 3 ? parts[0] : removeLastNWords(originalCalendarDate, parts.length - 2, ' ');
// Appends `kor` to time, remove the word that contains this substring
case 'hu':
return originalCalendarDate.replace(/ *\b\S*?kor\S*\b/g, '');
// Custom formats
// https://momentjs.com/docs/#/displaying/calendar-time/
case 'ar':
return moment(inp).calendar({
sameDay: '[اليوم]',
nextDay: '[غدًا]',
nextWeek: 'dddd',
lastDay: '[أمس]',
lastWeek: 'dddd',
sameElse: 'L',
});
case 'he':
return moment(inp).calendar({
sameDay: '[היום]',
nextDay: '[מחר]',
nextWeek: 'dddd',
lastDay: '[אתמול]',
lastWeek: 'dddd',
sameElse: 'L',
});
// Unsupported language, use default format with time
default:
return originalCalendarDate;
}
}
const today = new Date();
const yesterday = (new Date()).setDate((new Date()).getDate() - 1);
const coupleDaysAgo = (new Date()).setDate((new Date()).getDate() - 2);
const fewDaysAgo = (new Date()).setDate((new Date()).getDate() - 4);
const manyDaysAgo = (new Date()).setDate((new Date()).getDate() - 7);
const supportedLanguages = ['en', 'ar', 'bg', 'cs', 'da', 'de', 'el', 'es', 'et', 'fi', 'fr-ca', 'fr', 'he', 'hr', 'hu', 'hy-am', 'it', 'ja', 'kk', 'ko', 'lt', 'nl', 'nb', 'pl', 'pt', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'th', 'tr', 'uk', 'vi', 'zh-cn', 'zh-tw'];
let allDays = [];
supportedLanguages.forEach((lang) => {
moment.locale(lang);
allDays = [calendarNoTime(today), calendarNoTime(yesterday), calendarNoTime(coupleDaysAgo), calendarNoTime(fewDaysAgo), calendarNoTime(manyDaysAgo)];
document.write(`${lang}: `);
document.write(allDays.join(', '));
document.write('</br>');
});
And for the TypeScript fans:
const removeLastNWords = (str: string, n: number, separator: string): string => {}
const removeDigitsAndDateChars = (str: string): string => {}
const removeLastNWordsStripped = (str: string, n: number, separator: string): string => {}
const calendarNoTime = (inp?: moment.MomentInput, strict?: boolean): string => {}
And also a fiddle.
Edit 1
Fixed he, sr, sl languages. Updated fiddle to display all the supported language dates.
Upvotes: 0
Reputation: 871
Since Moment 2.25.0
it is now possible to call moment.calendar(formats) without reference time value
https://momentjs.com/docs/#/displaying/calendar-time/
Upvotes: 2
Reputation: 412
I have the same requirement and unfortunately this is not supported currently (version 2.24.0).
If you look at an arbitrary language file, like en-gb.js, you can see that all the calendar variants contain the time.
There is no option to leave out the time. That would require a variant of calendar without the time for every language.
Upvotes: 1
Reputation: 11
This code works partially. The problem lies on separating the word at from [Today at].
In the example below, a las (means at in English) is part of the result.
moment().locale()
// "es"
moment().calendar(null, {
sameDay: function (now) {
var sameDay = now._locale._calendar.sameDay
if (typeof sameDay === 'function') {
sameDay = sameDay.call(this)
}
return sameDay.replace(' LT', '')
}
})
// "hoy a las"
If the translations were "[Today] [at] LL", we would be able to separate the word at.
Upvotes: 1
Reputation: 5211
If you don't have custom times set and are only working on a day base, you can use this simple and multilang (tested for EN/DE/FR/IT) solution. (full days are always saved with a 00:00 timetag)
let dateParts = moment(DATE).calendar().split(' '),
index = dateParts.indexOf('00:00');
// ['Yesterday', 'at', '00:00', 'PM'] -> ['Yesterday']
dateParts.length = (index !== -1 ? (index - 1) : dateParts.length);
Upvotes: 0
Reputation: 1545
this is working perfectly. try it.
(moment(time).calendar().split(" at"))[0]
Upvotes: 4
Reputation: 32174
starting from moment 2.10.5 you can do:
moment(/*your date*/).calendar(null,{
lastDay : '[Yesterday]',
sameDay : '[Today]',
nextDay : '[Tomorrow]',
lastWeek : '[last] dddd',
nextWeek : 'dddd',
sameElse : 'L'
})
see: http://momentjs.com/docs/#/displaying/calendar-time/
Upvotes: 55
Reputation: 4918
I created an angular directive that provides a calendar with the date only. If you don't use angular, just use the function.
app.filter('amCalendarDate', function($translate) {
return function(dt) {
var md = moment(dt), key = '';
if (!dt || !md || (md + '').toLowerCase() == 'invalid date')
return '';
var today = moment();
var diff = today.diff(md, 'days');
if (!diff)
key = 'Today';
else if (diff == -1)
key = 'Tomorrow';
else if (diff == 1)
key = 'Yesterday';
else if (Math.abs(diff) <= 6) {
if (diff < 0)
key = 'Next';
else
key = 'Last';
var days = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
today.day(diff);
key += ' ' + days[today.day()];
}
if (key) {
// if you don't have translate, just return the key
return $translate.amCalendarDate[key];
} else
return md.format('L');
}
});
Upvotes: 0
Reputation: 47182
moment().calendar()
supports custom formatted strings and formatting functions.
moment().calendar();
>> "Today at 9:06 AM"
Then set your formatted strings
moment.locale('yourlang', {
calendar: {
lastDay: function () {
return '[last]';
},
sameDay: function () {
return '[Today]';
}
}
});
moment().calendar();
// Will now output
>> "Today"
should do the trick. The docs are an invaluable source
Upvotes: 8