2
2
3
3
## Intro
4
4
5
+ ** NOTE** : In the following document, DST refers to time shifts happening in
6
+ different timezones, not necesserily the ones occuring periodically for
7
+ "daylight-saving-time".
8
+
5
9
Datetime handling is relatively straight forward for UTC, but can get messy
6
10
around DST (Daylight Savings Time), when the local clock shifts forwards or
7
11
backwards one hour. During DST it's possible that a particular local time is
@@ -30,15 +34,15 @@ Of course local correct operations sometimes will hit an invalid or ambiguous
30
34
local date. There are a few available options for each case.
31
35
32
36
* for invalid dates:
33
- * jump forward by one (or more) hours until the time is valid
34
- * jump backward by one (or more) hours until the time is valid
35
- * jump forward to first valid time (might not be whole hours )
36
- * jump backward to first valid time (might not be whole hours )
37
+ * jump forward by the size of the gap (default)
38
+ * jump backward by the size of the gap
39
+ * jump forward to first valid time (might not be size of gap )
40
+ * jump backward to first valid time (might not be size of gap )
37
41
* declare the date as invalid
38
42
39
43
* for ambiguous dates:
44
+ * pick past offset (the one that was present before DST) (default)
40
45
* pick future offset (the one that continues after DST)
41
- * pick past offset (the one that was present before DST)
42
46
* pick current offset (in case this operation changes from old to new date,
43
47
the old date's offset could be used, if its one of the two alternatives
44
48
-- so this * option* is only best effort and needs to be backed up by
@@ -53,29 +57,23 @@ operations. All these choices could be configurable.
53
57
54
58
* Create from local time - given year, month, day, hour, min, sec in local
55
59
time, construct a moment. Note that the given time might be invalid or
56
- ambiguous. Invalid - jump forward by whole hours to first valid local time.
57
- Ambiguous - pick the offset that continues in the future.
60
+ ambiguous. Use the default operations for invalid/ambiguous.
58
61
* Add/Subtract
59
62
* add/subtract smaller units (hour or less) - these should be * UTC correct*
60
63
* add/subtract bigger units (day or more) - these should be * local correct*
64
+ To put it differently, they should behave like a Set operation.
61
65
The idea is that 1st May 12:34 + 15 days should be 16 May 12:34 (if
62
- possible), no matter what DST happened from 1st to 15th of May. If the
63
- time is invalid, jump forward (by whole hour) to valid; if ambiguous pick
64
- future. (For subtract jump backwards and pick past).
66
+ possible), no matter what DST happened from 1st to 15th of May. Use
67
+ default operations for invalid/ambiguous (same as set, create).
65
68
* Set one or several units - set is very similar to create (but instead of
66
- specifying all units, only the different ones are specified).
67
- * set smaller units (minute or less) - these should be * UTC correct* . That
68
- is, the set acts as an add/subtract depending on the current value. So if
69
- the minute is now 15, a set to 45 means add 30 minutes (check
70
- add/subtract above).
71
- * set bigger units (hour or more) - these should be * local correct* . So,
72
- again it acts as add/subtract depending on current value, and tries to be
73
- local correct.
69
+ specifying all units, only the different ones are specified). Use default
70
+ operations for invaid/ambiguous moments, resulting from a set.
74
71
* StartOf/EndOf
75
- * statof 'day' - in case the start of day is invalid - jump to first valid
76
- local time of today (not whole hours), in case its ambiguous - pick the
77
- future one
78
- * endof 'day' - same idea as start of but in reverse :)
72
+ * statof UNIT - in case the start of UNIT is invalid - jump to first valid
73
+ local time of the UNIT (that might not be the gap), in case its ambiguous
74
+ - pick the past one (that is the default).
75
+ * endof UNIT - this should just use startof UNIT - 1ms (UTC correct
76
+ -1ms).
79
77
* change timezone while keeping the local time - same idea applies as creating
80
78
local time from components
81
79
@@ -111,44 +109,46 @@ universally recognized way of keeping local time, but I'll still use it here
111
109
because its one number, instead of 2 or more). Note that it could be invalid or
112
110
ambiguous.
113
111
114
- * ` localToOffsets (lts)` -- we'll define a function that returns all offsets of
115
- a given local timestamp .
112
+ * ` correctLocalDefault (lts)` -- return [ new \_ lts, offset ] , new \_ lts might
113
+ differ from lts if lts is invalid .
116
114
117
115
``` javascript
118
- function localToOffsets (lts ) {
116
+ function correctLocaleDefault (lts ) {
119
117
var res = []
120
118
var of1 = getOffsetFromUTC (lts) // we treat local timestamp as unix to get
121
119
// a ballbpark estimate
122
120
var of2 = getOffsetFromUTC (lts - of1) // adjust local by probable offset
123
121
if (of1 == of2) {
124
- // (lts, of1) is valid
125
- res .push (of1)
126
- } else {
127
- var of3 = getOffsetFromUTC (lts - of2)
128
- if (of3 == of2) {
129
- // (lts, of2) is valid
130
- res .push (of2)
122
+ // (lts, of1) is valid, but could be ambigous (second)
123
+ of3 = getOffsetFromUTC (lts - of1 - 6h ); // subtract 6h to see if
124
+ // we're near DST
125
+ if (of1 == of3) {
126
+ return [lts, of1];
127
+ } else if (getOffsetFromUTC (lts - of3) == of3) {
128
+ // ambiguous, variants are [lts, of3], [lts, of1], of3 being
129
+ // the previous
130
+ return [lts, of3];
131
131
} else {
132
- // lts is invalid
132
+ // there was DST shortly before [lts, of1], but it fully passed
133
+ return [lts, of1];
133
134
}
134
- }
135
-
136
- if (res .length == 0 ) {
137
- // invalid
138
- return res;
139
135
} else {
140
- var of = res[0 ];
141
- // NOTE: This is heruistic, so might not work in all cases.
142
- // check the offsets of lts-of+1h and lts-of-1h
143
- var of4 = getOffsetFromUTC (lts- of - 1h )
144
- var of5 = getOffsetFromUTC (lts- of + 1h )
145
-
146
- if (of4 == of + 1h ) {
147
- res .push (of4)
148
- } else if (of5 == of - 1h ) {
149
- res .push (of5)
136
+ // we try a second time, this could happen around invalid time
137
+ var of3 = getOffsetFromUTC (lts - of2);
138
+ if (of3 == of2) {
139
+ return [lts, of2]
140
+ } else {
141
+ // invalid time!
142
+ if (of2 > of3) {
143
+ var tmp = of2; of2 = of3; of3 = tmp;
144
+ }
145
+ var dstGap = of3 - of2;
146
+ if (getOffsetFromUTC (lts + dstGap - of3) == of3) {
147
+ return [lts, of3];
148
+ } else {
149
+ throw new Error (" should never happen (test)" );
150
+ }
150
151
}
151
- return res;
152
152
}
153
153
}
154
154
```
0 commit comments