Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #19 from buildo/17-handle_localdatetime
Browse files Browse the repository at this point in the history
#17: Handle LocalDateTime (closes #17)
  • Loading branch information
veej authored May 2, 2018
2 parents 320a3db + c7b4f08 commit e52c659
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 15 deletions.
33 changes: 21 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# LocalDate
Replacement of `Date` for dealing with dates independent of time or timezone.
Replacement of `Date` for dealing with dates independent of timezone.

## Why
To avoid some:
Expand All @@ -23,7 +23,7 @@ const date = new Date('1991-06-04');
date.getDate(); // -> 3 (Date always applies user's timezone!)
```

`LocalDate` replaces the parser of `Date` with a simpler and stricter one which will consider only the date part (and conceptually **adapt** the timezone to it instead of the opposite).
`LocalDate` and `LocalDateTime` replace the parser of `Date` with a simpler and stricter one which will consider only the date part (or the date + time parts), and conceptually **adapt** the timezone to it instead of the opposite.

```js
// GMT -05:00 (New York)
Expand All @@ -36,7 +36,7 @@ new LocalDate('1991-06-04') == new Date(1991, 5, 4);

## Install
```
npm i --save local-date
yarn add local-date
```

## Browser Support
Expand All @@ -49,17 +49,17 @@ import 'local-date/lib/polyfills/array-from';
```

## Usage
`LocalDate` extends `Date` so it reflects its API for most things.
`LocalDate` and `LocalDateTime` extend `Date` so they reflect its API for most things.
The only parts that change are the parser and the formatter `toISOString`.

To help users check wether a string is a valid ISO date or not, `LocalDate` has also a static method `test`.
To help users check wether a string is a valid ISO date or not, `LocalDate` and `LocalDateTime` have also a static method `test`.

### Parser
There are three possible ways to instantiate a `LocalDate`:
There are three possible ways to instantiate a `LocalDate` (`LocalDateTime`):

1. ISO date
1. ISO date (datetime) string
2. no argument
3. another `LocalDate` instance
3. another `LocalDate` (`LocalDateTime`) instance

#### 1) ISO date
This is the standard way to instantiate a `LocalDate`: by passing it an ISO date string.
Expand All @@ -73,11 +73,17 @@ localDate.getMonth(); // -> 4 (timezone independent!)
localDate.getDate(); // -> 20 (timezone independent!)
```

Similarly, with a `LocalDateTime`:

```js
const localDateTime = new LocalDateTime('2016-05-20T10:10:42');
```

#### 2) no argument
`new LocalDate()` will return a `LocalDate` containing the current date for the user's timezone (internally it uses `new Date()`)
`new LocalDate()` (`new LocalDateTime()`) will return a `LocalDate` (`LocalDateTime`) containing the current date for the user's timezone (internally it uses `new Date()`)

#### 3) another `LocalDate` instance
This method is useful if you need to clone an instance of `LocalDate`:
#### 3) another `LocalDate` (`LocalDateTime`) instance
This method is useful if you need to clone an instance of `LocalDate` (`LocalDateTime`):

```js
const localDate = new LocalDate('2016-05-20');
Expand All @@ -100,9 +106,12 @@ date.toISOString(); // -> ""2016-05-20T00:00:00.000Z"" (it contains the time as
```

### `test`
To check if a string is a valid ISO date or not, you can use the static method `LocalDate.test`:
To check if a string is a valid ISO date or not, you can use the static method `LocalDate.test` (`LocalDateTime.test`):

```js
LocalDate.test('2016-05-20'); // -> true
LocalDate.test('2016-05-20T00:00:00.000Z'); // -> false
LocalDateTime.test('2016-05-20T00:00:00'); // -> true
LocalDateTime.test('2016-05-20T00:00:00.000'); // -> true
LocalDateTime.test('2016-05-20T00:00:00.000Z'); // -> false
```
4 changes: 1 addition & 3 deletions src/LocalDate.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// @flow

function pad2(number: number): number | string {
return number < 10 ? `0${number}` : number;
}
import { pad2 } from './util';

function warn(message: string) {
if (process.env.NODE_ENV !== 'production') {
Expand Down
47 changes: 47 additions & 0 deletions src/LocalDateTime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// @flow

import { pad2, pad3 } from './util';

export default class LocalDateTime extends Date {

static ISO_DATE_TIME_FORMAT = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.(\d{3}))?$/

static test(isoDateTime: string) {
return LocalDateTime.ISO_DATE_TIME_FORMAT.test(isoDateTime);
}

constructor(value: LocalDateTime | string | void) {
if (typeof value === 'undefined') {
super(new Date());
} else if (value instanceof LocalDateTime) {
super(
value.getFullYear(), value.getMonth(), value.getDate(),
value.getHours(), value.getMinutes(), value.getSeconds(), value.getMilliseconds()
);
} else if (typeof value === 'string' && LocalDateTime.ISO_DATE_TIME_FORMAT.test(value)) {
const [
year,
month,
date,
hours,
minutes,
seconds,
,
milliseconds
] = LocalDateTime.ISO_DATE_TIME_FORMAT.exec(value).slice(1).map(s => parseInt(s, 10));
super(year, month - 1, date, hours, minutes, seconds, milliseconds || 0);
} else {
throw new Error('Invalid date supplied. Please specify an ISO date time string (YYYY-MM-DDTHH:mm:SS) or a LocalDateTime object.\nhttps://github.com/buildo/local-date#parser'); // eslint-disable-line max-len
}
}

toISOString(): string {
const date = [this.getFullYear(), pad2(this.getMonth() + 1), pad2(this.getDate())].join('-');
const time = [
pad2(this.getHours()), pad2(this.getMinutes()), pad2(this.getSeconds())
].join(':');
const milliseconds = pad3(this.getMilliseconds());
return `${date}T${time}.${milliseconds}`;
}

}
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// @flow

import LocalDate from './LocalDate';
import LocalDateTime from './LocalDateTime';
export default LocalDate;
export { LocalDate, LocalDateTime };

9 changes: 9 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @flow

export function pad2(number: number): number | string {
return number < 10 ? `0${number}` : number;
}

export function pad3(number: number): number | string {
return number < 10 ? `00${number}` : number < 100 ? `0${number}` : number;
}
File renamed without changes.
File renamed without changes.
19 changes: 19 additions & 0 deletions test/tests/dateTime.getters.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @flow

import LocalDateTime from '../../src/LocalDateTime';

describe('Getters', () => {

const localDateTime = new LocalDateTime();

it('toISOString should return an ISO date time', () => {
expect(localDateTime.toISOString()).toMatch(LocalDateTime.ISO_DATE_TIME_FORMAT);
expect(() => new LocalDateTime(localDateTime.toISOString())).not.toThrow();
});

it('toJSON should return an ISO date time', () => {
expect(localDateTime.toJSON()).toMatch(LocalDateTime.ISO_DATE_TIME_FORMAT);
expect(() => new LocalDateTime(localDateTime.toJSON())).not.toThrow();
});

});
69 changes: 69 additions & 0 deletions test/tests/dateTime.parser.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import LocalDateTime from '../../src/LocalDateTime';

console.warn = () => {}; // eslint-disable-line no-console

describe('Parser', () => {

it('Should be an instance of Date', () => {
expect(new LocalDateTime() instanceof Date).toBe(true);
});

it('Should be an instance of LocalDateTime', () => {
expect(new LocalDateTime() instanceof LocalDateTime).toBe(true);
});

it('Should return a LocalDateTime with current date time if no argument is passed and should not consider the timezone', () => {
const today = new Date();
const todayLocalDateTime = new LocalDateTime();

expect(today.getFullYear()).toEqual(todayLocalDateTime.getFullYear());
expect(today.getMonth()).toEqual(todayLocalDateTime.getMonth());
expect(today.getDate()).toEqual(todayLocalDateTime.getDate());
expect(today.getHours()).toEqual(todayLocalDateTime.getHours());
expect(today.getMinutes()).toEqual(todayLocalDateTime.getMinutes());
expect(today.getSeconds()).toEqual(todayLocalDateTime.getSeconds());
expect(today.getMilliseconds()).toEqual(todayLocalDateTime.getMilliseconds());
});

it('Should parse ISO date times without considering the timezone', () => {
const year = 1991;
const month = 6;
const day = 4;
const hours = 10;
const minutes = 10;
const seconds = 42;
const milliseconds = 210;
const isoDate = `${year}-0${month}-0${day}T${hours}:${minutes}:${seconds}.${milliseconds}`;
const localDateTime = new LocalDateTime(isoDate);

expect(localDateTime.getFullYear()).toEqual(year);
expect(localDateTime.getMonth()).toEqual(month - 1);
expect(localDateTime.getDate()).toEqual(day);
expect(localDateTime.getHours()).toEqual(hours);
expect(localDateTime.getMinutes()).toEqual(minutes);
expect(localDateTime.getSeconds()).toEqual(seconds);
expect(localDateTime.getMilliseconds()).toEqual(milliseconds);
});

it('Should clone instances of LocalDateTimes', () => {
const localDateTime = new LocalDateTime('2016-05-20T10:10:42');
const clonedLocalDateTime = new LocalDateTime(localDateTime);

expect(localDateTime).toEqual(clonedLocalDateTime);
});

it('Should throw an error if argument is invalid', () => {
const newLocalDateTime = argument => () => {
return new LocalDateTime(argument);
};

expect(newLocalDateTime(new Date())).toThrow();
expect(newLocalDateTime(null)).toThrow();
expect(newLocalDateTime(1483549074687)).toThrow(); // timestamp
expect(newLocalDateTime(2016, 5, 21)).toThrow();
expect(newLocalDateTime('2017-01-04T18:04:52Z')).toThrow();
expect(newLocalDateTime('2017-01-04T18:04:52.438Z')).toThrow();
expect(newLocalDateTime('2017-01-04T18:04:52+00:00')).toThrow();
});

});
24 changes: 24 additions & 0 deletions test/tests/dateTime.staticProperties.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @flow

import LocalDateTime from '../../src/LocalDateTime';

describe('Static properties', () => {

it('ISO_DATE_TIME_FORMAT should be a valid RegExp', () => {
expect(LocalDateTime.ISO_DATE_TIME_FORMAT instanceof RegExp).toBe(true);
});

it('test should return "true" if argument is a valid (local) ISO date time', () => {
expect(LocalDateTime.test('2016-05-20T10:10:42')).toBe(true);
expect(LocalDateTime.test('2016-05-20T10:10:42.234')).toBe(true);
});

it('test should return "false" if argument is not a valid (local) ISO date time', () => {
expect(LocalDateTime.test('2017/01/04')).toBe(false);
expect(LocalDateTime.test('05-20-2017')).toBe(false);
expect(LocalDateTime.test('2017-01-04T18:04:52Z')).toBe(false);
expect(LocalDateTime.test('2017-01-04T18:04:52.438Z')).toBe(false);
expect(LocalDateTime.test('2017-01-04T18:04:52+00:00')).toBe(false);
});

});

0 comments on commit e52c659

Please sign in to comment.