date-fns 的 isSameOrBefore 实现

isSameOrBefore implementation for date-fns

随着 moment 被弃用,我决定从我的整个项目中隐藏 moment 并考虑使用 datefns 作为替代品。在浏览代码文件时,我遇到了一行内容,

moment(date1).isSameOrBefore(date2, year);

我开始寻找与该函数所做的类似的东西,但不幸的是,date-fns 没有任何东西可以执行基于精度的比较(即:年、月或日期比较)?谁能建议一些 date-fns 的解决方法?

使用getYear先获取两个日期的“年”部分,然后比较两个日期。

类似于:

const date1 = new Date(2021, 6, 20);
const date2 = new Date(2021, 7, 2);
const date3 = new Date(2020, 1, 15);
console.log(dateFns.getYear(date1) <= dateFns.getYear(date2));
console.log(dateFns.getYear(date1) <= dateFns.getYear(date3));
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.28.5/date_fns.min.js"></script>

使用 startOfSOMETHING() 您可以使用分钟、小时、天、月 ecc 来比较它们来检查日期是否早于或相同。

const dateBefore = dateFns.startOfHour(new Date(1988, 9, 26, 05, 35));
const dateSame = dateFns.startOfHour(new Date(1988, 9, 26, 11, 55));
const dateAfter = dateFns.startOfHour(new Date(1988, 9, 26, 16, 50));

const limit = dateFns.startOfHour(new Date(1988, 9, 26, 11, 55));

// dateBefore < limit => is before
if (dateFns.isBefore(dateBefore, limit) || dateFns.isEqual(dateBefore, limit)) console.log(dateFns.format(dateBefore), 'is before');
else console.log('is not before');

// dateSame === limit => is same
if (dateFns.isBefore(dateSame, limit) || dateFns.isEqual(dateSame, limit)) console.log(dateFns.format(dateSame), 'is same');
else console.log('is not same');

// dateAfter > limit =>  is after
if (dateFns.isBefore(dateAfter, limit) || dateFns.isEqual(dateAfter, limit)) console.log('you mustnt see that message ');
else console.log(dateFns.format(dateAfter), 'is after');
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.28.5/date_fns.min.js"></script>

在我看来这个库很糟糕,我更喜欢 day.js 与 moment.js 非常相似,或者用 Javascritp 的 new Date() 详细说明日期。

import { isBefore, isEqual } from 'date-fns';

isSameOrBefore(date1: Date, date2: Date, property?: string) {
  switch (property) {
    case 'full':
      return isBefore(date1, date2) || isEqual(date1, date2);
    case 'year':
      return date1.getFullYear() <= date2.getFullYear();
    case 'month':
      return (
        isBefore(date1.getFullYear(), date2.getFullYear()) ||
        (isEqual(date1.getFullYear(), date2.getFullYear()) &&
          date1.getMonth() <= date2.getMonth())
      );
    case 'date':
      return (
        isBefore(date1.getFullYear(), date2.getFullYear()) ||
        (isEqual(date1.getFullYear(), date2.getFullYear()) &&
          date1.getMonth() < date2.getMonth()) ||
        (isEqual(date1.getFullYear(), date2.getFullYear()) &&
          isEqual(date1.getMonth(), date2.getMonth()) &&
          date1.getDate() <= date2.getDate())
      );
    default:
      return compareDesc(date1, date2);
  }
}

您可以为日期、时刻添加更多实现....

您可以使用 differenceInYears:

const date1 = new Date(2021, 6, 20);;
const date2 = new Date(2021, 7, 2);

function isSameOrBefore(date1, date2) {
  return dateFns.differenceInYears(date1, date2) <= 0;
}

console.log(isSameOrBefore(date1, date2));

console.log(isSameOrBefore(date1, new Date('2022-06-20')));

console.log(isSameOrBefore(date1, new Date('2019-06-20')));
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.28.5/date_fns.min.js"></script>

只要您针对相关矩函数进行测试,您应该能够创建一个直接替换。

我们可以寻找相关的startOfXXX过程来比较指定unit时的日期,如果缺少也会抛出错误:

function isSameOrBefore(date1, date2, unit = '') {
    if (unit) {
        const procName = `startOf${unit[0].toUpperCase()}${unit.slice(1)}`;
        if (!dateFns[procName]) { 
            throw new Error(`isSameOrBefore: Unsupported unit: ${unit}`);
        }
        date1 = dateFns[procName](date1);
        date2 = dateFns[procName](date2);
    }
    return dateFns.isBefore(date1, date2) || dateFns.isEqual(date1, date2);
}

// Test against various inputs
const dts = ['2010-10-20 01:00', '2010-10-20 00:00', '2010-10-20 00:59:00', '2010-10-01', '2010-09-01', '2009-09-01'];
let units = ['year', 'month', 'day', 'hour', 'minute'];
let dt1 = '2010-10-20 01:00'
let allTests = [];
for(let dt2 of dts) {
    for(let unit of units) {
        // Ensure we get the same answer as moment().isSameOrBefore
        const testPassed = isSameOrBefore(dt1, dt2, unit) === moment(dt1).isSameOrBefore(dt2, unit)
        allTests.push(testPassed);
        console.log(`isSameOrBefore(${dt1}, ${dt2}, ${unit}):`, isSameOrBefore(dt1, dt2, unit));
        console.log(`isSameOrBefore(${dt1}, ${dt2}, ${unit}): Test passed:`, testPassed ? "TRUE": "FALSE")
    }
}
console.log("All tests passed:", allTests.every(res => res) ? "TRUE": "FALSE")
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.min.js" integrity="sha512-F+u8eWHrfY8Xw9BLzZ8rG/0wIvs0y+JyRJrXjp3VjtFPylAEEGwKbua5Ip/oiVhaTDaDs4eU2Xtsxjs/9ag2bQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

也让我post回答一下。它不需要您导入整个 date-fns

import { endOfDay, endOfWeek, endOfMonth, endOfYear } from 'date-fns';

export function isSameOrBefore(date1, date2, unit) {
    let method;
    switch (unit) {
        case 'day':
            method = endOfDay;
            break;
        case 'week':
            method = endOfWeek;
            break;
        case 'month':
            method = endOfMonth;
            break;
        case 'year':
            method = endOfYear;
            break;
        default:
            break;
    }
    const startTimestamp = method ? method(date1): date1;
    return dateFns.isBefore(startTimestamp, date2) || dateFns.isEqual(startTimestamp, date2);
}