1 /** 2 Bindings for the script Date class. This module contains both the D class definition and the script bindings. 3 4 ──────────────────────────────────────────────────────────────────────────────── 5 6 Copyright (C) 2021 pillager86.rf.gd 7 8 This program is free software: you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation, either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT ANY 14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 15 PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License along with 18 this program. If not, see <https://www.gnu.org/licenses/>. 19 */ 20 module mildew.stdlib.date; 21 22 import core.time: TimeException; 23 import std.datetime.systime; 24 import std.datetime.date; 25 import std.datetime.timezone; 26 27 import mildew.binder; 28 import mildew.environment; 29 import mildew.interpreter; 30 import mildew.types; 31 32 /** 33 * Initializes the Date library, which includes the Date() constructor. Documentation for the 34 * library can be found at https://pillager86.github.io/dmildew/ 35 * Params: 36 * interpreter = The Interpreter object for which to load the library. 37 */ 38 void initializeDateLibrary(Interpreter interpreter) 39 { 40 auto Date_ctor = new ScriptFunction("Date", &native_Date_ctor, true); 41 Date_ctor["prototype"]["getDate"] = new ScriptFunction("Date.prototype.getDate", &native_Date_getDate); 42 Date_ctor["prototype"]["getDay"] = new ScriptFunction("Date.prototype.getDay", &native_Date_getDay); 43 Date_ctor["prototype"]["getFullYear"] = new ScriptFunction("Date.prototype.getFullYear", 44 &native_Date_getFullYear); 45 Date_ctor["prototype"]["getHours"] = new ScriptFunction("Date.prototype.getHours", 46 &native_Date_getHours); 47 Date_ctor["prototype"]["getMilliseconds"] = new ScriptFunction("Date.prototype.getMilliseconds", 48 &native_Date_getMilliseconds); 49 Date_ctor["prototype"]["getMinutes"] = new ScriptFunction("Date.prototype.getMinutes", 50 &native_Date_getMinutes); 51 Date_ctor["prototype"]["getMonth"] = new ScriptFunction("Date.prototype.getMonth", 52 &native_Date_getMonth); 53 Date_ctor["prototype"]["getSeconds"] = new ScriptFunction("Date.prototype.getSeconds", 54 &native_Date_getSeconds); 55 Date_ctor["prototype"]["getTime"] = new ScriptFunction("Date.prototype.getTime", 56 &native_Date_getTime); 57 Date_ctor["prototype"]["getTimezone"] = new ScriptFunction("Date.prototype.getTimezone", 58 &native_Date_getTimezone); 59 Date_ctor["prototype"]["setDate"] = new ScriptFunction("Date.prototype.setDate", 60 &native_Date_setDate); 61 Date_ctor["prototype"]["setFullYear"] = new ScriptFunction("Date.prototype.setFullYear", 62 &native_Date_setFullYear); 63 Date_ctor["prototype"]["setHours"] = new ScriptFunction("Date.prototype.setHours", 64 &native_Date_setHours); 65 Date_ctor["prototype"]["setMilliseconds"] = new ScriptFunction("Date.prototype.setMillseconds", 66 &native_Date_setMilliseconds); 67 Date_ctor["prototype"]["setMinutes"] = new ScriptFunction("Date.prototype.setMinutes", 68 &native_Date_setMinutes); 69 Date_ctor["prototype"]["setMonth"] = new ScriptFunction("Date.prototype.setMonth", 70 &native_Date_setMonth); 71 Date_ctor["prototype"]["setSeconds"] = new ScriptFunction("Date.prototype.setSeconds", 72 &native_Date_setSeconds); 73 Date_ctor["prototype"]["setTime"] = new ScriptFunction("Date.prototype.setTime", 74 &native_Date_setTime); 75 Date_ctor["prototype"]["toDateString"] = new ScriptFunction("Date.prototype.toDateString", 76 &native_Date_toDateString); 77 Date_ctor["prototype"]["toISOString"] = new ScriptFunction("Date.prototype.toISOString", 78 &native_Date_toISOString); 79 Date_ctor["prototype"]["toUTC"] = new ScriptFunction("Date.prototype.toUTC", 80 &native_Date_toUTC); 81 interpreter.forceSetGlobal("Date", Date_ctor, false); 82 } 83 84 package: 85 86 /** 87 * The Date class 88 */ 89 class ScriptDate 90 { 91 public: 92 /** 93 * Creates a Date representing the time and date of object creation 94 */ 95 this() 96 { 97 _sysTime = Clock.currTime(); 98 } 99 100 this(in long num) 101 { 102 _sysTime = SysTime.fromUnixTime(num); 103 } 104 105 /// takes month 0-11 like JavaScript 106 this(in int year, in int monthIndex, in int day=1, in int hours=0, in int minutes=0, 107 in int seconds=0, in int milliseconds=0) 108 { 109 import core.time: msecs; 110 auto dt = DateTime(year, monthIndex+1, day, hours, minutes, seconds); 111 _sysTime = SysTime(dt, msecs(milliseconds), UTC()); 112 } 113 114 /// This string has to be formatted as "2020-Jan-01 00:00:00" for example. Anything different throws an exception 115 this(in string str) 116 { 117 auto dt = DateTime.fromSimpleString(str); 118 _sysTime = SysTime(dt, UTC()); 119 } 120 121 /// returns day of month 122 int getDate() const 123 { 124 auto dt = cast(DateTime)_sysTime; 125 return dt.day; 126 } 127 128 /// returns day of week 129 int getDay() const 130 { 131 auto dt = cast(DateTime)_sysTime; 132 return dt.dayOfWeek; 133 } 134 135 int getFullYear() const 136 { 137 auto dt = cast(DateTime)_sysTime; 138 return dt.year; 139 } 140 141 /// get the hour of the date 142 int getHours() const 143 { 144 return _sysTime.hour; 145 } 146 147 long getMilliseconds() const 148 { 149 return _sysTime.fracSecs.total!"msecs"; 150 } 151 152 int getMinutes() const 153 { 154 return _sysTime.minute; 155 } 156 157 /// returns month from 0-11 158 int getMonth() const 159 { 160 return cast(int)(_sysTime.month) - 1; 161 } 162 163 int getSeconds() const 164 { 165 return _sysTime.second; 166 } 167 168 long getTime() const 169 { 170 return _sysTime.toUnixTime * 1000; // TODO fix 171 } 172 173 long getTimezone() const 174 { 175 return _sysTime.timezone.utcOffsetAt(_sysTime.stdTime).total!"minutes"; 176 } 177 178 // TODO UTC stuff 179 180 void setDate(in int d) 181 { 182 _sysTime.day = d; 183 } 184 185 void setFullYear(in int year) 186 { 187 _sysTime.year = year; 188 } 189 190 void setHours(in int hours, in int minutes=0, in int seconds=0) 191 { 192 _sysTime.hour = hours; 193 _sysTime.minute = minutes; 194 _sysTime.second = seconds; 195 } 196 197 void setMilliseconds(in uint ms) 198 { 199 import core.time: msecs, Duration; 200 _sysTime.fracSecs = msecs(ms); 201 } 202 203 void setMinutes(in uint minutes) 204 { 205 _sysTime.minute = minutes; 206 } 207 208 void setMonth(in uint month) 209 { 210 _sysTime.month = cast(Month)(month % 12 + 1); 211 } 212 213 void setSeconds(in uint s) 214 { 215 _sysTime.second = cast(ubyte)(s%60); 216 } 217 218 void setTime(in long unixTimeMs) 219 { 220 _sysTime = _sysTime.fromUnixTime(unixTimeMs / 1000); // TODO fix 221 } 222 223 string toISOString() const 224 { 225 auto dt = cast(DateTime)_sysTime; 226 return dt.toISOString(); 227 } 228 229 ScriptDate toUTC() const 230 { 231 auto newSD = new ScriptDate(0); 232 newSD._sysTime = _sysTime.toUTC(); 233 return newSD; 234 } 235 236 override string toString() const 237 { 238 auto tz = _sysTime.timezone.dstName; 239 return (cast(DateTime)_sysTime).toString() ~ " " ~ tz; 240 } 241 242 private: 243 SysTime _sysTime; 244 } 245 246 private: 247 248 ScriptAny native_Date_ctor(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 249 { 250 if(!thisObj.isObject) 251 { 252 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 253 return ScriptAny.UNDEFINED; 254 } 255 auto obj = thisObj.toValue!ScriptObject; 256 try 257 { 258 if(args.length == 0) 259 obj.nativeObject = new ScriptDate(); 260 else if(args.length == 1) 261 { 262 if(args[0].isNumber) 263 obj.nativeObject = new ScriptDate(args[0].toValue!long); 264 else 265 obj.nativeObject = new ScriptDate(args[0].toString()); 266 } 267 else if(args.length >= 2) 268 { 269 immutable year = args[0].toValue!int; 270 immutable month = args[1].toValue!int; 271 immutable day = args.length > 2? args[2].toValue!int : 1; 272 immutable hours = args.length > 3 ? args[3].toValue!int : 0; 273 immutable minutes = args.length > 4 ? args[4].toValue!int : 0; 274 immutable seconds = args.length > 5 ? args[5].toValue!int : 0; 275 immutable mseconds = args.length > 6 ? args[6].toValue!int : 0; 276 obj.nativeObject = new ScriptDate(year, month, day, hours, minutes, seconds, mseconds); 277 } 278 else 279 nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS; 280 } 281 catch(TimeException tex) 282 { 283 nfe = NativeFunctionError.RETURN_VALUE_IS_EXCEPTION; 284 return ScriptAny(tex.msg); 285 } 286 return ScriptAny.UNDEFINED; 287 } 288 289 ScriptAny native_Date_getDate(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 290 { 291 auto date = thisObj.toNativeObject!ScriptDate; 292 if(date is null) 293 { 294 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 295 return ScriptAny.UNDEFINED; 296 } 297 return ScriptAny(date.getDate()); 298 } 299 300 ScriptAny native_Date_getDay(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 301 { 302 auto date = thisObj.toNativeObject!ScriptDate; 303 if(date is null) 304 { 305 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 306 return ScriptAny.UNDEFINED; 307 } 308 return ScriptAny(date.getDay()); 309 } 310 311 ScriptAny native_Date_getFullYear(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 312 { 313 auto date = thisObj.toNativeObject!ScriptDate; 314 if(date is null) 315 { 316 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 317 return ScriptAny.UNDEFINED; 318 } 319 return ScriptAny(date.getFullYear()); 320 } 321 322 ScriptAny native_Date_getHours(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 323 { 324 auto date = thisObj.toNativeObject!ScriptDate; 325 if(date is null) 326 { 327 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 328 return ScriptAny.UNDEFINED; 329 } 330 return ScriptAny(date.getHours()); 331 } 332 333 ScriptAny native_Date_getMilliseconds(Environment env, ScriptAny* thisObj, 334 ScriptAny[] args, ref NativeFunctionError nfe) 335 { 336 auto date = thisObj.toNativeObject!ScriptDate; 337 if(date is null) 338 { 339 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 340 return ScriptAny.UNDEFINED; 341 } 342 return ScriptAny(date.getMilliseconds()); 343 } 344 345 ScriptAny native_Date_getMinutes(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 346 { 347 auto date = thisObj.toNativeObject!ScriptDate; 348 if(date is null) 349 { 350 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 351 return ScriptAny.UNDEFINED; 352 } 353 return ScriptAny(date.getMinutes()); 354 } 355 356 ScriptAny native_Date_getMonth(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 357 { 358 if(!thisObj.isObject) 359 { 360 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 361 return ScriptAny.UNDEFINED; 362 } 363 auto dateObj = (*thisObj).toValue!ScriptDate; 364 if(dateObj is null) 365 { 366 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 367 return ScriptAny.UNDEFINED; 368 } 369 return ScriptAny(dateObj.getMonth); 370 } 371 372 ScriptAny native_Date_getSeconds(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 373 { 374 auto date = thisObj.toNativeObject!ScriptDate; 375 if(date is null) 376 { 377 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 378 return ScriptAny.UNDEFINED; 379 } 380 return ScriptAny(date.getSeconds()); 381 } 382 383 ScriptAny native_Date_getTime(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 384 { 385 auto date = thisObj.toNativeObject!ScriptDate; 386 if(date is null) 387 { 388 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 389 return ScriptAny.UNDEFINED; 390 } 391 return ScriptAny(date.getTime()); 392 } 393 394 ScriptAny native_Date_getTimezone(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 395 { 396 auto date = thisObj.toNativeObject!ScriptDate; 397 if(date is null) 398 { 399 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 400 return ScriptAny.UNDEFINED; 401 } 402 return ScriptAny(date.getTimezone()); 403 } 404 405 ScriptAny native_Date_setDate(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 406 { 407 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 408 mixin(TO_ARG_CHECK_INDEX!("d", 0, int)); 409 try 410 { 411 date.setDate(d); 412 } 413 catch(TimeException ex) 414 { 415 nfe = NativeFunctionError.RETURN_VALUE_IS_EXCEPTION; 416 return ScriptAny(ex.msg); 417 } 418 return ScriptAny.UNDEFINED; 419 } 420 421 ScriptAny native_Date_setFullYear(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 422 { 423 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 424 mixin(TO_ARG_CHECK_INDEX!("year", 0, int)); 425 try // this might not be needed here 426 { 427 date.setFullYear(year); 428 } 429 catch(TimeException ex) 430 { 431 nfe = NativeFunctionError.RETURN_VALUE_IS_EXCEPTION; 432 return ScriptAny(ex.msg); 433 } 434 return ScriptAny.UNDEFINED; 435 } 436 437 ScriptAny native_Date_setHours(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 438 { 439 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 440 mixin(TO_ARG_CHECK_INDEX!("hour", 0, int)); 441 mixin(TO_ARG_OPT!("minute", 1, 0, int)); 442 mixin(TO_ARG_OPT!("second", 2, 0, int)); 443 date.setHours(hour%24, minute%60, second%60); 444 return ScriptAny.UNDEFINED; 445 } 446 447 ScriptAny native_Date_setMilliseconds(Environment env, ScriptAny* thisObj, 448 ScriptAny[] args, ref NativeFunctionError nfe) 449 { 450 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 451 mixin(TO_ARG_CHECK_INDEX!("ms", 0, uint)); 452 date.setMilliseconds(ms % 1000); 453 return ScriptAny.UNDEFINED; 454 } 455 456 ScriptAny native_Date_setMinutes(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 457 { 458 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 459 mixin(TO_ARG_CHECK_INDEX!("minutes", 0, uint)); 460 date.setMinutes(minutes % 60); 461 return ScriptAny.UNDEFINED; 462 } 463 464 ScriptAny native_Date_setMonth(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 465 { 466 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 467 mixin(TO_ARG_CHECK_INDEX!("m", 0, uint)); 468 try 469 { 470 date.setMonth(m); 471 } 472 catch(TimeException ex) 473 { 474 nfe = NativeFunctionError.RETURN_VALUE_IS_EXCEPTION; 475 return ScriptAny(ex.msg); 476 } 477 return ScriptAny.UNDEFINED; 478 } 479 480 ScriptAny native_Date_setSeconds(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 481 { 482 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 483 mixin(TO_ARG_CHECK_INDEX!("s", 0, uint)); 484 date.setSeconds(s); 485 return ScriptAny.UNDEFINED; 486 } 487 488 ScriptAny native_Date_setTime(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 489 { 490 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 491 mixin(TO_ARG_CHECK_INDEX!("t", 0, long)); 492 date.setTime(t); 493 return ScriptAny.UNDEFINED; 494 } 495 496 ScriptAny native_Date_toDateString(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 497 { 498 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 499 return ScriptAny(date.toString()); 500 } 501 502 ScriptAny native_Date_toISOString(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 503 { 504 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 505 return ScriptAny(date.toISOString()); 506 } 507 508 ScriptAny native_Date_toUTC(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe) 509 { 510 mixin(CHECK_THIS_NATIVE_OBJECT!("date", ScriptDate)); 511 auto newDate = date.toUTC(); 512 auto newSD = new ScriptObject("Date", thisObj.toValue!ScriptObject.prototype, newDate); 513 return ScriptAny(newSD); 514 }