aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library.js198
-rw-r--r--tests/runner.py6
-rw-r--r--tests/time/output.txt38
-rw-r--r--tests/time/src.c106
4 files changed, 348 insertions, 0 deletions
diff --git a/src/library.js b/src/library.js
index 9572ed89..ffd10c3f 100644
--- a/src/library.js
+++ b/src/library.js
@@ -1694,7 +1694,14 @@ var Library = {
return 0; // NULL
},
+ // ==========================================================================
// time.h
+ // ==========================================================================
+
+ clock: function() {
+ if (_clock.start === undefined) _clock.start = new Date();
+ return (_clock.start.getTime() - Date.now())*1000;
+ },
time: function(ptr) {
var ret = Math.floor(Date.now()/1000);
@@ -1704,6 +1711,197 @@ var Library = {
return ret;
},
+ difftime: function(time1, time0) {
+ return time1 - time0;
+ },
+
+ __tm_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'tm_sec'],
+ ['i32', 'tm_min'],
+ ['i32', 'tm_hour'],
+ ['i32', 'tm_mday'],
+ ['i32', 'tm_mon'],
+ ['i32', 'tm_year'],
+ ['i32', 'tm_wday'],
+ ['i32', 'tm_yday'],
+ ['i32', 'tm_isdst'],
+ ['i32', 'tm_gmtoff'],
+ ['i8*', 'tm_zone'],
+ ]),
+ // Statically allocated time struct.
+ __tm_current: 0,
+ // Statically allocated timezone strings.
+ __tm_timezones: {},
+ // Statically allocated time string strings.
+ __tm_formatted: 0,
+
+ mktime__deps: ['__tm_struct_layout', 'tzset'],
+ mktime: function(tm_ptr) {
+ _tzset();
+ var year = {{{ makeGetValue('tm_ptr', '___tm_struct_layout.tm_year', 'i32') }}};
+ var timestamp = new Date(year >= 1900 ? year : year + 1900,
+ {{{ makeGetValue('tm_ptr', '___tm_struct_layout.tm_mon', 'i32') }}},
+ {{{ makeGetValue('tm_ptr', '___tm_struct_layout.tm_mday', 'i32') }}},
+ {{{ makeGetValue('tm_ptr', '___tm_struct_layout.tm_hour', 'i32') }}},
+ {{{ makeGetValue('tm_ptr', '___tm_struct_layout.tm_min', 'i32') }}},
+ {{{ makeGetValue('tm_ptr', '___tm_struct_layout.tm_sec', 'i32') }}},
+ 0).getTime() / 1000;
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_wday', 'new Date(timestamp).getDay()', 'i32') }}}
+ var yday = Math.round((timestamp - (new Date(year, 0, 1)).getTime()) / (1000 * 60 * 60 * 24));
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_yday', 'yday', 'i32') }}}
+ return timestamp;
+ },
+ timelocal: 'mktime',
+
+ gmtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'gmtime_r'],
+ gmtime: function(time) {
+ if (!___tm_current) ___tm_current = _malloc(___tm_struct_layout.__size__);
+ return _gmtime_r(time, ___tm_current);
+ },
+
+ gmtime_r__deps: ['__tm_struct_layout', '__tm_timezones'],
+ gmtime_r: function(time, tm_ptr) {
+ var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000);
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_sec', 'date.getUTCSeconds()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_min', 'date.getUTCMinutes()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_hour', 'date.getUTCHours()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_mday', 'date.getUTCDate()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_mon', 'date.getUTCMonth()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_year', 'date.getUTCFullYear()-1900', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_wday', 'date.getUTCDay()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_gmtoff', '0', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_isdst', '0', 'i32') }}}
+
+ var start = new Date(date.getFullYear(), 0, 1);
+ var yday = Math.round((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_yday', 'yday', 'i32') }}}
+
+ var timezone = "GMT";
+ if (!(timezone in ___tm_timezones)) {
+ ___tm_timezones[timezone] = Pointer_make(intArrayFromString(timezone), null, ALLOC_NORMAL, 'i8');
+ }
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_zone', '___tm_timezones[timezone]', 'i32') }}}
+
+ return tm_ptr;
+ },
+
+ timegm__deps: ['__tm_struct_layout', 'mktime'],
+ timegm: function(tm_ptr) {
+ _tzset();
+ var offset = {{{ makeGetValue('_timezone', 0, 'i32') }}};
+ var daylight = {{{ makeGetValue('_daylight', 0, 'i32') }}};
+ daylight = (daylight == 1) ? 60 * 60 : 0;
+ var ret = _mktime(tm_ptr) - (offset + daylight);
+ return ret;
+ },
+
+ localtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'localtime_r'],
+ localtime: function(time) {
+ if (!___tm_current) ___tm_current = _malloc(___tm_struct_layout.__size__);
+ return _localtime_r(time, ___tm_current);
+ },
+
+ localtime_r__deps: ['__tm_struct_layout', '__tm_timezones', 'tzset'],
+ localtime_r: function(time, tm_ptr) {
+ _tzset();
+ var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000);
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_sec', 'date.getSeconds()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_min', 'date.getMinutes()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_hour', 'date.getHours()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_mday', 'date.getDate()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_mon', 'date.getMonth()', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_year', 'date.getFullYear()-1900', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_wday', 'date.getDay()', 'i32') }}}
+
+ var start = new Date(date.getFullYear(), 0, 1);
+ var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_yday', 'yday', 'i32') }}}
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_gmtoff', '-start.getTimezoneOffset() * 60', 'i32') }}}
+
+ var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset());
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_isdst', 'dst', 'i32') }}}
+
+ var timezone = date.toString().match(/\(([A-Z]+)\)/)[1];
+ if (!(timezone in ___tm_timezones)) {
+ ___tm_timezones[timezone] = Pointer_make(intArrayFromString(timezone), null, ALLOC_NORMAL, 'i8');
+ }
+ {{{ makeSetValue('tm_ptr', '___tm_struct_layout.tm_zone', '___tm_timezones[timezone]', 'i32') }}}
+
+ return tm_ptr;
+ },
+
+ asctime__deps: ['malloc', '__tm_formatted', 'asctime_r'],
+ asctime: function(tm_ptr) {
+ if (!___tm_formatted) ___tm_formatted = _malloc(26);
+ return _asctime_r(tm_ptr, ___tm_formatted);
+ },
+
+ asctime_r__deps: ['__tm_formatted', 'mktime'],
+ asctime_r: function(tm_ptr, buf) {
+ var date = new Date(_mktime(tm_ptr)*1000);
+ var formatted = date.toString();
+ var datePart = formatted.replace(/\d{4}.*/, '').replace(/ 0/, ' ');
+ var timePart = formatted.match(/\d{2}:\d{2}:\d{2}/)[0];
+ formatted = datePart + timePart + ' ' + date.getFullYear() + '\n';
+ formatted.split('').forEach(function(chr, index) {
+ {{{ makeSetValue('buf', 'index', 'chr.charCodeAt(0)', 'i8') }}}
+ });
+ {{{ makeSetValue('buf', '25', '0', 'i8') }}}
+ return buf;
+ },
+
+ ctime__deps: ['localtime', 'asctime'],
+ ctime: function(timer) {
+ return _asctime(_localtime(timer));
+ },
+
+ ctime_r__deps: ['localtime', 'asctime'],
+ ctime_r: function(timer, buf) {
+ return _asctime_r(_localtime_r(timer, ___tm_current), buf);
+ },
+
+ dysize: function(year) {
+ var leap = ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)));
+ return leap ? 366 : 365;
+ },
+
+ // TODO: Initialize these to defaults on startup.
+ tzname: null,
+ daylight: null,
+ timezone: null,
+ tzset__deps: ['malloc', 'tzname', 'daylight', 'timezone'],
+ tzset: function() {
+ // TODO: Use (malleable) environment variables instead.
+ if (_tzname !== null) return;
+
+ _timezone = _malloc(QUANTUM_SIZE);
+ {{{ makeSetValue('_timezone', '0', '(new Date()).getTimezoneOffset() * 60', 'i32') }}}
+
+ _daylight = _malloc(QUANTUM_SIZE);
+ var winter = new Date(2000, 0, 1);
+ var summer = new Date(2000, 6, 1);
+ {{{ makeSetValue('_daylight', '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}}
+
+ var winterName = winter.toString().match(/\(([A-Z]+)\)/)[1];
+ var summerName = summer.toString().match(/\(([A-Z]+)\)/)[1];
+ var winterNamePtr = Pointer_make(intArrayFromString(winterName), null, ALLOC_NORMAL, 'i8');
+ var summerNamePtr = Pointer_make(intArrayFromString(summerName), null, ALLOC_NORMAL, 'i8');
+ _tzname = _malloc(2 * QUANTUM_SIZE);
+ {{{ makeSetValue('_tzname', '0', 'winterNamePtr', 'i32') }}}
+ {{{ makeSetValue('_tzname', QUANTUM_SIZE, 'summerNamePtr', 'i32') }}}
+ },
+
+ stime: function(when) {
+ // TODO: Set errno.
+ return -1;
+ },
+
+ // TODO: Implement strftime(), strptime() and getdate().
+
+ // ==========================================================================
+ // sys/time.h
+ // ==========================================================================
+
gettimeofday: function(ptr) {
// %struct.timeval = type { i32, i32 }
var indexes = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] });
diff --git a/tests/runner.py b/tests/runner.py
index f2137410..549e4691 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -1636,6 +1636,12 @@ if 'benchmark' not in sys.argv:
'''
self.do_test(src, '*1,2,3,5,5,6*\n*stdin==0:0*\n*%*\n*5*\n*66.0*\n*cleaned*')
+ def test_time(self):
+ src = open(path_from_root('tests', 'time', 'src.c'), 'r').read()
+ expected = open(path_from_root('tests', 'time', 'output.txt'), 'r').read()
+ self.do_test(src, expected)
+
+
def test_statics(self):
# static initializers save i16 but load i8 for some reason
global COMPILER_TEST_OPTS; COMPILER_TEST_OPTS = ['-g']
diff --git a/tests/time/output.txt b/tests/time/output.txt
new file mode 100644
index 00000000..c8f31001
--- /dev/null
+++ b/tests/time/output.txt
@@ -0,0 +1,38 @@
+stime: -1
+tzname[0] set: 1
+tzname[1] set: 1
+sec: 43
+min: 22
+hour: 3
+day: 25
+mon: 11
+year: 102
+wday: 3
+yday: 358
+dst: 0
+off: 0
+zone: GMT
+timegm <-> gmtime: 1
+old year: 102
+new year: 70
+old year again: 102
+localtime timezone: 1
+localtime daylight: 1
+localtime tzname: 1
+localtime <-> mktime: 1
+old year: 102
+new year: 70
+old year again: 102
+time: 1
+difftime+: 268848637.000000
+difftime-: -268848637.000000
+1854 days: 365
+2000 days: 366
+2001 days: 365
+2004 days: 366
+asctime: Wed Dec 25 05:22:43 2002
+old asctime: Wed Dec 25 05:22:43 2002
+new asctime_r: Sat Jul 2 22:33:20 2011
+old asctime again: Wed Dec 25 05:22:43 2002
+clock: 0
+ctime: 0
diff --git a/tests/time/src.c b/tests/time/src.c
new file mode 100644
index 00000000..df9ad264
--- /dev/null
+++ b/tests/time/src.c
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+
+int main() {
+ time_t xmas2002 = 1040786563ll;
+ struct tm* tm_ptr;
+
+ // Make sure stime() always fails.
+ printf("stime: %d\n", stime(&xmas2002));
+
+ // Verify that tzname sets *something*.
+ tzset();
+ printf("tzname[0] set: %d\n", strlen(tzname[0]) >= 3);
+ printf("tzname[1] set: %d\n", strlen(tzname[1]) >= 3);
+
+ // Verify gmtime() creates correct struct.
+ tm_ptr = gmtime(&xmas2002);
+ printf("sec: %d\n", tm_ptr->tm_sec);
+ printf("min: %d\n", tm_ptr->tm_min);
+ printf("hour: %d\n", tm_ptr->tm_hour);
+ printf("day: %d\n", tm_ptr->tm_mday);
+ printf("mon: %d\n", tm_ptr->tm_mon);
+ printf("year: %d\n", tm_ptr->tm_year);
+ printf("wday: %d\n", tm_ptr->tm_wday);
+ printf("yday: %d\n", tm_ptr->tm_yday);
+ printf("dst: %d\n", tm_ptr->tm_isdst);
+ printf("off: %d\n", tm_ptr->tm_gmtoff);
+ printf("zone: %s\n", tm_ptr->tm_zone);
+
+ // Verify timegm() reverses gmtime.
+ printf("timegm <-> gmtime: %d\n", timegm(tm_ptr) == xmas2002);
+
+ // Verify gmtime_r() doesn't clobber static data.
+ time_t t1 = 0;
+ struct tm tm1;
+ gmtime_r(&t1, &tm1);
+ printf("old year: %d\n", tm_ptr->tm_year);
+ printf("new year: %d\n", tm1.tm_year);
+ gmtime(&xmas2002);
+ printf("old year again: %d\n", tm_ptr->tm_year);
+
+ // Verify localtime() picks up timezone data.
+ time_t t2 = xmas2002 - 60 * 60 * 24 * 30 * 6;
+ tm_ptr = localtime(&t2);
+ printf("localtime timezone: %d\n", (timezone + tm_ptr->tm_isdst * 60 * 60 ==
+ -tm_ptr->tm_gmtoff));
+ printf("localtime daylight: %d\n", daylight == tm_ptr->tm_isdst);
+ printf("localtime tzname: %d\n", (!strcmp(tzname[0], tm_ptr->tm_zone) ||
+ !strcmp(tzname[1], tm_ptr->tm_zone)));
+
+ // Verify localtime() and mktime() reverse each other.
+ printf("localtime <-> mktime: %d\n", mktime(localtime(&xmas2002)) == xmas2002);
+
+ // Verify localtime_r() doesn't clobber static data.
+ time_t t3 = 0;
+ struct tm tm2;
+ localtime_r(&t3, &tm2);
+ printf("old year: %d\n", tm_ptr->tm_year);
+ printf("new year: %d\n", tm2.tm_year);
+ localtime(&xmas2002);
+ printf("old year again: %d\n", tm_ptr->tm_year);
+
+ // Verify time() returns reasonable value (between 2011 and 2030).
+ time_t t4 = 0;
+ time(&t4);
+ printf("time: %d\n", t4 > 1309635200ll && t4 < 1893362400ll);
+
+ // Verify difftime() calculates accurate time difference.
+ time_t t5 = 1309635200ll;
+ printf("difftime+: %lf\n", difftime(t5, xmas2002));
+ printf("difftime-: %lf\n", difftime(xmas2002, t5));
+
+ // Verify dysize() knows its leap years.
+ printf("1854 days: %d\n", dysize(1854));
+ printf("2000 days: %d\n", dysize(2000));
+ printf("2001 days: %d\n", dysize(2001));
+ printf("2004 days: %d\n", dysize(2004));
+
+ // Verify asctime() formatting().
+ printf("asctime: %s", asctime(localtime(&xmas2002)));
+
+ // Verify asctime_r() doesn't clobber static data.
+ time_t t6 = 1309635200ll;
+ tm_ptr = localtime(&xmas2002);
+ char* formatted = asctime(tm_ptr);
+ char buffer[32];
+ asctime_r(localtime(&t6), buffer);
+ printf("old asctime: %s", formatted);
+ printf("new asctime_r: %s", buffer);
+ asctime_r(tm_ptr, buffer);
+ printf("old asctime again: %s", formatted);
+
+ // Verify that clock() is initially 0 and doesn't crash.
+ printf("clock: %d\n", clock());
+
+ // Verify that ctime_r(x, buf) is equivalent to asctime_r(localtime(x), buf2).
+ time_t t7 = time(0);
+ char buffer2[30];
+ char buffer3[30];
+ printf("ctime: %d\n", strcmp(ctime_r(&t7, buffer2),
+ asctime_r(localtime(&t7), buffer3)));
+
+ return 0;
+}