dce-reactkit 4.2.4-beta-timestamp.1 → 4.2.4-beta.heat-seek.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  * @param opts.month Month (1-12)
8
8
  * @param opts.day Day of the month (1-31)
9
9
  * @param opts.hour Hour (0-23)
10
- * @param opts.minute Minute (0-59))
10
+ * @param opts.minute Minute (0-59)
11
11
  * @returns Timestamp in milliseconds since epoch
12
12
  */
13
13
  declare const getTimestampFromTimeInfoInET: (opts: {
package/dist/index.d.ts CHANGED
@@ -1574,7 +1574,7 @@ declare const cloneDeep: <T>(obj: T) => T;
1574
1574
  * @param opts.month Month (1-12)
1575
1575
  * @param opts.day Day of the month (1-31)
1576
1576
  * @param opts.hour Hour (0-23)
1577
- * @param opts.minute Minute (0-59))
1577
+ * @param opts.minute Minute (0-59)
1578
1578
  * @returns Timestamp in milliseconds since epoch
1579
1579
  */
1580
1580
  declare const getTimestampFromTimeInfoInET: (opts: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "4.2.4-beta-timestamp.1",
3
+ "version": "4.2.4-beta.heat-seek.1",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -6,6 +6,78 @@ import ReactKitErrorCode from '../types/ReactKitErrorCode';
6
6
  import getTimeInfoInET from './getTimeInfoInET';
7
7
  import padZerosLeft from './padZerosLeft';
8
8
 
9
+ /*----------------------------------------*/
10
+ /* --------------- Helpers -------------- */
11
+ /*----------------------------------------*/
12
+
13
+ /**
14
+ * Check if a timestamp is valid
15
+ * @param opts object containing all arguments
16
+ * @param opts.timestamp Timestamp in milliseconds since epoch
17
+ * @param opts.expectedYear Expected year
18
+ * @param opts.expectedMonth Expected month
19
+ * @param opts.expectedDay Expected day
20
+ * @param opts.expectedHour Expected hour
21
+ * @param opts.expectedMinute Expected minute
22
+ * @returns 1 if the timestamp needs to be increased, -1 if it needs to be decreased, 0 if it is valid
23
+ */
24
+ const checkTimestamp = (
25
+ opts: {
26
+ timestamp: number,
27
+ expectedYear: number,
28
+ expectedMonth: number,
29
+ expectedDay: number,
30
+ expectedHour: number,
31
+ expectedMinute: number,
32
+ },
33
+ ): number => {
34
+ const {
35
+ timestamp,
36
+ expectedYear,
37
+ expectedMonth,
38
+ expectedDay,
39
+ expectedHour,
40
+ expectedMinute,
41
+ } = opts;
42
+
43
+ const timeInfoInET = getTimeInfoInET(timestamp);
44
+ if (timeInfoInET.year < expectedYear) {
45
+ return 1;
46
+ }
47
+ if (timeInfoInET.year > expectedYear) {
48
+ return -1;
49
+ }
50
+ if (timeInfoInET.month < expectedMonth) {
51
+ return 1;
52
+ }
53
+ if (timeInfoInET.month > expectedMonth) {
54
+ return -1;
55
+ }
56
+ if (timeInfoInET.day < expectedDay) {
57
+ return 1;
58
+ }
59
+ if (timeInfoInET.day > expectedDay) {
60
+ return -1;
61
+ }
62
+ if (timeInfoInET.hour < expectedHour) {
63
+ return 1;
64
+ }
65
+ if (timeInfoInET.hour > expectedHour) {
66
+ return -1;
67
+ }
68
+ if (timeInfoInET.minute < expectedMinute) {
69
+ return 1;
70
+ }
71
+ if (timeInfoInET.minute > expectedMinute) {
72
+ return -1;
73
+ }
74
+ return 0;
75
+ };
76
+
77
+ /*----------------------------------------*/
78
+ /* ---------------- Main ---------------- */
79
+ /*----------------------------------------*/
80
+
9
81
  /**
10
82
  * Get a timestamp (ms since epoch) from time info (year, month, day, hour, minute, etc.) in Eastern Time (ET)
11
83
  * @author Gardenia Liu
@@ -15,7 +87,7 @@ import padZerosLeft from './padZerosLeft';
15
87
  * @param opts.month Month (1-12)
16
88
  * @param opts.day Day of the month (1-31)
17
89
  * @param opts.hour Hour (0-23)
18
- * @param opts.minute Minute (0-59))
90
+ * @param opts.minute Minute (0-59)
19
91
  * @returns Timestamp in milliseconds since epoch
20
92
  */
21
93
  const getTimestampFromTimeInfoInET = (
@@ -36,72 +108,69 @@ const getTimestampFromTimeInfoInET = (
36
108
  minute,
37
109
  } = opts;
38
110
 
111
+ // Determine if the date is in DST in Eastern Time
112
+ const tempDate = new Date(year, month - 1, day);
113
+ const janOffset = new Date(year, 0, 1).getTimezoneOffset();
114
+ const isDST = tempDate.getTimezoneOffset() < janOffset;
115
+ const etOffset = isDST ? '-04:00' : '-05:00';
116
+
39
117
  // Format with leading zeroes
40
118
  const mm = padZerosLeft(month, 2);
41
119
  const dd = padZerosLeft(day, 2);
42
120
  const hh = padZerosLeft(hour, 2);
43
121
  const min = padZerosLeft(minute, 2);
44
122
 
45
- // Use Intl.DateTimeFormat to get the ET offset for the given date
46
- const dateForET = new Date(Date.UTC(year, month - 1, day, hour, minute, 0));
47
- const options: Intl.DateTimeFormatOptions = {
48
- timeZone: 'America/New_York',
49
- year: 'numeric',
50
- month: '2-digit',
51
- day: '2-digit',
52
- hour: '2-digit',
53
- minute: '2-digit',
54
- second: '2-digit',
55
- hour12: false,
56
- };
57
- const parts: Intl.DateTimeFormatPart[] = new Intl.DateTimeFormat('en-US', options).formatToParts(dateForET);
58
- const etYear: string | undefined = parts.find((p: Intl.DateTimeFormatPart) => {
59
- return p.type === 'year';
60
- })?.value;
61
- const etMonth: string | undefined = parts.find((p: Intl.DateTimeFormatPart) => {
62
- return p.type === 'month';
63
- })?.value;
64
- const etDay: string | undefined = parts.find((p: Intl.DateTimeFormatPart) => {
65
- return p.type === 'day';
66
- })?.value;
67
- const etHour: string | undefined = parts.find((p: Intl.DateTimeFormatPart) => {
68
- return p.type === 'hour';
69
- })?.value;
70
- const etMinute: string | undefined = parts.find((p: Intl.DateTimeFormatPart) => {
71
- return p.type === 'minute';
72
- })?.value;
73
- const etSecond: string | undefined = parts.find((p: Intl.DateTimeFormatPart) => {
74
- return p.type === 'second';
75
- })?.value;
76
-
77
- // Build ET date string and parse as if local, then get UTC timestamp
78
- if (!etYear || !etMonth || !etDay || !etHour || !etMinute || !etSecond) {
79
- throw new ErrorWithCode(
80
- 'Failed to parse date parts from Intl.DateTimeFormat',
81
- ReactKitErrorCode.ETTimestampInvalid,
82
- );
123
+ // Build ET ISO string and convert to UTC timestamp
124
+ const etISOString = `${year}-${mm}-${dd}T${hh}:${min}:00${etOffset}`;
125
+ let timestamp = (new Date(etISOString)).getTime();
126
+
127
+ // Heat seek to get the right timestamp
128
+ const maxOffset = 24 * 60; // 24 hours in minutes
129
+ let currentOffset = 0; // minutes
130
+ const offsetIncrement = 15; // minutes
131
+ const offsetDirection = checkTimestamp({
132
+ timestamp,
133
+ expectedYear: year,
134
+ expectedMonth: month,
135
+ expectedDay: day,
136
+ expectedHour: hour,
137
+ expectedMinute: minute,
138
+ });
139
+ if (offsetDirection === 0) {
140
+ // Valid! Return the timestamp
141
+ return timestamp;
83
142
  }
84
- const etDateString: string = `${etYear}-${etMonth}-${etDay}T${etHour}:${etMinute}:${etSecond}`;
85
- const etDate: Date = new Date(etDateString);
86
- const timestamp: number = etDate.getTime();
87
143
 
88
- // Verify that the timestamp is correct
89
- const timeInfoInET = getTimeInfoInET(timestamp);
90
- if (
91
- timeInfoInET.year !== year
92
- || timeInfoInET.month !== month
93
- || timeInfoInET.day !== day
94
- || timeInfoInET.hour !== hour
95
- || timeInfoInET.minute !== minute
96
- ) {
97
- throw new ErrorWithCode(
98
- `Timestamp mismatch: expected ${year}-${mm}-${dd} ${hh}:${min}, got ${timeInfoInET.year}-${padZerosLeft(timeInfoInET.month, 2)}-${padZerosLeft(timeInfoInET.day, 2)} ${padZerosLeft(timeInfoInET.hour, 2)}:${padZerosLeft(timeInfoInET.minute, 2)}`,
99
- ReactKitErrorCode.ETTimestampInvalid,
100
- );
144
+ // Heat seek
145
+ while (Math.abs(currentOffset) < maxOffset) {
146
+ // Update offset
147
+ currentOffset += (offsetDirection * offsetIncrement);
148
+
149
+ // Update timestamp
150
+ const offsetMs = currentOffset * 60 * 1000;
151
+ timestamp += offsetMs;
152
+
153
+ // Check timestamp again
154
+ const newDirection = checkTimestamp({
155
+ timestamp,
156
+ expectedYear: year,
157
+ expectedMonth: month,
158
+ expectedDay: day,
159
+ expectedHour: hour,
160
+ expectedMinute: minute,
161
+ });
162
+ if (newDirection === 0) {
163
+ // Valid! Return the timestamp
164
+ return timestamp;
165
+ }
166
+
167
+ // Invalid. Keep looping.
101
168
  }
102
169
 
103
- // Valid! Return the timestamp
104
- return timestamp;
170
+ throw new ErrorWithCode(
171
+ `Timestamp mismatch: expected ${year}-${mm}-${dd} ${hh}:${min}, seeked to offset ${currentOffset} minutes but could not find a valid timestamp.`,
172
+ ReactKitErrorCode.ETTimestampInvalid,
173
+ );
105
174
  };
106
175
 
107
176
  export default getTimestampFromTimeInfoInET;