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'] |     MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] | ||||||
|     MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] |     MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] | ||||||
|     NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] |     NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] | ||||||
|     NCheckbox: typeof import('naive-ui')['NCheckbox'] |  | ||||||
|     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] |     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] | ||||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] |     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||||
|  |     NDatePicker: typeof import('naive-ui')['NDatePicker'] | ||||||
|     NDivider: typeof import('naive-ui')['NDivider'] |     NDivider: typeof import('naive-ui')['NDivider'] | ||||||
|     NEllipsis: typeof import('naive-ui')['NEllipsis'] |     NEllipsis: typeof import('naive-ui')['NEllipsis'] | ||||||
|  |     NFormItem: typeof import('naive-ui')['NFormItem'] | ||||||
|     NH1: typeof import('naive-ui')['NH1'] |     NH1: typeof import('naive-ui')['NH1'] | ||||||
|     NH3: typeof import('naive-ui')['NH3'] |     NH3: typeof import('naive-ui')['NH3'] | ||||||
|     NIcon: typeof import('naive-ui')['NIcon'] |     NIcon: typeof import('naive-ui')['NIcon'] | ||||||
|  |     NInputNumber: typeof import('naive-ui')['NInputNumber'] | ||||||
|     NLayout: typeof import('naive-ui')['NLayout'] |     NLayout: typeof import('naive-ui')['NLayout'] | ||||||
|     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] |     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] | ||||||
|     NMenu: typeof import('naive-ui')['NMenu'] |     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'] |     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'] |     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'] |     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'] |     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'] |     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'] |     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'] |     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'] |     XmlFormatter: typeof import('./src/tools/xml-formatter/xml-formatter.vue')['default'] | ||||||
|     XmlToJson: typeof import('./src/tools/xml-to-json/xml-to-json.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 base64StringConverter } from './base64-string-converter'; | ||||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||||
| import { tool as emailNormalizer } from './email-normalizer'; | import { tool as emailNormalizer } from './email-normalizer'; | ||||||
|  | import { tool as weekNumberConverter } from './week-number-converter'; | ||||||
| 
 | 
 | ||||||
| import { tool as asciiTextDrawer } from './ascii-text-drawer'; | import { tool as asciiTextDrawer } from './ascii-text-drawer'; | ||||||
| 
 | 
 | ||||||
| @ -116,6 +117,7 @@ export const toolsByCategory: ToolCategory[] = [ | |||||||
|       xmlToJson, |       xmlToJson, | ||||||
|       jsonToXml, |       jsonToXml, | ||||||
|       markdownToHtml, |       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