-
Notifications
You must be signed in to change notification settings - Fork 3
/
Date.winxed
637 lines (559 loc) · 18.1 KB
/
Date.winxed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
/* Rosella Date Library
The Date library provides utilities for working with dates and times. It is
not guaranteed to produce perfectly accurate results in all cases for all
operations on date and time. It is considered "good enough" for most casual
use and is open to iterative improvements and bugfixes. It specifically
omits certain complexity such as timezones and other details that would be
impossible to track in a library like this.
This namespace implements several utility routines and provides access to a
few global singletons used by the library.
*/
namespace Rosella.Date
{
// Get the day of the week as an integer from 0 (Sunday) to 6 (Saturday)
function get_day_of_week(int year, int month, int day)
{
return default_day_calculator().get_day(year, month, day);
}
// Get the day of the week as the string name
function get_day_of_week_name(int year, int month, int day)
{
int dow = default_day_calculator().get_day(year, month, day);
return get_day_of_week_name(dow);
}
// Get the day of week name given the integer index of the day (0=Sunday,
// 6=Saturday)
function get_day_of_week_name(int day)
{
if (day < 0 || day > 6)
Rosella.Error.error("Bad day %d. Must be 0 (Sunday) to 6 (Saturday)", day);
return all_week_days()[day];
}
// Get a Date object representing the current instant
function now()
{
return new Rosella.Date(time_ticks());
}
// Get a Date object representing the minimum representable date. This
// object is guaranteed to always compare lower than all other dates.
function min()
{
const string DATE_MINIMUM = "Rosella.Date.min";
return Rosella.Globals.autoget_global(DATE_MINIMUM,
function() { return new Rosella.Date.SpecialDate.Minimum(); }
);
}
// Get a Date object representing the maximum representable date. This
// object is guaranteed to always compare higher than all other dates.
function max()
{
const string DATE_MAXIMUM = "Rosella.Date.max";
return Rosella.Globals.autoget_global(DATE_MAXIMUM,
function() { return new Rosella.Date.SpecialDate.Maximum(); }
);
}
// Get an array with the names of all months in the calendar year
function all_months()
{
const string ALL_MONTHS_KEY = "Rosella.Date.all_months";
return Rosella.Globals.autoget_global(ALL_MONTHS_KEY,
function() {
string months[] = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November",
"December"];
return months;
}
);
}
// Get an array with the abbreviated names of all months in the calendar year
function all_months_short()
{
const string ALL_MONTHS_SHORT_KEY = "Rosella.Date.all_months_short";
return Rosella.Globals.autoget_global(ALL_MONTHS_SHORT_KEY,
function() {
string months[] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec"];
return months;
}
);
}
// Get an array containing the names of all days of the week
function all_week_days()
{
const string ALL_WEEK_DAYS_KEY = "Rosella.Date.all_week_days";
return Rosella.Globals.autoget_global(ALL_WEEK_DAYS_KEY,
function() {
string days[] = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
return days;
}
);
}
// Get an array containing counts of the number of days in each month of the
// given year. This changes depending on whether the given year is a leap
// year or not.
function month_day_counts(int year)
{
int leap_year = Rosella.Date.is_leap_year(year);
if (leap_year) {
const string DAY_COUNTS_LEAP = "Rosella.Date.month_day_counts_leap";
return Rosella.Globals.autoget_global(DAY_COUNTS_LEAP,
function() {
int days[] = [31, 29, 31, 30, 30, 31, 31, 31, 30, 31, 30, 31];
return days;
}
);
} else {
const string DAY_COUNTS_NORMAL = "Rosella.Date.month_day_counts";
return Rosella.Globals.autoget_global(DAY_COUNTS_NORMAL,
function() {
int days[] = [31, 28, 31, 30, 30, 31, 31, 31, 30, 31, 30, 31];
return days;
}
);
}
}
// Determine if the given year is a leap year
function is_leap_year(int year)
{
if (year % 4 != 0)
return false;
if (year % 400 == 0)
return true;
if (year % 100 == 0)
return false;
return true;
}
// Get the default Date formatter object
function default_date_formatter()
{
return Rosella.StringFormatter.autoget_type_formatter(class Rosella.Date,
function() { return new Rosella.Date.DateFormatter(); }
);
}
// Get the default TimeSpan formatter object
function default_timespan_formatter()
{
return Rosella.StringFormatter.autoget_type_formatter(class Rosella.Date.TimeSpan,
function() { return new Rosella.Date.TimeSpanFormatter(); }
);
}
// Get a calculator object for calculating day-of-week
const string DEFAULT_DAY_CALCULATOR = "Rosella.Date.default_day_calculator";
function default_day_calculator()
{
return Rosella.Globals.autoget_global(DEFAULT_DAY_CALCULATOR,
function() { return new Rosella.Date.Doomsday(); }
);
}
function set_day_calculator(var calc)
{
Rosella.Globals.register_global(DEFAULT_DAY_CALCULATOR, calc);
}
}
/* Rosella Date Object
This is the basic object which represents a Date/Time in Rosella. It stores
the necessary data to represent the date, and implements a few basic
operations to do on dates.
Date objects are immutable. Once created, you cannot alter the date/time
it represents.
*/
// TODO: Handle negative (BC) years
class Rosella.Date
{
var time_array;
var time_raw;
// Construct a new Date given an integer system-epoch time (like from the
// time_i opcode).
function Date(int sys_ms)
{
self.time_array = decodelocaltime(sys_ms);
self.time_raw = sys_ms;
}
// Construct a new Date given a year, month and day
function Date(int year, int month, int day)
{
if (month < 1 || month > 12)
Rosella.Error.error("Bad month %d. Must be in the range [1, 12]", month);
var month_day_counts = Rosella.Date.month_day_counts(year);
int days_in_month = month_day_counts[month - 1];
if (day < 1 || day > days_in_month)
Rosella.Error.error("Bad day of month %d. For month %d must be in range [1, %d]", day, month, days_in_month);
int day_of_year = day - 1;
for (int i = 0; i < month; i++)
day_of_year += int(month_day_counts[i]);
int weekday = Rosella.Date.get_day_of_week(year, month, day);
int raw[] = [0, 0, 0, day, month, year, weekday, day_of_year, 0];
self.time_array = raw;
self.time_raw = -1;
}
function Date(var year, var month, var day)
{
self.Date(int(year), int(month), int(day));
}
// Construct a complete Date/Time with all date components.
function Date(int year, int month, int day, int hour, int min, int s)
{
if (s < 0 || s > 59)
Rosella.Error.error("Invalid number of seconds %d", s);
if (min < 0 || min > 59)
Rosella.Error.error("Invalid number of minutes %d", min);
if (hour < 0 || hour > 23)
Rosella.Error.error("Invalid number of hours %d", hour);
self.Date(year, month, day);
var time = self.time_array;
time[0] = s;
time[1] = min;
time[2] = hour;
}
function Date(var year, var month, var day, var hour, var min, var s)
{
self.Date(int(year), int(month), int(day), int(hour), int(min), int(s));
}
function copy()
{
return new Rosella.Date(int(self.year()), int(self.month()), int(self.day()),
int(self.hours()), int(self.minutes()), int(self.seconds()));
}
/* Seconds
*/
// Get the number of seconds
function seconds()
{
return self.time_array[TM_SEC];
}
// Add the number of seconds to the date and return a new Date
function add_seconds(int s)
{
var n = clone(self);
n.__add_seconds(s);
return n;
}
// Internal second-adding helper
function __add_seconds(int s)
{
int m = 0;
s += int(self.seconds());
if (s >= 60 || s <= -60) {
m = int(s / 60);
s = s % 60;
}
self.time_array[TM_SEC] = s;
if (m != 0)
self.__add_minutes(m);
}
function to_total_seconds()
{
int time_s = self.seconds() +
60 * (self.minutes() +
60 * self.hours()
);
int year = self.year();
var month_day_counts = Rosella.Date.month_day_counts(year);
int day_of_year = self.day() - 1;
int month = self.month();
for (int i = 0; i < month; i++)
day_of_year += int(month_day_counts[i]);
int day_s = day_of_year * 24 * 60 * 60;
int base_year = year - (year % 4);
// TODO: We can probably loop by 100-year or 400-year blocks instead
int year_s = 0;
for (int i = 4; i < base_year; i += 4) {
year_s += (4 * 365 * 24 * 60 * 60);
if ((i % 100 != 0) || (i % 400 == 0))
year_s += (24 * 60 * 60);
}
return time_s + day_s + year_s;
}
/* Minutes
*/
// Get the number of minutes
function minutes()
{
return self.time_array[TM_MIN];
}
// Add the number of minutes and return a new Date
function add_minutes(int m)
{
var n = clone(self);
n.__add_minutes(m);
return n;
}
// Internal minute-adding helper
function __add_minutes(int m)
{
int h = 0;
m += int(self.minutes());
if (m >= 60 || m <= -60) {
h = int(m / 60);
m = m % 60;
}
self.time_array[TM_MIN] = m;
if (h != 0)
self.__add_hours(h);
}
/* Hours
*/
// get the number of hours
function hours()
{
return self.time_array[TM_HOUR];
}
// Add the number of hours and return a new Date
function add_hours(int h)
{
var n = clone(self);
n.__add_hours(h);
return n;
}
// Internal hour-adding helper
function __add_hours(int h)
{
int d = 0;
h += int(self.hours());
if (h >= 24 || h <= -24) {
d = int(h / 24);
h = h % 24;
}
self.time_array[TM_HOUR] = h;
if (d != 0)
self.__add_days(d);
}
/* Days
*/
// Get the number of Days
function day()
{
return self.time_array[TM_MDAY];
}
// Add days to the date and return a new Date
function add_days(int d)
{
var n = clone(self);
n.__add_days(d);
return n;
}
// Internal day-adding helper
function __add_days(int d)
{
d += int(self.day());
int cur_month = self.month() - 1;
int cur_year = int(self.year());
var counts = Rosella.Date.month_day_counts(cur_year);
int days_this_month;
int m = 0;
if (d >= 0) {
days_this_month = counts[cur_month];
while (d > days_this_month) {
d -= days_this_month;
m++;
cur_month = (cur_month + 1) % 12;
if (cur_month == 0) {
cur_year++;
counts = Rosella.Date.month_day_counts(cur_year);
}
days_this_month = counts[cur_month];
}
} else {
cur_month = (cur_month - 1) % 12;
days_this_month = counts[cur_month];
while (d < -days_this_month) {
d += days_this_month;
m--;
cur_month = (cur_month - 1) % 12;
if (cur_month == 11) {
cur_year--;
counts = Rosella.Date.month_day_counts(cur_year);
}
days_this_month = counts[cur_month];
}
if (d < 0)
d = days_this_month + d;
}
self.time_array[TM_MDAY] = d;
if (m != 0)
self.__add_months(m);
self.time_array[TM_WDAY] = Rosella.Date.get_day_of_week(self.year(), self.month(), self.day());
}
/* Week-Day
*/
// Get the week-day (integer)
function week_day()
{
return self.time_array[TM_WDAY];
}
// Get the week-day (string name)
function week_day_name()
{
return Rosella.Date.all_week_days()[int(self.week_day())];
}
/* Month
*/
// Get the month
function month()
{
return self.time_array[TM_MON];
}
// Add months and return a new Date
function add_months(int m)
{
var n = clone(self);
n.__add_months(m);
return n;
}
// Internal month-adding helper
function __add_months(int m)
{
int y = 0;
m += int(self.time_array[TM_MON]) - 1;
if (m >= 12 || m <= -12) {
y = int(m / 12);
m = m % 12;
}
self.time_array[TM_MON] = m + 1;
if (y != 0)
self.__add_years(y);
}
// Get the name of the month
function month_name()
{
return Rosella.Date.all_months()[int(self.month() - 1)];
}
/* Year
*/
// Get the year
function year()
{
return self.time_array[TM_YEAR];
}
// Add years. Return a new date
function add_years(int y)
{
var n = clone(self);
n.__add_years(y);
return n;
}
// Internal year-adding helper
function __add_years(int y)
{
self.time_array[TM_YEAR] = int(self.year()) + y;
}
/* Miscellaneous Methods
*/
// Get the raw time value, if any
function raw_time()
{
return self.time_raw;
}
// Get the Date portion only (not the hour/minute/second)
function date()
{
return new Rosella.Date(int(self.year()), int(self.month()), int(self.day()),
0, 0, 0);
}
// Get the Time portion only (not the year/month/day)
function time()
{
return new Rosella.Date(1, 1, 1,
int(self.hours()), int(self.minutes()), int(self.seconds()));
}
/* String-Related Methods
*/
// Format the Date into a string
function format_string(string f)
{
if (f == null || f == "")
return string(self);
return self.get_string_formatter().format(self, f);
}
// Get the string formatter to use
function get_string_formatter()
{
return Rosella.Date.default_date_formatter();
}
function to_string()
{
return self.get_string_formatter().format_default(self);
}
/* Operations and TimeSpans
*/
// Subtract a TimeSpan from this date, to get a new Date.
function subtract_span(var d)
{
if (d instanceof Rosella.Date.TimeSpan) {
var next = clone(self);
next.__add_seconds(-ts.seconds());
next.__add_minutes(-ts.minutes());
next.__add_hours(-ts.hours());
next.__add_days(-ts.days());
return next;
}
Rosella.Error.error("Unknown type in Date.substract_span: %s", typeof(d));
}
function diff(var d)
{
if (d instanceof Rosella.Date) {
if (self > d)
return new Rosella.Date.TimeSpan(d, self);
else
return new Rosella.Date.TimeSpan(self, d);
}
Rosella.Error.error("Unknown type in Date.diff: %s", typeof(d));
}
// Add a TimeSpan
function add_span(var ts)
{
if (ts instanceof Rosella.Date.TimeSpan) {
var next = clone(self);
next.__add_seconds(ts.seconds());
next.__add_minutes(ts.minutes());
next.__add_hours(ts.hours());
next.__add_days(ts.days());
return next;
}
Rosella.Error.error("Unknown type in Date.add: %s", typeof(ts));
}
// Determine if two Date objects are equal
function equals(var d)
{
if (!(d instanceof Rosella.Date))
return false;
if (d instanceof Rosella.Date.SpecialDate)
return d.equals(self);
var a = self.time_array;
var b = d.time_array;
for (int i = 0; i < 6; i++) {
if (a[i] != b[i])
return false;
}
return true;
}
function compare_to(var d)
{
// TODO: Can we attempt to coerce to a Date and compare in a sane way?
if (!(d instanceof Rosella.Date))
Rosella.Error.invalid("Cannot compare Date and non-Date PMC");
if (d instanceof Rosella.Date.SpecialDate)
return -d.compare_to(self);
var a = self.time_array;
var b = d.time_array;
for (int i = 5; i >= 0; i--) {
int _a = a[i];
int _b = b[i];
if (_a != _b)
return _a > _b ? 1 : -1;
}
return 0;
}
/* Vtables
*/
// Clone the date object
function clone[vtable]() { return self.copy(); }
// Get a string representation using a default format "yyyy-MM-dd hh:mm:ss"
function get_string[vtable]() { return self.to_string(); }
// Determine equality between Dates
function is_equal[vtable](var d) { return self.equals(d); }
// Compare two Dates
function cmp[vtable](var d) { return self.compare_to(d); }
function add[vtable](var d) { return self.add_span(d); }
function subtract[vtable](var d) { return self.subtract_span(d); }
}