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