scdate 1.0.1 → 1.1.0
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.
- package/README.md +248 -17
- package/dist/internal/zoned.d.ts +0 -4
- package/dist/internal/zoned.js +1 -20
- package/dist/sDate.d.ts +10 -0
- package/dist/sDate.js +21 -2
- package/dist/sTimestamp.d.ts +14 -4
- package/dist/sTimestamp.js +27 -8
- package/dist/sWeekdays.js +3 -3
- package/package.json +11 -15
package/README.md
CHANGED
|
@@ -1,42 +1,273 @@
|
|
|
1
1
|
# scDate
|
|
2
2
|
|
|
3
|
-
**Date and time library for
|
|
3
|
+
**Date and time library for working with schedules**
|
|
4
4
|
|
|
5
5
|
[](https://github.com/ericvera/scdate/blob/master/LICENSE)
|
|
6
6
|
[](https://npmjs.org/package/scdate)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Overview
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
scDate is a TypeScript library designed specifically for handling date and time operations in scheduling applications. It provides a set of immutable classes and utility functions that make working with dates, times, timestamps, and weekday patterns simple and predictable.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Comprehensive date/time handling**: Work with dates (`SDate`), times (`STime`), timestamps (`STimestamp`), and weekday patterns (`SWeekdays`)
|
|
15
|
+
- **Time zone aware**: Time zone information is only required when performing operations where it's relevant
|
|
16
|
+
- **Serializable**: All objects serialize to simple ISO-formatted strings for easy storage and transmission
|
|
17
|
+
- **Immutable**: All operations return new instances, ensuring data consistency
|
|
18
|
+
- **Flexible inputs**: All methods accept either serialized strings or class instances, simplifying code and improving readability
|
|
19
|
+
- **Schedule-focused**: Built specifically for applications that need to handle schedules and recurring patterns
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install scdate
|
|
25
|
+
# or
|
|
26
|
+
yarn add scdate
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Basic Usage
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { sDate, sTime, sTimestamp, sWeekdays, Weekday } from 'scdate'
|
|
33
|
+
|
|
34
|
+
// Working with dates
|
|
35
|
+
const today = getDateToday('America/Puerto_Rico')
|
|
36
|
+
const nextMonday = getNextDateByWeekday(today, Weekday.Mon)
|
|
37
|
+
|
|
38
|
+
// Working with times
|
|
39
|
+
const currentTime = getTimeNow('America/Puerto_Rico')
|
|
40
|
+
const inOneHour = addMinutesToTime(currentTime, 60)
|
|
41
|
+
|
|
42
|
+
// Working with timestamps
|
|
43
|
+
const now = getTimestampNow('America/Puerto_Rico')
|
|
44
|
+
const meeting = getTimestampFromDateAndTime(nextMonday, sTime('14:30'))
|
|
45
|
+
|
|
46
|
+
// Working with weekday patterns
|
|
47
|
+
const weekendDays = getWeekdaysFromWeekdayFlags(Weekday.Sat | Weekday.Sun)
|
|
48
|
+
const businessDays = getWeekdaysFromWeekdayFlags(
|
|
49
|
+
Weekday.Mon | Weekday.Tue | Weekday.Wed | Weekday.Thu | Weekday.Fri,
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API Documentation
|
|
54
|
+
|
|
55
|
+
### Date Operations (`SDate`)
|
|
56
|
+
|
|
57
|
+
`SDate` represents a date in the ISO 8601 format (`YYYY-MM-DD`).
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// Creating dates
|
|
61
|
+
const date1 = sDate('2023-12-25') // From ISO string
|
|
62
|
+
const date2 = getDateToday('America/Puerto_Rico') // Current date in timezone
|
|
63
|
+
const date3 = getDateForFirstDayOfMonth(date1) // First day of month
|
|
64
|
+
const date4 = getDateForLastDayOfMonth(date1) // Last day of month
|
|
65
|
+
|
|
66
|
+
// Date arithmetic
|
|
67
|
+
const nextDay = addDaysToDate(date1, 1) // Add days
|
|
68
|
+
const nextMonth = addMonthsToDate(date1, 1) // Add months
|
|
69
|
+
const nextYear = addYearsToDate(date1, 1) // Add years
|
|
70
|
+
|
|
71
|
+
// Date information
|
|
72
|
+
const year = getYearFromDate(date1) // Get year (number)
|
|
73
|
+
const month = getMonthFromDate(date1) // Get month (0-11)
|
|
74
|
+
const day = getDateFromDate(date1) // Get day of month
|
|
75
|
+
const weekday = getWeekdayFromDate(date1) // Get weekday (0-6)
|
|
76
|
+
|
|
77
|
+
// Date navigation
|
|
78
|
+
const nextTuesday = getNextDateByWeekday(date1, Weekday.Tue)
|
|
79
|
+
const prevFriday = getPreviousDateByWeekday(date1, Weekday.Fri)
|
|
80
|
+
|
|
81
|
+
// Date comparison
|
|
82
|
+
const isEqual = isSameDate(date1, date2)
|
|
83
|
+
const isBefore = isBeforeDate(date1, date2)
|
|
84
|
+
const isAfter = isAfterDate(date1, date2)
|
|
85
|
+
const isSameOrBefore = isSameDateOrBefore(date1, date2)
|
|
86
|
+
const isSameOrAfter = isSameDateOrAfter(date1, date2)
|
|
87
|
+
const isToday = isDateToday(date1, 'America/Puerto_Rico')
|
|
88
|
+
|
|
89
|
+
// Date formatting
|
|
90
|
+
const fullDateStr = getFullDateString(date1, 'en-US')
|
|
91
|
+
const shortDateStr = getShortDateString(date1, 'America/Puerto_Rico', 'en-US', {
|
|
92
|
+
includeWeekday: true,
|
|
93
|
+
onTodayText: () => 'Today',
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Important Behaviors and Edge Cases
|
|
98
|
+
|
|
99
|
+
- **`getNextDateByWeekday(date, weekday)`**: Always returns a date _after_ the provided date. If the current date falls on the specified weekday, it will return the _next_ occurrence (7 days later), not the current date.
|
|
100
|
+
|
|
101
|
+
- **`getPreviousDateByWeekday(date, weekday)`**: Always returns a date _before_ the provided date. If the current date falls on the specified weekday, it will return the _previous_ occurrence (7 days earlier), not the current date.
|
|
102
|
+
|
|
103
|
+
- **`getDateForFirstDayOfMonth(date)`**: Returns a new date set to the first day of the month from the input date, preserving the year and month.
|
|
104
|
+
|
|
105
|
+
- **`getDateForLastDayOfMonth(date)`**: Returns a new date set to the last day of the month, which varies depending on the month and year (accounting for leap years).
|
|
106
|
+
|
|
107
|
+
- **`addMonthsToDate(date, months)`**: Handles month overflow correctly. For example, adding one month to January 31 will result in either February 28 or 29 (depending on leap year), not March 3.
|
|
108
|
+
|
|
109
|
+
- **`isDateToday(date, timeZone)`**: The comparison is time-zone aware, so a date that is "today" in one time zone might not be "today" in another time zone.
|
|
110
|
+
|
|
111
|
+
### Time Operations (`STime`)
|
|
112
|
+
|
|
113
|
+
`STime` represents a time in the ISO 8601 format (`HH:MM`).
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Creating times
|
|
117
|
+
const time1 = sTime('14:30') // From ISO string
|
|
118
|
+
const time2 = getTimeNow('America/Puerto_Rico') // Current time in timezone
|
|
119
|
+
const time3 = getTimeAtMidnight() // 00:00
|
|
120
|
+
const time4 = getTimeFromMinutes(60) // 01:00 (60 minutes after midnight)
|
|
121
|
+
|
|
122
|
+
// Time arithmetic
|
|
123
|
+
const laterTime = addMinutesToTime(time1, 30) // Add minutes
|
|
124
|
+
|
|
125
|
+
// Time information
|
|
126
|
+
const hours = getHoursFromTime(time1) // Get hours (0-23)
|
|
127
|
+
const minutes = getMinutesFromTime(time1) // Get minutes (0-59)
|
|
128
|
+
const totalMinutes = getTimeInMinutes(time1) // Get total minutes since midnight
|
|
129
|
+
|
|
130
|
+
// Time formatting
|
|
131
|
+
const timeString = get12HourTimeString(time1) // e.g., "2:30 PM"
|
|
132
|
+
|
|
133
|
+
// Time comparison
|
|
134
|
+
const isEqual = isSameTime(time1, time2)
|
|
135
|
+
const isBefore = isBeforeTime(time1, time2)
|
|
136
|
+
const isAfter = isAfterTime(time1, time2)
|
|
137
|
+
const isSameOrBefore = isSameTimeOrBefore(time1, time2)
|
|
138
|
+
const isSameOrAfter = isSameTimeOrAfter(time1, time2)
|
|
139
|
+
const isPM = isTimePM(time1)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Important Behaviors and Edge Cases
|
|
143
|
+
|
|
144
|
+
- **`addMinutesToTime(time, minutes)`**: When adding minutes, the time wraps around a 24-hour clock. For example, adding 30 minutes to 23:45 results in 00:15, not 24:15.
|
|
145
|
+
|
|
146
|
+
- **`getTimeInMinutes(time, midnightIs24)`**: By default, midnight (00:00) is represented as 0 minutes. If `midnightIs24` is set to `true`, midnight will be represented as 1440 minutes (24 hours).
|
|
147
|
+
|
|
148
|
+
- **`isTimePM(time)`**: Hours from 12:00 to 23:59 are considered PM, while 00:00 to 11:59 are AM. 12:00 is considered PM, not AM.
|
|
149
|
+
|
|
150
|
+
### Timestamp Operations (`STimestamp`)
|
|
151
|
+
|
|
152
|
+
`STimestamp` combines a date and time in the ISO 8601 format (`YYYY-MM-DDTHH:MM`).
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// Creating timestamps
|
|
156
|
+
const ts1 = sTimestamp('2023-12-25T14:30') // From ISO string
|
|
157
|
+
const ts2 = getTimestampNow('America/Puerto_Rico') // Current timestamp in timezone
|
|
158
|
+
const ts3 = getTimestampFromDateAndTime(date1, time1) // From date and time
|
|
159
|
+
const ts4 = getTimestampFromUTCMilliseconds(
|
|
160
|
+
1640444400000,
|
|
161
|
+
'America/Puerto_Rico',
|
|
162
|
+
) // From UTC milliseconds
|
|
163
|
+
|
|
164
|
+
// Timestamp arithmetic
|
|
165
|
+
const tsNextDay = addDaysToTimestamp(ts1, 1)
|
|
166
|
+
const ts30MinLater = addMinutesToTimestamp(ts1, 30, 'America/Puerto_Rico')
|
|
167
|
+
|
|
168
|
+
// Timestamp breakdown
|
|
169
|
+
const tsDate = getDateFromTimestamp(ts1) // Extract date part
|
|
170
|
+
const tsTime = getTimeFromTimestamp(ts1) // Extract time part
|
|
171
|
+
|
|
172
|
+
// Timestamp conversion
|
|
173
|
+
const tsMilliseconds = getUTCMillisecondsFromTimestamp(
|
|
174
|
+
ts1,
|
|
175
|
+
'America/Puerto_Rico',
|
|
176
|
+
)
|
|
177
|
+
const tsNativeDate = getTimeZonedDateFromTimestamp(ts1, 'America/Puerto_Rico')
|
|
178
|
+
const secondsToTs = getSecondsToTimestamp(ts1, 'America/Puerto_Rico')
|
|
179
|
+
|
|
180
|
+
// Timestamp formatting
|
|
181
|
+
const tsString = getShortTimestampString(ts1, 'America/Puerto_Rico', 'en-US', {
|
|
182
|
+
includeWeekday: true,
|
|
183
|
+
onTodayAtText: () => 'Today at',
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
// Timestamp comparison
|
|
187
|
+
const isEqual = isSameTimestamp(ts1, ts2)
|
|
188
|
+
const isBefore = isBeforeTimestamp(ts1, ts2)
|
|
189
|
+
const isAfter = isAfterTimestamp(ts1, ts2)
|
|
190
|
+
const isSameOrBefore = isSameTimestampOrBefore(ts1, ts2)
|
|
191
|
+
const isSameOrAfter = isSameTimestampOrAfter(ts1, ts2)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### Important Behaviors and Edge Cases
|
|
195
|
+
|
|
196
|
+
- **`getTimestampFromUTCMilliseconds(milliseconds, timeZone)`**: Converts UTC milliseconds to a timestamp in the specified time zone, accounting for time zone offsets.
|
|
197
|
+
|
|
198
|
+
- **`addMinutesToTimestamp(timestamp, minutes, timeZone)`**: Time zone awareness is critical for this operation, especially around Daylight Saving Time transitions. When a timestamp lands in the "missing hour" during a spring-forward transition, it's adjusted to the valid time.
|
|
199
|
+
|
|
200
|
+
- **`getSecondsToTimestamp(timestamp, timeZone)`**: Returns a positive value for future timestamps and negative for past timestamps. Handles DST transitions correctly but might produce unexpected results for timestamps that fall in skipped or repeated hours during DST transitions.
|
|
201
|
+
|
|
202
|
+
- **`getUTCMillisecondsFromTimestamp(timestamp, timeZone)`**: Converts a timestamp to UTC milliseconds, accounting for the time zone offset at that specific date and time (important for historical dates with different time zone rules).
|
|
203
|
+
|
|
204
|
+
### Weekday Operations (`SWeekdays`)
|
|
205
|
+
|
|
206
|
+
`SWeekdays` represents a set of weekdays in the format `SMTWTFS`, where each position corresponds to a day of the week (Sunday to Saturday).
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// Creating weekday patterns
|
|
210
|
+
const weekdays1 = sWeekdays('SMTWTFS') // All days
|
|
211
|
+
const weekdays2 = sWeekdays('SM----S') // Sunday, Monday, Saturday
|
|
212
|
+
const weekdays3 = getWeekdaysFromWeekdayFlags(Weekday.Mon | Weekday.Wed) // Monday, Wednesday
|
|
213
|
+
const weekdays4 = getWeekdaysWithAllIncluded() // All days
|
|
214
|
+
const weekdays5 = getWeekdaysWithNoneIncluded() // No days
|
|
215
|
+
|
|
216
|
+
// Weekday operations
|
|
217
|
+
const shiftedWeekdays = shiftWeekdaysForward(weekdays2) // Shift pattern one day forward
|
|
218
|
+
const filteredWeekdays = filterWeekdaysForDates(
|
|
219
|
+
// Filter to days in date range
|
|
220
|
+
weekdays1,
|
|
221
|
+
'2023-12-25',
|
|
222
|
+
'2023-12-31',
|
|
223
|
+
)
|
|
224
|
+
const updatedWeekdays = addWeekdayToWeekdays(weekdays5, Weekday.Fri) // Add Friday to pattern
|
|
225
|
+
|
|
226
|
+
// Weekday queries
|
|
227
|
+
const includesMonday = doesWeekdaysIncludeWeekday(weekdays2, Weekday.Mon)
|
|
228
|
+
const hasOverlap = doesWeekdaysHaveOverlapWithWeekdays(weekdays2, weekdays3)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### Important Behaviors and Edge Cases
|
|
232
|
+
|
|
233
|
+
- **`sWeekdays(pattern)`**: The pattern must be exactly 7 characters long, with each position representing a day from Sunday to Saturday. Valid characters are the first letter of the English weekday name (S, M, T, W, T, F, S) or a dash '-' for excluded days.
|
|
234
|
+
|
|
235
|
+
- **`shiftWeekdaysForward(weekdays)`**: Shifts the pattern forward by one day with circular wrapping. For example, 'SM----S' becomes 'SMT----'. The last character (Saturday) wraps around to become the first day (Sunday) position.
|
|
236
|
+
|
|
237
|
+
- **`filterWeekdaysForDates(weekdays, fromDate, toDate)`**: Returns a new weekday pattern that only includes the weekdays that fall within the given date range. If the date range spans less than a week, only the applicable days are included.
|
|
238
|
+
|
|
239
|
+
- **`getWeekdaysFromWeekdayFlags(flags)`**: Uses bitwise operations to combine multiple weekday flags. Each weekday is represented by a power of 2, allowing for efficient combination and checking.
|
|
13
240
|
|
|
14
241
|
## Dependencies
|
|
15
242
|
|
|
16
243
|
This package has the following dependencies:
|
|
17
244
|
|
|
18
|
-
- `date-fns-tz`:
|
|
19
|
-
- `date-fns`:
|
|
20
|
-
- `@date-fns/utc`:
|
|
245
|
+
- `date-fns-tz`: Used for time zone calculations
|
|
246
|
+
- `date-fns`: Peer dependency of `date-fns-tz`
|
|
247
|
+
- `@date-fns/utc`: Used for its `UTCDateMini` implementation that simplifies time calculations
|
|
21
248
|
|
|
22
249
|
## Design Decisions
|
|
23
250
|
|
|
24
|
-
### ISO
|
|
251
|
+
### ISO 8601 Format
|
|
252
|
+
|
|
253
|
+
scDate uses a subset of the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard for all date and time representations. This format was chosen for several key benefits:
|
|
25
254
|
|
|
26
|
-
|
|
255
|
+
- **Human readability**: Dates and times are easy to read and interpret
|
|
256
|
+
- **String sortability**: ISO-formatted strings can be sorted chronologically using simple string comparison
|
|
257
|
+
- **Direct comparison**: Values can be compared directly as strings without conversion
|
|
27
258
|
|
|
28
|
-
-
|
|
29
|
-
- the values are easily sortable as strings
|
|
30
|
-
- the values are easily comparable as strings
|
|
259
|
+
### Minute-Level Precision
|
|
31
260
|
|
|
32
|
-
|
|
261
|
+
scDate intentionally omits seconds and milliseconds from time representations. This design choice reflects the library's focus on scheduling applications, where:
|
|
33
262
|
|
|
34
|
-
|
|
263
|
+
- Minute-level granularity is typically sufficient for most scheduling needs
|
|
264
|
+
- Simpler time representations lead to more intuitive API and usage patterns
|
|
265
|
+
- Performance is optimized by avoiding unnecessary precision
|
|
35
266
|
|
|
36
267
|
## Time zones
|
|
37
268
|
|
|
38
269
|
For a list of valid time zones run `Intl.supportedValuesOf('timeZone')` in your environment.
|
|
39
270
|
|
|
40
|
-
##
|
|
271
|
+
## License
|
|
41
272
|
|
|
42
|
-
|
|
273
|
+
MIT
|
package/dist/internal/zoned.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { SDate } from './SDate.js';
|
|
2
|
-
import { STimestamp } from './STimestamp.js';
|
|
3
|
-
export declare const getMillisecondsInUTCFromTimestamp: (timestamp: STimestamp, timeZone: string) => number;
|
|
4
|
-
export declare const getMillisecondsInUTCFromDate: (date: SDate, timeZone: string) => number;
|
|
5
1
|
/**
|
|
6
2
|
* @param timeZone For a list of valid time zones run
|
|
7
3
|
* `Intl.supportedValuesOf('timeZone')` on your environment.
|
package/dist/internal/zoned.js
CHANGED
|
@@ -1,23 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getDateAsUTCDateMini } from './date.js';
|
|
3
|
-
import { getTimestampAsUTCDateMini } from './timestamp.js';
|
|
4
|
-
const getValidatedTimeZoneOffset = (timeZone, utcDate) => {
|
|
5
|
-
const timeZoneOffset = getTimezoneOffset(timeZone, utcDate);
|
|
6
|
-
if (isNaN(timeZoneOffset)) {
|
|
7
|
-
throw new Error(`Invalid time zone. Time zone: '${timeZone}'`);
|
|
8
|
-
}
|
|
9
|
-
return timeZoneOffset;
|
|
10
|
-
};
|
|
11
|
-
export const getMillisecondsInUTCFromTimestamp = (timestamp, timeZone) => {
|
|
12
|
-
const utcDate = getTimestampAsUTCDateMini(timestamp);
|
|
13
|
-
const timeZoneOffset = getValidatedTimeZoneOffset(timeZone, utcDate);
|
|
14
|
-
return utcDate.getTime() - timeZoneOffset;
|
|
15
|
-
};
|
|
16
|
-
export const getMillisecondsInUTCFromDate = (date, timeZone) => {
|
|
17
|
-
const utcDate = getDateAsUTCDateMini(date);
|
|
18
|
-
const timeZoneOffset = getValidatedTimeZoneOffset(timeZone, utcDate);
|
|
19
|
-
return utcDate.getTime() - timeZoneOffset;
|
|
20
|
-
};
|
|
1
|
+
import { toZonedTime } from 'date-fns-tz';
|
|
21
2
|
/**
|
|
22
3
|
* @param timeZone For a list of valid time zones run
|
|
23
4
|
* `Intl.supportedValuesOf('timeZone')` on your environment.
|
package/dist/sDate.d.ts
CHANGED
|
@@ -90,6 +90,16 @@ export declare const getDateFromDate: (date: string | SDate) => number;
|
|
|
90
90
|
* in the YYYY-MM-DD format.
|
|
91
91
|
*/
|
|
92
92
|
export declare const getWeekdayFromDate: (date: string | SDate) => number;
|
|
93
|
+
/**
|
|
94
|
+
* Returns the number of milliseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC)
|
|
95
|
+
* for the given date in the specified time zone.
|
|
96
|
+
*
|
|
97
|
+
* @param date The date to convert to UTC milliseconds. It can be an SDate or a string
|
|
98
|
+
* in the YYYY-MM-DD format.
|
|
99
|
+
* @param timeZone The time zone to use when converting the date. See
|
|
100
|
+
* `Intl.supportedValuesOf('timeZone')` for a list of valid time zones.
|
|
101
|
+
*/
|
|
102
|
+
export declare const getUTCMillisecondsFromDate: (date: string | SDate, timeZone: string) => number;
|
|
93
103
|
/**
|
|
94
104
|
* Returns a native Date adjusted so that the local time of that date matches
|
|
95
105
|
* the local time at the specified time zone.
|
package/dist/sDate.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { getTimezoneOffset } from 'date-fns-tz';
|
|
1
2
|
import { SDate } from './internal/SDate.js';
|
|
2
3
|
import { DayToWeekday, DaysInWeek, MillisecondsInDay, } from './internal/constants.js';
|
|
3
4
|
import { getDateAsUTCDateMini, getISODateFromISODate, getISODateFromZonedDate, getISOMonthFromISODate, getISOYearFromISODate, } from './internal/date.js';
|
|
4
5
|
import { getAtIndex } from './internal/utils.js';
|
|
5
6
|
import { getIndexForWeekday } from './internal/weekdays.js';
|
|
6
|
-
import {
|
|
7
|
+
import { getTimeZonedDate } from './internal/zoned.js';
|
|
7
8
|
/**
|
|
8
9
|
* --- Factory ---
|
|
9
10
|
*/
|
|
@@ -142,6 +143,24 @@ export const getWeekdayFromDate = (date) => {
|
|
|
142
143
|
const nativeDate = getDateAsUTCDateMini(sDateValue);
|
|
143
144
|
return getAtIndex(DayToWeekday, nativeDate.getDay());
|
|
144
145
|
};
|
|
146
|
+
/**
|
|
147
|
+
* Returns the number of milliseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC)
|
|
148
|
+
* for the given date in the specified time zone.
|
|
149
|
+
*
|
|
150
|
+
* @param date The date to convert to UTC milliseconds. It can be an SDate or a string
|
|
151
|
+
* in the YYYY-MM-DD format.
|
|
152
|
+
* @param timeZone The time zone to use when converting the date. See
|
|
153
|
+
* `Intl.supportedValuesOf('timeZone')` for a list of valid time zones.
|
|
154
|
+
*/
|
|
155
|
+
export const getUTCMillisecondsFromDate = (date, timeZone) => {
|
|
156
|
+
const sDateValue = sDate(date);
|
|
157
|
+
const utcDate = getDateAsUTCDateMini(sDateValue);
|
|
158
|
+
const timeZoneOffset = getTimezoneOffset(timeZone, utcDate);
|
|
159
|
+
if (isNaN(timeZoneOffset)) {
|
|
160
|
+
throw new Error(`Invalid time zone. Time zone: '${timeZone}'`);
|
|
161
|
+
}
|
|
162
|
+
return utcDate.getTime() - timeZoneOffset;
|
|
163
|
+
};
|
|
145
164
|
/**
|
|
146
165
|
* Returns a native Date adjusted so that the local time of that date matches
|
|
147
166
|
* the local time at the specified time zone.
|
|
@@ -153,7 +172,7 @@ export const getWeekdayFromDate = (date) => {
|
|
|
153
172
|
*/
|
|
154
173
|
export const getTimeZonedDateFromDate = (date, timeZone) => {
|
|
155
174
|
const sDateValue = sDate(date);
|
|
156
|
-
const milliseconds =
|
|
175
|
+
const milliseconds = getUTCMillisecondsFromDate(sDateValue, timeZone);
|
|
157
176
|
const zonedTime = getTimeZonedDate(milliseconds, timeZone);
|
|
158
177
|
return zonedTime;
|
|
159
178
|
};
|
package/dist/sTimestamp.d.ts
CHANGED
|
@@ -49,6 +49,16 @@ export declare const getTimestampFromDateAndTime: (date: string | SDate, time: s
|
|
|
49
49
|
/**
|
|
50
50
|
* --- Getters ---
|
|
51
51
|
*/
|
|
52
|
+
/**
|
|
53
|
+
* Returns the number of milliseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC)
|
|
54
|
+
* for the given timestamp in the specified time zone.
|
|
55
|
+
*
|
|
56
|
+
* @param timestamp The timestamp to convert to UTC milliseconds. It can be an STimestamp
|
|
57
|
+
* or a string in the YYYY-MM-DDTHH:MM format.
|
|
58
|
+
* @param timeZone The time zone to use when converting the timestamp. See
|
|
59
|
+
* `Intl.supportedValuesOf('timeZone')` for a list of valid time zones.
|
|
60
|
+
*/
|
|
61
|
+
export declare const getUTCMillisecondsFromTimestamp: (timestamp: string | STimestamp, timeZone: string) => number;
|
|
52
62
|
/**
|
|
53
63
|
* Returns a native Date adjusted so that the local time of that date matches
|
|
54
64
|
* the local timestamp at the specified time zone.
|
|
@@ -197,10 +207,10 @@ export declare const addDaysToTimestamp: (timestamp: string | STimestamp, days:
|
|
|
197
207
|
*
|
|
198
208
|
* Time is converted from the given time zone to
|
|
199
209
|
* UTC before the minutes are added, and then converted back to the specified
|
|
200
|
-
* time zone. This results in the resulting time being adjusted for daylight
|
|
201
|
-
* changes. (e.g. Adding 60 minutes to 2024-03-10T01:59 in
|
|
202
|
-
* result in 2024-03-10T03:59 as time move forward one
|
|
203
|
-
* time at 2024-03-10T02:00.)
|
|
210
|
+
* time zone. This results in the resulting time being adjusted for daylight
|
|
211
|
+
* saving time changes. (e.g. Adding 60 minutes to 2024-03-10T01:59 in
|
|
212
|
+
* America/New_York will result in 2024-03-10T03:59 as time move forward one
|
|
213
|
+
* hour for daylight saving time at 2024-03-10T02:00.)
|
|
204
214
|
*
|
|
205
215
|
* For example, adding one minute to 2024-03-10T01:59 will result in
|
|
206
216
|
* 2024-03-10T03:00 as expected. However, trying to add one minute to
|
package/dist/sTimestamp.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { getTimezoneOffset } from 'date-fns-tz';
|
|
1
2
|
import { STimestamp } from './internal/STimestamp.js';
|
|
2
3
|
import { MillisecondsInMinute, MillisecondsInSecond, } from './internal/constants.js';
|
|
3
4
|
import { getISODateFromISOTimestamp, getISOTimeFromISOTimestamp, getISOTimestampFromZonedDate, getTimestampAsUTCDateMini, } from './internal/timestamp.js';
|
|
4
|
-
import {
|
|
5
|
+
import { getTimeZonedDate } from './internal/zoned.js';
|
|
5
6
|
import { getShortDateString, sDate } from './sDate.js';
|
|
6
7
|
import { get12HourTimeString, sTime } from './sTime.js';
|
|
7
8
|
/**
|
|
@@ -62,6 +63,24 @@ export const getTimestampFromDateAndTime = (date, time) => {
|
|
|
62
63
|
/**
|
|
63
64
|
* --- Getters ---
|
|
64
65
|
*/
|
|
66
|
+
/**
|
|
67
|
+
* Returns the number of milliseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC)
|
|
68
|
+
* for the given timestamp in the specified time zone.
|
|
69
|
+
*
|
|
70
|
+
* @param timestamp The timestamp to convert to UTC milliseconds. It can be an STimestamp
|
|
71
|
+
* or a string in the YYYY-MM-DDTHH:MM format.
|
|
72
|
+
* @param timeZone The time zone to use when converting the timestamp. See
|
|
73
|
+
* `Intl.supportedValuesOf('timeZone')` for a list of valid time zones.
|
|
74
|
+
*/
|
|
75
|
+
export const getUTCMillisecondsFromTimestamp = (timestamp, timeZone) => {
|
|
76
|
+
const sTimestampValue = sTimestamp(timestamp);
|
|
77
|
+
const utcDate = getTimestampAsUTCDateMini(sTimestampValue);
|
|
78
|
+
const timeZoneOffset = getTimezoneOffset(timeZone, utcDate);
|
|
79
|
+
if (isNaN(timeZoneOffset)) {
|
|
80
|
+
throw new Error(`Invalid time zone. Time zone: '${timeZone}'`);
|
|
81
|
+
}
|
|
82
|
+
return utcDate.getTime() - timeZoneOffset;
|
|
83
|
+
};
|
|
65
84
|
/**
|
|
66
85
|
* Returns a native Date adjusted so that the local time of that date matches
|
|
67
86
|
* the local timestamp at the specified time zone.
|
|
@@ -73,7 +92,7 @@ export const getTimestampFromDateAndTime = (date, time) => {
|
|
|
73
92
|
*/
|
|
74
93
|
export const getTimeZonedDateFromTimestamp = (timestamp, timeZone) => {
|
|
75
94
|
const sTimestampValue = sTimestamp(timestamp);
|
|
76
|
-
const dateInUTCMilliseconds =
|
|
95
|
+
const dateInUTCMilliseconds = getUTCMillisecondsFromTimestamp(sTimestampValue, timeZone);
|
|
77
96
|
return getTimeZonedDate(dateInUTCMilliseconds, timeZone);
|
|
78
97
|
};
|
|
79
98
|
/**
|
|
@@ -130,7 +149,7 @@ export const getTimeZonedDateFromTimestamp = (timestamp, timeZone) => {
|
|
|
130
149
|
export const getSecondsToTimestamp = (timestamp, timeZone) => {
|
|
131
150
|
const sTimestampValue = sTimestamp(timestamp);
|
|
132
151
|
const millisecondsNow = Date.now();
|
|
133
|
-
const millisecondsAtTimestamp =
|
|
152
|
+
const millisecondsAtTimestamp = getUTCMillisecondsFromTimestamp(sTimestampValue, timeZone);
|
|
134
153
|
return Math.floor((millisecondsAtTimestamp - millisecondsNow) / MillisecondsInSecond);
|
|
135
154
|
};
|
|
136
155
|
/**
|
|
@@ -240,10 +259,10 @@ export const addDaysToTimestamp = (timestamp, days) => {
|
|
|
240
259
|
*
|
|
241
260
|
* Time is converted from the given time zone to
|
|
242
261
|
* UTC before the minutes are added, and then converted back to the specified
|
|
243
|
-
* time zone. This results in the resulting time being adjusted for daylight
|
|
244
|
-
* changes. (e.g. Adding 60 minutes to 2024-03-10T01:59 in
|
|
245
|
-
* result in 2024-03-10T03:59 as time move forward one
|
|
246
|
-
* time at 2024-03-10T02:00.)
|
|
262
|
+
* time zone. This results in the resulting time being adjusted for daylight
|
|
263
|
+
* saving time changes. (e.g. Adding 60 minutes to 2024-03-10T01:59 in
|
|
264
|
+
* America/New_York will result in 2024-03-10T03:59 as time move forward one
|
|
265
|
+
* hour for daylight saving time at 2024-03-10T02:00.)
|
|
247
266
|
*
|
|
248
267
|
* For example, adding one minute to 2024-03-10T01:59 will result in
|
|
249
268
|
* 2024-03-10T03:00 as expected. However, trying to add one minute to
|
|
@@ -278,7 +297,7 @@ export const addDaysToTimestamp = (timestamp, days) => {
|
|
|
278
297
|
*/
|
|
279
298
|
export const addMinutesToTimestamp = (timestamp, minutes, timeZone) => {
|
|
280
299
|
const sTimestampValue = sTimestamp(timestamp);
|
|
281
|
-
const newMillisecondsInUTC =
|
|
300
|
+
const newMillisecondsInUTC = getUTCMillisecondsFromTimestamp(sTimestampValue, timeZone) +
|
|
282
301
|
minutes * MillisecondsInMinute;
|
|
283
302
|
const newTimestamp = getTimestampFromUTCMilliseconds(newMillisecondsInUTC, timeZone);
|
|
284
303
|
return newTimestamp;
|
package/dist/sWeekdays.js
CHANGED
|
@@ -62,7 +62,7 @@ export const sWeekdays = (weekdays) => {
|
|
|
62
62
|
* ```
|
|
63
63
|
*/
|
|
64
64
|
export const getWeekdaysFromWeekdayFlags = (weekdays) => {
|
|
65
|
-
const newWeekdays =
|
|
65
|
+
const newWeekdays = Array.from(AllWeekdaysIncludedMask);
|
|
66
66
|
for (let i = 0; i < DayToWeekday.length; i++) {
|
|
67
67
|
const weekday = getAtIndex(DayToWeekday, i);
|
|
68
68
|
if (!hasFlag(weekdays, weekday)) {
|
|
@@ -102,7 +102,7 @@ export const getWeekdaysWithNoneIncluded = () => {
|
|
|
102
102
|
*/
|
|
103
103
|
export const shiftWeekdaysForward = (weekdays) => {
|
|
104
104
|
const sWeekdaysInstance = sWeekdays(weekdays);
|
|
105
|
-
const after =
|
|
105
|
+
const after = Array.from(NoWeekdaysIncluded);
|
|
106
106
|
const DayShift = 1;
|
|
107
107
|
for (let i = 0; i < WeekdaysCount; i++) {
|
|
108
108
|
const prevDayIndex = (WeekdaysCount - DayShift + i) % WeekdaysCount;
|
|
@@ -163,7 +163,7 @@ export const filterWeekdaysForDates = (weekdays, fromDate, toDate) => {
|
|
|
163
163
|
*/
|
|
164
164
|
export const addWeekdayToWeekdays = (weekdays, weekdayToAdd) => {
|
|
165
165
|
const sWeekdaysInstance = sWeekdays(weekdays);
|
|
166
|
-
const newWeekdays =
|
|
166
|
+
const newWeekdays = Array.from(sWeekdaysInstance.weekdays);
|
|
167
167
|
const weekdayIndex = getIndexForWeekday(weekdayToAdd);
|
|
168
168
|
newWeekdays[weekdayIndex] = getAtIndex(AllWeekdaysIncludedMask, weekdayIndex);
|
|
169
169
|
return sWeekdays(newWeekdays.join(''));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scdate",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js"
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
"test:watch": "vitest",
|
|
24
24
|
"test:utc:watch": "TZ=Etc/Universal yarn test:watch",
|
|
25
25
|
"smoke": "yarn build && yarn lint && yarn test:utc",
|
|
26
|
-
"docs": "typedoc && prettier --ignore-unknown --write docs/",
|
|
27
26
|
"-- PRE-COMMIT HOOKS --": "",
|
|
28
27
|
"localAfterInstall": "is-ci || husky || true",
|
|
29
28
|
"prepublishOnly": "pinst --disable",
|
|
@@ -35,21 +34,18 @@
|
|
|
35
34
|
"date-fns-tz": "^3.2.0"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
|
-
"@eslint/js": "^9.
|
|
37
|
+
"@eslint/js": "^9.25.0",
|
|
39
38
|
"@tsconfig/strictest": "^2.0.5",
|
|
40
|
-
"@types/node": "^22.
|
|
41
|
-
"eslint": "^9.
|
|
42
|
-
"husky": "^9.1.
|
|
43
|
-
"
|
|
44
|
-
"lint-staged": "^15.2.10",
|
|
39
|
+
"@types/node": "^22.14.1",
|
|
40
|
+
"eslint": "^9.25.0",
|
|
41
|
+
"husky": "^9.1.7",
|
|
42
|
+
"lint-staged": "^15.5.1",
|
|
45
43
|
"pinst": "^3.0.0",
|
|
46
|
-
"prettier": "^3.
|
|
44
|
+
"prettier": "^3.5.3",
|
|
47
45
|
"rimraf": "^6.0.1",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"typescript-eslint": "^8.8.0",
|
|
52
|
-
"vitest": "^2.1.1"
|
|
46
|
+
"typescript": "^5.8.3",
|
|
47
|
+
"typescript-eslint": "^8.30.1",
|
|
48
|
+
"vitest": "^3.1.1"
|
|
53
49
|
},
|
|
54
50
|
"prettier": {
|
|
55
51
|
"tabWidth": 2,
|
|
@@ -74,5 +70,5 @@
|
|
|
74
70
|
"*.{ts,tsx,mjs}": "eslint --cache",
|
|
75
71
|
"*": "prettier --ignore-unknown --write"
|
|
76
72
|
},
|
|
77
|
-
"packageManager": "yarn@4.
|
|
73
|
+
"packageManager": "yarn@4.9.1"
|
|
78
74
|
}
|