Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 6 additions & 4 deletions packages/my-timezone/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# my-timezone [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![npm version](https://img.shields.io/npm/v/my-timezone.svg?style=flat)](https://www.npmjs.com/package/my-timezone)

Get the exact time based on your location by calculating the time difference in seconds from UTC (good explanation on [CS4FN](http://www.cs4fn.org/mobile/owntimezone.php)).
Calculate the true solar time at any geographic location based on its longitude.

This is **not** political timezone data (UTC+1, DST, etc.) — it is [local mean time](https://en.wikipedia.org/wiki/Solar_time#Mean_solar_time): the astronomically correct time at a given longitude, where every degree equals exactly 4 minutes of offset from UTC. See [CS4FN](http://www.cs4fn.org/mobile/owntimezone.php) for a good explanation of the underlying concept.

## Prerequisites

Expand All @@ -21,7 +23,7 @@ Add the module to your project with `yarn add my-timezone` or install it globall
import {MyTimezone} from 'my-timezone';

new MyTimezone()
.getTimeByAddress('Berlin, Germany')
.getDateByAddress('Berlin, Germany')
.then(date => {
console.log(date.toString()); // Sun Sep 03 2017 14:29:49 GMT+0200
})
Expand All @@ -30,7 +32,7 @@ new MyTimezone()
// or

new MyTimezone()
.getTimeByLocation('13.394')
.getDateByLongitude(13.394)
.then(date => {
console.log(date.toString()); // Sun Sep 03 2017 14:29:49 GMT+0200
})
Expand All @@ -42,7 +44,7 @@ new MyTimezone()
```
Usage: my-timezone [options] <location>

Get the exact time based on your location.
Get the true solar time based on your location.
Use a city name or longitude value as location.

Options:
Expand Down
2 changes: 1 addition & 1 deletion packages/my-timezone/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"commander": "14.0.3",
"ntpclient": "*"
},
"description": "Get the exact time based on your location.",
"description": "Calculate the true solar time at any geographic location.",
"devDependencies": {
"nock": "14.0.11",
"tsx": "4.21.0",
Expand Down
59 changes: 58 additions & 1 deletion packages/my-timezone/src/MyTimezone.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import nock from 'nock';
import {describe, expect, test, vi} from 'vitest';
import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest';

import {MyTimezone} from './MyTimezone.js';

Expand Down Expand Up @@ -69,4 +69,61 @@ describe('MyTimezone', () => {
const dataMinsk = await myTimezone.getDateByAddress('Minsk, Belarus');
expect(dataMinsk).toBeDefined();
});

describe('getDateByLongitude offset accuracy', () => {
// Fix a known UTC time to make offset assertions deterministic
const knownUTC = new Date('2024-06-21T12:00:00.000Z'); // noon UTC
const ONE_HOUR_MS = 3_600_000;
const ONE_MINUTE_MS = 60_000;
const LONGITUDE_QUARTER = 90;
const LONGITUDE_HALF = 180;
const OFFSET_QUARTER = 6;
const OFFSET_HALF = 12;
const MINUTES_PER_DEGREE = 4;

beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(knownUTC);
});

afterEach(() => {
vi.useRealTimers();
});

test('longitude 0 (Greenwich) returns UTC time', async () => {
const result = await myTimezone.getDateByLongitude(0);
expect(result.getTime()).toBe(knownUTC.getTime());
});

test('longitude 180 returns UTC+12', async () => {
const result = await myTimezone.getDateByLongitude(LONGITUDE_HALF);
const expectedMillis = knownUTC.getTime() + OFFSET_HALF * ONE_HOUR_MS;
expect(result.getTime()).toBe(expectedMillis);
});

test('longitude -180 returns UTC-12', async () => {
const result = await myTimezone.getDateByLongitude(-LONGITUDE_HALF);
const expectedMillis = knownUTC.getTime() - OFFSET_HALF * ONE_HOUR_MS;
expect(result.getTime()).toBe(expectedMillis);
});

test('longitude 90 returns UTC+6', async () => {
const result = await myTimezone.getDateByLongitude(LONGITUDE_QUARTER);
const expectedMillis = knownUTC.getTime() + OFFSET_QUARTER * ONE_HOUR_MS;
expect(result.getTime()).toBe(expectedMillis);
});

test('longitude -90 returns UTC-6', async () => {
const result = await myTimezone.getDateByLongitude(-LONGITUDE_QUARTER);
const expectedMillis = knownUTC.getTime() - OFFSET_QUARTER * ONE_HOUR_MS;
expect(result.getTime()).toBe(expectedMillis);
});

test('each degree equals 4 minutes offset', async () => {
const result1 = await myTimezone.getDateByLongitude(1);
const result2 = await myTimezone.getDateByLongitude(2);
const diffMinutes = (result2.getTime() - result1.getTime()) / ONE_MINUTE_MS;
expect(diffMinutes).toBe(MINUTES_PER_DEGREE);
});
});
});
Loading