Merge ef37a13037 into 07eea0f484
				
					
				
			This commit is contained in:
		
						commit
						a1af08ec68
					
				
							
								
								
									
										7
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -130,19 +130,19 @@ declare module '@vue/runtime-core' { | ||||
|     MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] | ||||
|     MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] | ||||
|     NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] | ||||
|     NCheckbox: typeof import('naive-ui')['NCheckbox'] | ||||
|     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] | ||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||
|     NDatePicker: typeof import('naive-ui')['NDatePicker'] | ||||
|     NDivider: typeof import('naive-ui')['NDivider'] | ||||
|     NEllipsis: typeof import('naive-ui')['NEllipsis'] | ||||
|     NFormItem: typeof import('naive-ui')['NFormItem'] | ||||
|     NH1: typeof import('naive-ui')['NH1'] | ||||
|     NH3: typeof import('naive-ui')['NH3'] | ||||
|     NIcon: typeof import('naive-ui')['NIcon'] | ||||
|     NInputNumber: typeof import('naive-ui')['NInputNumber'] | ||||
|     NLayout: typeof import('naive-ui')['NLayout'] | ||||
|     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] | ||||
|     NMenu: typeof import('naive-ui')['NMenu'] | ||||
|     NSpace: typeof import('naive-ui')['NSpace'] | ||||
|     NTable: typeof import('naive-ui')['NTable'] | ||||
|     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] | ||||
|     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] | ||||
|     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] | ||||
| @ -185,6 +185,7 @@ declare module '@vue/runtime-core' { | ||||
|     UserAgentParser: typeof import('./src/tools/user-agent-parser/user-agent-parser.vue')['default'] | ||||
|     UserAgentResultCards: typeof import('./src/tools/user-agent-parser/user-agent-result-cards.vue')['default'] | ||||
|     UuidGenerator: typeof import('./src/tools/uuid-generator/uuid-generator.vue')['default'] | ||||
|     WeekNumberConverter: typeof import('./src/tools/week-number-converter/week-number-converter.vue')['default'] | ||||
|     WifiQrCodeGenerator: typeof import('./src/tools/wifi-qr-code-generator/wifi-qr-code-generator.vue')['default'] | ||||
|     XmlFormatter: typeof import('./src/tools/xml-formatter/xml-formatter.vue')['default'] | ||||
|     XmlToJson: typeof import('./src/tools/xml-to-json/xml-to-json.vue')['default'] | ||||
|  | ||||
| @ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; | ||||
| import { tool as base64StringConverter } from './base64-string-converter'; | ||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||
| import { tool as emailNormalizer } from './email-normalizer'; | ||||
| import { tool as weekNumberConverter } from './week-number-converter'; | ||||
| 
 | ||||
| import { tool as asciiTextDrawer } from './ascii-text-drawer'; | ||||
| 
 | ||||
| @ -116,6 +117,7 @@ export const toolsByCategory: ToolCategory[] = [ | ||||
|       xmlToJson, | ||||
|       jsonToXml, | ||||
|       markdownToHtml, | ||||
|       weekNumberConverter, | ||||
|     ], | ||||
|   }, | ||||
|   { | ||||
|  | ||||
							
								
								
									
										12
									
								
								src/tools/week-number-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/week-number-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| import { Calendar } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Week Numbers Converter', | ||||
|   path: '/week-number-converter', | ||||
|   description: 'Convert between ISO Week number, Week number in month and date', | ||||
|   keywords: ['week', 'month', 'number', 'iso', 'converter'], | ||||
|   component: () => import('./week-number-converter.vue'), | ||||
|   icon: Calendar, | ||||
|   createdAt: new Date('2024-08-15'), | ||||
| }); | ||||
| @ -0,0 +1,20 @@ | ||||
| import { describe, expect, it } from 'vitest'; | ||||
| import { getWeekOfMonth } from 'date-fns'; | ||||
| import { getFirstMondayFromISOWeek, getFirstMondayFromMonthWeek } from './week-number-converter.service'; | ||||
| 
 | ||||
| describe('week-number-converter', () => { | ||||
|   describe('getFirstMondayFromISOWeek', () => { | ||||
|     it('return right monday date from week number', () => { | ||||
|       expect(getFirstMondayFromISOWeek(11, 2022).toDateString()).toBe('Mon Mar 14 2022'); | ||||
|       expect(getFirstMondayFromISOWeek(1, 2023).toDateString()).toBe('Mon Jan 02 2023'); | ||||
|       expect(getFirstMondayFromISOWeek(53, 2026).toDateString()).toBe('Mon Dec 28 2026'); | ||||
|     }); | ||||
|   }); | ||||
|   describe('getFirstMondayFromMonthWeek', () => { | ||||
|     it('return right date from month week number', () => { | ||||
|       expect(getFirstMondayFromMonthWeek(getWeekOfMonth(new Date('2022-03-14')), 3, 2022).toDateString()).toBe('Mon Mar 14 2022'); | ||||
|       expect(getFirstMondayFromMonthWeek(getWeekOfMonth(new Date('2023-01-02')), 1, 2023).toDateString()).toBe('Mon Jan 02 2023'); | ||||
|       expect(getFirstMondayFromMonthWeek(getWeekOfMonth(new Date('2026-12-28')), 12, 2026).toDateString()).toBe('Mon Dec 28 2026'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @ -0,0 +1,15 @@ | ||||
| // Returns the first day (Monday) of the specified week
 | ||||
| 
 | ||||
| // Year defaults to the current local calendar year
 | ||||
| export function getFirstMondayFromISOWeek(weekInYear: number, year = new Date().getFullYear()) { | ||||
|   const d = new Date(year, 0, 4); | ||||
|   d.setDate(d.getDate() - (d.getDay() || 7) + 1 + 7 * (weekInYear - 1)); | ||||
|   return d; | ||||
| } | ||||
| export function getFirstMondayFromMonthWeek(weekInMonth: number, month = new Date().getMonth() + 1, year = new Date().getFullYear()) { | ||||
|   const d = new Date(year, month - 1, 4); | ||||
|   const day = d.getDay() || 7; | ||||
|   d.setDate(d.getDate() - day + 1); | ||||
|   d.setDate(d.getDate() + 7 * (weekInMonth - 1)); | ||||
|   return d; | ||||
| } | ||||
							
								
								
									
										71
									
								
								src/tools/week-number-converter/week-number-converter.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/tools/week-number-converter/week-number-converter.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| <script setup lang="ts"> | ||||
| import { getISOWeek, getWeek, getWeekOfMonth } from 'date-fns'; | ||||
| import { getFirstMondayFromISOWeek, getFirstMondayFromMonthWeek } from './week-number-converter.service'; | ||||
| 
 | ||||
| const now = new Date(); | ||||
| 
 | ||||
| const inputDate = ref(now.getTime()); | ||||
| const outputWeekInMonth = computed(() => getWeekOfMonth(inputDate.value)); | ||||
| const outputLocalWeekInYear = computed(() => getWeek(inputDate.value)); | ||||
| const outputISOWeekInYear = computed(() => getISOWeek(inputDate.value)); | ||||
| 
 | ||||
| const inputWeekInMonth = ref({ | ||||
|   week: getWeekOfMonth(now), | ||||
|   month: now.getMonth() + 1, | ||||
|   year: now.getFullYear(), | ||||
| }); | ||||
| const outputWeekInMonthMonday = computed(() => getFirstMondayFromMonthWeek(inputWeekInMonth.value.week, inputWeekInMonth.value.month, inputWeekInMonth.value.year)); | ||||
| 
 | ||||
| const inputWeekInYear = ref({ | ||||
|   week: getWeek(now), | ||||
|   year: now.getFullYear(), | ||||
| }); | ||||
| const outputWeekInYearMonday = computed(() => getFirstMondayFromISOWeek(inputWeekInYear.value.week, inputWeekInYear.value.year)); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <c-card title="Date to Week numbers" mb-2> | ||||
|       <n-form-item label="Date:" label-placement="left"> | ||||
|         <n-date-picker v-model:value="inputDate" type="date" /> | ||||
|       </n-form-item> | ||||
| 
 | ||||
|       <n-divider /> | ||||
| 
 | ||||
|       <input-copyable readonly label="Local Week in Year:" label-position="left" label-width="130px" :value="outputLocalWeekInYear" mb-1 /> | ||||
|       <input-copyable readonly label="ISO Week (in Year):" label-position="left" label-width="130px" :value="outputISOWeekInYear" mb-1 /> | ||||
|       <input-copyable readonly label="Week in Month:" label-position="left" label-width="130px" :value="outputWeekInMonth" mb-1 /> | ||||
|     </c-card> | ||||
|     <c-card title="ISO Week number to date" mb-2> | ||||
|       <div flex items-baseline gap-2> | ||||
|         <n-form-item label="ISO Week number:" label-placement="left" flex-1> | ||||
|           <n-input-number v-model:value="inputWeekInYear.week" :min="1" :max="53" /> | ||||
|         </n-form-item> | ||||
|         <n-form-item label="Year:" label-placement="left" flex-1> | ||||
|           <n-input-number v-model:value="inputWeekInYear.year" /> | ||||
|         </n-form-item> | ||||
|       </div> | ||||
| 
 | ||||
|       <n-divider /> | ||||
| 
 | ||||
|       <input-copyable readonly label="First Monday" label-position="left" :value="outputWeekInYearMonday" /> | ||||
|     </c-card> | ||||
|     <c-card title="Week number in month to date" mb-2> | ||||
|       <div flex items-baseline gap-2> | ||||
|         <n-form-item label="Week in month:" label-placement="left" flex-1> | ||||
|           <n-input-number v-model:value="inputWeekInMonth.week" :min="1" :max="5" /> | ||||
|         </n-form-item> | ||||
|         <n-form-item label="Month:" label-placement="left" flex-1> | ||||
|           <n-input-number v-model:value="inputWeekInMonth.month" :min="1" :max="12" /> | ||||
|         </n-form-item> | ||||
|         <n-form-item label="Year:" label-placement="left" flex-1> | ||||
|           <n-input-number v-model:value="inputWeekInMonth.year" /> | ||||
|         </n-form-item> | ||||
|       </div> | ||||
| 
 | ||||
|       <n-divider /> | ||||
| 
 | ||||
|       <input-copyable readonly label="First Monday" label-position="left" :value="outputWeekInMonthMonday" /> | ||||
|     </c-card> | ||||
|   </div> | ||||
| </template> | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user