Skip to content

Commit f4388ec

Browse files
committed
calculate duration dynamically
Signed-off-by: rishichawda <rishichawda@users.noreply.github.com>
1 parent daf7304 commit f4388ec

File tree

2 files changed

+193
-3
lines changed

2 files changed

+193
-3
lines changed

src/pages/career.astro

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Schema } from "astro-seo-schema";
55
import favicon from "../../content/assets/logo/Group 489.png";
66
import { fetchMetadata, type WebsiteMeta } from "../utils/metadataFetcher";
77
import LinkCard from "../components/LinkCard.astro";
8+
import { calculateDuration, calculateCompanyDuration } from "../utils/dateUtils";
89
interface Language {
910
name: string;
1011
level: string;
@@ -18,6 +19,8 @@ interface Social {
1819
interface Position {
1920
title: string;
2021
period: string;
22+
start_date: string;
23+
end_date: string | null;
2124
duration: string;
2225
location?: string;
2326
description?: string;
@@ -29,6 +32,8 @@ interface Position {
2932
interface ClubPosition {
3033
title: string;
3134
period: string;
35+
start_date: string;
36+
end_date: string | null;
3237
duration: string;
3338
location?: string;
3439
description?: string;
@@ -52,6 +57,8 @@ interface Education {
5257
institution: string;
5358
degree: string;
5459
period: string;
60+
start_date?: string;
61+
end_date?: string;
5562
extra_curriculars?: ExtraCurricular[];
5663
}
5764
@@ -379,7 +386,10 @@ const siteUrl = new URL(
379386
{exp.company}
380387
</h3>
381388
<p class="text-sm text-gray-500 font-['Montserrat_Variable'] mb-4">
382-
{exp.duration}
389+
{(() => {
390+
const dynamicDuration = calculateCompanyDuration(exp.positions);
391+
return dynamicDuration || exp.duration;
392+
})()}
383393
</p>
384394

385395
{exp.websitesMeta && exp.websitesMeta.length > 0 && (
@@ -407,7 +417,11 @@ const siteUrl = new URL(
407417
{position.title}
408418
</h4>
409419
<p class="text-sm text-gray-500 font-['Montserrat_Variable'] mb-3">
410-
{position.period} { position.duration.length > 0 ? `(${position.duration})` : "" }
420+
{position.period}
421+
{(() => {
422+
const dynamicDuration = calculateDuration(position.start_date, position.end_date);
423+
return dynamicDuration ? `(${dynamicDuration})` : (position.duration ? `(${position.duration})` : "");
424+
})()}
411425
</p>
412426
{position.location && (
413427
<p class="text-sm text-gray-600 font-['Montserrat_Variable'] mb-3 flex items-center">
@@ -513,7 +527,11 @@ const siteUrl = new URL(
513527
{position.title}
514528
</h6>
515529
<p class="text-xs text-gray-500 font-['Montserrat_Variable'] mb-2">
516-
{position.period} ({position.duration})
530+
{position.period}
531+
{(() => {
532+
const dynamicDuration = calculateDuration(position.start_date, position.end_date);
533+
return dynamicDuration ? `(${dynamicDuration})` : (position.duration ? `(${position.duration})` : "");
534+
})()}
517535
</p>
518536
{position.location && (
519537
<p class="text-xs text-gray-500 font-['Montserrat_Variable'] mb-2 flex items-center">

src/utils/dateUtils.ts

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/**
2+
* Utility functions for date and duration calculations
3+
*/
4+
5+
/**
6+
* Calculate the duration between start and end dates using MM-YYYY format
7+
* @param startDate - The start date in format "MM-YYYY" (e.g., "06-2024")
8+
* @param endDate - Optional end date in format "MM-YYYY" or null for ongoing positions
9+
* @returns A human-readable duration string (e.g., "1 year 7 months")
10+
*/
11+
export function calculateDuration(startDate: string, endDate: string | null = null): string {
12+
const currentDate = new Date();
13+
14+
// Parse start date
15+
const start = parseDateFromFormat(startDate);
16+
if (!start) {
17+
return "";
18+
}
19+
20+
// Parse end date (use current date if null/ongoing)
21+
let end: Date;
22+
if (!endDate) {
23+
end = currentDate;
24+
} else {
25+
const parsedEnd = parseDateFromFormat(endDate);
26+
if (!parsedEnd) {
27+
return "";
28+
}
29+
end = parsedEnd;
30+
}
31+
32+
// Calculate difference
33+
let years = end.getFullYear() - start.getFullYear();
34+
let months = end.getMonth() - start.getMonth();
35+
36+
// Include the current month by adding 1
37+
months += 1;
38+
39+
// Adjust for negative months
40+
if (months < 0) {
41+
years--;
42+
months += 12;
43+
}
44+
45+
// Handle month overflow
46+
if (months >= 12) {
47+
years += Math.floor(months / 12);
48+
months = months % 12;
49+
}
50+
51+
// Format the result
52+
const parts: string[] = [];
53+
54+
if (years > 0) {
55+
parts.push(`${years} year${years > 1 ? 's' : ''}`);
56+
}
57+
58+
if (months > 0) {
59+
parts.push(`${months} month${months > 1 ? 's' : ''}`);
60+
}
61+
62+
// If both are 0 after calculation, return 1 month as minimum
63+
if (years === 0 && months === 0) {
64+
return "1 month";
65+
}
66+
67+
return parts.join(' ');
68+
}
69+
70+
/**
71+
* Parse a date string in format "MM-YYYY" to a Date object
72+
* @param dateString - Date string like "06-2024" or "12-2022"
73+
* @returns Date object or null if parsing fails
74+
*/
75+
function parseDateFromFormat(dateString: string): Date | null {
76+
try {
77+
// Split the date string by hyphen
78+
const parts = dateString.trim().split('-');
79+
if (parts.length !== 2) {
80+
return null;
81+
}
82+
83+
const [monthStr, yearStr] = parts;
84+
const month = parseInt(monthStr, 10) - 1; // Convert to 0-based month
85+
const year = parseInt(yearStr, 10);
86+
87+
if (isNaN(month) || isNaN(year) || month < 0 || month > 11) {
88+
return null;
89+
}
90+
91+
return new Date(year, month, 1);
92+
} catch (error) {
93+
return null;
94+
}
95+
}
96+
97+
/**
98+
* Calculate duration for company experience based on all positions
99+
* @param positions - Array of positions with start_date and end_date
100+
* @returns A human-readable duration string
101+
*/
102+
export function calculateCompanyDuration(positions: Array<{start_date: string, end_date: string | null}>): string {
103+
if (!positions || positions.length === 0) {
104+
return "";
105+
}
106+
107+
// Find the earliest start date and latest end date
108+
let earliestStart: Date | null = null;
109+
let latestEnd: Date | null = null;
110+
111+
for (const position of positions) {
112+
const startDate = parseDateFromFormat(position.start_date);
113+
if (startDate && (!earliestStart || startDate < earliestStart)) {
114+
earliestStart = startDate;
115+
}
116+
117+
let endDate: Date;
118+
if (!position.end_date) {
119+
// Ongoing position
120+
endDate = new Date();
121+
} else {
122+
const parsedEnd = parseDateFromFormat(position.end_date);
123+
if (!parsedEnd) continue;
124+
endDate = parsedEnd;
125+
}
126+
127+
if (!latestEnd || endDate > latestEnd) {
128+
latestEnd = endDate;
129+
}
130+
}
131+
132+
if (!earliestStart || !latestEnd) {
133+
return "";
134+
}
135+
136+
// Calculate difference
137+
let years = latestEnd.getFullYear() - earliestStart.getFullYear();
138+
let months = latestEnd.getMonth() - earliestStart.getMonth();
139+
140+
// Include the current month by adding 1
141+
months += 1;
142+
143+
// Adjust for negative months
144+
if (months < 0) {
145+
years--;
146+
months += 12;
147+
}
148+
149+
// Handle month overflow
150+
if (months >= 12) {
151+
years += Math.floor(months / 12);
152+
months = months % 12;
153+
}
154+
155+
// Format the result
156+
const parts: string[] = [];
157+
158+
if (years > 0) {
159+
parts.push(`${years} year${years > 1 ? 's' : ''}`);
160+
}
161+
162+
if (months > 0) {
163+
parts.push(`${months} month${months > 1 ? 's' : ''}`);
164+
}
165+
166+
// If both are 0 after calculation, return 1 month as minimum
167+
if (years === 0 && months === 0) {
168+
return "1 month";
169+
}
170+
171+
return parts.join(' ');
172+
}

0 commit comments

Comments
 (0)