Add new tool: 日期计算器 — 日期加减与两个日期间隔天数计算

This commit is contained in:
lidong-sal 2026-06-16 10:52:46 +08:00
parent 07fecf0555
commit fb4f9bbb60
6 changed files with 195 additions and 0 deletions

1
components.d.ts vendored
View file

@ -67,6 +67,7 @@ declare module '@vue/runtime-core' {
CTooltip: typeof import('./src/ui/c-tooltip/c-tooltip.vue')['default']
'CTooltip.demo': typeof import('./src/ui/c-tooltip/c-tooltip.demo.vue')['default']
CurlToCode: typeof import('./src/tools/curl-to-code/curl-to-code.vue')['default']
DateCalculator: typeof import('./src/tools/date-calculator/date-calculator.vue')['default']
DateTimeConverter: typeof import('./src/tools/date-time-converter/date-time-converter.vue')['default']
'DemoHome.page': typeof import('./src/ui/demo/demo-home.page.vue')['default']
DemoWrapper: typeof import('./src/ui/demo/demo-wrapper.vue')['default']

View file

@ -297,6 +297,25 @@ tools:
title: JWT parser
description: Parse and decode your JSON Web Token (jwt) and display its content.
date-calculator:
title: Date calculator
description: Add or subtract days from a date, or calculate the number of days between two dates.
offset:
sectionTitle: Date after / before N days
baseDate: Start date
days: Days offset
daysHint: Use a negative number to go backwards
resultLabel: Result date
diff:
sectionTitle: Days between two dates
startDate: Start date
targetDate: Target date
resultLabel: Difference
dayUnit: days
directionAfter: Target date is after the start date
directionBefore: Target date is before the start date
directionSame: Same date
date-converter:
title: Date-time converter
description: Convert date and time into the various different formats

View file

@ -293,6 +293,25 @@ tools:
title: JWT 解析器
description: 解析和解码JSON Web Tokenjwt并显示其内容。
date-calculator:
title: 日期计算器
description: 推算几天前/后的日期,或计算两个日期之间相差的天数。
offset:
sectionTitle: 推算几天前 / 后的日期
baseDate: 起始日期
days: 偏移天数
daysHint: 输入负数则往前推算
resultLabel: 结果日期
diff:
sectionTitle: 计算两个日期相差
startDate: 开始日期
targetDate: 目标日期
resultLabel: 相差
dayUnit:
directionAfter: 目标日期比起始日期晚
directionBefore: 目标日期比起始日期早
directionSame: 两个日期为同一天
date-converter:
title: 日期时间转换器
description: 将日期和时间转换为各种不同的格式

View file

@ -0,0 +1,141 @@
<script setup lang="ts">
import { addDays, differenceInCalendarDays, format, isValid } from 'date-fns';
import { enUS, zhCN } from 'date-fns/locale';
import { useI18n } from 'vue-i18n';
const { t, locale } = useI18n();
const DAY_MS = 24 * 60 * 60 * 1000;
// 0
function startOfToday() {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
}
// /
const offsetBaseDate = ref<number>(startOfToday());
const offsetDays = ref(30);
//
const diffStartDate = ref<number>(startOfToday());
const diffTargetDate = ref<number>(startOfToday() + 30 * DAY_MS);
// date-fns locale
const dateLocale = computed(() => (locale.value?.startsWith('zh') ? zhCN : enUS));
const datePattern = computed(() =>
locale.value?.startsWith('zh') ? 'yyyy年M月d日 EEEE' : 'EEEE, MMMM d, yyyy',
);
const offsetResultDate = computed(() => {
if (offsetBaseDate.value == null || !Number.isFinite(offsetDays.value)) {
return null;
}
const base = new Date(offsetBaseDate.value);
return isValid(base) ? addDays(base, offsetDays.value) : null;
});
const offsetResultText = computed(() => {
const date = offsetResultDate.value;
if (!date || !isValid(date)) {
return '';
}
return format(date, datePattern.value, { locale: dateLocale.value });
});
const diffDays = computed(() => {
if (diffStartDate.value == null || diffTargetDate.value == null) {
return null;
}
const start = new Date(diffStartDate.value);
const target = new Date(diffTargetDate.value);
if (!isValid(start) || !isValid(target)) {
return null;
}
return differenceInCalendarDays(target, start);
});
const absDiffDays = computed(() => (diffDays.value === null ? null : Math.abs(diffDays.value)));
</script>
<template>
<div style="flex: 0 0 100%">
<div style="margin: 0 auto; max-width: 620px">
<!-- 功能一推算几天前 / 后的日期 -->
<c-card mb-4 :title="t('tools.date-calculator.offset.sectionTitle')">
<n-form-item :label="t('tools.date-calculator.offset.baseDate')" :show-feedback="false">
<n-date-picker v-model:value="offsetBaseDate" type="date" />
</n-form-item>
<n-form-item
mt-4
:label="t('tools.date-calculator.offset.days')"
:show-feedback="false"
>
<div w-full flex items-center gap-3>
<n-input-number v-model:value="offsetDays" />
<span whitespace-nowrap text-sm op-60>
{{ t('tools.date-calculator.offset.daysHint') }}
</span>
</div>
</n-form-item>
<n-divider />
<div flex justify-center>
<n-statistic :label="t('tools.date-calculator.offset.resultLabel')">
{{ offsetResultText || '—' }}
</n-statistic>
</div>
</c-card>
<!-- 功能二计算两个日期相差的天数 -->
<c-card :title="t('tools.date-calculator.diff.sectionTitle')">
<n-form-item :label="t('tools.date-calculator.diff.startDate')" :show-feedback="false">
<n-date-picker v-model:value="diffStartDate" type="date" />
</n-form-item>
<n-form-item
mt-4
:label="t('tools.date-calculator.diff.targetDate')"
:show-feedback="false"
>
<n-date-picker v-model:value="diffTargetDate" type="date" />
</n-form-item>
<n-divider />
<div flex flex-col items-center gap-2>
<n-statistic :label="t('tools.date-calculator.diff.resultLabel')">
<span>{{ absDiffDays === null ? '—' : absDiffDays }}</span>
<span text-sm font-normal op-70>
{{ t('tools.date-calculator.diff.dayUnit') }}
</span>
</n-statistic>
<div text-sm op-60>
<span v-if="diffDays === null || diffDays === 0">
{{ t('tools.date-calculator.diff.directionSame') }}
</span>
<span v-else-if="diffDays > 0">
{{ t('tools.date-calculator.diff.directionAfter') }}
</span>
<span v-else>
{{ t('tools.date-calculator.diff.directionBefore') }}
</span>
</div>
</div>
</c-card>
</div>
</div>
</template>
<style lang="less" scoped>
.n-date-picker,
.n-input-number {
width: 100%;
}
.n-input-number {
max-width: 220px;
}
</style>

View file

@ -0,0 +1,13 @@
import { Calculator } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: translate('tools.date-calculator.title'),
path: '/date-calculator',
description: translate('tools.date-calculator.description'),
keywords: ['date', 'difference', 'days', 'calculator', 'add', 'subtract', 'duration', 'between', 'offset'],
component: () => import('./date-calculator.vue'),
icon: Calculator,
createdAt: new Date('2026-06-15'),
});

View file

@ -61,6 +61,7 @@ import { tool as chmodCalculator } from './chmod-calculator';
import { tool as chronometer } from './chronometer';
import { tool as colorConverter } from './color-converter';
import { tool as crontabGenerator } from './crontab-generator';
import { tool as dateCalculator } from './date-calculator';
import { tool as dateTimeConverter } from './date-time-converter';
import { tool as deviceInformation } from './device-information';
import { tool as cypher } from './encryption';
@ -127,6 +128,7 @@ export const toolsByCategory: ToolCategory[] = [
name: 'Converter',
components: [
byteUnitConverter,
dateCalculator,
dateTimeConverter,
baseConverter,
romanNumeralConverter,