/src/moddable/xs/sources/xsDate.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2016-2025 Moddable Tech, Inc. |
3 | | * |
4 | | * This file is part of the Moddable SDK Runtime. |
5 | | * |
6 | | * The Moddable SDK Runtime is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * The Moddable SDK Runtime is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | * This file incorporates work covered by the following copyright and |
20 | | * permission notice: |
21 | | * |
22 | | * Copyright (C) 2010-2016 Marvell International Ltd. |
23 | | * Copyright (C) 2002-2010 Kinoma, Inc. |
24 | | * |
25 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
26 | | * you may not use this file except in compliance with the License. |
27 | | * You may obtain a copy of the License at |
28 | | * |
29 | | * http://www.apache.org/licenses/LICENSE-2.0 |
30 | | * |
31 | | * Unless required by applicable law or agreed to in writing, software |
32 | | * distributed under the License is distributed on an "AS IS" BASIS, |
33 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
34 | | * See the License for the specific language governing permissions and |
35 | | * limitations under the License. |
36 | | */ |
37 | | |
38 | | #include "xsAll.h" |
39 | | |
40 | | typedef struct sxDateTime { |
41 | | txNumber year; |
42 | | txNumber month; |
43 | | txNumber date; |
44 | | txNumber hours; |
45 | | txNumber minutes; |
46 | | txNumber seconds; |
47 | | txNumber milliseconds; |
48 | | txInteger day; |
49 | | txInteger offset; |
50 | | txNumber value; |
51 | | } txDateTime; |
52 | | |
53 | | static txSlot* fxNewDateInstance(txMachine* the); |
54 | | |
55 | | static void fx_Date_aux(txMachine* the, txFlag secure); |
56 | | static txInteger fx_Date_parse_number(txByte* theCharacter, txString* theString, txBoolean* overflow); |
57 | | static txInteger fx_Date_parse_fraction(txByte* theCharacter, txString* theString); |
58 | | static txBoolean fx_Date_prototype_get_aux(txMachine* the, txDateTime* td, txBoolean utc, txSlot* slot); |
59 | | static void fx_Date_prototype_set_aux(txMachine* the, txDateTime* td, txBoolean utc, txSlot* slot); |
60 | | |
61 | | static txSlot* fxDateCheck(txMachine* the); |
62 | | static txNumber fxDateClip(txNumber value); |
63 | | static txNumber fxDateFullYear(txMachine* the, txSlot* slot); |
64 | | static txNumber fxDateMerge(txDateTime* dt, txBoolean utc); |
65 | | static txString fxDatePrint2Digits(txString p, txInteger value); |
66 | | static txString fxDatePrint3Digits(txString p, txInteger value); |
67 | | static txString fxDatePrint4Digits(txString p, txInteger value); |
68 | | static txString fxDatePrintDate(txString p, txInteger year, txInteger month, txInteger date); |
69 | | static txString fxDatePrintDateUTC(txString p, txInteger year, txInteger month, txInteger date); |
70 | | static txString fxDatePrintDay(txString p, txInteger day); |
71 | | static txString fxDatePrintTime(txString p, txInteger hours, txInteger minutes, txInteger seconds); |
72 | | static txString fxDatePrintTimezone(txString p, txInteger offset); |
73 | | static txString fxDatePrintYear(txString p, txInteger value); |
74 | | static txInteger fxDateSimilarYear(txInteger year); |
75 | | static void fxDateSplit(txNumber value, txBoolean utc, txDateTime* dt); |
76 | | |
77 | | void fxBuildDate(txMachine* the) |
78 | 33.8k | { |
79 | 33.8k | txSlot* slot; |
80 | 33.8k | mxPush(mxObjectPrototype); |
81 | 33.8k | slot = fxLastProperty(the, fxNewObjectInstance(the)); |
82 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getMilliseconds), 0, mxID(_getMilliseconds), XS_DONT_ENUM_FLAG); |
83 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getSeconds), 0, mxID(_getSeconds), XS_DONT_ENUM_FLAG); |
84 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getMinutes), 0, mxID(_getMinutes), XS_DONT_ENUM_FLAG); |
85 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getHours), 0, mxID(_getHours), XS_DONT_ENUM_FLAG); |
86 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getDay), 0, mxID(_getDay), XS_DONT_ENUM_FLAG); |
87 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getDate), 0, mxID(_getDate), XS_DONT_ENUM_FLAG); |
88 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getMonth), 0, mxID(_getMonth), XS_DONT_ENUM_FLAG); |
89 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getYear), 0, mxID(_getYear), XS_DONT_ENUM_FLAG); |
90 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getFullYear), 0, mxID(_getFullYear), XS_DONT_ENUM_FLAG); |
91 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getUTCMilliseconds), 0, mxID(_getUTCMilliseconds), XS_DONT_ENUM_FLAG); |
92 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getUTCSeconds), 0, mxID(_getUTCSeconds), XS_DONT_ENUM_FLAG); |
93 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getUTCMinutes), 0, mxID(_getUTCMinutes), XS_DONT_ENUM_FLAG); |
94 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getUTCHours), 0, mxID(_getUTCHours), XS_DONT_ENUM_FLAG); |
95 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getUTCDay), 0, mxID(_getUTCDay), XS_DONT_ENUM_FLAG); |
96 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getUTCDate), 0, mxID(_getUTCDate), XS_DONT_ENUM_FLAG); |
97 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getUTCMonth), 0, mxID(_getUTCMonth), XS_DONT_ENUM_FLAG); |
98 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getUTCFullYear), 0, mxID(_getUTCFullYear), XS_DONT_ENUM_FLAG); |
99 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_valueOf), 0, mxID(_getTime), XS_DONT_ENUM_FLAG); |
100 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_getTimezoneOffset), 0, mxID(_getTimezoneOffset), XS_DONT_ENUM_FLAG); |
101 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setDate), 1, mxID(_setDate), XS_DONT_ENUM_FLAG); |
102 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setFullYear), 3, mxID(_setFullYear), XS_DONT_ENUM_FLAG); |
103 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setHours), 4, mxID(_setHours), XS_DONT_ENUM_FLAG); |
104 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setMilliseconds), 1, mxID(_setMilliseconds), XS_DONT_ENUM_FLAG); |
105 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setMinutes), 3, mxID(_setMinutes), XS_DONT_ENUM_FLAG); |
106 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setMonth), 2, mxID(_setMonth), XS_DONT_ENUM_FLAG); |
107 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setSeconds), 2, mxID(_setSeconds), XS_DONT_ENUM_FLAG); |
108 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setTime), 1, mxID(_setTime), XS_DONT_ENUM_FLAG); |
109 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setYear), 1, mxID(_setYear), XS_DONT_ENUM_FLAG); |
110 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setUTCDate), 1, mxID(_setUTCDate), XS_DONT_ENUM_FLAG); |
111 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setUTCFullYear), 3, mxID(_setUTCFullYear), XS_DONT_ENUM_FLAG); |
112 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setUTCHours), 4, mxID(_setUTCHours), XS_DONT_ENUM_FLAG); |
113 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setUTCMilliseconds), 1, mxID(_setUTCMilliseconds), XS_DONT_ENUM_FLAG); |
114 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setUTCMinutes), 3, mxID(_setUTCMinutes), XS_DONT_ENUM_FLAG); |
115 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setUTCMonth), 2, mxID(_setUTCMonth), XS_DONT_ENUM_FLAG); |
116 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_setUTCSeconds), 2, mxID(_setUTCSeconds), XS_DONT_ENUM_FLAG); |
117 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toDateString), 0, mxID(_toDateString), XS_DONT_ENUM_FLAG); |
118 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toISOString), 0, mxID(_toISOString), XS_DONT_ENUM_FLAG); |
119 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toJSON), 1, mxID(_toJSON), XS_DONT_ENUM_FLAG); |
120 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toDateString), 0, mxID(_toLocaleDateString), XS_DONT_ENUM_FLAG); |
121 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toString), 0, mxID(_toLocaleString), XS_DONT_ENUM_FLAG); |
122 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toTimeString), 0, mxID(_toLocaleTimeString), XS_DONT_ENUM_FLAG); |
123 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toString), 0, mxID(_toString), XS_DONT_ENUM_FLAG); |
124 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toTimeString), 0, mxID(_toTimeString), XS_DONT_ENUM_FLAG); |
125 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toUTCString), 0, mxID(_toUTCString), XS_DONT_ENUM_FLAG); |
126 | 33.8k | slot = fxNextSlotProperty(the, slot, slot, mxID(_toGMTString), XS_DONT_ENUM_FLAG); |
127 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_valueOf), 0, mxID(_valueOf), XS_DONT_ENUM_FLAG); |
128 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_prototype_toPrimitive), 1, mxID(_Symbol_toPrimitive), XS_DONT_ENUM_FLAG | XS_DONT_SET_FLAG); |
129 | 33.8k | mxDatePrototype = *the->stack; |
130 | 33.8k | slot = fxBuildHostConstructor(the, mxCallback(fx_Date), 7, mxID(_Date)); |
131 | 33.8k | mxDateConstructor = *the->stack; |
132 | 33.8k | slot = fxLastProperty(the, slot); |
133 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_now), 0, mxID(_now), XS_DONT_ENUM_FLAG); |
134 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_parse), 1, mxID(_parse), XS_DONT_ENUM_FLAG); |
135 | 33.8k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_Date_UTC), 7, mxID(_UTC), XS_DONT_ENUM_FLAG); |
136 | 33.8k | mxPop(); |
137 | 33.8k | } |
138 | | |
139 | | txSlot* fxNewDateInstance(txMachine* the) |
140 | 160k | { |
141 | 160k | txSlot* instance; |
142 | 160k | txSlot* property; |
143 | 160k | instance = fxNewObjectInstance(the); |
144 | 160k | property = fxNextNumberProperty(the, instance, 0, XS_NO_ID, XS_INTERNAL_FLAG); |
145 | 160k | property->kind = XS_DATE_KIND; |
146 | 160k | property->value.number = C_NAN; |
147 | 160k | return instance; |
148 | 160k | } |
149 | | |
150 | | void fx_Date(txMachine* the) |
151 | 182k | { |
152 | 182k | fx_Date_aux(the, 0); |
153 | 182k | } |
154 | | |
155 | | void fx_Date_aux(txMachine* the, txFlag secure) |
156 | 182k | { |
157 | 182k | txInteger c = mxArgc; |
158 | 182k | txDateTime dt; |
159 | 182k | txSlot* instance; |
160 | 182k | if (mxIsUndefined(mxTarget)) { |
161 | 22.0k | char buffer[256]; |
162 | 22.0k | txString p = buffer; |
163 | 22.0k | txDateTime dt; |
164 | 22.0k | if (secure) |
165 | 0 | mxTypeError("secure mode"); |
166 | 22.0k | mxResult->value.number = fxDateNow(); |
167 | 22.0k | mxResult->kind = XS_NUMBER_KIND; |
168 | 22.0k | fxDateSplit(mxResult->value.number, 0, &dt); |
169 | 22.0k | p = fxDatePrintDay(p, dt.day); |
170 | 22.0k | *p++ = ' '; |
171 | 22.0k | p = fxDatePrintDate(p, (txInteger)dt.year, (txInteger)dt.month, (txInteger)dt.date); |
172 | 22.0k | *p++ = ' '; |
173 | 22.0k | p = fxDatePrintTime(p, (txInteger)dt.hours, (txInteger)dt.minutes, (txInteger)dt.seconds); |
174 | 22.0k | *p++ = ' '; |
175 | 22.0k | p = fxDatePrintTimezone(p, (txInteger)dt.offset); |
176 | 22.0k | *p = 0; |
177 | 22.0k | fxCopyStringC(the, mxResult, buffer); |
178 | 22.0k | return; |
179 | 22.0k | } |
180 | 160k | mxPushSlot(mxTarget); |
181 | 160k | fxGetPrototypeFromConstructor(the, &mxDatePrototype); |
182 | 160k | instance = fxNewDateInstance(the); |
183 | 160k | mxPullSlot(mxResult); |
184 | 160k | if (c > 1) { |
185 | 61.3k | dt.year = fxDateFullYear(the, mxArgv(0)); |
186 | 61.3k | dt.month = fxToNumber(the, mxArgv(1)); |
187 | 61.3k | dt.date = (c > 2) ? fxToNumber(the, mxArgv(2)) : 1; |
188 | 61.3k | dt.hours = (c > 3) ? fxToNumber(the, mxArgv(3)) : 0; |
189 | 61.3k | dt.minutes = (c > 4) ? fxToNumber(the, mxArgv(4)) : 0; |
190 | 61.3k | dt.seconds = (c > 5) ?fxToNumber(the, mxArgv(5)) : 0; |
191 | 61.3k | dt.milliseconds = (c > 6) ? fxToNumber(the, mxArgv(6)) : 0; |
192 | 61.3k | instance->next->value.number = fxDateMerge(&dt, 0); |
193 | 61.3k | return; |
194 | 61.3k | } |
195 | 98.9k | if (c > 0) { |
196 | 96.5k | txSlot* slot = mxArgv(0); |
197 | 96.5k | if (slot->kind == XS_REFERENCE_KIND) { |
198 | 2.97k | txSlot* date = slot->value.reference; |
199 | 2.97k | if ((date->next) && (date->next->kind == XS_DATE_KIND)) { |
200 | 597 | instance->next->value.number = date->next->value.number; |
201 | 597 | return; |
202 | 597 | } |
203 | 2.97k | } |
204 | 95.9k | fxToPrimitive(the, slot, XS_NO_HINT); |
205 | 95.9k | if ((slot->kind == XS_STRING_KIND) || (slot->kind == XS_STRING_X_KIND)) { |
206 | 17.9k | mxPushSlot(mxFunction); |
207 | 17.9k | mxDub(); |
208 | 17.9k | mxGetID(mxID(_parse)); |
209 | 17.9k | mxCall(); |
210 | 17.9k | mxPushSlot(slot); |
211 | 17.9k | mxRunCount(1); |
212 | 17.9k | instance->next->value.number = the->stack->value.number; |
213 | 17.9k | mxPop(); |
214 | 17.9k | return; |
215 | 17.9k | } |
216 | 78.0k | instance->next->value.number = fxDateClip(fxToNumber(the, slot)); |
217 | 78.0k | return; |
218 | 95.9k | } |
219 | 2.39k | if (secure) |
220 | 0 | mxTypeError("secure mode"); |
221 | 2.39k | instance->next->value.number = fxDateNow(); |
222 | 2.39k | } |
223 | | |
224 | | void fx_Date_secure(txMachine* the) |
225 | 0 | { |
226 | 0 | fx_Date_aux(the, 1); |
227 | 0 | } |
228 | | |
229 | | void fx_Date_now(txMachine* the) |
230 | 3 | { |
231 | 3 | mxResult->value.number = fxDateNow(); |
232 | 3 | mxResult->kind = XS_NUMBER_KIND; |
233 | 3 | } |
234 | | |
235 | | void fx_Date_now_secure(txMachine* the) |
236 | 0 | { |
237 | 0 | mxTypeError("secure mode"); |
238 | 0 | } |
239 | | |
240 | | txInteger fx_Date_parse_number(txByte* theCharacter, txString* theString, txBoolean* overflow) |
241 | 639k | { |
242 | 639k | txByte c = *theCharacter; |
243 | 639k | txString p = *theString; |
244 | 639k | txInteger aResult = c - '0'; |
245 | 639k | c = c_read8(p++); |
246 | 1.43M | while (('0' <= c) && (c <= '9')) { |
247 | 796k | #if __has_builtin(__builtin_add_overflow) && __has_builtin(__builtin_mul_overflow) |
248 | 796k | if (__builtin_mul_overflow(aResult, 10, &aResult) || |
249 | 769k | __builtin_add_overflow(aResult, c - '0', &aResult)) |
250 | 37.0k | *overflow = 1; |
251 | | #else |
252 | | aResult = (aResult * 10) + c - '0'; |
253 | | if (aResult < 0) |
254 | | *overflow = 1; |
255 | | #endif |
256 | 796k | c = c_read8(p++); |
257 | 796k | } |
258 | 639k | *theCharacter = c; |
259 | 639k | *theString = p; |
260 | 639k | return aResult; |
261 | 639k | } |
262 | | |
263 | | txInteger fx_Date_parse_fraction(txByte* theCharacter, txString* theString) |
264 | 3.16k | { |
265 | 3.16k | txByte c = *theCharacter; |
266 | 3.16k | txString p = *theString; |
267 | 3.16k | txNumber fraction = 100; |
268 | 3.16k | txNumber aResult = ((c - '0') * fraction); |
269 | 3.16k | c = c_read8(p++); |
270 | 8.93k | while (('0' <= c) && (c <= '9')) { |
271 | 5.76k | fraction /= 10; |
272 | 5.76k | aResult = aResult + ((c - '0') * fraction); |
273 | 5.76k | c = c_read8(p++); |
274 | 5.76k | } |
275 | 3.16k | *theCharacter = c; |
276 | 3.16k | *theString = p; |
277 | 3.16k | return (txInteger)c_trunc(aResult); |
278 | 3.16k | } |
279 | | |
280 | | void fx_Date_parse(txMachine* the) |
281 | 551k | { |
282 | 958k | #define mxDayCount 7 |
283 | 551k | static const char* const gxDays[mxDayCount] ICACHE_RODATA_ATTR = { |
284 | 551k | "monday", |
285 | 551k | "tuesday", |
286 | 551k | "wednesday", |
287 | 551k | "thursday", |
288 | 551k | "friday", |
289 | 551k | "saturday", |
290 | 551k | "sunday" |
291 | 551k | }; |
292 | 1.34M | #define mxMonthCount 12 |
293 | 551k | static const char* const gxMonths[mxMonthCount] ICACHE_RODATA_ATTR = { |
294 | 551k | "january", |
295 | 551k | "february", |
296 | 551k | "march", |
297 | 551k | "april", |
298 | 551k | "may", |
299 | 551k | "june", |
300 | 551k | "july", |
301 | 551k | "august", |
302 | 551k | "september", |
303 | 551k | "october", |
304 | 551k | "november", |
305 | 551k | "december" |
306 | 551k | }; |
307 | 1.15M | #define mxZoneCount 11 |
308 | 551k | static const char* const gxZones[mxZoneCount] ICACHE_RODATA_ATTR = { |
309 | 551k | "gmt", "ut", "utc", |
310 | 551k | "est", "edt", |
311 | 551k | "cst", "cdt", |
312 | 551k | "mst", "mdt", |
313 | 551k | "pst", "pdt" |
314 | 551k | }; |
315 | 551k | static const int gxDeltas[mxZoneCount] ICACHE_XS6RO2_ATTR = { |
316 | 551k | 0, 0, 0, |
317 | 551k | -5, -4, |
318 | 551k | -6, -5, |
319 | 551k | -7, -6, |
320 | 551k | -8, -7 |
321 | 551k | }; |
322 | | |
323 | 551k | txString aString; |
324 | 551k | txBoolean overflow = 0; |
325 | 551k | txDateTime dt; |
326 | 551k | txString p; |
327 | 551k | txString q; |
328 | 551k | txByte c; |
329 | 551k | char buffer[10]; /* base type should be the same as txString */ |
330 | 551k | txInteger aComment; |
331 | 551k | txInteger aDelta; |
332 | 551k | txInteger aValue; |
333 | 551k | txSize aLength; |
334 | 551k | txInteger i; |
335 | 551k | txInteger yearSign = 1; |
336 | | |
337 | 551k | if (mxArgc < 1) |
338 | 1 | goto fail; |
339 | 551k | aString = fxToString(the, mxArgv(0)); |
340 | | |
341 | 551k | dt.seconds = -1; |
342 | 551k | dt.minutes = -1; |
343 | 551k | dt.hours = -1; |
344 | 551k | dt.date = -1; |
345 | 551k | dt.month = -1; |
346 | 551k | dt.year = -1; |
347 | 551k | dt.milliseconds = -1; |
348 | 551k | aComment = 0; |
349 | 551k | aDelta = -1; |
350 | | |
351 | 551k | c = c_read8(aString++); |
352 | 8.70M | while (c) { |
353 | 8.69M | if (c == '(') { |
354 | 1.93k | aComment++; |
355 | 1.93k | c = c_read8(aString++); |
356 | 1.93k | continue; |
357 | 1.93k | } |
358 | 8.68M | else if (c == ')') { |
359 | 10.3k | if (aComment) { |
360 | 1.06k | aComment--; |
361 | 1.06k | c = c_read8(aString++); |
362 | 1.06k | continue; |
363 | 1.06k | } |
364 | 9.28k | else |
365 | 9.28k | goto fail; |
366 | 10.3k | } |
367 | 8.67M | else if (aComment) { |
368 | 124k | c = c_read8(aString++); |
369 | 124k | continue; |
370 | 124k | } |
371 | | |
372 | 8.55M | if ((c <= ' ') || (c == ',')) { |
373 | 7.63M | c = c_read8(aString++); |
374 | 7.63M | continue; |
375 | 7.63M | } |
376 | | |
377 | 923k | else if ((c == '-') | (c == '+')) { |
378 | 137k | txInteger aSign; |
379 | 137k | if (c == '-') |
380 | 132k | aSign = -1; |
381 | 4.75k | else |
382 | 4.75k | aSign = 1; |
383 | 137k | c = c_read8(aString++); |
384 | 137k | if (('0' <= c) && (c <= '9')) { |
385 | 29.6k | aValue = fx_Date_parse_number(&c, &aString, &overflow); |
386 | 29.6k | if (c == '-') { |
387 | 10.0k | if (dt.year >= 0) |
388 | 1.18k | goto fail; |
389 | 8.84k | dt.year = aValue; |
390 | 8.84k | yearSign = aSign; |
391 | 8.84k | c = c_read8(aString++); |
392 | 8.84k | if (('0' <= c) && (c <= '9')) { |
393 | 7.12k | dt.month = fx_Date_parse_number(&c, &aString, &overflow) - 1; |
394 | 7.12k | if (c == '-') { |
395 | 4.22k | c = c_read8(aString++); |
396 | 4.22k | if (('0' <= c) && (c <= '9')) |
397 | 3.35k | dt.date = fx_Date_parse_number(&c, &aString, &overflow); |
398 | 878 | else |
399 | 878 | dt.date = 1; |
400 | 4.22k | } |
401 | 7.12k | } |
402 | 1.71k | else |
403 | 1.71k | dt.month = 0; |
404 | 8.84k | } |
405 | 19.6k | else { |
406 | 19.6k | if ((aDelta != 0) && (aDelta != -1)) |
407 | 2.53k | goto fail; |
408 | 17.0k | if (c == ':') { |
409 | 3.44k | aDelta = 60 * aValue; |
410 | 3.44k | c = c_read8(aString++); |
411 | 3.44k | if (('0' <= c) && (c <= '9')) { |
412 | 678 | aDelta += fx_Date_parse_number(&c, &aString, &overflow); |
413 | 678 | } |
414 | 3.44k | } |
415 | 13.6k | else { |
416 | 13.6k | if (aValue < 24) |
417 | 8.43k | aDelta = aValue * 60; |
418 | 5.19k | else |
419 | 5.19k | aDelta = (aValue % 100) + ((aValue / 100) * 60); |
420 | 13.6k | } |
421 | 17.0k | aDelta *= aSign; |
422 | 17.0k | } |
423 | 29.6k | } |
424 | 107k | else |
425 | 107k | goto fail; |
426 | 137k | } |
427 | 786k | else if (('0' <= c) && (c <= '9')) { |
428 | 308k | aValue = fx_Date_parse_number(&c, &aString, &overflow); |
429 | 308k | if (c == ':') { |
430 | 76.1k | if (dt.hours >= 0) |
431 | 4.02k | goto fail; |
432 | 72.1k | dt.hours = aValue; |
433 | 72.1k | c = c_read8(aString++); |
434 | 72.1k | if (('0' <= c) && (c <= '9')) { |
435 | 58.6k | dt.minutes = fx_Date_parse_number(&c, &aString, &overflow); |
436 | 58.6k | if (c == ':') { |
437 | 54.9k | c = c_read8(aString++); |
438 | 54.9k | if (('0' <= c) && (c <= '9')) { |
439 | 52.1k | dt.seconds = fx_Date_parse_number(&c, &aString, &overflow); |
440 | 52.1k | if (c == '.') { |
441 | 5.02k | c = c_read8(aString++); |
442 | 5.02k | if (('0' <= c) && (c <= '9')) { |
443 | 3.16k | dt.milliseconds = fx_Date_parse_fraction(&c, &aString); |
444 | 3.16k | } |
445 | 5.02k | } |
446 | 52.1k | } |
447 | 2.83k | else |
448 | 2.83k | dt.seconds = 0; |
449 | 54.9k | } |
450 | 58.6k | } |
451 | 13.5k | else |
452 | 13.5k | dt.seconds = 0; |
453 | 72.1k | } |
454 | 232k | else if (c == '/') { |
455 | 4.68k | if (dt.year >= 0) |
456 | 212 | goto fail; |
457 | 4.47k | dt.year = /*(aValue < 100) ? aValue + 1900 :*/ aValue; |
458 | 4.47k | c = c_read8(aString++); |
459 | 4.47k | if (('0' <= c) && (c <= '9')) { |
460 | 3.42k | dt.month = fx_Date_parse_number(&c, &aString, &overflow) - 1; |
461 | 3.42k | if (c == '/') { |
462 | 1.48k | c = c_read8(aString++); |
463 | 1.48k | if (('0' <= c) && (c <= '9')) { |
464 | 1.19k | dt.date = fx_Date_parse_number(&c, &aString, &overflow); |
465 | 1.19k | } |
466 | 288 | else |
467 | 288 | dt.date = 1; |
468 | 1.48k | } |
469 | 3.42k | } |
470 | 1.04k | else |
471 | 1.04k | dt.month = 0; |
472 | 4.47k | } |
473 | 227k | else if (c == '-') { |
474 | 176k | if (dt.year >= 0) |
475 | 643 | goto fail; |
476 | 175k | dt.year = /*(aValue < 100) ? aValue + 1900 :*/ aValue; |
477 | 175k | c = c_read8(aString++); |
478 | 175k | if (('0' <= c) && (c <= '9')) { |
479 | 173k | dt.month = fx_Date_parse_number(&c, &aString, &overflow) - 1; |
480 | 173k | if (c == '-') { |
481 | 1.40k | c = c_read8(aString++); |
482 | 1.40k | if (('0' <= c) && (c <= '9')) |
483 | 1.07k | dt.date = fx_Date_parse_number(&c, &aString, &overflow); |
484 | 336 | else |
485 | 336 | dt.date = 1; |
486 | 1.40k | } |
487 | 173k | } |
488 | 2.64k | else |
489 | 2.64k | dt.month = 0; |
490 | 175k | } |
491 | 51.4k | else { |
492 | 51.4k | if (aValue < 70) { |
493 | 35.2k | if (dt.date < 0) |
494 | 26.6k | dt.date = aValue; |
495 | 8.60k | else |
496 | 8.60k | goto fail; |
497 | 35.2k | } |
498 | 16.1k | else { |
499 | 16.1k | if (dt.year < 0) |
500 | 13.1k | dt.year = /*(aValue < 100) ? aValue + 1900 :*/ aValue; |
501 | 2.93k | else |
502 | 2.93k | goto fail; |
503 | 16.1k | } |
504 | 51.4k | } |
505 | 308k | } |
506 | 477k | else if ((('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z'))) { |
507 | 310k | txSize cmpLength; |
508 | 310k | p = buffer; |
509 | 310k | q = p + sizeof(buffer) - 1; |
510 | 2.11M | do { |
511 | 2.11M | if (p == q) goto fail; |
512 | 1.92M | *p++ = (c >= 'a') ? c : (c + ('a' - 'A')); |
513 | 1.92M | c = c_read8(aString++); |
514 | 1.92M | } while ((('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z'))); |
515 | 122k | *p = 0; |
516 | 122k | aLength = mxPtrDiff(p - (txString)buffer); |
517 | 122k | cmpLength = (aLength >= 3) ? aLength : 3; |
518 | 122k | if (c_strcmp("am", buffer) == 0) { |
519 | 7.89k | if ((dt.hours < 0) || (12 < dt.hours)) |
520 | 4.27k | goto fail; |
521 | 3.62k | if (dt.hours == 12) |
522 | 1.59k | dt.hours = 0; |
523 | 3.62k | continue; |
524 | 7.89k | } |
525 | 114k | if (c_strcmp("pm", buffer) == 0) { |
526 | 3.85k | if ((dt.hours < 0) || (12 < dt.hours)) |
527 | 1.74k | goto fail; |
528 | 2.10k | if (dt.hours != 12) |
529 | 1.34k | dt.hours += 12; |
530 | 2.10k | continue; |
531 | 3.85k | } |
532 | 847k | for (i = 0; i < mxDayCount; i++) |
533 | 746k | if (c_strncmp(gxDays[i], buffer, cmpLength) == 0) |
534 | 9.92k | break; |
535 | 110k | if (i < mxDayCount) |
536 | 9.92k | continue; |
537 | 1.24M | for (i = 0; i < mxMonthCount; i++) |
538 | 1.15M | if (c_strncmp(gxMonths[i], buffer, cmpLength) == 0) |
539 | 10.0k | break; |
540 | 100k | if (i < mxMonthCount) { |
541 | 10.0k | if (dt.month < 0) { |
542 | 9.93k | dt.month = i; |
543 | 9.93k | continue; |
544 | 9.93k | } |
545 | 127 | else |
546 | 127 | goto fail; |
547 | 10.0k | } |
548 | 1.06M | for (i = 0; i < mxZoneCount; i++) |
549 | 979k | if (c_strcmp(gxZones[i], buffer) == 0) |
550 | 2.08k | break; |
551 | 90.6k | if (i < mxZoneCount) { |
552 | 2.08k | if (aDelta == -1) { |
553 | 486 | aDelta = gxDeltas[i] * 60; |
554 | 486 | continue; |
555 | 486 | } |
556 | 1.59k | else |
557 | 1.59k | goto fail; |
558 | 2.08k | } |
559 | 88.5k | if (c_strcmp("t", buffer) == 0) { |
560 | 45.5k | if (dt.year < 0) |
561 | 300 | goto fail; |
562 | 45.2k | continue; |
563 | 45.5k | } |
564 | 43.0k | if (c_strcmp("z", buffer) == 0) { |
565 | 10.4k | if (dt.hours < 0) |
566 | 3.10k | goto fail; |
567 | 7.37k | aDelta = 0; |
568 | 7.37k | continue; |
569 | 10.4k | } |
570 | 32.5k | goto fail; |
571 | 43.0k | } |
572 | 167k | else |
573 | 167k | goto fail; |
574 | 8.55M | } |
575 | 15.1k | if (overflow) |
576 | 1.19k | goto fail; |
577 | 13.9k | if (dt.year < 0) |
578 | 3.91k | goto fail; |
579 | 10.0k | if ((yearSign < 0) && (dt.year == 0)) |
580 | 280 | goto fail; |
581 | 9.76k | if (dt.month < 0) |
582 | 4.35k | dt.month = 0; |
583 | 9.76k | if (dt.date < 0) |
584 | 6.56k | dt.date = 1; |
585 | 9.76k | if ((aDelta < 0) && (dt.hours < 0) && (dt.minutes < 0) && (dt.seconds < 0) && (dt.milliseconds < 0)) |
586 | 3.77k | aDelta = 0; |
587 | 9.76k | if (dt.hours < 0) |
588 | 3.90k | dt.hours = 0; |
589 | 9.76k | if (dt.minutes < 0) |
590 | 6.79k | dt.minutes = 0; |
591 | 9.76k | if (dt.seconds < 0) |
592 | 5.68k | dt.seconds = 0; |
593 | 9.76k | if (dt.milliseconds < 0) |
594 | 8.77k | dt.milliseconds = 0; |
595 | 9.76k | dt.year *= yearSign; |
596 | 9.76k | mxResult->value.number = fxDateMerge(&dt, (aDelta != -1) ? 1 : 0); |
597 | 9.76k | if (aDelta != -1) |
598 | 7.33k | mxResult->value.number -= (txNumber)aDelta * 60000.0; |
599 | 9.76k | mxResult->kind = XS_NUMBER_KIND; |
600 | 9.76k | return; |
601 | 541k | fail: |
602 | 541k | mxResult->value.number = C_NAN; |
603 | 541k | mxResult->kind = XS_NUMBER_KIND; |
604 | | //mxSyntaxError("invalid parameter"); |
605 | 541k | } |
606 | | |
607 | | void fx_Date_UTC(txMachine* the) |
608 | 95.1k | { |
609 | 95.1k | txInteger c = mxArgc; |
610 | 95.1k | txDateTime dt; |
611 | 95.1k | dt.year = (c > 0) ? fxDateFullYear(the, mxArgv(0)) : C_NAN; |
612 | 95.1k | dt.month = (c > 1) ? fxToNumber(the, mxArgv(1)) : 0; |
613 | 95.1k | dt.date = (c > 2) ? fxToNumber(the, mxArgv(2)) : 1; |
614 | 95.1k | dt.hours = (c > 3) ? fxToNumber(the, mxArgv(3)) : 0; |
615 | 95.1k | dt.minutes = (c > 4) ? fxToNumber(the, mxArgv(4)) : 0; |
616 | 95.1k | dt.seconds = (c > 5) ?fxToNumber(the, mxArgv(5)) : 0; |
617 | 95.1k | dt.milliseconds = (c > 6) ? fxToNumber(the, mxArgv(6)) : 0; |
618 | 95.1k | mxResult->value.number = fxDateMerge(&dt, 1); |
619 | 95.1k | mxResult->kind = XS_NUMBER_KIND; |
620 | 95.1k | } |
621 | | |
622 | | txBoolean fx_Date_prototype_get_aux(txMachine* the, txDateTime* dt, txBoolean utc, txSlot* slot) |
623 | 147k | { |
624 | 147k | txNumber number; |
625 | | #if mxAliasInstance |
626 | | txSlot* instance = mxThis->value.reference; |
627 | | if (instance->ID) { |
628 | | txSlot* alias = the->aliasArray[instance->ID]; |
629 | | if (alias) { |
630 | | instance = alias; |
631 | | slot = instance->next; |
632 | | } |
633 | | } |
634 | | #endif |
635 | 147k | number = slot->value.number; |
636 | 147k | if (c_isnan(number)) { |
637 | 101k | mxResult->value.number = C_NAN; |
638 | 101k | mxResult->kind = XS_NUMBER_KIND; |
639 | 101k | dt->value = C_NAN; |
640 | 101k | return 0; |
641 | 101k | } |
642 | 46.7k | fxDateSplit(slot->value.number, utc, dt); |
643 | 46.7k | return 1; |
644 | 147k | } |
645 | | |
646 | | void fx_Date_prototype_set_aux(txMachine* the, txDateTime* dt, txBoolean utc, txSlot* slot) |
647 | 393 | { |
648 | 393 | txNumber number = dt->value; |
649 | | #if mxAliasInstance |
650 | | txSlot* instance = mxThis->value.reference; |
651 | | if (instance->ID) { |
652 | | txSlot* alias = the->aliasArray[instance->ID]; |
653 | | if (alias) |
654 | | instance = alias; |
655 | | else |
656 | | instance = fxAliasInstance(the, instance); |
657 | | slot = instance->next; |
658 | | } |
659 | | #endif |
660 | 393 | if (c_isnan(number)) |
661 | 81 | return; |
662 | 312 | if (slot->flag & XS_DONT_SET_FLAG) |
663 | 0 | mxTypeError("this: read-only Date instance"); |
664 | 312 | mxResult->value.number = slot->value.number = fxDateMerge(dt, utc); |
665 | 312 | mxResult->kind = XS_NUMBER_KIND; |
666 | 312 | } |
667 | | |
668 | | void fx_Date_prototype_getMilliseconds(txMachine* the) |
669 | 56 | { |
670 | 56 | txDateTime dt; |
671 | 56 | txSlot* slot = fxDateCheck(the); |
672 | 56 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
673 | 14 | mxResult->value.integer = (txInteger)dt.milliseconds; |
674 | 14 | mxResult->kind = XS_INTEGER_KIND; |
675 | 14 | } |
676 | 56 | } |
677 | | |
678 | | void fx_Date_prototype_getSeconds(txMachine* the) |
679 | 28 | { |
680 | 28 | txDateTime dt; |
681 | 28 | txSlot* slot = fxDateCheck(the); |
682 | 28 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
683 | 9 | mxResult->value.integer = (txInteger)dt.seconds; |
684 | 9 | mxResult->kind = XS_INTEGER_KIND; |
685 | 9 | } |
686 | 28 | } |
687 | | |
688 | | void fx_Date_prototype_getMinutes(txMachine* the) |
689 | 39 | { |
690 | 39 | txDateTime dt; |
691 | 39 | txSlot* slot = fxDateCheck(the); |
692 | 39 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
693 | 21 | mxResult->value.integer = (txInteger)dt.minutes; |
694 | 21 | mxResult->kind = XS_INTEGER_KIND; |
695 | 21 | } |
696 | 39 | } |
697 | | |
698 | | void fx_Date_prototype_getHours(txMachine* the) |
699 | 49 | { |
700 | 49 | txDateTime dt; |
701 | 49 | txSlot* slot = fxDateCheck(the); |
702 | 49 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
703 | 23 | mxResult->value.integer = (txInteger)dt.hours; |
704 | 23 | mxResult->kind = XS_INTEGER_KIND; |
705 | 23 | } |
706 | 49 | } |
707 | | |
708 | | void fx_Date_prototype_getDay(txMachine* the) |
709 | 76.0k | { |
710 | 76.0k | txDateTime dt; |
711 | 76.0k | txSlot* slot = fxDateCheck(the); |
712 | 76.0k | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
713 | 20 | mxResult->value.integer = dt.day; |
714 | 20 | mxResult->kind = XS_INTEGER_KIND; |
715 | 20 | } |
716 | 76.0k | } |
717 | | |
718 | | void fx_Date_prototype_getDate(txMachine* the) |
719 | 73 | { |
720 | 73 | txDateTime dt; |
721 | 73 | txSlot* slot = fxDateCheck(the); |
722 | 73 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
723 | 18 | mxResult->value.integer = (txInteger)dt.date; |
724 | 18 | mxResult->kind = XS_INTEGER_KIND; |
725 | 18 | } |
726 | 73 | } |
727 | | |
728 | | void fx_Date_prototype_getMonth(txMachine* the) |
729 | 38 | { |
730 | 38 | txDateTime dt; |
731 | 38 | txSlot* slot = fxDateCheck(the); |
732 | 38 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
733 | 20 | mxResult->value.integer = (txInteger)dt.month; |
734 | 20 | mxResult->kind = XS_INTEGER_KIND; |
735 | 20 | } |
736 | 38 | } |
737 | | |
738 | | void fx_Date_prototype_getYear(txMachine* the) |
739 | 2 | { |
740 | 2 | txDateTime dt; |
741 | 2 | txSlot* slot = fxDateCheck(the); |
742 | 2 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
743 | 1 | mxResult->value.integer = (txInteger)dt.year - 1900; |
744 | 1 | mxResult->kind = XS_INTEGER_KIND; |
745 | 1 | } |
746 | 2 | } |
747 | | |
748 | | void fx_Date_prototype_getFullYear(txMachine* the) |
749 | 30 | { |
750 | 30 | txDateTime dt; |
751 | 30 | txSlot* slot = fxDateCheck(the); |
752 | 30 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
753 | 12 | mxResult->value.integer = (txInteger)dt.year; |
754 | 12 | mxResult->kind = XS_INTEGER_KIND; |
755 | 12 | } |
756 | 30 | } |
757 | | |
758 | | void fx_Date_prototype_getUTCMilliseconds(txMachine* the) |
759 | 74 | { |
760 | 74 | txDateTime dt; |
761 | 74 | txSlot* slot = fxDateCheck(the); |
762 | 74 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
763 | 11 | mxResult->value.integer = (txInteger)dt.milliseconds; |
764 | 11 | mxResult->kind = XS_INTEGER_KIND; |
765 | 11 | } |
766 | 74 | } |
767 | | |
768 | | void fx_Date_prototype_getUTCSeconds(txMachine* the) |
769 | 604 | { |
770 | 604 | txDateTime dt; |
771 | 604 | txSlot* slot = fxDateCheck(the); |
772 | 604 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
773 | 20 | mxResult->value.integer = (txInteger)dt.seconds; |
774 | 20 | mxResult->kind = XS_INTEGER_KIND; |
775 | 20 | } |
776 | 604 | } |
777 | | |
778 | | void fx_Date_prototype_getUTCMinutes(txMachine* the) |
779 | 44 | { |
780 | 44 | txDateTime dt; |
781 | 44 | txSlot* slot = fxDateCheck(the); |
782 | 44 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
783 | 18 | mxResult->value.integer = (txInteger)dt.minutes; |
784 | 18 | mxResult->kind = XS_INTEGER_KIND; |
785 | 18 | } |
786 | 44 | } |
787 | | |
788 | | void fx_Date_prototype_getUTCHours(txMachine* the) |
789 | 41 | { |
790 | 41 | txDateTime dt; |
791 | 41 | txSlot* slot = fxDateCheck(the); |
792 | 41 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
793 | 18 | mxResult->value.integer = (txInteger)dt.hours; |
794 | 18 | mxResult->kind = XS_INTEGER_KIND; |
795 | 18 | } |
796 | 41 | } |
797 | | |
798 | | void fx_Date_prototype_getUTCDay(txMachine* the) |
799 | 59 | { |
800 | 59 | txDateTime dt; |
801 | 59 | txSlot* slot = fxDateCheck(the); |
802 | 59 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
803 | 31 | mxResult->value.integer = dt.day; |
804 | 31 | mxResult->kind = XS_INTEGER_KIND; |
805 | 31 | } |
806 | 59 | } |
807 | | |
808 | | void fx_Date_prototype_getUTCDate(txMachine* the) |
809 | 54 | { |
810 | 54 | txDateTime dt; |
811 | 54 | txSlot* slot = fxDateCheck(the); |
812 | 54 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
813 | 23 | mxResult->value.integer = (txInteger)dt.date; |
814 | 23 | mxResult->kind = XS_INTEGER_KIND; |
815 | 23 | } |
816 | 54 | } |
817 | | |
818 | | void fx_Date_prototype_getUTCMonth(txMachine* the) |
819 | 42 | { |
820 | 42 | txDateTime dt; |
821 | 42 | txSlot* slot = fxDateCheck(the); |
822 | 42 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
823 | 18 | mxResult->value.integer = (txInteger)dt.month; |
824 | 18 | mxResult->kind = XS_INTEGER_KIND; |
825 | 18 | } |
826 | 42 | } |
827 | | |
828 | | void fx_Date_prototype_getUTCFullYear(txMachine* the) |
829 | 47 | { |
830 | 47 | txDateTime dt; |
831 | 47 | txSlot* slot = fxDateCheck(the); |
832 | 47 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
833 | 13 | mxResult->value.integer = (txInteger)dt.year; |
834 | 13 | mxResult->kind = XS_INTEGER_KIND; |
835 | 13 | } |
836 | 47 | } |
837 | | |
838 | | void fx_Date_prototype_getTimezoneOffset(txMachine* the) |
839 | 52 | { |
840 | 52 | txDateTime dt; |
841 | 52 | txSlot* slot = fxDateCheck(the); |
842 | 52 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
843 | 13 | mxResult->value.integer = 0 - dt.offset; |
844 | 13 | mxResult->kind = XS_INTEGER_KIND; |
845 | 13 | } |
846 | 52 | } |
847 | | |
848 | | void fx_Date_prototype_setMilliseconds(txMachine* the) |
849 | 44 | { |
850 | 44 | txInteger c = mxArgc; |
851 | 44 | txDateTime dt; |
852 | 44 | txSlot* slot = fxDateCheck(the); |
853 | 44 | fx_Date_prototype_get_aux(the, &dt, 0, slot); |
854 | 44 | dt.milliseconds = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
855 | 44 | fx_Date_prototype_set_aux(the, &dt, 0, slot); |
856 | 44 | } |
857 | | |
858 | | void fx_Date_prototype_setSeconds(txMachine* the) |
859 | 63 | { |
860 | 63 | txInteger c = mxArgc; |
861 | 63 | txDateTime dt; |
862 | 63 | txSlot* slot = fxDateCheck(the); |
863 | 63 | fx_Date_prototype_get_aux(the, &dt, 0, slot); |
864 | 63 | dt.seconds = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
865 | 63 | if (c > 1) dt.milliseconds = fxToNumber(the, mxArgv(1)); |
866 | 63 | fx_Date_prototype_set_aux(the, &dt, 0, slot); |
867 | 63 | } |
868 | | |
869 | | void fx_Date_prototype_setMinutes(txMachine* the) |
870 | 63 | { |
871 | 63 | txInteger c = mxArgc; |
872 | 63 | txDateTime dt; |
873 | 63 | txSlot* slot = fxDateCheck(the); |
874 | 63 | fx_Date_prototype_get_aux(the, &dt, 0, slot); |
875 | 63 | dt.minutes = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
876 | 63 | if (c > 1) dt.seconds = fxToNumber(the, mxArgv(1)); |
877 | 63 | if (c > 2) dt.milliseconds = fxToNumber(the, mxArgv(2)); |
878 | 63 | fx_Date_prototype_set_aux(the, &dt, 0, slot); |
879 | 63 | } |
880 | | |
881 | | void fx_Date_prototype_setHours(txMachine* the) |
882 | 71 | { |
883 | 71 | txInteger c = mxArgc; |
884 | 71 | txDateTime dt; |
885 | 71 | txSlot* slot = fxDateCheck(the); |
886 | 71 | fx_Date_prototype_get_aux(the, &dt, 0, slot); |
887 | 71 | dt.hours = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
888 | 71 | if (c > 1) dt.minutes = fxToNumber(the, mxArgv(1)); |
889 | 71 | if (c > 2) dt.seconds = fxToNumber(the, mxArgv(2)); |
890 | 71 | if (c > 3) dt.milliseconds = fxToNumber(the, mxArgv(3)); |
891 | 71 | fx_Date_prototype_set_aux(the, &dt, 0, slot); |
892 | 71 | } |
893 | | |
894 | | void fx_Date_prototype_setDate(txMachine* the) |
895 | 38 | { |
896 | 38 | txInteger c = mxArgc; |
897 | 38 | txDateTime dt; |
898 | 38 | txSlot* slot = fxDateCheck(the); |
899 | 38 | fx_Date_prototype_get_aux(the, &dt, 0, slot); |
900 | 38 | dt.date = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
901 | 38 | fx_Date_prototype_set_aux(the, &dt, 0, slot); |
902 | 38 | } |
903 | | |
904 | | void fx_Date_prototype_setMonth(txMachine* the) |
905 | 60 | { |
906 | 60 | txInteger c = mxArgc; |
907 | 60 | txDateTime dt; |
908 | 60 | txSlot* slot = fxDateCheck(the); |
909 | 60 | fx_Date_prototype_get_aux(the, &dt, 0, slot); |
910 | 60 | dt.month = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
911 | 60 | if (c > 1) dt.date = fxToNumber(the, mxArgv(1)); |
912 | 60 | fx_Date_prototype_set_aux(the, &dt, 0, slot); |
913 | 60 | } |
914 | | |
915 | | void fx_Date_prototype_setYear(txMachine* the) |
916 | 0 | { |
917 | 0 | txInteger c = mxArgc; |
918 | 0 | txDateTime dt; |
919 | 0 | txSlot* slot = fxDateCheck(the); |
920 | 0 | if (c_isnan(slot->value.number)) { |
921 | 0 | slot->value.number = 0; |
922 | 0 | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
923 | 0 | } |
924 | 0 | else |
925 | 0 | fx_Date_prototype_get_aux(the, &dt, 0, slot); |
926 | 0 | dt.year = (mxArgc > 0) ? fxDateFullYear(the, mxArgv(0)) : C_NAN; |
927 | 0 | if (c > 1) dt.month = fxToNumber(the, mxArgv(1)); |
928 | 0 | if (c > 2) dt.date = fxToNumber(the, mxArgv(2)); |
929 | 0 | fx_Date_prototype_set_aux(the, &dt, 0, slot); |
930 | 0 | } |
931 | | |
932 | | void fx_Date_prototype_setFullYear(txMachine* the) |
933 | 1.90k | { |
934 | 1.90k | txInteger c = mxArgc; |
935 | 1.90k | txDateTime dt; |
936 | 1.90k | txSlot* slot = fxDateCheck(the); |
937 | 1.90k | if (c_isnan(slot->value.number)) { |
938 | 9 | slot->value.number = 0; |
939 | 9 | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
940 | 9 | } |
941 | 1.89k | else |
942 | 1.89k | fx_Date_prototype_get_aux(the, &dt, 0, slot); |
943 | 1.90k | dt.year = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
944 | 1.90k | if (c > 1) dt.month = fxToNumber(the, mxArgv(1)); |
945 | 1.90k | if (c > 2) dt.date = fxToNumber(the, mxArgv(2)); |
946 | 1.90k | fx_Date_prototype_set_aux(the, &dt, 0, slot); |
947 | 1.90k | } |
948 | | |
949 | | void fx_Date_prototype_setTime(txMachine* the) |
950 | 69 | { |
951 | 69 | txSlot* slot = fxDateCheck(the); |
952 | 69 | if (slot->flag & XS_DONT_SET_FLAG) |
953 | 0 | mxTypeError("this: read-only Date instance"); |
954 | 69 | if (mxArgc < 1) |
955 | 2 | slot->value.number = C_NAN; |
956 | 67 | else { |
957 | 67 | txNumber number = fxToNumber(the, mxArgv(0)); |
958 | 67 | int fpclass = c_fpclassify(number); |
959 | 67 | if (fpclass != C_FP_NAN) { |
960 | 27 | if (c_fabs(number) > 8.64e15) |
961 | 1 | number = C_NAN; |
962 | 26 | else |
963 | 26 | number = c_trunc(number); |
964 | 27 | } |
965 | 67 | slot->value.number = number; |
966 | 67 | } |
967 | 69 | mxResult->value.number = slot->value.number; |
968 | 69 | mxResult->kind = XS_NUMBER_KIND; |
969 | 69 | } |
970 | | |
971 | | void fx_Date_prototype_setUTCMilliseconds(txMachine* the) |
972 | 13 | { |
973 | 13 | txInteger c = mxArgc; |
974 | 13 | txDateTime dt; |
975 | 13 | txSlot* slot = fxDateCheck(the); |
976 | 13 | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
977 | 13 | dt.milliseconds = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
978 | 13 | fx_Date_prototype_set_aux(the, &dt, 1, slot); |
979 | 13 | } |
980 | | |
981 | | void fx_Date_prototype_setUTCSeconds(txMachine* the) |
982 | 12 | { |
983 | 12 | txInteger c = mxArgc; |
984 | 12 | txDateTime dt; |
985 | 12 | txSlot* slot = fxDateCheck(the); |
986 | 12 | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
987 | 12 | dt.seconds = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
988 | 12 | if (c > 1) dt.milliseconds = fxToNumber(the, mxArgv(1)); |
989 | 12 | fx_Date_prototype_set_aux(the, &dt, 1, slot); |
990 | 12 | } |
991 | | |
992 | | void fx_Date_prototype_setUTCMinutes(txMachine* the) |
993 | 40 | { |
994 | 40 | txInteger c = mxArgc; |
995 | 40 | txDateTime dt; |
996 | 40 | txSlot* slot = fxDateCheck(the); |
997 | 40 | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
998 | 40 | dt.minutes = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
999 | 40 | if (c > 1) dt.seconds = fxToNumber(the, mxArgv(1)); |
1000 | 40 | if (c > 2) dt.milliseconds = fxToNumber(the, mxArgv(2)); |
1001 | 40 | fx_Date_prototype_set_aux(the, &dt, 1, slot); |
1002 | 40 | } |
1003 | | |
1004 | | void fx_Date_prototype_setUTCHours(txMachine* the) |
1005 | 28 | { |
1006 | 28 | txInteger c = mxArgc; |
1007 | 28 | txDateTime dt; |
1008 | 28 | txSlot* slot = fxDateCheck(the); |
1009 | 28 | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
1010 | 28 | dt.hours = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
1011 | 28 | if (c > 1) dt.minutes = fxToNumber(the, mxArgv(1)); |
1012 | 28 | if (c > 2) dt.seconds = fxToNumber(the, mxArgv(2)); |
1013 | 28 | if (c > 3) dt.milliseconds = fxToNumber(the, mxArgv(3)); |
1014 | 28 | fx_Date_prototype_set_aux(the, &dt, 1, slot); |
1015 | 28 | } |
1016 | | |
1017 | | void fx_Date_prototype_setUTCDate(txMachine* the) |
1018 | 3 | { |
1019 | 3 | txInteger c = mxArgc; |
1020 | 3 | txDateTime dt; |
1021 | 3 | txSlot* slot = fxDateCheck(the); |
1022 | 3 | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
1023 | 3 | dt.date = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
1024 | 3 | fx_Date_prototype_set_aux(the, &dt, 1, slot); |
1025 | 3 | } |
1026 | | |
1027 | | void fx_Date_prototype_setUTCMonth(txMachine* the) |
1028 | 1.85k | { |
1029 | 1.85k | txInteger c = mxArgc; |
1030 | 1.85k | txDateTime dt; |
1031 | 1.85k | txSlot* slot = fxDateCheck(the); |
1032 | 1.85k | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
1033 | 1.85k | dt.month = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
1034 | 1.85k | if (c > 1) dt.date = fxToNumber(the, mxArgv(1)); |
1035 | 1.85k | fx_Date_prototype_set_aux(the, &dt, 1, slot); |
1036 | 1.85k | } |
1037 | | |
1038 | | void fx_Date_prototype_setUTCFullYear(txMachine* the) |
1039 | 7 | { |
1040 | 7 | txInteger c = mxArgc; |
1041 | 7 | txDateTime dt; |
1042 | 7 | txSlot* slot = fxDateCheck(the); |
1043 | 7 | if (c_isnan(slot->value.number)) slot->value.number = 0; |
1044 | 7 | fx_Date_prototype_get_aux(the, &dt, 1, slot); |
1045 | 7 | dt.year = (c > 0) ? fxToNumber(the, mxArgv(0)) : C_NAN; |
1046 | 7 | if (c > 1) dt.month = fxToNumber(the, mxArgv(1)); |
1047 | 7 | if (c > 2) dt.date = fxToNumber(the, mxArgv(2)); |
1048 | 7 | fx_Date_prototype_set_aux(the, &dt, 1, slot); |
1049 | 7 | } |
1050 | | |
1051 | | void fx_Date_prototype_toDateString(txMachine* the) |
1052 | 4.61k | { |
1053 | 4.61k | char buffer[256]; |
1054 | 4.61k | txDateTime dt; |
1055 | 4.61k | txSlot* slot = fxDateCheck(the); |
1056 | 4.61k | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
1057 | 1.84k | txString p = buffer; |
1058 | 1.84k | p = fxDatePrintDay(p, dt.day); |
1059 | 1.84k | *p++ = ' '; |
1060 | 1.84k | p = fxDatePrintDate(p, (txInteger)dt.year, (txInteger)dt.month, (txInteger)dt.date); |
1061 | 1.84k | *p = 0; |
1062 | 1.84k | } |
1063 | 2.76k | else |
1064 | 2.76k | c_strcpy(buffer, "Invalid Date"); |
1065 | 4.61k | fxCopyStringC(the, mxResult, buffer); |
1066 | 4.61k | } |
1067 | | |
1068 | | void fx_Date_prototype_toISOString(txMachine* the) |
1069 | 22 | { |
1070 | 22 | char buffer[256]; |
1071 | 22 | txDateTime dt; |
1072 | 22 | txSlot* slot = fxDateCheck(the); |
1073 | 22 | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
1074 | 15 | txString p = buffer; |
1075 | 15 | p = fxDatePrintYear(p, (txInteger)dt.year); |
1076 | 15 | *p++ = '-'; |
1077 | 15 | p = fxDatePrint2Digits(p, (txInteger)dt.month + 1); |
1078 | 15 | *p++ = '-'; |
1079 | 15 | p = fxDatePrint2Digits(p, (txInteger)dt.date); |
1080 | 15 | *p++ = 'T'; |
1081 | 15 | p = fxDatePrintTime(p, (txInteger)dt.hours, (txInteger)dt.minutes, (txInteger)dt.seconds); |
1082 | 15 | *p++ = '.'; |
1083 | 15 | p = fxDatePrint3Digits(p, (txInteger)dt.milliseconds); |
1084 | 15 | *p++ = 'Z'; |
1085 | 15 | *p = 0; |
1086 | 15 | } |
1087 | 7 | else |
1088 | 7 | mxRangeError("Invalid Date"); |
1089 | 15 | fxCopyStringC(the, mxResult, buffer); |
1090 | 15 | } |
1091 | | |
1092 | | void fx_Date_prototype_toJSON(txMachine* the) |
1093 | 11 | { |
1094 | 11 | fxToInstance(the, mxThis); |
1095 | 11 | mxPushSlot(mxThis); |
1096 | 11 | fxToPrimitive(the, the->stack, XS_NUMBER_HINT); |
1097 | 11 | if ((the->stack->kind == XS_NUMBER_KIND) && !c_isfinite(the->stack->value.number)) { |
1098 | 3 | mxPop(); |
1099 | 3 | mxResult->kind = XS_NULL_KIND; |
1100 | 3 | } |
1101 | 8 | else { |
1102 | 8 | mxPop(); |
1103 | 8 | mxPushSlot(mxThis); |
1104 | 8 | mxDub(); |
1105 | 8 | mxGetID(mxID(_toISOString)); |
1106 | 8 | mxCall(); |
1107 | 8 | mxRunCount(0); |
1108 | 8 | mxPullSlot(mxResult); |
1109 | 8 | } |
1110 | 11 | } |
1111 | | |
1112 | | void fx_Date_prototype_toPrimitive(txMachine* the) |
1113 | 74.1k | { |
1114 | 74.1k | if (mxIsReference(mxThis)) { |
1115 | 74.1k | txInteger hint = XS_NO_HINT; |
1116 | 74.1k | txInteger ids[2], i; |
1117 | 74.1k | if (mxArgc > 0) { |
1118 | 74.1k | txSlot* slot = mxArgv(0); |
1119 | 74.1k | if ((slot->kind == XS_STRING_KIND) || (slot->kind == XS_STRING_X_KIND)) { |
1120 | 74.0k | if (!c_strcmp(slot->value.string, "default")) |
1121 | 60.2k | hint = XS_STRING_HINT; |
1122 | 13.8k | else if (!c_strcmp(slot->value.string, "number")) |
1123 | 13.7k | hint = XS_NUMBER_HINT; |
1124 | 83 | else if (!c_strcmp(slot->value.string, "string")) |
1125 | 73 | hint = XS_STRING_HINT; |
1126 | 74.0k | } |
1127 | 74.1k | } |
1128 | 74.1k | if (hint == XS_STRING_HINT) { |
1129 | 60.3k | ids[0] = mxID(_toString); |
1130 | 60.3k | ids[1] = mxID(_valueOf); |
1131 | 60.3k | } |
1132 | 13.7k | else if (hint == XS_NUMBER_HINT) { |
1133 | 13.7k | ids[0] = mxID(_valueOf); |
1134 | 13.7k | ids[1] = mxID(_toString); |
1135 | 13.7k | } |
1136 | 25 | else |
1137 | 25 | mxTypeError("invalid hint"); |
1138 | 74.0k | for (i = 0; i < 2; i++) { |
1139 | 74.0k | mxPushSlot(mxThis); |
1140 | 74.0k | mxPushSlot(mxThis); |
1141 | 74.0k | mxGetID(ids[i]); |
1142 | 74.0k | if (fxIsCallable(the, the->stack)) { |
1143 | 74.0k | mxCall(); |
1144 | 74.0k | mxRunCount(0); |
1145 | 74.0k | if (mxIsReference(the->stack)) |
1146 | 2 | mxPop(); |
1147 | 74.0k | else { |
1148 | 74.0k | mxPullSlot(mxResult); |
1149 | 74.0k | return; |
1150 | 74.0k | } |
1151 | 74.0k | } |
1152 | 5 | else { |
1153 | 5 | mxPop(); |
1154 | 5 | mxPop(); |
1155 | 5 | } |
1156 | 74.0k | } |
1157 | 3 | if (hint == XS_STRING_HINT) |
1158 | 2 | mxTypeError("cannot coerce object to string"); |
1159 | 1 | else |
1160 | 1 | mxTypeError("cannot coerce object to number"); |
1161 | 3 | } |
1162 | 11 | else { |
1163 | 11 | mxTypeError("invalid this"); |
1164 | 11 | } |
1165 | 74.1k | } |
1166 | | |
1167 | | void fx_Date_prototype_toString(txMachine* the) |
1168 | 60.3k | { |
1169 | 60.3k | char buffer[256]; |
1170 | 60.3k | txDateTime dt; |
1171 | 60.3k | txSlot* slot = fxDateCheck(the); |
1172 | 60.3k | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
1173 | 40.8k | txString p = buffer; |
1174 | 40.8k | p = fxDatePrintDay(p, dt.day); |
1175 | 40.8k | *p++ = ' '; |
1176 | 40.8k | p = fxDatePrintDate(p, (txInteger)dt.year, (txInteger)dt.month, (txInteger)dt.date); |
1177 | 40.8k | *p++ = ' '; |
1178 | 40.8k | p = fxDatePrintTime(p, (txInteger)dt.hours, (txInteger)dt.minutes, (txInteger)dt.seconds); |
1179 | 40.8k | *p++ = ' '; |
1180 | 40.8k | p = fxDatePrintTimezone(p, (txInteger)dt.offset); |
1181 | 40.8k | *p = 0; |
1182 | 40.8k | } |
1183 | 19.5k | else |
1184 | 19.5k | c_strcpy(buffer, "Invalid Date"); |
1185 | 60.3k | fxCopyStringC(the, mxResult, buffer); |
1186 | 60.3k | } |
1187 | | |
1188 | | void fx_Date_prototype_toTimeString(txMachine* the) |
1189 | 2 | { |
1190 | 2 | char buffer[256]; |
1191 | 2 | txDateTime dt; |
1192 | 2 | txSlot* slot = fxDateCheck(the); |
1193 | 2 | if (fx_Date_prototype_get_aux(the, &dt, 0, slot)) { |
1194 | 1 | txString p = buffer; |
1195 | 1 | p = fxDatePrintTime(p, (txInteger)dt.hours, (txInteger)dt.minutes, (txInteger)dt.seconds); |
1196 | 1 | *p++ = ' '; |
1197 | 1 | p = fxDatePrintTimezone(p, (txInteger)dt.offset); |
1198 | 1 | *p = 0; |
1199 | 1 | } |
1200 | 1 | else |
1201 | 1 | c_strcpy(buffer, "Invalid Date"); |
1202 | 2 | fxCopyStringC(the, mxResult, buffer); |
1203 | 2 | } |
1204 | | |
1205 | | void fx_Date_prototype_toUTCString(txMachine* the) |
1206 | 1.99k | { |
1207 | 1.99k | char buffer[256]; |
1208 | 1.99k | txDateTime dt; |
1209 | 1.99k | txSlot* slot = fxDateCheck(the); |
1210 | 1.99k | if (fx_Date_prototype_get_aux(the, &dt, 1, slot)) { |
1211 | 1.58k | txString p = buffer; |
1212 | 1.58k | p = fxDatePrintDay(p, dt.day); |
1213 | 1.58k | *p++ = ','; |
1214 | 1.58k | *p++ = ' '; |
1215 | 1.58k | p = fxDatePrintDateUTC(p, (txInteger)dt.year, (txInteger)dt.month, (txInteger)dt.date); |
1216 | 1.58k | *p++ = ' '; |
1217 | 1.58k | p = fxDatePrintTime(p, (txInteger)dt.hours, (txInteger)dt.minutes, (txInteger)dt.seconds); |
1218 | 1.58k | *p++ = ' '; |
1219 | 1.58k | *p++ = 'G'; |
1220 | 1.58k | *p++ = 'M'; |
1221 | 1.58k | *p++ = 'T'; |
1222 | 1.58k | *p = 0; |
1223 | 1.58k | } |
1224 | 409 | else |
1225 | 409 | c_strcpy(buffer, "Invalid Date"); |
1226 | 1.99k | fxCopyStringC(the, mxResult, buffer); |
1227 | 1.99k | } |
1228 | | |
1229 | | void fx_Date_prototype_valueOf(txMachine* the) |
1230 | 14.1k | { |
1231 | 14.1k | txSlot* slot = fxDateCheck(the); |
1232 | 14.1k | mxResult->kind = XS_NUMBER_KIND; |
1233 | 14.1k | mxResult->value = slot->value; |
1234 | 14.1k | } |
1235 | | |
1236 | | // Thanks Google V8 for the years offset |
1237 | | // Thanks Mozilla JS for the similar years |
1238 | | |
1239 | | static const txString gxDayNames[] ICACHE_XS6RO2_ATTR = { |
1240 | | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" |
1241 | | }; |
1242 | | static const txString gxMonthNames[] ICACHE_XS6RO2_ATTR = { |
1243 | | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
1244 | | }; |
1245 | | static const txInteger gxCommonYearMonthsDays[12] ICACHE_XS6RO2_ATTR = { |
1246 | | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 |
1247 | | }; |
1248 | | static const txInteger gxLeapYearMonthsDays[12] ICACHE_XS6RO2_ATTR = { |
1249 | | 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 |
1250 | | }; |
1251 | | static const txInteger gxSimilarCommonYears[7] ICACHE_XS6RO2_ATTR = { |
1252 | | 1978, 1973, 1974, 1975, 1981, 1971, 1977 |
1253 | | }; |
1254 | | static const txInteger gxSimilarLeapYears[7] ICACHE_XS6RO2_ATTR = { |
1255 | | 1984, 1996, 1980, 1992, 1976, 1988, 1972 |
1256 | | }; |
1257 | | |
1258 | 925k | #define mx1YearDays 365 |
1259 | 694k | #define mx4YearsDays ((4 * mx1YearDays) + 1) |
1260 | 462k | #define mx100YearsDays ((25 * mx4YearsDays) - 1) |
1261 | 231k | #define mx400YearsDays ((4 * mx100YearsDays) + 1) |
1262 | 494k | #define mxDayMilliseconds 86400000.0 |
1263 | | #define mxYearDays(YEAR) \ |
1264 | 535k | ((365 * (YEAR)) + ((YEAR) / 4) - ((YEAR) / 100) + ((YEAR) / 400) + 1) |
1265 | | #define mxIsLeapYear(YEAR) \ |
1266 | 194k | (((YEAR) % 4) ? 0 : ((YEAR) % 100) ? 1 : ((YEAR) % 400) ? 0 : 1) |
1267 | 456k | #define mxYearsOffset 400000 |
1268 | | |
1269 | | txSlot* fxDateCheck(txMachine* the) |
1270 | 162k | { |
1271 | 162k | txSlot* it = mxThis; |
1272 | 162k | if (it->kind == XS_REFERENCE_KIND) { |
1273 | 162k | txSlot* instance = it->value.reference; |
1274 | 162k | it = instance->next; |
1275 | 162k | if ((it) && (it->flag & XS_INTERNAL_FLAG) && (it->kind == XS_DATE_KIND)) |
1276 | 162k | return it; |
1277 | 162k | } |
1278 | 162k | mxTypeError("this: not a Date instance"); |
1279 | 0 | return C_NULL; |
1280 | 162k | } |
1281 | | |
1282 | | txNumber fxDateClip(txNumber value) |
1283 | 548k | { |
1284 | 548k | if (!c_isfinite(value)) |
1285 | 77.5k | value = C_NAN; |
1286 | 471k | else if (c_fabs(value) > 8.64e15) |
1287 | 54.2k | value = C_NAN; |
1288 | 417k | else { |
1289 | 417k | value = c_trunc(value); |
1290 | 417k | if (value == 0) |
1291 | 2.68k | value = 0; |
1292 | 417k | } |
1293 | 548k | return value; |
1294 | 548k | } |
1295 | | |
1296 | | txNumber fxDateFullYear(txMachine* the, txSlot* slot) |
1297 | 155k | { |
1298 | 155k | txNumber result = fxToNumber(the, slot); |
1299 | 155k | if (c_isfinite(result)) { |
1300 | 138k | txInteger value = fxToInteger(the, slot); |
1301 | 138k | if ((0 <= value) && (value <= 99)) |
1302 | 43.4k | result = 1900 + value; |
1303 | 138k | } |
1304 | 155k | return result; |
1305 | 155k | } |
1306 | | |
1307 | | txNumber fxDateMerge(txDateTime* dt, txBoolean utc) |
1308 | 317k | { |
1309 | 317k | txNumber year, month; |
1310 | 317k | txInteger monthIndex; |
1311 | 317k | txBoolean leap; |
1312 | 317k | txNumber value; |
1313 | 317k | if ((!c_isfinite(dt->year)) |
1314 | 299k | || (!c_isfinite(dt->month)) |
1315 | 299k | || (!c_isfinite(dt->date)) |
1316 | 299k | || (!c_isfinite(dt->hours)) |
1317 | 263k | || (!c_isfinite(dt->minutes)) |
1318 | 263k | || (!c_isfinite(dt->seconds)) |
1319 | 261k | || (!c_isfinite(dt->milliseconds))) |
1320 | 55.6k | return C_NAN; |
1321 | 261k | year = c_trunc(dt->year); |
1322 | 261k | month = c_trunc(dt->month); |
1323 | 261k | year += c_floor(month / 12); |
1324 | 261k | monthIndex = (txInteger)c_fmod(month, 12.0); |
1325 | 261k | if (monthIndex < 0) |
1326 | 668 | monthIndex += 12; |
1327 | 261k | leap = (c_fmod(year, 4) == 0) && ((c_fmod(year, 100) != 0) || (c_fmod(year, 400) == 0)); |
1328 | 261k | year += mxYearsOffset - 1; |
1329 | 261k | value = (365 * year) + c_floor(year / 4) - c_floor(year / 100) + c_floor(year / 400) + 1; |
1330 | 261k | value -= (txNumber)mxYearDays(1970 + mxYearsOffset - 1); |
1331 | 261k | if (leap) |
1332 | 33.1k | value += gxLeapYearMonthsDays[monthIndex]; |
1333 | 228k | else |
1334 | 228k | value += gxCommonYearMonthsDays[monthIndex]; |
1335 | 261k | value += c_trunc(dt->date) - 1; |
1336 | 261k | value *= mxDayMilliseconds; |
1337 | 261k | value += c_trunc(dt->hours) * 60 * 60 * 1000; |
1338 | 261k | value += c_trunc(dt->minutes) * 60 * 1000; |
1339 | 261k | value += c_trunc(dt->seconds) * 1000; |
1340 | 261k | value += c_trunc(dt->milliseconds); |
1341 | 261k | if (!utc) { |
1342 | 46.8k | txNumber former = value; |
1343 | 46.8k | txInteger similar; |
1344 | 46.8k | c_tm tm; |
1345 | 46.8k | c_time_t time; |
1346 | 46.8k | fxDateSplit(value, 1, dt); |
1347 | 46.8k | similar = fxDateSimilarYear((txInteger)dt->year); |
1348 | 46.8k | c_memset(&tm, 0, sizeof(c_tm)); |
1349 | 46.8k | tm.tm_year = similar - 1900; |
1350 | 46.8k | tm.tm_mon = (txInteger)dt->month; |
1351 | 46.8k | tm.tm_mday = (txInteger)dt->date; |
1352 | 46.8k | tm.tm_hour = (txInteger)dt->hours; |
1353 | 46.8k | tm.tm_min = (txInteger)dt->minutes; |
1354 | 46.8k | tm.tm_sec = (txInteger)dt->seconds; |
1355 | 46.8k | tm.tm_isdst =-1; |
1356 | 46.8k | time = c_mktime(&tm); |
1357 | 46.8k | if (time == -1) |
1358 | 0 | value = NAN; |
1359 | 46.8k | else { |
1360 | 46.8k | value = (txNumber)time; |
1361 | 46.8k | value *= 1000; |
1362 | 46.8k | value += dt->milliseconds; |
1363 | 46.8k | if (similar != year) { |
1364 | 46.8k | dt->year = similar; |
1365 | 46.8k | value += former - fxDateMerge(dt, 1); |
1366 | 46.8k | } |
1367 | 46.8k | } |
1368 | 46.8k | } |
1369 | 261k | return fxDateClip(value); |
1370 | 317k | } |
1371 | | |
1372 | | txNumber fxDateNow() |
1373 | 209k | { |
1374 | 209k | c_timeval tv; |
1375 | 209k | c_gettimeofday(&tv, NULL); |
1376 | 209k | return fxDateClip(((txNumber)(tv.tv_sec) * 1000.0) + ((txNumber)(tv.tv_usec / 1000))); |
1377 | 209k | } |
1378 | | |
1379 | | txString fxDatePrint2Digits(txString p, txInteger value) |
1380 | 452k | { |
1381 | 452k | *p++ = '0' + value / 10; |
1382 | 452k | *p++ = '0' + value % 10; |
1383 | 452k | return p; |
1384 | 452k | } |
1385 | | |
1386 | | txString fxDatePrint3Digits(txString p, txInteger value) |
1387 | 66.3k | { |
1388 | 66.3k | *p++ = '0' + value / 100; |
1389 | 66.3k | return fxDatePrint2Digits(p, value % 100); |
1390 | 66.3k | } |
1391 | | |
1392 | | txString fxDatePrint4Digits(txString p, txInteger value) |
1393 | 66.3k | { |
1394 | 66.3k | *p++ = '0' + value / 1000; |
1395 | 66.3k | return fxDatePrint3Digits(p, value % 1000); |
1396 | 66.3k | } |
1397 | | |
1398 | | txString fxDatePrintDate(txString p, txInteger year, txInteger month, txInteger date) |
1399 | 64.7k | { |
1400 | 64.7k | c_strcpy(p, gxMonthNames[month]); |
1401 | 64.7k | p += 3; |
1402 | 64.7k | *p++ = ' '; |
1403 | 64.7k | p = fxDatePrint2Digits(p, date); |
1404 | 64.7k | *p++ = ' '; |
1405 | 64.7k | p = fxDatePrintYear(p, year); |
1406 | 64.7k | return p; |
1407 | 64.7k | } |
1408 | | |
1409 | | txString fxDatePrintDateUTC(txString p, txInteger year, txInteger month, txInteger date) |
1410 | 1.58k | { |
1411 | 1.58k | p = fxDatePrint2Digits(p, date); |
1412 | 1.58k | *p++ = ' '; |
1413 | 1.58k | c_strcpy(p, gxMonthNames[month]); |
1414 | 1.58k | p += 3; |
1415 | 1.58k | *p++ = ' '; |
1416 | 1.58k | p = fxDatePrintYear(p, year); |
1417 | 1.58k | return p; |
1418 | 1.58k | } |
1419 | | |
1420 | | txString fxDatePrintDay(txString p, txInteger day) |
1421 | 66.3k | { |
1422 | 66.3k | c_strcpy(p, gxDayNames[day]); |
1423 | 66.3k | p += 3; |
1424 | 66.3k | return p; |
1425 | 66.3k | } |
1426 | | |
1427 | | txString fxDatePrintTime(txString p, txInteger hours, txInteger minutes, txInteger seconds) |
1428 | 64.4k | { |
1429 | 64.4k | p = fxDatePrint2Digits(p, hours); |
1430 | 64.4k | *p++ = ':'; |
1431 | 64.4k | p = fxDatePrint2Digits(p, minutes); |
1432 | 64.4k | *p++ = ':'; |
1433 | 64.4k | p = fxDatePrint2Digits(p, seconds); |
1434 | 64.4k | return p; |
1435 | 64.4k | } |
1436 | | |
1437 | | txString fxDatePrintTimezone(txString p, txInteger offset) |
1438 | 62.8k | { |
1439 | 62.8k | *p++ = 'G'; |
1440 | 62.8k | *p++ = 'M'; |
1441 | 62.8k | *p++ = 'T'; |
1442 | 62.8k | if (offset < 0) { |
1443 | 0 | offset = 0 - offset; |
1444 | 0 | *p++ = '-'; |
1445 | 0 | } |
1446 | 62.8k | else |
1447 | 62.8k | *p++ = '+'; |
1448 | 62.8k | p = fxDatePrint2Digits(p, offset / 60); |
1449 | 62.8k | p = fxDatePrint2Digits(p, offset % 60); |
1450 | 62.8k | return p; |
1451 | 62.8k | } |
1452 | | |
1453 | | txString fxDatePrintYear(txString p, txInteger value) |
1454 | 66.3k | { |
1455 | 66.3k | if ((value < 0) || (9999 < value)) { |
1456 | 4.24k | if (value < 0) { |
1457 | 2.02k | *p++ = '-'; |
1458 | 2.02k | value = -value; |
1459 | 2.02k | } |
1460 | 2.21k | else |
1461 | 2.21k | *p++ = '+'; |
1462 | 4.24k | if (99999 < value) { |
1463 | 1.42k | *p++ = '0' + value / 100000; |
1464 | 1.42k | value %= 100000; |
1465 | 1.42k | } |
1466 | 4.24k | if (9999 < value) { |
1467 | 2.86k | *p++ = '0' + value / 10000; |
1468 | 2.86k | value %= 10000; |
1469 | 2.86k | } |
1470 | 4.24k | } |
1471 | 66.3k | return fxDatePrint4Digits(p, value); |
1472 | 66.3k | } |
1473 | | |
1474 | | txInteger fxDateSimilarYear(txInteger year) |
1475 | 113k | { |
1476 | 113k | txInteger leap, day; |
1477 | 113k | if ((1970 <= year) && (year < 2038)) |
1478 | 34.6k | return year; |
1479 | 79.1k | leap = mxIsLeapYear(year); |
1480 | 79.1k | year += mxYearsOffset - 1; |
1481 | 79.1k | day = mxYearDays(year) - mxYearDays(1970 + mxYearsOffset - 1); |
1482 | 79.1k | day = (day + 4) % 7; |
1483 | 79.1k | if (day < 0) |
1484 | 61.9k | day += 7; |
1485 | 79.1k | return (leap) ? gxSimilarLeapYears[day] : gxSimilarCommonYears[day]; |
1486 | 113k | } |
1487 | | |
1488 | | void fxDateSplit(txNumber value, txBoolean utc, txDateTime* dt) |
1489 | 115k | { |
1490 | 115k | txInteger date, time, year, leap, month; |
1491 | 115k | const txInteger* monthsDays; |
1492 | 115k | date = (txInteger)c_trunc(value / mxDayMilliseconds); |
1493 | 115k | time = (txInteger)c_fmod(value, mxDayMilliseconds); |
1494 | 115k | if (time < 0) { |
1495 | 1.65k | date--; |
1496 | 1.65k | time += (txInteger)mxDayMilliseconds; |
1497 | 1.65k | } |
1498 | 115k | dt->offset = 0; |
1499 | 115k | dt->day = (date + 4) % 7; |
1500 | 115k | if (dt->day < 0) |
1501 | 57.3k | dt->day += 7; |
1502 | 115k | dt->milliseconds = time % 1000; |
1503 | 115k | time /= 1000; |
1504 | 115k | dt->seconds = time % 60; |
1505 | 115k | time /= 60; |
1506 | 115k | dt->minutes = time % 60; |
1507 | 115k | time /= 60; |
1508 | 115k | dt->hours = time; |
1509 | 115k | date += mxYearDays(1970 + mxYearsOffset); |
1510 | 115k | year = 400 * (date / mx400YearsDays); |
1511 | 115k | date %= mx400YearsDays; |
1512 | 115k | date--; |
1513 | 115k | year += 100 * (date / mx100YearsDays); |
1514 | 115k | date %= mx100YearsDays; |
1515 | 115k | date++; |
1516 | 115k | year += 4 * (date / mx4YearsDays); |
1517 | 115k | date %= mx4YearsDays; |
1518 | 115k | date--; |
1519 | 115k | year += date / mx1YearDays; |
1520 | 115k | date %= mx1YearDays; |
1521 | 115k | year -= mxYearsOffset; |
1522 | 115k | leap = mxIsLeapYear(year); |
1523 | 115k | date += leap; |
1524 | 115k | monthsDays = leap ? gxLeapYearMonthsDays : gxCommonYearMonthsDays; |
1525 | 805k | for (month = 0; month < 11; month++) { |
1526 | 798k | if (date < monthsDays[month + 1]) |
1527 | 108k | break; |
1528 | 798k | } |
1529 | 115k | date -= monthsDays[month]; |
1530 | 115k | dt->date = date + 1; |
1531 | 115k | dt->month = month; |
1532 | 115k | dt->year = year; |
1533 | 115k | if (!utc) { |
1534 | 66.9k | txNumber former = value; |
1535 | 66.9k | txInteger similar; |
1536 | 66.9k | c_time_t time; |
1537 | 66.9k | c_tm tm; |
1538 | 66.9k | similar = fxDateSimilarYear(year); |
1539 | 66.9k | if (similar != year) { |
1540 | 37.1k | dt->year = similar; |
1541 | 37.1k | value = fxDateMerge(dt, 1); |
1542 | 37.1k | } |
1543 | 66.9k | time = (txInteger)c_floor(value / 1000.0); |
1544 | 66.9k | tm = *c_localtime(&time); |
1545 | 66.9k | dt->milliseconds = c_floor(c_fmod(value, 1000.0)); |
1546 | 66.9k | dt->day = tm.tm_wday; |
1547 | 66.9k | dt->seconds = tm.tm_sec; |
1548 | 66.9k | dt->minutes = tm.tm_min; |
1549 | 66.9k | dt->hours = tm.tm_hour; |
1550 | 66.9k | dt->date = tm.tm_mday; |
1551 | 66.9k | dt->month = tm.tm_mon; |
1552 | 66.9k | dt->year = tm.tm_year + 1900 + year - similar; |
1553 | 66.9k | dt->offset = (txInteger)c_trunc((fxDateMerge(dt, 1) - former) / 60000.0); |
1554 | 66.9k | } |
1555 | 115k | dt->value = value; |
1556 | 115k | } |
1557 | | |
1558 | | |
1559 | | |
1560 | | |