Skip to content

Commit e83678b

Browse files
committed
Merge branch 'feat/weeks-converter' into chore/all-my-stuffs
# Conflicts: # src/tools/index.ts
2 parents a83988a + 699f327 commit e83678b

File tree

6 files changed

+120
-0
lines changed

6 files changed

+120
-0
lines changed

pnpm-lock.yaml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { tool as dataStorageUnitConverter } from './data-storage-unit-converter'
5151
import { tool as unicodeSearch } from './unicode-search';
5252
import { tool as vatValidator } from './vat-validator';
5353
import { tool as websocketTester } from './websocket-tester';
54+
import { tool as weekNumberConverter } from './week-number-converter';
5455

5556
import { tool as cssXpathConverter } from './css-xpath-converter';
5657
import { tool as cssSelectorsMemo } from './css-selectors-memo';
@@ -259,6 +260,7 @@ export const toolsByCategory: ToolCategory[] = [
259260
markdownTocGenerator,
260261
rmbNumbers,
261262
smartRawConverter,
263+
weekNumberConverter,
262264
],
263265
},
264266
{
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Calendar } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Week Numbers Converter',
6+
path: '/week-number-converter',
7+
description: 'Compute Week Number in Year/Month vs Date',
8+
keywords: ['week', 'month', 'number', 'converter'],
9+
component: () => import('./week-number-converter.vue'),
10+
icon: Calendar,
11+
createdAt: new Date('2024-08-15'),
12+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { getWeekOfMonth } from 'date-fns';
3+
import { getFirstMondayFromISOWeek, getFirstMondayFromMonthWeek } from './week-number-converter.service';
4+
5+
describe('week-number-converter', () => {
6+
describe('getFirstMondayFromISOWeek', () => {
7+
it('return right monday date from week number', () => {
8+
expect(getFirstMondayFromISOWeek(11, 2022).toDateString()).toBe('Mon Mar 14 2022');
9+
expect(getFirstMondayFromISOWeek(1, 2023).toDateString()).toBe('Mon Jan 02 2023');
10+
expect(getFirstMondayFromISOWeek(53, 2026).toDateString()).toBe('Mon Dec 28 2026');
11+
});
12+
});
13+
describe('getFirstMondayFromMonthWeek', () => {
14+
it('return right date from month week number', () => {
15+
expect(getFirstMondayFromMonthWeek(getWeekOfMonth(new Date('2022-03-14')), 3, 2022).toDateString()).toBe('Mon Mar 14 2022');
16+
expect(getFirstMondayFromMonthWeek(getWeekOfMonth(new Date('2023-01-02')), 1, 2023).toDateString()).toBe('Mon Jan 02 2023');
17+
expect(getFirstMondayFromMonthWeek(getWeekOfMonth(new Date('2026-12-28')), 12, 2026).toDateString()).toBe('Mon Dec 28 2026');
18+
});
19+
});
20+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Returns the first day (Monday) of the specified week
2+
3+
// Year defaults to the current local calendar year
4+
export function getFirstMondayFromISOWeek(weekInYear: number, year = new Date().getFullYear()) {
5+
const d = new Date(year, 0, 4);
6+
d.setDate(d.getDate() - (d.getDay() || 7) + 1 + 7 * (weekInYear - 1));
7+
return d;
8+
}
9+
export function getFirstMondayFromMonthWeek(weekInMonth: number, month = new Date().getMonth() + 1, year = new Date().getFullYear()) {
10+
const d = new Date(year, month - 1, 4);
11+
const day = d.getDay() || 7;
12+
d.setDate(d.getDate() - day + 1);
13+
d.setDate(d.getDate() + 7 * (weekInMonth - 1));
14+
return d;
15+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script setup lang="ts">
2+
import { getWeek, getWeekOfMonth } from 'date-fns';
3+
import { getFirstMondayFromISOWeek, getFirstMondayFromMonthWeek } from './week-number-converter.service';
4+
5+
const now = new Date();
6+
7+
const inputDate = ref(now.getTime());
8+
const outputWeekInMonth = computed(() => getWeekOfMonth(inputDate.value));
9+
const outputWeekInYear = computed(() => getWeek(inputDate.value));
10+
11+
const inputWeekInMonth = ref({
12+
week: getWeekOfMonth(now),
13+
month: now.getMonth() + 1,
14+
year: now.getFullYear(),
15+
});
16+
const outputWeekInMonthMonday = computed(() => getFirstMondayFromMonthWeek(inputWeekInMonth.value.week, inputWeekInMonth.value.month, inputWeekInMonth.value.year));
17+
18+
const inputWeekInYear = ref({
19+
week: getWeek(now),
20+
year: now.getFullYear(),
21+
});
22+
const outputWeekInYearMonday = computed(() => getFirstMondayFromISOWeek(inputWeekInYear.value.week, inputWeekInYear.value.year));
23+
</script>
24+
25+
<template>
26+
<div>
27+
<c-card title="Date to Week numbers" mb-2>
28+
<n-form-item label="Date:" label-placement="left">
29+
<n-date-picker v-model:value="inputDate" type="date" />
30+
</n-form-item>
31+
32+
<n-divider />
33+
34+
<input-copyable readonly label="Week in Year:" label-position="left" label-width="120px" :value="outputWeekInYear" mb-1 />
35+
<input-copyable readonly label="Week in Month:" label-position="left" label-width="120px" :value="outputWeekInMonth" mb-1 />
36+
</c-card>
37+
<c-card title="Year Week Number to Date" mb-2>
38+
<div flex items-baseline gap-2>
39+
<n-form-item label="Week in Year:" label-placement="left" flex-1>
40+
<n-input-number v-model:value="inputWeekInYear.week" :min="1" :max="53" />
41+
</n-form-item>
42+
<n-form-item label="Year:" label-placement="left" flex-1>
43+
<n-input-number v-model:value="inputWeekInYear.year" />
44+
</n-form-item>
45+
</div>
46+
47+
<n-divider />
48+
49+
<input-copyable readonly label="First Monday" label-position="left" :value="outputWeekInYearMonday" />
50+
</c-card>
51+
<c-card title="Month Week Number to Date" mb-2>
52+
<div flex items-baseline gap-2>
53+
<n-form-item label="Week in Month:" label-placement="left" flex-1>
54+
<n-input-number v-model:value="inputWeekInMonth.week" :min="1" :max="5" />
55+
</n-form-item>
56+
<n-form-item label="Month:" label-placement="left" flex-1>
57+
<n-input-number v-model:value="inputWeekInMonth.month" :min="1" :max="12" />
58+
</n-form-item>
59+
<n-form-item label="Year:" label-placement="left" flex-1>
60+
<n-input-number v-model:value="inputWeekInMonth.year" />
61+
</n-form-item>
62+
</div>
63+
64+
<n-divider />
65+
66+
<input-copyable readonly label="First Monday" label-position="left" :value="outputWeekInMonthMonday" />
67+
</c-card>
68+
</div>
69+
</template>

0 commit comments

Comments
 (0)