Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add timezone getter function in dateTime module [ATLAS-170] #125

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
803a5fc
[feat]: i18nify-data alias path added for static imports
tarun-khanna Apr 2, 2024
a8665df
Merge branch 'master' of github.com:razorpay/i18nify into feat/i18nif…
tarun-khanna Apr 2, 2024
345bbcb
Create six-seals-matter.md
tarun-khanna Apr 2, 2024
31116f1
[chore]: alias mapping added in jest config
tarun-khanna Apr 2, 2024
a33b6ed
Merge branch 'feat/i18nify-data-alias' of github.com:razorpay/i18nify…
tarun-khanna Apr 2, 2024
57de989
[feat]: add getter functions
RgnDunes Apr 4, 2024
3f6669c
[chore]: back merge master
RgnDunes Apr 4, 2024
438529e
[chore]: modify timezone apis to fetch geo data from githus source
RgnDunes Apr 5, 2024
066a321
[fix]: fix type error
RgnDunes Apr 5, 2024
4e66862
[test]: add e2e for timezone apis
RgnDunes Apr 5, 2024
97a5116
[docs]: add docs for timezone apis
RgnDunes Apr 5, 2024
7eda595
Create neat-years-remain.md
RgnDunes Apr 5, 2024
f669891
Merge remote-tracking branch 'origin/master' into feat/dateTime-geo-d…
RgnDunes Apr 9, 2024
eb245c5
Merge remote-tracking branch 'origin/feat/dateTime-geo-data-migration…
RgnDunes Apr 9, 2024
7a61e4e
Merge remote-tracking branch 'origin/master' into feat/dateTime-geo-d…
RgnDunes Apr 12, 2024
2536c02
Update neat-years-remain.md
RgnDunes Apr 12, 2024
7132aa1
Merge remote-tracking branch 'origin/master' into feat/dateTime-geo-d…
RgnDunes Apr 29, 2024
df647d6
Merge remote-tracking branch 'origin/feat/dateTime-geo-data-migration…
RgnDunes Apr 29, 2024
5a21124
Merge branch 'master' into feat/dateTime-geo-data-migration
RgnDunes Apr 29, 2024
7b786b6
Merge remote-tracking branch 'origin/master' into feat/dateTime-geo-d…
RgnDunes May 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/neat-years-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@razorpay/i18nify-js": patch
---

feat: add timezone getter function in dateTime module
42 changes: 42 additions & 0 deletions packages/i18nify-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,48 @@ console.log(parsed3); // Outputs object with date components for January 23, 202

💡 Pro Tip: Leverage `parseDateTime` in applications where detailed date analysis and manipulation are key, such as in calendar apps, scheduling tools, or date-sensitive data processing. It's like having a Swiss Army knife for all things related to dates and times! 📅🛠️

#### getTimezoneList()

`getTimezoneList` 🌍 dynamically imports country-specific timezone information 🕒, offering a detailed map 🗺️ of timezones and capital city timezones keyed by country codes. Ideal for applications needing timezone data for location-based services 📍 or global scheduling features 📅. It returns a Promise 🔄 resolving to a map of country codes to timezone information, including each country's timezones 🌐 and the timezone of its capital 🏛️.

##### Examples

```javascript

console.log(await getTimezoneList());
/*
{
AF: {
timezone_of_capital: 'Asia/Kabul',
timezones: {
'Asia/Kabul': {
utc_offset: 'UTC +04:30',
},
},
},
IN: {
timezone_of_capital: 'Asia/Kolkata',
timezones: {
'Asia/Kolkata': {
utc_offset: 'UTC +05:30',
},
},
},
// rest of the countries
}
*/
```

#### getTimeZoneByCountry(countryCode)

`getTimeZoneByCountry` asynchronously fetches the time zone ⏰ for the capital city 🏙️ of a given country, using its country code 🌍. It's crafted for applications that require displaying or working with time zone-specific information 🕒 across different locations globally 🌎.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not return timezones too ?


##### Examples

```javascript
console.log(await getTimeZoneByCountry('AF')); // 'Asia/Kabul'
```

#### Calendar, CalendarDate, CalendarDateTime, Time, ZonedDateTime

Leverage the power of Adobe's @internationalized/date with our module, designed to offer a sophisticated, locale-sensitive approach to managing dates and times. Utilize these advanced tools to create applications that are both intuitive and efficient, ensuring they connect with users worldwide.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { test } from '@playwright/test';
import { getTimeZoneByCountry } from '../index';
import { assertScriptText, injectScript } from '../../../blackbox/utils';

test.describe('getTimeZoneByCountry', () => {
test.beforeEach(async ({ page }) => {
await page.exposeFunction('getTimeZoneByCountry', getTimeZoneByCountry);
});

test('should print the correct timezone of a country capital from countries meta data', async ({
page,
}) => {
await injectScript(
page,
`await getTimeZoneByCountry('AF').then(res => res)`,
);

await assertScriptText(page, 'Asia/Kabul');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { getTimeZoneByCountry } from '../index';
import { COUNTRIES_METADATA } from '../mocks/country';
import { CountryMetaType } from '../types';

type MockResponse = {
metadata_information: Record<string, CountryMetaType>;
};

global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve<MockResponse>(COUNTRIES_METADATA as any),
} as Response),
);

describe('getTimeZoneByCountry', () => {
it('fetches country metadata correctly', async () => {
const timeZoneAF = await getTimeZoneByCountry('AF');
expect(timeZoneAF).toBe('Asia/Kabul');

const timeZoneIN = await getTimeZoneByCountry('IN');
expect(timeZoneIN).toBe('Asia/Kolkata');
});

it('handles API errors', async () => {
global.fetch = jest.fn(() => Promise.reject('API Error'));
await expect(getTimeZoneByCountry('XYZ')).rejects.toThrow(
'Error in API response',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { test } from '@playwright/test';
import { getTimezoneList } from '../index';
import { assertScriptText, injectScript } from '../../../blackbox/utils';

test.describe('getTimezoneList', () => {
test.beforeEach(async ({ page }) => {
await page.exposeFunction('getTimezoneList', getTimezoneList);
});

test('should print the correct timezone of a country capital from countries meta data', async ({
page,
}) => {
await injectScript(
page,
`await getTimezoneList().then(res => res.AF.timezone_of_capital)`,
);

await assertScriptText(page, 'Asia/Kabul');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { getTimezoneList } from '../index';
import { COUNTRIES_METADATA } from '../mocks/country';
import { CountryMetaType } from '../types';

type MockResponse = {
metadata_information: Record<string, CountryMetaType>;
};

global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve<MockResponse>(COUNTRIES_METADATA as any),
} as Response),
);

describe('getTimezoneList', () => {
it('fetches country metadata correctly', async () => {
const expectedTimezoneListObj = {
AF: {
timezone_of_capital: 'Asia/Kabul',
timezones: {
'Asia/Kabul': {
utc_offset: 'UTC +04:30',
},
},
},
IN: {
timezone_of_capital: 'Asia/Kolkata',
timezones: {
'Asia/Kolkata': {
utc_offset: 'UTC +05:30',
},
},
},
};
const timeZoneList = await getTimezoneList();
expect(timeZoneList).toEqual(expectedTimezoneListObj);
});

it('handles API errors', async () => {
global.fetch = jest.fn(() => Promise.reject('API Error'));
await expect(getTimezoneList()).rejects.toThrow('Error in API response');
});
});
3 changes: 3 additions & 0 deletions packages/i18nify-js/src/modules/dateTime/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ export const ALLOWED_FORMAT_PARTS_KEYS = [
'year',
'yearName',
] as const;

export const I18NIFY_DATA_SOURCE =
'https://raw.githubusercontent.com/razorpay/i18nify/master/i18nify-data';
33 changes: 33 additions & 0 deletions packages/i18nify-js/src/modules/dateTime/getTimeZoneByCountry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { withErrorBoundary } from '../../common/errorBoundary';
import { I18NIFY_DATA_SOURCE } from './constants';

/**
* Asynchronously retrieves the timezone of the capital city for a given country code.
*
* @param countryCode The country code for which the timezone of the capital city is requested.
* @returns A promise that resolves to the timezone of the capital city for the specified country code.
* @throws Error if the country code is not found in the dynamically imported dataset or if there's an API response error.
*/
const getTimeZoneByCountry = async (countryCode: string): Promise<string> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's return timezone list too ?

try {
const response = await fetch(
`${I18NIFY_DATA_SOURCE}/country/metadata/data.json`,
);
const data = await response.json();

const timezone =
data.metadata_information[countryCode]?.timezone_of_capital;

if (!timezone) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this error be reported in catch block of promise ?
for eg,

getTimeZoneByCountry(IN).then().catch((err) => console.log(err)) ?

throw new Error(`Invalid countryCode: ${countryCode}`);
}

return timezone;
} catch (err) {
throw new Error(`Error in API response: ${(err as Error).message}`);
}
};

export default withErrorBoundary<typeof getTimeZoneByCountry>(
getTimeZoneByCountry,
);
36 changes: 36 additions & 0 deletions packages/i18nify-js/src/modules/dateTime/getTimezoneList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { withErrorBoundary } from '../../common/errorBoundary';
import { I18NIFY_DATA_SOURCE } from './constants';
import { CountryDataApiResponse, CountryMetaType } from './types';

/**
* Dynamically imports country data and extracts timezone information for each country.
*
* This function transforms the imported COUNTRY_DATA dataset to a map where each key is a country code,
* associated with an object that includes the timezones (an object with timezone names as keys
* and their utc_offset as values) and the timezone_of_capital (the timezone in which the capital city resides).
*
* @returns A Promise that resolves to a map with country codes as keys and their respective timezone information.
*/
const getTimezoneList = async (): Promise<Record<string, CountryMetaType>> => {
try {
const response = await fetch(
`${I18NIFY_DATA_SOURCE}/country/metadata/data.json`,
);
const data: CountryDataApiResponse = await response.json();

return Object.entries(data.metadata_information).reduce(
(acc, [countryCode, countryMetadata]) => ({
...acc,
[countryCode]: {
timezones: countryMetadata.timezones,
timezone_of_capital: countryMetadata.timezone_of_capital,
},
}),
{},
);
} catch (err) {
throw new Error(`Error in API response: ${(err as Error).message}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will it always have message attribute ?

}
};

export default withErrorBoundary<typeof getTimezoneList>(getTimezoneList);
3 changes: 2 additions & 1 deletion packages/i18nify-js/src/modules/dateTime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export { default as formatDateTime } from './formatDateTime';
export { default as getRelativeTime } from './getRelativeTime';
export { default as getWeekdays } from './getWeekdays';
export { default as parseDateTime } from './parseDateTime';

export { default as getTimezoneList } from './getTimezoneList';
export { default as getTimeZoneByCountry } from './getTimeZoneByCountry';
// For additional information, refer to the documentation: https://react-spectrum.adobe.com/internationalized/date/index.html
/**
* Direct exports include Calendar, CalendarDate, CalendarDateTime, Time, and ZonedDateTime.
Expand Down
Loading
Loading