• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package escompat;
17
18// TODO To offer protection against timing attacks and fingerprinting, the precision of `Date.getTime()`,
19// `Date.now()` might get rounded depending on browser (target) settings.
20// In Firefox, the `privacy.reduceTimerPrecision` preference is enabled by default
21// and defaults to 20µs in Firefox 59; in 60 it will be 2ms.
22
23/** Hours in a day. */
24const hoursPerDay: int = 24;
25
26/** Minutes in an hour. */
27const minutesPerHour: int = 60;
28
29/** Seconds in a minute. */
30const secondsPerMinute: long = 60;
31
32/** Milliseconds in a second. */
33const msPerSecond: long = 1000;
34
35/** msPerMinute == 60000 */
36const msPerMinute: long = msPerSecond * secondsPerMinute;
37
38/** msPerHour == 3600000 */
39const msPerHour: long = msPerMinute * minutesPerHour;
40
41/** msPerDay == 86400000 */
42const msPerDay: long = msPerHour * hoursPerDay;
43
44/** Days in a year */
45const dayCountInYear: int = 365;
46
47/** Days in a leap year */
48const dayCountInLeapYear: int = 366;
49
50/**
51 * This gives a range of 8,640,000,000,000,000 milliseconds
52 * to either side of 01 January, 1970 UTC.
53 */
54const maxDateOffset: long = 8.64e15;
55
56/** Day names */
57const dayNames: String[] = [
58    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
59];
60
61/** Month names */
62const monthNames: String[] = [
63    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
64];
65
66/** First day of a month in a year */
67const firstDayInMonthNormal: int[] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
68/** First day of a month in a leap year */
69const firstDayInMonthLeap: int[]   = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];
70
71/**
72 * Returns first day with respect to year and month
73 *
74 * @param year
75 *
76 * @param month
77 *
78 * @returns first day
79 */
80function getFirstDayInMonth(year: int, month: int): int {
81    assert (year == 0 || year == 1) && (month >= 0 && month <= 11) : "Invalid year/month pair";
82    if (year == 0) {
83        return firstDayInMonthNormal[month];
84    } else {
85        return firstDayInMonthLeap[month];
86    }
87}
88
89/**
90 * Calculate the elapsed days since Unix Epoch.
91 *
92 * @returns elapsed days since Unix Epoch.
93 */
94function ecmaDayFromTime(time: long): int {
95    if (time < 0) {
96        time -= msPerDay - 1;
97    }
98    return (time / msPerDay) as int;
99}
100
101/**
102 *  @see ECMA-262, 20.4.1.3
103 *
104 * @returns number of days in the given year.
105 */
106function ecmaDaysInYear(year: int): int {
107    if ((year % 4 != 0) || ((year % 100 == 0) && (year % 400 != 0))) {
108        return dayCountInYear;
109    }
110
111    return dayCountInLeapYear;
112}
113
114/**
115 * @see ECMA-262, 21.4.1.3
116 *
117 * @returns Year, corresponding to the given time.
118 */
119function ecmaYearFromTime(time: long): int {
120    let approx: int = floor(time / msPerDay / 365.2425) as int + 1970 as int;
121    let yearMs: long = ecmaDayFromYear(approx) * msPerDay;
122
123    if (yearMs > time) {
124        approx--;
125    }
126
127    if ((yearMs + ecmaDaysInYear(approx) * msPerDay) <= time) {
128        approx++;
129    }
130
131    return approx;
132}
133
134/**
135 * @see ECMA-262, 21.4.1.4
136 *
137 * @returns Month number (within year).
138 */
139function ecmaMonthFromTime(time: long): byte {
140    let year = ecmaYearFromTime(time);
141    let dayWithinYear = ecmaDayFromTime(time) - ecmaDayFromYear(year);
142
143    assert dayWithinYear >= 0 && dayWithinYear < dayCountInLeapYear;
144
145    let inLeapYear = ecmaInLeapYear(year);
146
147    for (let i = 1; i < 12; i++) {
148        if (dayWithinYear < getFirstDayInMonth(inLeapYear, i)) {
149            return (i - 1) as byte;
150        }
151    }
152
153    return 11;
154}
155
156/**
157 * @see ECMA-262, 21.4.1.5
158 *
159 * @returns Date number (within month).
160 */
161function ecmaDateFromTime(time: long): byte {
162    let year = ecmaYearFromTime(time);
163    let dayWithinYear = ecmaDayFromTime(time) - ecmaDayFromYear(year);
164
165    assert dayWithinYear >= 0 && dayWithinYear < dayCountInLeapYear;
166
167    let inLeapYear = ecmaInLeapYear(year);
168
169    let month: byte = 11;
170
171    for (let i = 1; i < 12; i++) {
172        if (dayWithinYear < getFirstDayInMonth(inLeapYear, i)) {
173            month = (i - 1) as byte;
174            break;
175        }
176    }
177
178    return (dayWithinYear + 1 - getFirstDayInMonth(inLeapYear, month)) as byte;
179}
180
181/**
182 * @see ECMA-262, 21.4.1.5
183 *
184 * @param time
185 *
186 * @returns Week day.
187 */
188function ecmaWeekDay(time: long): byte {
189    let day = ecmaDayFromTime (time);
190    let weekDay = ((day + 4) % 7) as byte;
191
192    return weekDay >= 0 ? weekDay : (weekDay + 7) as byte;
193}
194
195/**
196 * @see ECMA-262, 21.4.1.5
197 *
198 * @param time
199 *
200 * @returns Week day.
201 */
202function ecmaTimeInDayFromTime(time: long): long {
203    let day = ecmaDayFromTime(time);
204    return (time - (day * msPerDay));
205}
206
207/**
208 * @see ECMA-262, 21.4.1.13
209 *
210 * @returns Hour component of a given time.
211 */
212function ecmaHourFromTime(time: long): byte {
213    let timeInDay = ecmaTimeInDayFromTime(time);
214    return (timeInDay / msPerHour) as byte;
215}
216
217/**
218 * @see ECMA-262, 21.4.1.13
219 *
220 * @returns Minute component of a given time.
221 */
222function ecmaMinFromTime(time: long): byte {
223    let timeInDay = ecmaTimeInDayFromTime(time);
224    return ((timeInDay / msPerMinute) % minutesPerHour) as byte;
225}
226
227/**
228 * @see ECMA-262, 21.4.1.13
229 *
230 * @returns Seconds component of a given time.
231 */
232function ecmaSecFromTime(time: long): byte {
233    let timeInDay = ecmaTimeInDayFromTime(time);
234    return ((timeInDay / msPerSecond) % secondsPerMinute) as byte;
235}
236
237/**
238 * @see ECMA-262, 21.4.1.13
239 *
240 * @returns Milliseconds component of a given time.
241 */
242function ecmaMsFromTime(time: long): short {
243    let timeInDay = ecmaTimeInDayFromTime(time);
244    return (timeInDay % msPerSecond) as short;
245}
246
247/**
248 * @see ECMA-262, 21.4.1.14
249 *
250 * @param hr
251 *
252 * @param min
253 *
254 * @param sec
255 *
256 * @param ms
257 *
258 * @returns Constructed time in milliseconds.
259 */
260function ecmaMakeTime(hr: long, min: long, sec: long, ms: long): long {
261    return hr * msPerHour + min * msPerMinute + sec * msPerSecond + ms;
262}
263
264/**
265 * @see ECMA-262, 21.4.1.14
266 *
267 * @param hr
268 *
269 * @param min
270 *
271 * @param sec
272 *
273 * @param ms
274 *
275 * @returns Constructed time in milliseconds.
276 */
277function ecmaMakeTime(hr: double, min: double, sec: double, ms: double): long {
278    let hh = trunc(hr)  as long;
279    let mm = trunc(min) as long;
280    let ss = trunc(sec) as long;
281    let mi = trunc(ms)  as long;
282
283    return ecmaMakeTime(hh, mm, ss, mi);
284}
285
286
287/**
288 * @see ECMA-262, 21.4.1.15
289 *
290 * @param year
291 *
292 * @param month
293 *
294 * @param date
295 *
296 * @returns Elapsed number of days since Unix Epoch.
297 */
298function ecmaMakeDay(year: long, month: long, date: long): long {
299    let ym = (year + floor(month as double / 12.0 as double)) as int;
300    let mn = (month % 12) as byte;
301
302    if (mn < 0) {
303        mn += 12;
304    }
305
306    let days: long = (ecmaDayFromYear(ym) as long + getFirstDayInMonth(ecmaInLeapYear(ym), mn) as long + (date as long) - 1);
307
308    return days;
309}
310
311/**
312 * @see ECMA-262, 21.4.1.15
313 *
314 * @param year
315 *
316 * @param month
317 *
318 * @param day
319 *
320 * @returns Elapsed number of days since Unix Epoch.
321 */
322function ecmaMakeDay(year: double, month: double, day: double): long {
323    let y = trunc(year)  as long;
324    let m = trunc(month) as long;
325    let d = trunc(day)   as long;
326
327    return ecmaMakeDay(y, m, d);
328}
329
330/**
331 * @see ECMA-262, 21.4.1.16
332 *
333 * @returns Elapsed number of milliseconds since Unix Epoch.
334 *
335 */
336function ecmaMakeDate(day:long, time: long): long {
337    return day * msPerDay + time;
338}
339
340/**
341 * @see ECMA-262, 21.4.1.17
342 *
343 * @returns Elapsed number of milliceconds since Unix Epoch.
344 *
345 */
346function ecmaTimeClip(time: long): long throws {
347    if ((time > maxDateOffset) || (time < -maxDateOffset)) {
348        throw new InvalidDate();
349    }
350
351    return time;
352}
353
354/**
355 * Returns first day of a given year since epoch.
356 *
357 * @param year
358 *
359 * @returns day from year
360 *
361 * @see ECMA-262, 21.4.1.3, DayFromYear
362 */
363function ecmaDayFromYear(year: int): int {
364    return (365 * (year - 1970) +
365        floor((year - 1969) / 4.0) -
366        floor((year - 1901) / 100.0) +
367        floor((year - 1601) / 400.0)) as int;
368}
369
370/**
371 * Returns first day of a given year since epoch.
372 *
373 * @param year
374 *
375 * @returns day from year
376 *
377 * @see ECMA-262, 21.4.1.3, DayFromYear
378 */
379function ecmaTimeFromYear(year: int): long {
380    return msPerDay as long * ecmaDayFromYear(year) as long;
381}
382
383/**
384 * The leap-year function is 1 for a time within a leap year and otherwise is 0.
385 *
386 * @see ECMA-262, 21.4.1.3
387 *
388 * @returns 1 - if the year is leap
389 *          0 - otherwise
390 */
391function ecmaInLeapYear(year: int): byte {
392    return (ecmaDaysInYear(year) - dayCountInYear) as byte;
393}
394
395/**
396 * Date JS API-compatible class
397 */
398export final class Date {
399    /** Stored value of Date, milliseconds */
400    private ms: long;
401    private TZOffset: long;
402
403    /**
404     * The `parseDateFromString()` static method parses a string representation of a date,
405     * and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC
406     * or raises `InvalidDate` if the string is unrecognized or, in some cases,
407     * contains illegal date values (e.g. 2015-02-31).
408     *
409     * Only the @link{https://tc39.es/ecma262/#sec-date-time-string-format|ISO 8601 format}
410     * (YYYY-MM-DDTHH:mm:ss.sssZ) is explicitly specified to be supported.
411     * Other formats are implementation-defined and may not work across all browsers (targets).
412     * A library can help if many different formats are to be accommodated.
413     *
414     * @see ECMA-262, 21.4.3.2
415     *
416     * @returns A number representing the milliseconds elapsed since January 1, 1970, 00:00:00 UTC
417     * and the date obtained by parsing the given string representation of a date.
418     * If the argument doesn't represent a valid date, `InvalidDate` exception is thrown.
419     */
420    private static parseDateFromString(dateStr: String): number {
421        let date = new Date();
422        let sub = dateStr.substring(0, 4);
423        let val = Double.parseInt(sub, 10) as int;
424        date.setUTCFullYear(val);
425        sub = dateStr.substring(5, 7);
426        val = Double.parseInt(sub, 10) as int;
427        date.setUTCMonth(val);
428        sub = dateStr.substring(8, 10);
429        val = Double.parseInt(sub, 10) as int;
430        date.setUTCDate(val as byte);
431        sub = dateStr.substring(11, 13);
432        val = Double.parseInt(sub, 10) as int;
433        date.setUTCHours(val as byte);
434        sub = dateStr.substring(14, 16);
435        val = Double.parseInt(sub, 10) as int;
436        date.setUTCMinutes(val as byte);
437        sub = dateStr.substring(17, 19);
438        val = Double.parseInt(sub, 10) as int;
439        date.setUTCSeconds(val as byte);
440        sub = dateStr.substring(20, 23);
441        val = Double.parseInt(sub, 10) as int;
442        date.setUTCMilliseconds(val as byte);
443        return date.valueOf();
444    }
445    /**
446     * Default constructor.
447     *
448     * @see ECMA-262, 21.4.2.1
449     *
450     * @description Initializes Date instance with current time.
451     */
452    constructor() {
453        this.ms = Date.now() as long;
454        this.TZOffset = Date.getLocalTimezoneOffset()
455    }
456    /**
457     * `Date` constructor.
458     *
459     * @param d
460     *
461     * @see ECMA-262, 21.4.2.1
462     *
463     * @description Initializes `Date` instance with another `Date` instance.
464     */
465
466    constructor(d: Date) {
467        this.ms = d.valueOf() as long;
468        this.TZOffset = Date.getLocalTimezoneOffset()
469    }
470
471    /**
472     * `Date` constructor.
473     *
474     * @param ms
475     *
476     * @see ECMA-262, 21.4.2.1
477     *
478     * @description Initialize `Date` instance with milliseconds given.
479     */
480    constructor(ms: long) {
481        this.ms = ms
482        this.TZOffset = Date.getLocalTimezoneOffset()
483    }
484
485    /**
486     * `Date` constructor.
487     *
488     * @param year
489     *
490     * @param month
491     *
492     * @see ECMA-262, 21.4.2.1
493     *
494     * @description Initialize `Date` instance with year and month given.
495     */
496    constructor(year: long, month: long) {
497        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, 1 as long), ecmaMakeTime(0 as long, 0 as long, 0 as long, 0 as long))
498        this.TZOffset = Date.getLocalTimezoneOffset()
499    }
500
501    /**
502     * `Date` constructor.
503     *
504     * @param year
505     *
506     * @param month
507     *
508     * @param day
509     *
510     * @see ECMA-262, 21.4.2.1
511     *
512     * @description Initialize `Date` instance with year, month and day given.
513     */
514    constructor(year: long, month: long, day: long) {
515        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(0 as long, 0 as long, 0 as long, 0 as long))
516        this.TZOffset = Date.getLocalTimezoneOffset()
517    }
518
519    /**
520     * `Date` constructor.
521     *
522     * @param year
523     *
524     * @param month
525     *
526     * @param day
527     *
528     * @param hours
529     *
530     * @see ECMA-262, 21.4.2.1
531     *
532     * @description Initialize `Date` instance with year, month, day and hours given.
533     */
534    constructor(year: long, month: long, day: long, hours: long) {
535        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, 0 as long, 0 as long, 0 as long))
536        this.TZOffset = Date.getLocalTimezoneOffset()
537    }
538
539    /**
540     * `Date` constructor.
541     *
542     * @param year
543     *
544     * @param month
545     *
546     * @param day
547     *
548     * @param hours
549     *
550     * @param minutes
551     *
552     * @see ECMA-262, 21.4.2.1
553     *
554     * @description Initialize `Date` instance with year, month, day, hours and minutes given.
555     */
556    constructor(year: long, month: long, day: long, hours: long, minutes: long) {
557        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, 0 as long, 0 as long))
558        this.TZOffset = Date.getLocalTimezoneOffset()
559    }
560
561    /**
562     * `Date` constructor.
563     *
564     * @param year
565     *
566     * @param month
567     *
568     * @param day
569     *
570     * @param hours
571     *
572     * @param minutes
573     *
574     * @param seconds
575     *
576     * @see ECMA-262, 21.4.2.1
577     *
578     * @description Initialize `Date` instance with year, month, day, hours, minutes and seconds given.
579     */
580    constructor(year: long, month: long, day: long, hours: long, minutes: long, seconds: long) {
581        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, seconds, 0 as long))
582        this.TZOffset = Date.getLocalTimezoneOffset()
583    }
584
585    /**
586     * `Date` constructor.
587     *
588     * @param year
589     *
590     * @param month
591     *
592     * @param day
593     *
594     * @param hours
595     *
596     * @param minutes
597     *
598     * @param seconds
599     *
600     * @param ms
601     *
602     * @see ECMA-262, 21.4.2.1
603     *
604     * @description Initialize `Date` instance with year, month, day, hours, minutes, seconds and milliseconds given.
605     */
606    constructor(year: long, month: long, day: long, hours: long, minutes: long, seconds: long, ms: long) {
607        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, seconds, ms))
608        this.TZOffset = Date.getLocalTimezoneOffset()
609    }
610
611    /**
612     * `Date` constructor.
613     *
614     * @param ms
615     *
616     * @see ECMA-262, 21.4.2.1
617     */
618    constructor(ms: double) {
619        this.ms = trunc(ms) as long;
620        this.TZOffset = Date.getLocalTimezoneOffset()
621    }
622
623    /**
624     * `Date` constructor.
625     *
626     * @param year
627     *
628     * @param month
629     *
630     * @see ECMA-262, 21.4.2.1
631     */
632    constructor(year: double, month: double) {
633        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, 1.0), ecmaMakeTime(0.0, 0.0, 0.0, 0.0))
634        this.TZOffset = Date.getLocalTimezoneOffset()
635    }
636
637    /**
638     * `Date` constructor.
639     *
640     * @param year
641     *
642     * @param month
643     *
644     * @param day
645     *
646     * @see ECMA-262, 21.4.2.1
647     */
648    constructor(year: double, month: double, day: double) {
649        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(0.0, 0.0, 0.0, 0.0))
650        this.TZOffset = Date.getLocalTimezoneOffset()
651    }
652
653    /**
654     * `Date` constructor.
655     *
656     * @param year
657     *
658     * @param month
659     *
660     * @param day
661     *
662     * @param hours
663     *
664     * @see ECMA-262, 21.4.2.1
665     */
666    constructor(year: double, month: double, day: double, hours: double) {
667        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, 0.0, 0.0, 0.0))
668        this.TZOffset = Date.getLocalTimezoneOffset()
669    }
670
671    /**
672     * `Date` constructor.
673     *
674     * @param year
675     *
676     * @param month
677     *
678     * @param day
679     *
680     * @param hours
681     *
682     * @param minutes
683     *
684     * @see ECMA-262, 21.4.2.1
685     */
686    constructor(year: double, month: double, day: double, hours: double, minutes: double) {
687        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, 0.0, 0.0))
688        this.TZOffset = Date.getLocalTimezoneOffset()
689    }
690
691    /**
692     * `Date` constructor.
693     *
694     * @param year
695     *
696     * @param month
697     *
698     * @param day
699     *
700     * @param hours
701     *
702     * @param minutes
703     *
704     * @param seconds
705     *
706     * @see ECMA-262, 21.4.2.1
707     */
708    constructor(year: double, month: double, day: double, hours: double, minutes: double, seconds: double) {
709        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, seconds, 0.0))
710        this.TZOffset = Date.getLocalTimezoneOffset()
711    }
712
713    /**
714     * `Date` constructor.
715     *
716     * @param year
717     *
718     * @param month
719     *
720     * @param day
721     *
722     * @param hours
723     *
724     * @param minutes
725     *
726     * @param seconds
727     *
728     * @param ms
729     *
730     * @see ECMA-262, 21.4.2.1
731     */
732    constructor(year: double, month: double, day: double, hours: double, minutes: double, seconds: double, ms: double) {
733        this.ms = ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, seconds, ms))
734        this.TZOffset = Date.getLocalTimezoneOffset()
735    }
736
737    /**
738     * The `isDateValid()` method checks if given date `ms` is maximum of ±100,000,000
739     * (one hundred million) days relative to January 1, 1970 UTC
740     * (that is, April 20, 271821 BCE ~ September 13, 275760 CE) can be represented
741     * by the standard `Date` object (equivalent to ±8,640,000,000,000,000 milliseconds).
742     */
743    private isDateValid(ms: long): boolean {
744        return ms >= -maxDateOffset && ms <= maxDateOffset;
745    }
746
747    /**
748     * The `isDateValid()` method checks if constructed date is maximum of ±100,000,000
749     * (one hundred million) days relative to January 1, 1970 UTC
750     * (that is, April 20, 271821 BCE ~ September 13, 275760 CE) can be represented
751     * by the standard Date object (equivalent to ±8,640,000,000,000,000 milliseconds).
752     *
753     */
754    public isDateValid(): boolean {
755        return this.isDateValid(this.ms);
756    }
757
758    /**
759     * The `valueOf()` method returns the primitive value of a `Date` object.
760     *
761     * @see ECMA-262, 21.4.4.44
762     *
763     * @returns The number of milliseconds between 1 January 1970 00:00:00 UTC and the given date.
764     *
765     * @throws InvalidDate - Throws if Date object is invalid (@link{isDateValid} is `false`).
766     */
767    public valueOf(): number {
768        if (!this.isDateValid()) {
769            return NaN;
770        }
771        return this.ms as number;
772    }
773
774    /**
775     * The `now()` static method returns the number of milliseconds elapsed since the epoch,
776     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
777     *
778     * @see ECMA-262, 21.4.3.1
779     *
780     * @returns A number representing the number of milliseconds elapsed since the epoch,
781     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
782     */
783    public static native now(): number;
784
785    /**
786     * Gets local time offset.
787     *
788     * @returns local time offset.
789     */
790    public static native getLocalTimezoneOffset(): long;
791
792    /**
793     * Gets time zone name.
794     *
795     * @returns time zone name.
796     */
797    public static native getTimezoneName(): String;
798
799    /**
800     * Gets locale string representation according to format.
801     *
802     * @param format
803     *
804     * @param locale
805     *
806     * @param ms
807     *
808     * @param isUTC
809     *
810     * @returns locale string in the specified format.
811     */
812    public static native getLocaleString(format: String, locale: String, ms: long, isUTC: boolean): String;
813
814    /**
815     * Gets a string with a language-sensitive representation of the time portion of the date.
816     *
817     * @returns a language-sensitive representation of the time portion of the date.
818     */
819    public toLocaleTimeString(): string {
820        return Date.getLocaleString("%EX", "", this.ms, false);
821    }
822
823    /**
824     * Gets a string with a language-sensitive representation of the time portion of the date with respect to locale.
825     *
826     * @param locale
827     *
828     * @returns a language-sensitive representation of the time portion of the date with respect to locale.
829     */
830    public toLocaleTimeString(locale: string): string {
831        return Date.getLocaleString("%EX", locale, this.ms, false);
832    }
833
834    /**
835     * Gets a string with a language-sensitive representation of this date.
836     *
837     * @returns a language-sensitive representation of this date.
838     */
839    public toLocaleString(): string {
840        return Date.getLocaleString("%Ex %EX", "", this.ms, false);
841    }
842
843    /**
844     * Gets a string with a language-sensitive representation of this date with respect to locale.
845     *
846     * @param locale
847     *
848     * @returns a language-sensitive representation of this date.
849     */
850    public toLocaleString(locale: string): string {
851        return Date.getLocaleString("%Ex %EX", locale, this.ms, false);
852    }
853
854    /**
855     * Gets a string with a language-sensitive representation
856     * of the date portion of the specified date in the user agent's timezone.
857     *
858     * @returns a string with a language-sensitive representation
859     * of the date portion of the specified date in the user agent's timezone.
860     */
861    public toLocaleDateString(): string {
862        return Date.getLocaleString("%Ex", "", this.ms, false);
863    }
864
865    /**
866     * Returns a string with a language-sensitive representation
867     * of the date portion of the specified date in the user agent's timezone.
868     *
869     * @returns a string with a language-sensitive representation
870     * of the date portion of the specified date in the user agent's timezone.
871     */
872    public toLocaleDateString(locale: string): string {
873        return Date.getLocaleString("%Ex", locale, this.ms, false);
874    }
875
876    /**
877     * The `getDate()` method returns the day of the month for the specified date according to local time.
878     *
879     * @see ECMA-262, 21.4.4.2
880     *
881     * @returns An integer number, between 1 and 31, representing the day of the month for the given date according to local time.
882     */
883    public getDate(): number {
884        let localTime = this.ms - this.TZOffset * 60 * msPerSecond;
885        let day: int = ecmaDayFromTime(localTime) - ecmaDayFromYear(ecmaYearFromTime(localTime));
886        let inLeapYear = ecmaInLeapYear(this.getFullYear() as int);
887        let firstDay: int = getFirstDayInMonth(inLeapYear, ecmaMonthFromTime(localTime));
888        return (day - firstDay + 1) as number;
889    }
890
891    /**
892     * Changes the day of the month of a given Date instance, based on local time.
893     *
894     * @param value new day.
895     */
896    public setDate(value: byte): void {
897        let day = this.getDate();
898        this.ms -= day * msPerDay;
899        this.ms += value * msPerDay;
900    }
901
902    /**
903     * Changes the day of the month of a given Date instance, based on local time.
904     *
905     * @param value new day.
906     */
907    public setDate(value: number): number {
908        this.setDate(value as byte);
909        return this.ms as number;
910    }
911
912    /**
913     * Alias to @link{setDate} and left for compatibility with ECMA-262.
914     *
915     * @param value new day.
916     */
917    public setDay(value: byte): void {
918        this.setDate(value);
919    }
920
921    /**
922     * Returns the day of the month (from 1 to 31) in the specified date according to universal time.
923     *
924     * @returns An integer number, between 1 and 31, representing the day of the month for the given date according to local time.
925     */
926    public getUTCDate(): number {
927        let localTime = this.ms;
928        let day: int = ecmaDayFromTime(localTime) - ecmaDayFromYear(ecmaYearFromTime(localTime));
929        let inLeapYear = ecmaInLeapYear(this.getFullYear() as int);
930        let firstDay: int = getFirstDayInMonth(inLeapYear, ecmaMonthFromTime(localTime));
931        return (day - firstDay) as number;
932    }
933
934    /**
935     * Changes the day of the month of a given Date instance, based on UTC time.
936     *
937     * @param value new day.
938     */
939    public setUTCDate(value: byte): void {
940        let day = this.getUTCDate() as byte;
941        this.ms -= day * msPerDay;
942        this.ms += value * msPerDay;
943    }
944
945    /**
946     * Changes the day of the month of a given Date instance, based on UTC time.
947     *
948     * @param value new day.
949     */
950    public setUTCDate(value: number): number {
951        this.setUTCDate(value as byte);
952        return this.ms as number;
953    }
954
955    /**
956     * Changes the day of the month of a given Date instance, based on UTC time.
957     *
958     * @param value new day.
959     */
960    public setUTCDay(value: byte): void {
961        this.setUTCDate(value);
962    }
963
964    /**
965     * Changes the day of the month of a given Date instance, based on UTC time.
966     *
967     * @param value new day.
968     */
969    public setUTCDay(value: number): number {
970        this.setUTCDate(value);
971        return this.ms as number;
972    }
973
974    /**
975     * Returns the day of the week for the specified date according to local time,
976     * where 0 represents Sunday. For the day of the month, see @link{getDayOfMonth}.
977     *
978     * @see ECMA-262, 21.4.4.3
979     *
980     * @returns An integer number, between 0 and 6, corresponding to the day of the week
981     * for the given date, according to local time: 0 for Sunday, 1 for Monday, 2 for Tuesday, and so on.
982     */
983    public getDay(): number {
984        return ecmaWeekDay(this.ms - this.TZOffset * 60 * msPerSecond) as number;
985    }
986
987    /**
988     * Returns the day of the week in the specified date according to universal time, where 0 represents Sunday.
989     *
990     * @returns An integer number, between 0 and 6, corresponding to the day of the week
991     * for the given date, according to local time: 0 for Sunday, 1 for Monday, 2 for Tuesday, and so on.
992     */
993    public getUTCDay(): number {
994        return ecmaWeekDay(this.ms) as number;
995    }
996
997    /**
998     * Returns the year of the specified date according to local time.
999     *
1000     * @see ECMA-262, 21.4.4.4
1001     * deprecated
1002     * @note This function is an alias to @link{getFullYear} and left for compatibility with ECMA-262.
1003     *
1004     * @returns year
1005     */
1006    public getYear(): int {
1007        return this.getFullYear() as int;
1008    }
1009
1010    /**
1011     * Returns the year of the specified date according to local time.
1012     *
1013     * @returns A year of the specified date according to local time.
1014     *
1015     * @description The value returned by `getUTCFullYear()` is an absolute number.
1016     * For dates between the years 1000 and 9999, `getUTCFullYear()` returns a four-digit number,
1017     * for example, 1995. Use this function to make sure a year is compliant with years after 2000.
1018     *
1019     * @returns year
1020     */
1021    public getUTCFullYear(): number {
1022        return ecmaYearFromTime(this.ms) as number;
1023    }
1024
1025    /**
1026     * Returns the year of the specified date according to local time.
1027     *
1028     * @see ECMA-262, 21.4.4.4
1029     *
1030     * @returns A number corresponding to the year of the given date, according to local time.
1031     *
1032     * @description The value returned by `getFullYear()` is an absolute number.
1033     * For dates between the years 1000 and 9999, `getFullYear()` returns a four-digit number,
1034     * for example, 1995. Use this function to make sure a year is compliant with years after 2000.
1035     *
1036     * @example
1037     * ```sts
1038     * const today = new Date();
1039     * const year = today.getYearFull();
1040     * ```
1041     */
1042    public getFullYear(): number {
1043        let localTime = this.ms - this.TZOffset * 60 * msPerSecond;
1044        return ecmaYearFromTime(localTime) as number;
1045    }
1046
1047    /**
1048     * Sets the full year for a specified date according to universal time.
1049     *
1050     * @param value new year
1051     */
1052    public setUTCFullYear(value: number, month?: Number, date?: Number): number {
1053        this.setUTCFullYear(value as int);
1054        if (month !== undefined) {
1055            this.setUTCMonth(month?.intValue());
1056        }
1057        if (date !== undefined) {
1058            this.setUTCDate(date?.byteValue());
1059        }
1060        return this.ms as number;
1061    }
1062
1063    /**
1064     * Sets the full year for a specified date according to universal time.
1065     *
1066     * @param value new year
1067     */
1068    public setUTCFullYear(value: int): void {
1069        let year = ecmaYearFromTime(this.ms);
1070        this.ms -= ecmaDayFromYear(year) * msPerDay as long;
1071        this.ms += ecmaDayFromYear(value) * msPerDay as long;
1072    }
1073
1074    /**
1075     * This function is an alias to @link{setFullYear} and left for compatibility with ECMA-262.
1076     *
1077     * @param value new year
1078     */
1079    public setYear(value: number): void {
1080        this.setYear(value as int)
1081    }
1082
1083    /**
1084     * This function is an alias to @link{setFullYear} and left for compatibility with ECMA-262.
1085     *
1086     * @param value new year
1087     */
1088    public setYear(value: int): void {
1089        this.setFullYear(value);
1090    }
1091
1092    /**
1093     * Sets the full year for a specified date according to local time.
1094     *
1095     * @param value new year
1096     */
1097    public setFullYear(value: number, month?: Number, date?: Number): number {
1098        this.setFullYear(value as int);
1099        if (month !== undefined) {
1100            this.setMonth(month?.intValue());
1101        }
1102        if (date !== undefined) {
1103            this.setDate(date?.byteValue());
1104        }
1105        return this.ms as number;
1106    }
1107
1108    /**
1109     * Sets the full year for a specified date according to local time.
1110     *
1111     * @param value new year
1112     */
1113    public setFullYear(value: int): void {
1114        let year = ecmaYearFromTime(this.ms - this.TZOffset * 60 * msPerSecond);
1115        this.ms -= ecmaDayFromYear(year) * msPerDay as long;
1116        this.ms += ecmaDayFromYear(value) * msPerDay as long;
1117    }
1118
1119    /**
1120     * Returns the hour for the specified date, according to local time.
1121     *
1122     * @see ECMA-262, 21.4.4.5
1123     *
1124     * @returns An integer number, between 0 and 23, representing the hour for the given date according to local time.
1125     *
1126     * @example
1127     * ```sts
1128     * const today = new Date();
1129     * const hours = today.getHour();
1130     * ```
1131     */
1132    public getHours(): number {
1133        let localTime = this.ms - this.TZOffset * 60 * msPerSecond;
1134        return ecmaHourFromTime(localTime) as number;
1135    }
1136
1137    /**
1138     * Returns the hours in the specified date according to universal time.
1139     *
1140     * @returns An integer number, between 0 and 23, representing the hour for the given date according to UTC time.
1141     */
1142    public getUTCHours(): number {
1143        return ecmaHourFromTime(this.ms) as number;
1144    }
1145
1146    /**
1147     * Sets the hours for a specified date according to local time.
1148     *
1149     * @param value new hours
1150     */
1151    public setHours(value: byte): void {
1152        assert value >= 0 && value <= 23;
1153        let hours = ecmaHourFromTime(this.ms - this.TZOffset * 60 * msPerSecond);
1154        this.ms -= msPerHour * hours;
1155        this.ms += msPerHour * value;
1156    }
1157
1158    /**
1159     * Sets the hours for a specified date according to local time.
1160     *
1161     * @param value new hours
1162     */
1163    public setHours(value: number, min?: Number, sec?: Number, ms?: Number): number {
1164        this.setHours(value as byte);
1165        if (min !== undefined) {
1166            this.setMinutes(min?.byteValue());
1167        }
1168        if (sec !== undefined) {
1169            this.setSeconds(sec?.byteValue());
1170        }
1171        if (ms !== undefined) {
1172            this.setMilliseconds(ms?.shortValue());
1173        }
1174        return this.ms as number;
1175    }
1176
1177    /**
1178     * Sets the hour for a specified date according to universal time.
1179     *
1180     * @param value new hours
1181     */
1182    public setUTCHours(value: byte): void {
1183        assert value >= 0 && value <= 23;
1184        let hours = ecmaHourFromTime(this.ms);
1185        this.ms -= msPerHour * hours;
1186        this.ms += msPerHour * value;
1187    }
1188
1189    /**
1190     * Sets the hour for a specified date according to universal time.
1191     *
1192     * @param value new hours
1193     */
1194    public setUTCHours(value: number, min?: Number, sec?: Number, ms?: Number): number {
1195        this.setUTCHours(value as byte);
1196        if (min !== undefined) {
1197            this.setUTCMinutes(min?.byteValue());
1198        }
1199        if (sec !== undefined) {
1200            this.setUTCSeconds(sec?.byteValue());
1201        }
1202        if (ms !== undefined) {
1203            this.setUTCMilliseconds(ms?.shortValue());
1204        }
1205        return this.ms as number;
1206    }
1207
1208    /**
1209     * Returns the milliseconds in the specified date according to local time.
1210     *
1211     * @see ECMA-262, 21.4.4.6
1212     *
1213     * @returns A number between 0 and 999 representing the milliseconds for the given date according to local time.
1214     *
1215     * @example
1216     * ```sts
1217     * const today = new Date();
1218     * const milliseconds = today.getMilliseconds();
1219     * ```
1220     */
1221    public getMilliseconds(): number {
1222        let localTime = this.ms - this.TZOffset * 60 * msPerSecond;
1223        return ecmaMsFromTime(localTime) as number;
1224    }
1225
1226    /**
1227     * Returns the number of milliseconds elapsed since the epoch,
1228     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1229     *
1230     * @param d to be converted to milliseconds.
1231     *
1232     * @see ECMA-262, 21.4.3.1
1233     *
1234     * @returns A number representing the number of milliseconds elapsed since the epoch,
1235     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1236     */
1237    public static UTC(d: Date): long {
1238        return d.valueOf() as long;
1239    }
1240
1241    /**
1242     * Returns the number of milliseconds elapsed since the epoch,
1243     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1244     *
1245     * @param year to be converted to milliseconds.
1246     *
1247     * @returns A number representing the number of milliseconds elapsed since the epoch,
1248     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1249     */
1250    public static UTC(year: long): long {
1251        return ecmaMakeDate(ecmaMakeDay(year, 0 as long, 1 as long), ecmaMakeTime(0 as long, 0 as long, 0 as long, 0 as long))
1252    }
1253
1254    /**
1255     * Returns the number of milliseconds elapsed since the epoch,
1256     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1257     *
1258     * @param year to be converted to milliseconds.
1259     *
1260     * @param month to be converted to milliseconds.
1261     *
1262     * @returns A number representing the number of milliseconds elapsed since the epoch,
1263     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1264     */
1265    public static UTC(year: long, month: long): long {
1266        return ecmaMakeDate(ecmaMakeDay(year, month, 1 as long), ecmaMakeTime(0 as long, 0 as long, 0 as long, 0 as long))
1267    }
1268
1269    /**
1270     * Returns the number of milliseconds elapsed since the epoch,
1271     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1272     *
1273     * @param year to be converted to milliseconds.
1274     *
1275     * @param month to be converted to milliseconds.
1276     *
1277     * @param day to be converted to milliseconds.
1278     *
1279     * @returns A number representing the number of milliseconds elapsed since the epoch,
1280     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1281     */
1282    public static UTC(year: long, month: long, day: long): long {
1283        return ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(0 as long, 0 as long, 0 as long, 0 as long))
1284    }
1285
1286    /**
1287     * Returns the number of milliseconds elapsed since the epoch,
1288     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1289     *
1290     * @param year to be converted to milliseconds.
1291     *
1292     * @param month to be converted to milliseconds.
1293     *
1294     * @param day to be converted to milliseconds.
1295     *
1296     * @param hours to be converted to milliseconds.
1297     *
1298     * @returns A number representing the number of milliseconds elapsed since the epoch,
1299     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1300     */
1301    public static UTC(year: long, month: long, day: long, hours: long): long {
1302        return ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, 0 as long, 0 as long, 0 as long))
1303    }
1304
1305    /**
1306     * Returns the number of milliseconds elapsed since the epoch,
1307     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1308     *
1309     * @param year to be converted to milliseconds.
1310     *
1311     * @param month to be converted to milliseconds.
1312     *
1313     * @param day to be converted to milliseconds.
1314     *
1315     * @param hours to be converted to milliseconds.
1316     *
1317     * @param minutes to be converted to milliseconds.
1318     *
1319     * @returns A number representing the number of milliseconds elapsed since the epoch,
1320     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1321     */
1322    public static UTC(year: long, month: long, day: long, hours: long, minutes: long): long {
1323        return ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, 0 as long, 0 as long))
1324    }
1325
1326    /**
1327     * Returns the number of milliseconds elapsed since the epoch,
1328     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1329     *
1330     * @param year to be converted to milliseconds.
1331     *
1332     * @param month to be converted to milliseconds.
1333     *
1334     * @param day to be converted to milliseconds.
1335     *
1336     * @param hours to be converted to milliseconds.
1337     *
1338     * @param minutes to be converted to milliseconds.
1339     *
1340     * @param seconds to be converted to milliseconds.
1341     *
1342     * @returns A number representing the number of milliseconds elapsed since the epoch,
1343     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1344     */
1345    public static UTC(year: long, month: long, day: long, hours: long, minutes: long, seconds: long): long {
1346        return ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, seconds, 0 as long))
1347    }
1348
1349    /**
1350     * Returns the number of milliseconds elapsed since the epoch,
1351     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1352     *
1353     * @param year to be converted to milliseconds.
1354     *
1355     * @param month to be converted to milliseconds.
1356     *
1357     * @param day to be converted to milliseconds.
1358     *
1359     * @param hours to be converted to milliseconds.
1360     *
1361     * @param minutes to be converted to milliseconds.
1362     *
1363     * @param seconds to be converted to milliseconds.
1364     *
1365     * @param ms to be converted to milliseconds.
1366     *
1367     * @returns A number representing the number of milliseconds elapsed since the epoch,
1368     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1369     */
1370    public static UTC(year: long, month: long, day: long, hours: long, minutes: long, seconds: long, ms: long): long {
1371        return ecmaMakeDate(ecmaMakeDay(year, month, day), ecmaMakeTime(hours, minutes, seconds, ms))
1372    }
1373
1374    /**
1375     * Returns the number of milliseconds elapsed since the epoch,
1376     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1377     *
1378     * @param year to be converted to milliseconds.
1379     *
1380     * @returns A number representing the number of milliseconds elapsed since the epoch,
1381     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1382     */
1383    public static UTC(year: number): number {
1384        return ecmaMakeDate(ecmaMakeDay(year, 0.0, 1.0), ecmaMakeTime(0.0, 0.0, 0.0, 0.0)) as number
1385    }
1386
1387    /**
1388     * Returns the number of milliseconds elapsed since the epoch,
1389     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1390     *
1391     * @param year to be converted to milliseconds.
1392     *
1393     * @param month to be converted to milliseconds.
1394     *
1395     * @param day to be converted to milliseconds.
1396     *
1397     * @param hours to be converted to milliseconds.
1398     *
1399     * @param minutes to be converted to milliseconds.
1400     *
1401     * @param seconds to be converted to milliseconds.
1402     *
1403     * @param ms to be converted to milliseconds.
1404     *
1405     * @returns A number representing the number of milliseconds elapsed since the epoch,
1406     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1407     */
1408    public static UTC(year: number, month: number, day?: Number, hours?: Number, minutes?: Number, seconds?: Number, ms?: Number): number {
1409        let defaultDay = day !== undefined ? day.valueOf() : 1.0;
1410        let defaultHours = hours !== undefined ? hours.valueOf() : 0.0;
1411        let defaultMinutes = minutes !== undefined ? minutes.valueOf() : 0.0;
1412        let defaultSeconds = seconds !== undefined ? seconds.valueOf() : 0.0;
1413        let defaultMS = ms !== undefined ? ms.valueOf() : 0.0;
1414        return ecmaMakeDate(ecmaMakeDay(year, month, defaultDay), ecmaMakeTime(defaultHours, defaultMinutes, defaultSeconds, defaultMS)) as number;
1415    }
1416
1417    /**
1418     * Returns the milliseconds portion of the time object's value according to universal time.
1419     *
1420     * @returns the milliseconds portion of the time object's value according to universal time.
1421     */
1422    public getUTCMilliseconds(): number {
1423        let localTime = this.ms;
1424        return ecmaMsFromTime(localTime) as number;
1425    }
1426
1427    /**
1428     * Sets the milliseconds for a specified date according to local time.
1429     *
1430     * @param value new ms
1431     */
1432    public setMilliseconds(value: short): void {
1433        let ms = ecmaMsFromTime(this.ms - this.TZOffset * 60 * msPerSecond);
1434        this.ms -= ms;
1435        this.ms += value;
1436    }
1437
1438    /**
1439     * Sets the milliseconds for a specified date according to local time.
1440     *
1441     * @param value new ms
1442     */
1443    public setMilliseconds(value: number): number {
1444        this.setMilliseconds(value as short);
1445        return this.ms as number;
1446    }
1447
1448    /**
1449     * Sets the milliseconds for a specified date according to universal time.
1450     *
1451     * @param value new ms
1452     */
1453    public setUTCMilliseconds(value: short): void {
1454        let ms = ecmaMsFromTime(this.ms);
1455        this.ms -= ms;
1456        this.ms += value;
1457    }
1458
1459    /**
1460     * Sets the milliseconds for a specified date according to universal time.
1461     *
1462     * @param value new ms
1463     */
1464    public setUTCMilliseconds(value: number): number {
1465        this.setUTCMilliseconds(value as short);
1466        return this.ms as number;
1467    }
1468
1469    /**
1470     * Returns the seconds in the specified date according to local time.
1471     *
1472     * @see ECMA-262, 21.4.4.9
1473     *
1474     * @returns An integer number, between 0 and 59, representing the seconds in the given date according to local time.
1475     *
1476     * @example
1477     * ```sts
1478     * const today = new Date();
1479     * const seconds = today.getSeconds();
1480     * ```
1481     */
1482    public getSeconds(): number {
1483        let localTime = this.ms - this.TZOffset * 60 * msPerSecond;
1484        return ecmaSecFromTime(localTime) as number;
1485    }
1486
1487    /**
1488     * Returns the seconds in the specified date according to universal time.
1489     *
1490     * @returns the seconds in the specified date according to universal time.
1491     */
1492    public getUTCSeconds(): number {
1493        return ecmaSecFromTime(this.ms) as number;
1494    }
1495
1496    /**
1497     * Sets the seconds for a specified date according to local time.
1498     *
1499     * @param value new seconds
1500     */
1501    public setSeconds(value: number, ms?: Number): number {
1502        this.setSeconds(value as byte);
1503        if (ms !== undefined) {
1504            this.setMilliseconds(ms?.shortValue());
1505        }
1506        return this.ms as number;
1507    }
1508
1509    /**
1510     * Sets the seconds for a specified date according to local time.
1511     *
1512     * @param value new seconds
1513     */
1514    public setSeconds(value: byte): void {
1515        assert value >= 0 && value < 60;
1516        let sec = ecmaSecFromTime(this.ms - this.TZOffset * 60 * msPerSecond);
1517        this.ms -= sec * msPerSecond;
1518        this.ms += value * msPerSecond;
1519    }
1520
1521    /**
1522     * Sets the seconds for a specified date according to universal time.
1523     *
1524     * @param value new seconds
1525     */
1526    public setUTCSeconds(value: byte): void {
1527        assert value >= 0 && value < 60;
1528        let sec = ecmaSecFromTime(this.ms);
1529        this.ms -= sec * msPerSecond;
1530        this.ms += value * msPerSecond;
1531    }
1532
1533    /**
1534     * Sets the seconds for a specified date according to universal time.
1535     *
1536     * @param value new seconds
1537     */
1538    public setUTCSeconds(value: number, ms?: Number): number {
1539        this.setUTCSeconds(value as byte);
1540        if (ms !== undefined) {
1541            this.setUTCMilliseconds(ms?.shortValue());
1542        }
1543        return this.ms as number;
1544    }
1545
1546    /**
1547     * Returns the minutes in the specified date according to local time.
1548     *
1549     * @see ECMA-262, 21.4.4.7
1550     *
1551     * @returns An integer number, between 0 and 59, representing the minutes in the given date according to local time.
1552     *
1553     * @example
1554     * ```sts
1555     * const today = new Date();
1556     * const minutes = today.getMinutes();
1557     * ```
1558     */
1559    public getMinutes(): number {
1560        let localTime = this.ms - this.TZOffset * 60 * msPerSecond;
1561        return ecmaMinFromTime(localTime) as number;
1562    }
1563
1564    /**
1565     * Sets the minutes for a specified date according to universal time.
1566     *
1567     * @param value new minutes
1568     */
1569    public setUTCMinutes(value: byte): void {
1570        assert value >= 0 && value < 60;
1571        let min = ecmaMinFromTime(this.ms);
1572        this.ms -= min * msPerMinute;
1573        this.ms += value * msPerMinute;
1574    }
1575
1576    /**
1577     * Sets the minutes for a specified date according to universal time.
1578     *
1579     * @param value new minutes
1580     */
1581    public setUTCMinutes(value: number, sec?: Number, ms?: Number): number {
1582        this.setUTCMinutes(value as byte);
1583        if (sec !== undefined) {
1584            this.setUTCSeconds(sec?.byteValue());
1585        }
1586        if (ms !== undefined) {
1587            this.setUTCMilliseconds(ms?.shortValue());
1588        }
1589        return this.ms as number;
1590    }
1591
1592    /**
1593     * Returns the minutes in the specified date according to universal time.
1594     *
1595     * @returns the minutes in the specified date according to universal time.
1596     */
1597    public getUTCMinutes(): number {
1598        return ecmaMinFromTime(this.ms) as number;
1599    }
1600
1601    /**
1602     * Sets the minutes for a specified date according to local time.
1603     *
1604     * @param value new minutes
1605     */
1606    public setMinutes(value: byte): void {
1607        assert value >= 0 && value < 60;
1608        let min = ecmaMinFromTime(this.ms - this.TZOffset * 60 * msPerSecond);
1609        this.ms -= min * msPerMinute;
1610        this.ms += value * msPerMinute;
1611    }
1612
1613    /**
1614     * Sets the minutes for a specified date according to local time.
1615     *
1616     * @param value new minutes
1617     */
1618    public setMinutes(value: number, sec?: Number, ms?: Number): number {
1619        this.setMinutes(value as byte);
1620        if (sec !== undefined) {
1621            this.setSeconds(sec?.byteValue());
1622        }
1623        if (ms !== undefined) {
1624            this.setMilliseconds(ms.shortValue());
1625        }
1626        return this.ms as number;
1627    }
1628
1629    /**
1630     * Returns the month in the specified date according to local time,
1631     * as a zero-based value (where zero indicates the first month of the year).
1632     *
1633     * @see ECMA-262, 21.4.4.8
1634     *
1635     * @returns  An integer number, between 0 and 11, representing the month in the given date according to local time.
1636     * 0 corresponds to January, 1 to February, and so on.
1637     *
1638     * @example
1639     * ```sts
1640     * const today = new Date();
1641     * const month = today.getMonth();
1642     * ```
1643     */
1644    public getMonth(): number {
1645        let localTime = this.ms - this.TZOffset * 60 * msPerSecond;
1646        return ecmaMonthFromTime(localTime) as number;
1647    }
1648
1649    /**
1650     * Returns the month of the specified date according to universal time, as a zero-based value (where zero indicates the first month of the year).
1651     *
1652     * @returns  An integer number, between 0 and 11, representing the month in the given date according to UTC time.
1653     * 0 corresponds to January, 1 to February, and so on.
1654     */
1655    public getUTCMonth(): number {
1656        return ecmaMonthFromTime(this.ms) as number;
1657    }
1658
1659    /**
1660     * Sets the month for a specified date according to the currently set year.
1661     *
1662     * @param month new month
1663     */
1664    public setMonth(month: number, date?: Number): number {
1665        this.setMonth(month as int);
1666        if (date !== undefined) {
1667            this.setDate(date.byteValue());
1668        }
1669        return this.ms as number;
1670    }
1671
1672    /**
1673     * Sets the month for a specified date according to the currently set year.
1674     *
1675     * @param month new month
1676     */
1677    public setMonth(month: int): void {
1678        let inLeapYear = ecmaInLeapYear(this.getFullYear() as int);
1679        let daysNow: int = getFirstDayInMonth(inLeapYear, ecmaMonthFromTime(this.ms - this.TZOffset * 60 * msPerSecond));
1680        let daysNew: int = getFirstDayInMonth(inLeapYear, month);
1681        this.ms -= daysNow * msPerDay;
1682        this.ms += daysNew * msPerDay;
1683    }
1684
1685    /**
1686     * Sets the month for a specified date according to universal time.
1687     *
1688     * @param month new month
1689     */
1690    public setUTCMonth(month: number, date?: Number): number {
1691        this.setUTCMonth(month as int);
1692        if (date !== undefined) {
1693            this.setUTCDate(date?.byteValue());
1694        }
1695        return this.ms as number;
1696    }
1697
1698    /**
1699     * Sets the month for a specified date according to universal time.
1700     *
1701     * @param month new month
1702     */
1703    public setUTCMonth(month: int): void {
1704        let inLeapYear = ecmaInLeapYear(this.getFullYear() as int);
1705        let daysNow: int = getFirstDayInMonth(inLeapYear, ecmaMonthFromTime(this.ms));
1706        let daysNew: int = getFirstDayInMonth(inLeapYear, month);
1707        this.ms -= daysNow * msPerDay;
1708        this.ms += daysNew * msPerDay;
1709    }
1710
1711    /**
1712     * Returns the number of milliseconds since the epoch,
1713     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1714     *
1715     * @see ECMA-262, 21.4.4.10
1716     *
1717     * @returns A number representing the milliseconds elapsed between 1 January 1970 00:00:00 UTC and the given date.
1718     */
1719    public getTime(): number {
1720        return this.valueOf();
1721    }
1722
1723    /**
1724     * Sets the number of milliseconds since the epoch,
1725     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1726     *
1727     * @param value new ms
1728     *
1729     * @see ECMA-262, 21.4.4.10
1730     *
1731     * @returns A number representing the milliseconds elapsed between 1 January 1970 00:00:00 UTC and the given date.
1732     */
1733    public setTime(value: long): void {
1734        this.ms = value;
1735    }
1736
1737    /**
1738     * Sets the number of milliseconds since the epoch,
1739     * which is defined as the midnight at the beginning of January 1, 1970, UTC.
1740     *
1741     * @param value new ms
1742     *
1743     * @see ECMA-262, 21.4.4.10
1744     *
1745     * @returns A number representing the milliseconds elapsed between 1 January 1970 00:00:00 UTC and the given date.
1746     */
1747    public setTime(value: number): number {
1748        this.setTime(value as long);
1749        return this.ms as number;
1750    }
1751
1752    /**
1753     * Parses a string representation of a date,
1754     * and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC
1755     * or raises `InvalidDate` if the string is unrecognized or, in some cases,
1756     * contains illegal date values (e.g. 2015-02-31).
1757     *
1758     * Only the @link{https://tc39.es/ecma262/#sec-date-time-string-format|ISO 8601 format}
1759     * (YYYY-MM-DDTHH:mm:ss.sssZ) is explicitly specified to be supported.
1760     * Other formats are implementation-defined and may not work across all browsers (targets).
1761     * A library can help if many different formats are to be accommodated.
1762     *
1763     * @param dateStr to be parsed
1764     *
1765     * @see ECMA-262, 21.4.3.2
1766     *
1767     * @returns A number representing the milliseconds elapsed since January 1, 1970, 00:00:00 UTC
1768     * and the date obtained by parsing the given string representation of a date.
1769     * If the argument doesn't represent a valid date, `InvalidDate` exception is thrown.
1770     */
1771    public static parse(dateStr: string): number {
1772        return Date.parseDateFromString(dateStr) as number;
1773    }
1774
1775    /**
1776     * Returns the difference, in minutes, between a date as
1777     * evaluated in the UTC time zone, and the same date as evaluated in the local time zone.
1778     *
1779     * @returns the difference, in minutes, between a date as
1780     * evaluated in the UTC time zone, and the same date as evaluated in the local time zone.
1781     */
1782    public getTimezoneOffset(): number {
1783        return this.TZOffset as number;
1784    }
1785
1786    /**
1787     * Sets the difference, in minutes, between a date as
1788     * evaluated in the UTC time zone, and the same date as evaluated in the local time zone.
1789     *
1790     * @param value new timezone offset
1791     */
1792    public setTimezoneOffset(value: number): number {
1793        return this.setTimezoneOffset(value as int) as number
1794    }
1795
1796    /**
1797     * Sets the difference, in minutes, between a date as
1798     * evaluated in the UTC time zone, and the same date as evaluated in the local time zone.
1799     *
1800     * @param value new timezone offset
1801     */
1802    public setTimezoneOffset(value: int): long {
1803        this.TZOffset = value;
1804    }
1805
1806    /**
1807     * Returns a string in simplified extended ISO format (ISO 8601),
1808     * which is always 24 or 27 characters long (YYYY-MM-DDTHH:mm:ss.sssZ or
1809     * ±YYYYYY-MM-DDTHH:mm:ss.sssZ, respectively). The timezone is always zero UTC offset,
1810     * as denoted by the suffix Z.
1811     *
1812     * @see ECMA-262, 21.4.4.36
1813     *
1814     * @returns A string representing the given date in the ISO 8601 format according to universal time.
1815     * It's the same format as the one required to be recognized by @link{parse()}.
1816     *
1817     * @example
1818     * ```sts
1819     * const today = new Date();
1820     * console.println(today.toISOString()); // Returns 2023-02-05T14:48:00.000Z
1821     * ```
1822     */
1823    public toISOString(): string {
1824        let sb = new StringBuilder();
1825        let year = this.getUTCFullYear();
1826        if(year < 0 || year > 9999) {
1827            if(year < 0) {
1828                sb.append("-");
1829            }
1830            year = abs(year) as int;
1831            sb.append(year / 100000);
1832            sb.append((year / 10000) % 10);
1833        }
1834        sb.append((year / 1000) % 10);
1835        sb.append((year / 100) % 10);
1836        sb.append((year / 10) % 10);
1837        sb.append(year % 10);
1838        sb.append("-");
1839        let month = this.getUTCMonth();
1840        if(month < 10){
1841            sb.append("0");
1842        }
1843        sb.append(month + 1);
1844        sb.append("-");
1845        let day = this.getUTCDate();
1846        if(day < 10){
1847            sb.append("0");
1848        }
1849        sb.append(day);
1850        sb.append("T");
1851        let h = this.getUTCHours();
1852        if(h < 10) {
1853            sb.append("0");
1854        }
1855        sb.append(h);
1856        sb.append(":");
1857        let m = this.getUTCMinutes();
1858        if(m < 10) {
1859            sb.append("0");
1860        }
1861        sb.append(m);
1862        sb.append(":");
1863        let s = this.getUTCSeconds();
1864        if(s < 10) {
1865            sb.append("0");
1866        }
1867        sb.append(s);
1868        sb.append(".");
1869        let ms = this.getUTCMilliseconds();
1870        sb.append(ms / 100);
1871        sb.append((ms / 10) % 10);
1872        sb.append(ms % 10);
1873        sb.append("Z");
1874        return sb.toString();
1875    }
1876
1877    /**
1878     * Returns a string representation of the Date object.
1879     *
1880     * @returns JSON representation of the current instance
1881     */
1882    public toJSON(): string {
1883        return this.toISOString();
1884    }
1885
1886    /**
1887     * Returns the time portion of a `Date` object interpreted in the local timezone in English.
1888     *
1889     * @see ECMA-262, 21.4.4.42
1890     *
1891     * @returns A string representing the time portion of the given date in human-readable form in English.
1892     *
1893     * @example
1894     * ```
1895     * let d = new Date(1979.0, 9.0, 27.0, 13.0, 12.8, 57.0, 444.1);
1896     * console.println(d.toTimeString()); // 13:12:57 GMT
1897     * ```
1898     */
1899    public toTimeString(): string {
1900        return this.timeString() + " " + this.timeZoneString();
1901    }
1902
1903    /**
1904     * Returns string representation
1905     */
1906    private timeString(): String {
1907        if (!this.isDateValid()) {
1908            throw new Error("Invalid Date");
1909        }
1910
1911        let sb = new StringBuilder();
1912        let h = this.getHours();
1913        if (h < 10) {
1914            sb.append("0");
1915        }
1916        sb.append(h);
1917        sb.append(":");
1918        let m = this.getMinutes();
1919        if (m < 10) {
1920            sb.append("0");
1921        }
1922        sb.append(m);
1923        sb.append(":");
1924        let s = this.getSeconds();
1925        if (s < 10) {
1926            sb.append("0");
1927        }
1928        sb.append(s);
1929        return sb.toString();
1930    }
1931
1932    /**
1933     * Returns the date portion of a `Date` object interpreted in the local timezone in English.
1934     *
1935     * @see ECMA-262, 21.4.4.35
1936     *
1937     * @returns A string representing the date portion of the given Date object in human-readable form in English.
1938     *
1939     * @example
1940     * ```
1941     * let d = new Date(1979.0, 9.0, 27.0, 13.0, 12.8, 57.0, 444.1);
1942     * console.println(d.toDateString()); // Sat Oct 27 1979
1943     * ```
1944     */
1945    public toDateString(): string {
1946        return this.dateString();
1947    }
1948
1949    /**
1950     * Returns a string representation
1951     */
1952    private dateString(): String {
1953        if (!this.isDateValid()) {
1954            throw new Error("Invalid Date");
1955        }
1956
1957        let sb = new StringBuilder();
1958        sb.append(dayNames[this.getDay() as int]);
1959        sb.append(" ");
1960        sb.append(monthNames[this.getMonth() as int]);
1961        sb.append(" ");
1962        let d = this.getDate();
1963        if (d < 10) {
1964            sb.append("0");
1965        }
1966        sb.append(d);
1967        sb.append(" ");
1968        let y = this.getFullYear();
1969        if (y < 0) {
1970            sb.append("-");
1971        }
1972        if (abs(y) < 1000) {
1973            sb.append("0");
1974        }
1975        if (abs(y) < 100) {
1976            sb.append("0");
1977        }
1978        if (abs(y) < 10) {
1979            sb.append("0");
1980        }
1981        sb.append(abs(y) as long);
1982        return sb.toString();
1983    }
1984
1985    /**
1986     * TODO Until TZ support implemented, the time is calculated in UTC+0 and TZ string is hardcoded
1987     */
1988    private timeZoneString(): String {
1989        let sb = new StringBuilder();
1990        sb.append("GMT");
1991        let offset = -this.TZOffset;
1992        let hours = offset / 60;
1993        if(hours < 0) {
1994            sb.append("-");
1995        } else {
1996            sb.append("+")
1997        }
1998        if (abs(hours) < 10) {
1999            sb.append("0");
2000        }
2001        sb.append(abs(hours));
2002        let minutes = abs(offset % 60);
2003        if (minutes < 10) {
2004            sb.append("0");
2005        }
2006        sb.append(minutes);
2007        sb.append(" (");
2008        sb.append(Date.getTimezoneName());
2009        sb.append(")");
2010
2011        return sb.toString();
2012    }
2013    /**
2014     * Returns a string representing the specified `Date` object interpreted in the local timezone.
2015     *
2016     * @see ECMA-262, 21.4.4.41
2017     *
2018     * @returns A string representing the given date.
2019     *
2020     * @example
2021     * ```
2022     * let d = new Date(1979.0, 9.0, 27.0, 13.0, 12.8, 57.0, 444.1);
2023     * console.println(d.toString()); // Sat Oct 27 1979 13:12:57 GMT
2024     * ```
2025     */
2026    public override toString(): string {
2027        let res: String = "Invalid date";
2028        try {
2029            res = this.toDateString() + " " + this.toTimeString();
2030        }
2031        catch (e) {}
2032        return res;
2033    }
2034
2035    /**
2036     * Returns a string representing the specified `Date` object interpreted in UTC.
2037     *
2038     * @see ECMA-262, 21.4.4.41
2039     *
2040     * @returns A string representing the given date.
2041     *
2042     * @example
2043     * ```
2044     * let d = new Date(1979.0, 9.0, 27.0, 13.0, 12.8, 57.0, 444.1);
2045     * console.println(d.toUTCString()); // Sat Oct 27 1979 13:12:57 GMT
2046     * ```
2047     */
2048    public toUTCString(): string {
2049        if (!this.isDateValid()) {
2050            throw new Error("Invalid Date");
2051        }
2052        this.ms -= this.TZOffset * 60 * msPerSecond;
2053        let s: String = this.toDateString() + " " + this.toTimeString();
2054        this.ms += this.TZOffset * 60 * msPerSecond;
2055        return s;
2056    }
2057}
2058