在 javascript 中以自然语言输出日期

Outputting dates in natural language in javascript

我的朋友让我做一个显然很痛苦的事情,即将日期从日期选择器转换为自然语言,首先是英语,然后是法语,因为他是一名公证人,他想要一个工具 copy/paste 将自然日期写入他的文档中,这样他就不会出错。

所需的输出格式为:twenty-fourth day of January two thousand and twenty-two

我已经浏览了大约 8 个现有的库,但 none 似乎用自然语言来计算日历日或年。

这样做的方法是分别转换日期的每个实体。 (答案使用对 Transform numbers to words in lakh / crore system 的引用将年份转换为根据您的用例修改的单词。)

var a = ['','one ','two ','three ','four ', 'five ','six ','seven ','eight ','nine ','ten ','eleven ','twelve ','thirteen ','fourteen ','fifteen ','sixteen ','seventeen ','eighteen ','nineteen '];
var b = ['', '', 'twenty','thirty','forty','fifty', 'sixty','seventy','eighty','ninety'];



function yearInWords (num) {
    if ((num = num.toString()).length > 9) return 'overflow';
    n = ('000000000' + num).substr(-9).match(/^(\d{2})(\d{2})(\d{2})(\d{1})(\d{2})$/);
    if (!n) return; var str = '';
    str += (n[1] != 0) ? (a[Number(n[1])] || b[n[1][0]] + ' ' + a[n[1][1]]) + 'crore ' : '';
    str += (n[2] != 0) ? (a[Number(n[2])] || b[n[2][0]] + ' ' + a[n[2][1]]) + 'lakh ' : '';
    str += (n[3] != 0) ? (a[Number(n[3])] || b[n[3][0]] + ' ' + a[n[3][1]]) + 'thousand ' : '';
    str += (n[4] != 0) ? (a[Number(n[4])] || b[n[4][0]] + ' ' + a[n[4][1]]) + 'hundred ' : '';
    str += (n[5] != 0) ? ((str != '') ? 'and ' : '') + (a[Number(n[5])] || b[n[5][0]] + (a[n[5][1]] ? '-' : '') + a[n[5][1]]) : '';
    return str;
}

 var days = ['first','second','third','fourth','fifth', 'sixth','seventh','eigth','ninth','tenth','eleventh','twelth','thirteenth','fouteenth','fifteenth','sixteenth','seventeen',
 'eighteenth','nineteenth', 'twentieth', 'twenty', 'thirtieth', 'thirty'];

 function dayInWords (num) {
    if(num > 31) return 'invalid day';
    if(num < 21) return days[num-1];
    if(num < 30) return days[20] + '-' + days[(num % 10)-1];
    if(num == 30) return days[21];
    return days[22] + '-' + days[(num % 10)-1];
    return str;
}

let date = new Date("01/31/2022");
let day = dayInWords(date.getDate());
let month = date.toLocaleString('default', { month: 'long' });
let year = yearInWords(date.getFullYear());
finalDateInWords = day + ' day of ' + month + ' ' + year;
console.log(finalDateInWords);

是的,做到了,谢谢!。快速 mod 到最后一点,以便我可以从我的主脚本中调用它:

function englisDate(_date){
  let date = new Date(_date);
  let day = dayInWords(date.getDate());
  let month = date.toLocaleString('default', { month: 'long' });
  let year = yearInWords(date.getFullYear());
  finalDateInWords = day + ' day of ' + month + ' ' + year;
  console.log(finalDateInWords);
  return(finalDateInWords);
}

如果您只对数字小于 10,000 的日期感兴趣,以下可能适合。

可以压缩数字到名称的映射,但使映射更简单会使逻辑更复杂,对于日期,我看不到增加复杂性的意义。但它可以被清理并变得更简洁。

部分复杂性在于确定是写数字名称还是序数(例如“一”或“第一”),以及是否以及在何处添加“和”(例如“一百零六”或“一千 twenty-six").

function toSpeakableDate(date = new Date()) {
  let toWord = (num, ord) => {
    let numberMap = {
     0: {name: 'zero', ordinal: 'zeroth'},
     1: {name: 'one', ordinal: 'first'}, 
     2: {name: 'two', ordinal: 'second'},
     3: {name: 'three', ordinal: 'third'}, 
     4: {name: 'four', ordinal: 'fourth'}, 
     5: {name: 'five', ordinal: 'fifth'}, 
     6: {name: 'six', ordinal: 'sixth'}, 
     7: {name: 'seven', ordinal: 'seventh'}, 
     8: {name: 'eight', ordinal: 'eighth'}, 
     9: {name: 'nine', ordinal: 'ninth'},
     10: {name: 'ten', ordinal: 'tenth'},
     11: {name: 'eleven', ordinal: 'eleventh'},
     12: {name: 'twelve', ordinal: 'twelfth'},
     13: {name: 'thirteen', ordinal: 'thirteenth'},
     14: {name: 'fourteen', ordinal: 'fourteenth'},
     15: {name: 'fifteen', ordinal: 'fifteenth'},
     16: {name: 'sixteen', ordinal: 'sixteenth'},
     17: {name: 'seventeen', ordinal: 'seventeenth'},
     18: {name: 'eighteen', ordinal: 'eighteenth'},
     19: {name: 'nineteen', ordinal: 'nineteenth'},
     20: {name: 'twenty', ordinal: 'twentyth'},
     30: {name: 'thirty', ordinal: 'thirtyth'},
     40: {name: 'fourty', ordinal: 'fourtyth'},
     50: {name: 'fifty', ordinal: 'fiftyth'},
     60: {name: 'sixty', ordinal: 'sixtyth'},
     70: {name: 'seventy', ordinal: 'sventyth'},
     80: {name: 'eighty', ordinal: 'eightyth'},
     90: {name: 'ninety', ordinal: 'ninetieth'}
    };
    
    let n = String(num);
    let text;
    
    if (n < 21) {
      text = numberMap[n][ord? 'ordinal' : 'name'];
    } else if (n < 1e2) {
      let units = n % 10;
      let tens = Math.floor(n / 10) * 10;
      text = units? `${numberMap[tens].name}-${toWord(units, ord)}` : `${numberMap[tens].ordinal}`;
    } else if (n < 1e3) {
      let hundreds = Math.floor(n / 1e2);
      let remainder = n % 1e2;
      text = `${toWord(hundreds)} hundred` +
        (remainder? ` and ${toWord(remainder)}` : ''); 
    } else if (n < 1e4) {
      let thousands = Math.floor(n / 1e3);
      let noHundreds = String(n).slice(-3,-2) == '0';
      let remainder = n % 1e3;
      text = `${toWord(thousands)} thousand` +
        `${noHundreds? ' and' : ''}` +
        (remainder? ` ${toWord(remainder)}` : ''); 
    }
    return text;
  }
  
  let fullDate = '';
  let day = toWord(date.getDate(), true);
  fullDate += day + ' day of ';
  let month = date.toLocaleString('en',{month:'long'});
  fullDate += month + ' ';
  let year = toWord(date.getFullYear());
  fullDate += year;

  return fullDate;
}

// Examples
// Simple date formatter
let f = date => date.toLocaleString('en-GB', {
 year:'numeric',month:'short',day:'2-digit'});

[new Date(666,0,1),
 new Date(1215,4,31),
 new Date(),
 new Date(2222,5,13)
].forEach(d => console.log(f(d) + '\n' + toSpeakableDate(d)));