UNPKG

2.65 kBJavaScriptView Raw
1import _ from 'lodash';
2import { CraftAiTimeError } from './errors';
3import moment from 'moment';
4import isTimezone, { timezones } from './timezones';
5
6// From 'moment/src/lib/parse/regex.js'
7const OFFSET_REGEX = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
8
9export function tzFromOffset(offset) {
10 if (_.isInteger(offset)) {
11 const sign = offset >= 0 ? '+' : '-';
12 const abs = Math.abs(offset);
13 // If the offset belongs to [-15, 15] it is considered to represent hours
14 // This reproduces Moment's utcOffset behaviour.
15 if (abs < 16)
16 return `${sign}${_.padStart(abs, 2, '0')}:00`;
17 return `${sign}${_.padStart(Math.floor(abs / 60), 2, '0')}:${_.padStart(abs % 60, 2, '0')}`;
18 }
19 else {
20 return offset;
21 }
22}
23
24export default function Time(t = undefined, tz = undefined) {
25 // Make sure it works with or without new.
26 if (!(this instanceof Time)) {
27 return new Time(t, tz);
28 }
29
30 let m;
31 if (t instanceof Time) {
32 // t is an instance of Time
33 m = moment.unix(t.timestamp);
34 m.utcOffset(t.timezone);
35 }
36 else if (t instanceof moment) {
37 // t is an instance of moment
38 m = t;
39 }
40 else if (_.isDate(t)) {
41 // t is a js Date
42 m = moment(t);
43 }
44 else if (_.isNumber(t)) {
45 // t is a posix timestamp
46 // it might be a float as retrieved by Date.now() / 1000
47 m = moment.unix(t);
48 }
49 else if (_.isString(t)) {
50 // To make it consistent everywhere we only handle ISO 8601 format
51 if (t.match(OFFSET_REGEX)) {
52 // String with a explicit offset
53 m = moment.parseZone(t, moment.ISO_8601);
54 }
55 else {
56 // No explicit offset
57 m = moment(t, moment.ISO_8601);
58 }
59 }
60 else if (_.isUndefined(t)) {
61 m = moment();
62 }
63
64 if (m === undefined || !m.isValid()) {
65 throw new CraftAiTimeError(`Time error, given "${t}" is invalid.`);
66 }
67
68 if (tz) {
69 // tz formats should be parseable by moment
70 if (!isTimezone(tz)) {
71 throw new CraftAiTimeError(`Time error, the given timezone "${tz}" is invalid.
72 Please refer to the client's documentation to see accepted formats:
73 https://beta.craft.ai/doc/http#context-properties-types.`);
74 }
75 m.utcOffset(timezones[tz] || tz);
76 }
77
78 const minuteOffset = m.utcOffset();
79
80 return _.extend(this, {
81 timestamp: m.unix(),
82 timezone: tzFromOffset(minuteOffset),
83 time_of_day: m.hour() + m.minute() / 60 + m.second() / 3600,
84 day_of_month: m.date(), // we want day to be in [1;31]
85 month_of_year: m.month() + 1, // we want months to be in [1;12]
86 day_of_week: m.isoWeekday() - 1, // we want week day to be in [0;6]
87 utc: m.toISOString()
88 });
89}