Skip to content

Commit 09d6e76

Browse files
committed
Change jerry_port interface to allow for a correct implementation of timezones.
The previous jerry_port interface did not allow timezones to be handled correctly, even if the host system was up to the task. This PR changes the jerry_port interface to allow a completely correct implementation to exist, and provides one (for Linux/BSD systems) in default-date.c. Fixes jerryscript-project#1661 JerryScript-DCO-1.0-Signed-off-by: crazy2be crazy1be@gmail.com
1 parent afe2a80 commit 09d6e76

File tree

15 files changed

+160
-212
lines changed

15 files changed

+160
-212
lines changed

Diff for: docs/05.PORT-API.md

+32-31
Original file line numberDiff line numberDiff line change
@@ -76,27 +76,34 @@ void jerry_port_log (jerry_log_level_t level, const char *fmt, ...);
7676
7777
```c
7878
/**
79-
* Jerry time zone structure
80-
*/
81-
typedef struct
82-
{
83-
int offset; /**< minutes from west */
84-
int daylight_saving_time; /**< daylight saving time (1 - DST applies, 0 - not on DST) */
85-
} jerry_time_zone_t;
86-
87-
/**
88-
* Get timezone and daylight saving data
79+
* Get local time zone adjustment, in milliseconds, for the given timestamp.
80+
* The timestamp can be specified in either UTC or local time, depending on
81+
* the value of is_utc. Adding the value returned from this function to
82+
* a timestamp in UTC time should result in local time for the current time
83+
* zone.
84+
*
85+
* Ideally, this function should satisfy the stipulations applied to LocalTZA
86+
* in section 20.3.1.7 of the ECMAScript version 9.0 spec.
87+
*
88+
* See Also:
89+
* ECMA-262 v9, 20.3.1.7
8990
*
9091
* Note:
9192
* This port function is called by jerry-core when
9293
* CONFIG_DISABLE_DATE_BUILTIN is _not_ defined. Otherwise this function is
9394
* not used.
9495
*
95-
* @param[out] tz_p time zone structure to fill.
96-
* @return true - if success
97-
* false - otherwise
96+
* @param unix_ms The unix timestamp we want an offset for, given in
97+
* millisecond precision (could be now, in the future,
98+
* or in the past)
99+
* @param is_utc Is the given timestamp in UTC time? If false, it is in local
100+
* time.
101+
*
102+
* @return milliseconds between local time and UTC for the given timestamp,
103+
* if available
104+
*. 0 if not available / we are in UTC.
98105
*/
99-
bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p);
106+
double jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc);
100107
101108
/**
102109
* Get system time
@@ -198,27 +205,21 @@ jerry_port_log (jerry_log_level_t level, /**< log level */
198205
#include "jerryscript-port.h"
199206

200207
/**
201-
* Default implementation of jerry_port_get_time_zone.
208+
* Default implementation of jerry_port_get_local_time_zone_adjustment.
202209
*/
203-
bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p)
210+
double jerry_port_get_local_time_zone_adjustment (double unix_ms, /**< ms since unix epoch */
211+
bool is_utc) /**< is the time above in UTC? */
204212
{
205-
struct timeval tv;
206-
struct timezone tz;
207-
208-
/* gettimeofday may not fill tz, so zero-initializing */
209-
tz.tz_minuteswest = 0;
210-
tz.tz_dsttime = 0;
211-
212-
if (gettimeofday (&tv, &tz) != 0)
213+
struct tm tm;
214+
time_t now = (time_t) (unix_ms / 1000);
215+
localtime_r (&now, &tm);
216+
if (!is_utc)
213217
{
214-
return false;
218+
now -= tm.tm_gmtoff;
219+
localtime_r (&now, &tm);
215220
}
216-
217-
tz_p->offset = tz.tz_minuteswest;
218-
tz_p->daylight_saving_time = tz.tz_dsttime > 0 ? 1 : 0;
219-
220-
return true;
221-
} /* jerry_port_get_time_zone */
221+
return ((double) tm.tm_gmtoff) * 1000;
222+
} /* jerry_port_get_local_time_zone_adjustment */
222223

223224
/**
224225
* Default implementation of jerry_port_get_current_time.

Diff for: jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ ecma_builtin_date_prototype_dispatch_routine (uint16_t builtin_routine_id, /**<
600600

601601
if (!BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id))
602602
{
603-
this_num += ecma_date_local_time_zone (this_num);
603+
this_num += ecma_date_local_time_zone_adjustment (this_num);
604604
}
605605

606606
if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET)

Diff for: jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c

+13-64
Original file line numberDiff line numberDiff line change
@@ -302,83 +302,32 @@ ecma_date_week_day (ecma_number_t time) /**< time value */
302302
} /* ecma_date_week_day */
303303

304304
/**
305-
* Helper function to get local time zone adjustment.
306-
*
307-
* See also:
308-
* ECMA-262 v5, 15.9.1.7
309-
*
310-
* @return local time zone adjustment
311-
*/
312-
static inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE
313-
ecma_date_local_tza (jerry_time_zone_t *tz) /**< time zone information */
314-
{
315-
return tz->offset * -ECMA_DATE_MS_PER_MINUTE;
316-
} /* ecma_date_local_tza */
317-
318-
/**
319-
* Helper function to get the daylight saving time adjustment.
320-
*
321-
* See also:
322-
* ECMA-262 v5, 15.9.1.8
323-
*
324-
* @return daylight saving time adjustment
325-
*/
326-
static inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE
327-
ecma_date_daylight_saving_ta (jerry_time_zone_t *tz, /**< time zone information */
328-
ecma_number_t time) /**< time value */
329-
{
330-
JERRY_ASSERT (!ecma_number_is_nan (time));
331-
332-
/*
333-
* TODO: Fix daylight saving calculation.
334-
* https://github.com/jerryscript-project/jerryscript/issues/1661
335-
*/
336-
return tz->daylight_saving_time * ECMA_DATE_MS_PER_HOUR;
337-
} /* ecma_date_daylight_saving_ta */
338-
339-
/**
340-
* Helper function to get local time from UTC.
305+
* Helper function to get the local time zone offset at a given UTC timestamp.
306+
* You can add this number to the given UTC timestamp to get local time.
341307
*
342308
* See also:
343309
* ECMA-262 v5, 15.9.1.9
344310
*
345-
* @return local time
311+
* @return local time zone adjustment
346312
*/
347313
inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE
348-
ecma_date_local_time_zone (ecma_number_t time) /**< time value */
314+
ecma_date_local_time_zone_adjustment (ecma_number_t time) /**< time value */
349315
{
350-
jerry_time_zone_t tz;
351-
352-
if (ecma_number_is_nan (time)
353-
|| !jerry_port_get_time_zone (&tz))
354-
{
355-
return ecma_number_make_nan ();
356-
}
357-
358-
return ecma_date_local_tza (&tz) + ecma_date_daylight_saving_ta (&tz, time);
359-
} /* ecma_date_local_time_zone */
316+
return jerry_port_get_local_time_zone_adjustment (time, true);
317+
} /* ecma_date_local_time_zone_adjustment */
360318

361319
/**
362-
* Helper function to get utc from local time.
320+
* Helper function to get UTC time from local time.
363321
*
364322
* See also:
365323
* ECMA-262 v5, 15.9.1.9
366324
*
367-
* @return utc time
325+
* @return UTC time
368326
*/
369327
ecma_number_t
370328
ecma_date_utc (ecma_number_t time) /**< time value */
371329
{
372-
jerry_time_zone_t tz;
373-
374-
if (ecma_number_is_nan (time)
375-
|| !jerry_port_get_time_zone (&tz))
376-
{
377-
return ecma_number_make_nan ();
378-
}
379-
380-
ecma_number_t simple_utc_time = time - ecma_date_local_tza (&tz);
381-
return simple_utc_time - ecma_date_daylight_saving_ta (&tz, simple_utc_time);
330+
return time - jerry_port_get_local_time_zone_adjustment (time, false);
382331
} /* ecma_date_utc */
383332

384333
/**
@@ -615,7 +564,7 @@ ecma_date_timezone_offset (ecma_number_t time) /**< time value */
615564
{
616565
JERRY_ASSERT (!ecma_number_is_nan (time));
617566

618-
return (-ecma_date_local_time_zone (time)) / ECMA_DATE_MS_PER_MINUTE;
567+
return (-ecma_date_local_time_zone_adjustment (time)) / ECMA_DATE_MS_PER_MINUTE;
619568
} /* ecma_date_timezone_offset */
620569

621570
/**
@@ -724,7 +673,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */
724673
}
725674
case LIT_CHAR_LOWERCASE_Z: /* Time zone minutes part. */
726675
{
727-
int32_t time_zone = (int32_t) ecma_date_local_time_zone (datetime_number);
676+
int32_t time_zone = (int32_t) ecma_date_local_time_zone_adjustment (datetime_number);
728677

729678
if (time_zone >= 0)
730679
{
@@ -744,7 +693,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */
744693
{
745694
JERRY_ASSERT (*format_p == LIT_CHAR_UPPERCASE_Z); /* Time zone seconds part. */
746695

747-
int32_t time_zone = (int32_t) ecma_date_local_time_zone (datetime_number);
696+
int32_t time_zone = (int32_t) ecma_date_local_time_zone_adjustment (datetime_number);
748697

749698
if (time_zone < 0)
750699
{
@@ -805,7 +754,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */
805754
ecma_value_t
806755
ecma_date_value_to_string (ecma_number_t datetime_number) /**< datetime */
807756
{
808-
datetime_number += ecma_date_local_time_zone (datetime_number);
757+
datetime_number += ecma_date_local_time_zone_adjustment (datetime_number);
809758
return ecma_date_to_string_format (datetime_number, "$W $M $D $Y $h:$m:$s GMT$z:$Z");
810759
} /* ecma_date_value_to_string */
811760

Diff for: jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ ecma_number_t ecma_date_year_from_time (ecma_number_t time);
100100
ecma_number_t ecma_date_month_from_time (ecma_number_t time);
101101
ecma_number_t ecma_date_date_from_time (ecma_number_t time);
102102
ecma_number_t ecma_date_week_day (ecma_number_t time);
103-
ecma_number_t ecma_date_local_time_zone (ecma_number_t time);
103+
ecma_number_t ecma_date_local_time_zone_adjustment (ecma_number_t time);
104104
ecma_number_t ecma_date_utc (ecma_number_t time);
105105
ecma_number_t ecma_date_hour_from_time (ecma_number_t time);
106106
ecma_number_t ecma_date_min_from_time (ecma_number_t time);

Diff for: jerry-core/include/jerryscript-port.h

+21-14
Original file line numberDiff line numberDiff line change
@@ -111,27 +111,34 @@ void JERRY_ATTR_FORMAT (printf, 2, 3) jerry_port_log (jerry_log_level_t level, c
111111
*/
112112

113113
/**
114-
* Jerry time zone structure
115-
*/
116-
typedef struct
117-
{
118-
int offset; /**< minutes from west */
119-
int daylight_saving_time; /**< daylight saving time (1 - DST applies, 0 - not on DST) */
120-
} jerry_time_zone_t;
121-
122-
/**
123-
* Get timezone and daylight saving data
114+
* Get local time zone adjustment, in milliseconds, for the given timestamp.
115+
* The timestamp can be specified in either UTC or local time, depending on
116+
* the value of is_utc. Adding the value returned from this function to
117+
* a timestamp in UTC time should result in local time for the current time
118+
* zone.
119+
*
120+
* Ideally, this function should satisfy the stipulations applied to LocalTZA
121+
* in section 20.3.1.7 of the ECMAScript version 9.0 spec.
122+
*
123+
* See Also:
124+
* ECMA-262 v9, 20.3.1.7
124125
*
125126
* Note:
126127
* This port function is called by jerry-core when
127128
* CONFIG_DISABLE_DATE_BUILTIN is _not_ defined. Otherwise this function is
128129
* not used.
129130
*
130-
* @param[out] tz_p time zone structure to fill.
131-
* @return true - if success
132-
* false - otherwise
131+
* @param unix_ms The unix timestamp we want an offset for, given in
132+
* millisecond precision (could be now, in the future,
133+
* or in the past)
134+
* @param is_utc Is the given timestamp in UTC time? If false, it is in local
135+
* time.
136+
*
137+
* @return milliseconds between local time and UTC for the given timestamp,
138+
* if available
139+
*. 0 if not available / we are in UTC.
133140
*/
134-
bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p);
141+
double jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc);
135142

136143
/**
137144
* Get system time

Diff for: jerry-port/default/CMakeLists.txt

+13
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ file(GLOB SOURCE_PORT_DEFAULT *.c)
2626
# (should only be necessary if we used compiler default libc but not checking that)
2727
set(DEFINES_PORT_DEFAULT _BSD_SOURCE _DEFAULT_SOURCE)
2828

29+
INCLUDE (CheckStructHasMember)
30+
# CHECK_STRUCT_HAS_MEMBER works by trying to compile some C code that accesses the
31+
# given field of the given struct. However, our default compiler options break this
32+
# C code, so turn a couple of them off for this.
33+
set(CMAKE_REQUIRED_FLAGS "-Wno-error=strict-prototypes -Wno-error=old-style-definition")
34+
# tm.tm_gmtoff is non-standard, so glibc doesn't expose it in c99 mode
35+
# (our default). Define some macros to expose it anyway.
36+
set(CMAKE_REQUIRED_DEFINITIONS "-D_BSD_SOURCE -D_DEFAULT_SOURCE")
37+
CHECK_STRUCT_HAS_MEMBER ("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
38+
if(HAVE_TM_GMTOFF)
39+
set(DEFINES_PORT_DEFAULT ${DEFINES_PORT_DEFAULT} HAVE_TM_GMTOFF)
40+
endif()
41+
2942
# Sleep function availability check
3043
INCLUDE (CheckIncludeFiles)
3144
CHECK_INCLUDE_FILES (time.h HAVE_TIME_H)

Diff for: jerry-port/default/default-date.c

+24-22
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
* limitations under the License.
1414
*/
1515

16+
#ifdef HAVE_TM_GMTOFF
17+
#include <time.h>
18+
#endif
1619
#ifdef __GNUC__
1720
#include <sys/time.h>
1821
#endif
@@ -21,33 +24,32 @@
2124
#include "jerryscript-port-default.h"
2225

2326
/**
24-
* Default implementation of jerry_port_get_time_zone. Uses 'gettimeofday' if
25-
* available on the system, does nothing otherwise.
27+
* Default implementation of jerry_port_get_local_time_zone_adjustment. Uses the 'tm_gmtoff' field
28+
* of 'struct tm' (a GNU extension) filled by 'localtime_r' if available on the
29+
* system, does nothing otherwise.
2630
*
27-
* @return true - if 'gettimeofday' is available and executed successfully,
28-
* false - otherwise.
31+
* @return offset between UTC and local time at the given unix timestamp, if
32+
* available. Otherwise, returns 0, assuming UTC time.
2933
*/
30-
bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p) /**< [out] time zone structure to fill */
34+
double jerry_port_get_local_time_zone_adjustment (double unix_ms, /**< ms since unix epoch */
35+
bool is_utc) /**< is the time above in UTC? */
3136
{
32-
#ifdef __GNUC__
33-
struct timeval tv;
34-
struct timezone tz;
35-
36-
/* gettimeofday may not fill tz, so zero-initializing */
37-
tz.tz_minuteswest = 0;
38-
tz.tz_dsttime = 0;
39-
40-
if (gettimeofday (&tv, &tz) == 0)
37+
#ifdef HAVE_TM_GMTOFF
38+
struct tm tm;
39+
time_t now = (time_t) (unix_ms / 1000);
40+
localtime_r (&now, &tm);
41+
if (!is_utc)
4142
{
42-
tz_p->offset = tz.tz_minuteswest;
43-
tz_p->daylight_saving_time = tz.tz_dsttime > 0 ? 1 : 0;
44-
45-
return true;
43+
now -= tm.tm_gmtoff;
44+
localtime_r (&now, &tm);
4645
}
47-
#endif /* __GNUC__ */
48-
49-
return false;
50-
} /* jerry_port_get_time_zone */
46+
return ((double) tm.tm_gmtoff) * 1000;
47+
#else
48+
(void) unix_ms;
49+
(void) is_utc;
50+
return 0.0;
51+
#endif /* HAVE_TM_GMTOFF */
52+
} /* jerry_port_get_local_time_zone_adjustment */
5153

5254
/**
5355
* Default implementation of jerry_port_get_current_time. Uses 'gettimeofday' if

Diff for: targets/curie_bsp/source/curie-bsp-port.c

+4-7
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,13 @@ void jerry_port_fatal (jerry_fatal_code_t code)
5252
} /* jerry_port_fatal */
5353

5454
/**
55-
* Curie BSP implementation of jerry_port_get_time_zone.
55+
* Curie BSP implementation of jerry_port_get_local_time_zone_adjustment.
5656
*/
57-
bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p)
57+
double jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc)
5858
{
5959
//EMPTY implementation
60-
tz_p->offset = 0;
61-
tz_p->daylight_saving_time = 0;
62-
63-
return true;
64-
} /* jerry_port_get_time_zone */
60+
return 0;
61+
} /* jerry_port_get_local_time_zone_adjustment */
6562

6663
/**
6764
* Curie BSP implementation of jerry_port_get_current_time.

0 commit comments

Comments
 (0)