diff --git a/docs/05.PORT-API.md b/docs/05.PORT-API.md index 41c72a3252..2b503ef3d5 100644 --- a/docs/05.PORT-API.md +++ b/docs/05.PORT-API.md @@ -76,27 +76,34 @@ void jerry_port_log (jerry_log_level_t level, const char *fmt, ...); ```c /** - * Jerry time zone structure - */ -typedef struct -{ - int offset; /**< minutes from west */ - int daylight_saving_time; /**< daylight saving time (1 - DST applies, 0 - not on DST) */ -} jerry_time_zone_t; - -/** - * Get timezone and daylight saving data + * Get local time zone adjustment, in milliseconds, for the given timestamp. + * The timestamp can be specified in either UTC or local time, depending on + * the value of is_utc. Adding the value returned from this function to + * a timestamp in UTC time should result in local time for the current time + * zone. + * + * Ideally, this function should satisfy the stipulations applied to LocalTZA + * in section 20.3.1.7 of the ECMAScript version 9.0 spec. + * + * See Also: + * ECMA-262 v9, 20.3.1.7 * * Note: * This port function is called by jerry-core when * CONFIG_DISABLE_DATE_BUILTIN is _not_ defined. Otherwise this function is * not used. * - * @param[out] tz_p time zone structure to fill. - * @return true - if success - * false - otherwise + * @param unix_ms The unix timestamp we want an offset for, given in + * millisecond precision (could be now, in the future, + * or in the past) + * @param is_utc Is the given timestamp in UTC time? If false, it is in local + * time. + * + * @return milliseconds between local time and UTC for the given timestamp, + * if available + *. 0 if not available / we are in UTC. */ -bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p); +double jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc); /** * Get system time @@ -198,27 +205,21 @@ jerry_port_log (jerry_log_level_t level, /**< log level */ #include "jerryscript-port.h" /** - * Default implementation of jerry_port_get_time_zone. + * Default implementation of jerry_port_get_local_time_zone_adjustment. */ -bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p) +double jerry_port_get_local_time_zone_adjustment (double unix_ms, /**< ms since unix epoch */ + bool is_utc) /**< is the time above in utc? */ { - struct timeval tv; - struct timezone tz; - - /* gettimeofday may not fill tz, so zero-initializing */ - tz.tz_minuteswest = 0; - tz.tz_dsttime = 0; - - if (gettimeofday (&tv, &tz) != 0) + struct tm tm; + time_t now = (time_t) (unix_ms / 1000); + localtime_r (&now, &tm); + if (!is_utc) { - return false; + now -= tm.tm_gmtoff; + localtime_r (&now, &tm); } - - tz_p->offset = tz.tz_minuteswest; - tz_p->daylight_saving_time = tz.tz_dsttime > 0 ? 1 : 0; - - return true; -} /* jerry_port_get_time_zone */ + return ((double) tm.tm_gmtoff) * 1000; +} /* jerry_port_get_local_time_zone_adjustment */ /** * Default implementation of jerry_port_get_current_time. diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c index 4cba274628..454dafa981 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c @@ -600,7 +600,7 @@ ecma_builtin_date_prototype_dispatch_routine (uint16_t builtin_routine_id, /**< if (!BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id)) { - this_num += ecma_date_local_time_zone (this_num); + this_num += ecma_date_local_time_zone_adjustment (this_num); } if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c index 084b0ff479..32476a33a5 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c @@ -302,64 +302,22 @@ ecma_date_week_day (ecma_number_t time) /**< time value */ } /* ecma_date_week_day */ /** - * Helper function to get local time zone adjustment. - * - * See also: - * ECMA-262 v5, 15.9.1.7 - * - * @return local time zone adjustment - */ -static inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE -ecma_date_local_tza (jerry_time_zone_t *tz) /**< time zone information */ -{ - return tz->offset * -ECMA_DATE_MS_PER_MINUTE; -} /* ecma_date_local_tza */ - -/** - * Helper function to get the daylight saving time adjustment. - * - * See also: - * ECMA-262 v5, 15.9.1.8 - * - * @return daylight saving time adjustment - */ -static inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE -ecma_date_daylight_saving_ta (jerry_time_zone_t *tz, /**< time zone information */ - ecma_number_t time) /**< time value */ -{ - JERRY_ASSERT (!ecma_number_is_nan (time)); - - /* - * TODO: Fix daylight saving calculation. - * https://github.com/jerryscript-project/jerryscript/issues/1661 - */ - return tz->daylight_saving_time * ECMA_DATE_MS_PER_HOUR; -} /* ecma_date_daylight_saving_ta */ - -/** - * Helper function to get local time from UTC. + * Helper function to get the local time zone offset at a given UTC timestamp. + * You can add this number to the given UTC timestamp to get local time. * * See also: * ECMA-262 v5, 15.9.1.9 * - * @return local time + * @return local time zone adjustment */ inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE -ecma_date_local_time_zone (ecma_number_t time) /**< time value */ +ecma_date_local_time_zone_adjustment (ecma_number_t time) /**< time value */ { - jerry_time_zone_t tz; - - if (ecma_number_is_nan (time) - || !jerry_port_get_time_zone (&tz)) - { - return ecma_number_make_nan (); - } - - return ecma_date_local_tza (&tz) + ecma_date_daylight_saving_ta (&tz, time); -} /* ecma_date_local_time_zone */ + return jerry_port_get_local_time_zone_adjustment (time, true); +} /* ecma_date_local_time_zone_adjustment */ /** - * Helper function to get utc from local time. + * Helper function to get utc time from local time. * * See also: * ECMA-262 v5, 15.9.1.9 @@ -369,16 +327,7 @@ ecma_date_local_time_zone (ecma_number_t time) /**< time value */ ecma_number_t ecma_date_utc (ecma_number_t time) /**< time value */ { - jerry_time_zone_t tz; - - if (ecma_number_is_nan (time) - || !jerry_port_get_time_zone (&tz)) - { - return ecma_number_make_nan (); - } - - ecma_number_t simple_utc_time = time - ecma_date_local_tza (&tz); - return simple_utc_time - ecma_date_daylight_saving_ta (&tz, simple_utc_time); + return time - jerry_port_get_local_time_zone_adjustment (time, false); } /* ecma_date_utc */ /** @@ -615,7 +564,7 @@ ecma_date_timezone_offset (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - return (-ecma_date_local_time_zone (time)) / ECMA_DATE_MS_PER_MINUTE; + return (-ecma_date_local_time_zone_adjustment (time)) / ECMA_DATE_MS_PER_MINUTE; } /* ecma_date_timezone_offset */ /** @@ -724,7 +673,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ } case LIT_CHAR_LOWERCASE_Z: /* Time zone minutes part. */ { - int32_t time_zone = (int32_t) ecma_date_local_time_zone (datetime_number); + int32_t time_zone = (int32_t) ecma_date_local_time_zone_adjustment (datetime_number); if (time_zone >= 0) { @@ -744,7 +693,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ { JERRY_ASSERT (*format_p == LIT_CHAR_UPPERCASE_Z); /* Time zone seconds part. */ - int32_t time_zone = (int32_t) ecma_date_local_time_zone (datetime_number); + int32_t time_zone = (int32_t) ecma_date_local_time_zone_adjustment (datetime_number); if (time_zone < 0) { @@ -805,7 +754,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ ecma_value_t ecma_date_value_to_string (ecma_number_t datetime_number) /**< datetime */ { - datetime_number += ecma_date_local_time_zone (datetime_number); + datetime_number += ecma_date_local_time_zone_adjustment (datetime_number); return ecma_date_to_string_format (datetime_number, "$W $M $D $Y $h:$m:$s GMT$z:$Z"); } /* ecma_date_value_to_string */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index 68df529952..83f867eb3c 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -100,7 +100,7 @@ ecma_number_t ecma_date_year_from_time (ecma_number_t time); ecma_number_t ecma_date_month_from_time (ecma_number_t time); ecma_number_t ecma_date_date_from_time (ecma_number_t time); ecma_number_t ecma_date_week_day (ecma_number_t time); -ecma_number_t ecma_date_local_time_zone (ecma_number_t time); +ecma_number_t ecma_date_local_time_zone_adjustment (ecma_number_t time); ecma_number_t ecma_date_utc (ecma_number_t time); ecma_number_t ecma_date_hour_from_time (ecma_number_t time); ecma_number_t ecma_date_min_from_time (ecma_number_t time); diff --git a/jerry-core/include/jerryscript-port.h b/jerry-core/include/jerryscript-port.h index 59e4b61d29..3787f6c5b2 100644 --- a/jerry-core/include/jerryscript-port.h +++ b/jerry-core/include/jerryscript-port.h @@ -111,27 +111,34 @@ void JERRY_ATTR_FORMAT (printf, 2, 3) jerry_port_log (jerry_log_level_t level, c */ /** - * Jerry time zone structure - */ -typedef struct -{ - int offset; /**< minutes from west */ - int daylight_saving_time; /**< daylight saving time (1 - DST applies, 0 - not on DST) */ -} jerry_time_zone_t; - -/** - * Get timezone and daylight saving data + * Get local time zone adjustment, in milliseconds, for the given timestamp. + * The timestamp can be specified in either UTC or local time, depending on + * the value of is_utc. Adding the value returned from this function to + * a timestamp in UTC time should result in local time for the current time + * zone. + * + * Ideally, this function should satisfy the stipulations applied to LocalTZA + * in section 20.3.1.7 of the ECMAScript version 9.0 spec. + * + * See Also: + * ECMA-262 v9, 20.3.1.7 * * Note: * This port function is called by jerry-core when * CONFIG_DISABLE_DATE_BUILTIN is _not_ defined. Otherwise this function is * not used. * - * @param[out] tz_p time zone structure to fill. - * @return true - if success - * false - otherwise + * @param unix_ms The unix timestamp we want an offset for, given in + * millisecond precision (could be now, in the future, + * or in the past) + * @param is_utc Is the given timestamp in UTC time? If false, it is in local + * time. + * + * @return milliseconds between local time and UTC for the given timestamp, + * if available + *. 0 if not available / we are in UTC. */ -bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p); +double jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc); /** * Get system time diff --git a/jerry-port/default/CMakeLists.txt b/jerry-port/default/CMakeLists.txt index e388dc9936..025965a24d 100644 --- a/jerry-port/default/CMakeLists.txt +++ b/jerry-port/default/CMakeLists.txt @@ -26,6 +26,12 @@ file(GLOB SOURCE_PORT_DEFAULT *.c) # (should only be necessary if we used compiler default libc but not checking that) set(DEFINES_PORT_DEFAULT _BSD_SOURCE _DEFAULT_SOURCE) +INCLUDE (CheckStructHasMember) +CHECK_STRUCT_HAS_MEMBER ("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF) +if(HAVE_TM_GMTOFF) + set(DEFINES_PORT_DEFAULT ${DEFINES_PORT_DEFAULT} HAVE_TM_GMTOFF) +endif() + # Sleep function availability check INCLUDE (CheckIncludeFiles) CHECK_INCLUDE_FILES (time.h HAVE_TIME_H) diff --git a/jerry-port/default/default-date.c b/jerry-port/default/default-date.c index 837ef7d80e..e2db60b3e8 100644 --- a/jerry-port/default/default-date.c +++ b/jerry-port/default/default-date.c @@ -13,6 +13,9 @@ * limitations under the License. */ +#ifdef HAVE_TM_GMTOFF +#include +#endif #ifdef __GNUC__ #include #endif @@ -21,33 +24,32 @@ #include "jerryscript-port-default.h" /** - * Default implementation of jerry_port_get_time_zone. Uses 'gettimeofday' if - * available on the system, does nothing otherwise. + * Default implementation of jerry_port_get_local_time_zone_adjustment. Uses the 'tm_gmtoff' field + * of 'struct tm' (a GNU extension) filled by 'localtime_r' if available on the + * system, does nothing otherwise. * - * @return true - if 'gettimeofday' is available and executed successfully, - * false - otherwise. + * @return offset between UTC and local time at the given unix timestamp, if + * available. Otherwise, returns 0, assuming UTC time. */ -bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p) /**< [out] time zone structure to fill */ +double jerry_port_get_local_time_zone_adjustment (double unix_ms, /**< ms since unix epoch */ + bool is_utc) /**< is the time above in UTC? */ { -#ifdef __GNUC__ - struct timeval tv; - struct timezone tz; - - /* gettimeofday may not fill tz, so zero-initializing */ - tz.tz_minuteswest = 0; - tz.tz_dsttime = 0; - - if (gettimeofday (&tv, &tz) == 0) +#ifdef HAVE_TM_GMTOFF + struct tm tm; + time_t now = (time_t) (unix_ms / 1000); + localtime_r (&now, &tm); + if (!is_utc) { - tz_p->offset = tz.tz_minuteswest; - tz_p->daylight_saving_time = tz.tz_dsttime > 0 ? 1 : 0; - - return true; + now -= tm.tm_gmtoff; + localtime_r (&now, &tm); } -#endif /* __GNUC__ */ - - return false; -} /* jerry_port_get_time_zone */ + return ((double) tm.tm_gmtoff) * 1000; +#else + (void) unix_ms; + (void) is_utc; + return 0.0; +#endif /* HAVE_TM_GMTOFF */ +} /* jerry_port_get_local_time_zone_adjustment */ /** * Default implementation of jerry_port_get_current_time. Uses 'gettimeofday' if diff --git a/targets/curie_bsp/source/curie-bsp-port.c b/targets/curie_bsp/source/curie-bsp-port.c index 97cb896c42..1def05e2ec 100644 --- a/targets/curie_bsp/source/curie-bsp-port.c +++ b/targets/curie_bsp/source/curie-bsp-port.c @@ -52,16 +52,13 @@ void jerry_port_fatal (jerry_fatal_code_t code) } /* jerry_port_fatal */ /** - * Curie BSP implementation of jerry_port_get_time_zone. + * Curie BSP implementation of jerry_port_get_local_time_zone_adjustment. */ -bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p) +double jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc) { //EMPTY implementation - tz_p->offset = 0; - tz_p->daylight_saving_time = 0; - - return true; -} /* jerry_port_get_time_zone */ + return 0; +} /* jerry_port_get_local_time_zone_adjustment */ /** * Curie BSP implementation of jerry_port_get_current_time. diff --git a/targets/esp8266/user/jerry_port.c b/targets/esp8266/user/jerry_port.c index 7ba25e43ad..f4d5876973 100644 --- a/targets/esp8266/user/jerry_port.c +++ b/targets/esp8266/user/jerry_port.c @@ -61,16 +61,13 @@ jerry_port_get_current_time (void) } /* jerry_port_get_current_time */ /** - * Dummy function to get the time zone. + * Dummy function to get the time zone adjustment. * - * @return true + * @return 0 */ -bool -jerry_port_get_time_zone (jerry_time_zone_t *tz_p) +double +jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc) { /* We live in UTC. */ - tz_p->offset = 0; - tz_p->daylight_saving_time = 0; - - return true; -} /* jerry_port_get_time_zone */ + return 0; +} /* jerry_port_get_local_time_zone_adjustment */ diff --git a/targets/mbedos5/source/jerry_port_mbed.c b/targets/mbedos5/source/jerry_port_mbed.c index 3e225b0136..3572647157 100644 --- a/targets/mbedos5/source/jerry_port_mbed.c +++ b/targets/mbedos5/source/jerry_port_mbed.c @@ -46,17 +46,15 @@ jerry_port_log (jerry_log_level_t level, /**< log level */ #endif /* JSMBED_OVERRIDE_JERRY_PORT_LOG */ /** - * Implementation of jerry_port_get_time_zone. + * Implementation of jerry_port_get_local_time_zone_adjustment. * - * @return true - if success + * @return 0, as we live in UTC. */ -bool -jerry_port_get_time_zone (jerry_time_zone_t *tz_p) /**< timezone pointer */ +double +jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc) { - tz_p->offset = 0; - tz_p->daylight_saving_time = 0; - return true; -} /* jerry_port_get_time_zone */ + return 0; +} /* jerry_port_get_local_time_zone_adjustment */ /** * Implementation of jerry_port_get_current_time. diff --git a/targets/nuttx-stm32f4/jerry_main.c b/targets/nuttx-stm32f4/jerry_main.c index ef6a63c798..61419b99b9 100644 --- a/targets/nuttx-stm32f4/jerry_main.c +++ b/targets/nuttx-stm32f4/jerry_main.c @@ -492,19 +492,16 @@ jerry_port_log (jerry_log_level_t level, /**< log level */ } /* jerry_port_log */ /** - * Dummy function to get the time zone. + * Dummy function to get the time zone adjustment. * - * @return true + * @return 0 */ -bool -jerry_port_get_time_zone (jerry_time_zone_t *tz_p) +double +jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc) { /* We live in UTC. */ - tz_p->offset = 0; - tz_p->daylight_saving_time = 0; - - return true; -} /* jerry_port_get_time_zone */ + return 0; +} /* jerry_port_get_local_time_zone_adjustment */ /** * Dummy function to get the current time. diff --git a/targets/tizenrt-artik053/apps/jerryscript/jerry_main.c b/targets/tizenrt-artik053/apps/jerryscript/jerry_main.c index ce8cbe23ae..2774c58193 100644 --- a/targets/tizenrt-artik053/apps/jerryscript/jerry_main.c +++ b/targets/tizenrt-artik053/apps/jerryscript/jerry_main.c @@ -474,19 +474,16 @@ jerry_port_log (jerry_log_level_t level, /**< log level */ } /* jerry_port_log */ /** - * Dummy function to get the time zone. + * Dummy function to get the time zone adjustment. * - * @return true + * @return 0 */ -bool -jerry_port_get_time_zone (jerry_time_zone_t *tz_p) +double +jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc) { /* We live in UTC. */ - tz_p->offset = 0; - tz_p->daylight_saving_time = 0; - - return true; -} /* jerry_port_get_time_zone */ + return 0; +} /* jerry_port_get_local_time_zone_adjustment */ /** * Dummy function to get the current time. diff --git a/targets/zephyr/src/jerry-port.c b/targets/zephyr/src/jerry-port.c index b1eb9dcb67..5a129d8fbe 100644 --- a/targets/zephyr/src/jerry-port.c +++ b/targets/zephyr/src/jerry-port.c @@ -59,19 +59,16 @@ jerry_port_get_current_time (void) } /* jerry_port_get_current_time */ /** - * Dummy function to get the time zone. + * Dummy function to get the time zone adjustment. * - * @return true + * @return 0 */ -bool -jerry_port_get_time_zone (jerry_time_zone_t *tz_p) +double +jerry_port_get_local_time_zone_adjustment (double unix_ms, bool is_utc) { /* We live in UTC. */ - tz_p->offset = 0; - tz_p->daylight_saving_time = 0; - - return true; -} /* jerry_port_get_time_zone */ + return 0; +} /* jerry_port_get_local_time_zone_adjustment */ /** * Provide the implementation of jerryx_port_handler_print_char.