From e820cd81fd974f6b86b85aca6d1c6829d2716c27 Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 1 Mar 2016 14:30:23 +0800 Subject: [PATCH 01/30] Update bundle schema --- data/ntp.json | 8 -------- data/ntp.json.factory | 2 +- index.py | 8 ++++---- systime/ntp.py | 4 ++-- tests/test_systime/test_ntp.py | 2 +- 5 files changed, 8 insertions(+), 16 deletions(-) delete mode 100644 data/ntp.json diff --git a/data/ntp.json b/data/ntp.json deleted file mode 100644 index 9ad43e9..0000000 --- a/data/ntp.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "timezone": "+08:00,0", - "ntp": { - "enable": 1, - "server": "pool.ntp.org", - "interval": 7200 - } -} \ No newline at end of file diff --git a/data/ntp.json.factory b/data/ntp.json.factory index a72796e..aeb4ffe 100644 --- a/data/ntp.json.factory +++ b/data/ntp.json.factory @@ -1,7 +1,7 @@ { "timezone": "+08:00,0", "ntp": { - "enable": 0, + "enable": false, "server": "pool.ntp.org", "interval": 7200 } diff --git a/index.py b/index.py index 907f5f4..13080e0 100755 --- a/index.py +++ b/index.py @@ -23,7 +23,7 @@ class Index(Sanji): PUT_SCHEMA = Schema({ "timezone": All(str, Length(8)), "ntp": { - "enable": All(int, Range(min=0, max=1)), + "enable": bool, "server": All(str, Length(1, 2048)), "interval": All(int, Range(min=60, max=60*60*24*30)) } @@ -52,8 +52,8 @@ def put(self, message, response): rc = self.ntp.update(message.data["ntp"]) if rc is False: raise RuntimeWarning("Update ntp settings failed.") - self.model.db["ntp"] = dict(self.model.db["ntp"].items() - + message.data["ntp"].items()) + self.model.db["ntp"] = dict(self.model.db["ntp"].items() + + message.data["ntp"].items()) # change timezone if "timezone" in message.data: @@ -64,7 +64,7 @@ def put(self, message, response): # manual change sys time if "time" in message.data: - if self.model.db["ntp"]["enable"] == 1: + if self.model.db["ntp"]["enable"] is True: _logger.debug("NTP enabled. skipping time setup.") else: rc = SysTime.set_system_time(message.data["time"]) diff --git a/systime/ntp.py b/systime/ntp.py index eceacab..1517668 100644 --- a/systime/ntp.py +++ b/systime/ntp.py @@ -35,7 +35,7 @@ def __init__(self, model): self._ntp_deamon_event = Event() self._ntp_thread = Thread(target=self._ntp_update) self._ntp_thread.daemon = True - if self.model.db["ntp"]["enable"] == 1: + if self.model.db["ntp"]["enable"] is True: self.start() def update(self, config): @@ -45,7 +45,7 @@ def update(self, config): # restart ntp daemon, if enable otherwise stop it. self.stop() - if self.model.db["ntp"]["enable"] == 1: + if self.model.db["ntp"]["enable"] is True: NtpDate(self.model.db["ntp"]["server"]) self.start() diff --git a/tests/test_systime/test_ntp.py b/tests/test_systime/test_ntp.py index 64f882f..d64036e 100644 --- a/tests/test_systime/test_ntp.py +++ b/tests/test_systime/test_ntp.py @@ -73,7 +73,7 @@ def test_update(self): self.ntp.stop = Mock() self.ntp.start = Mock() with patch("systime.ntp.NtpDate") as NtpDate: - self.ntp.update({"enable": 1}) + self.ntp.update({"enable": True}) NtpDate.assert_called_once_with(self.model.db["ntp"]["server"]) self.ntp.stop.assert_called_once_with() self.ntp.start.assert_called_once_with() From 89cfa599961cd893b5783055fd6c14cefd9fbe31 Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 1 Mar 2016 14:30:53 +0800 Subject: [PATCH 02/30] Add OpenAPI doc --- schema/index.yaml | 109 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 schema/index.yaml diff --git a/schema/index.yaml b/schema/index.yaml new file mode 100644 index 0000000..8057280 --- /dev/null +++ b/schema/index.yaml @@ -0,0 +1,109 @@ +swagger: '2.0' + +info: + title: System Time API + description: System Time management + version: '0.0.1' + +schemes: +- http +- https + +produces: +- application/json + +paths: + /system/time: + get: + description: Get current time settings + responses: + 200: + description: success + schema: + $ref: '#/definitions/Time' + examples: + { + "application/json": { + $ref: '#/externalDocs/x-mocks/TimeExample' + } + } + + put: + parameters: + - name: body + in: body + required: true + schema: + $ref: '#/definitions/Time' + description: Update time settings + responses: + 200: + description: success + schema: + $ref: '#/definitions/Time' + examples: + { + "application/json": { + $ref: '#/externalDocs/x-mocks/TimeExample' + } + } + +definitions: + Time: + description: Time configuration + type: object + properties: + time: + description: 'Current system time (format: iso 8601)' + type: string + timezone: + description: Current timezone + type: string + ntp: + $ref: '#/definitions/NTP' + example: + $ref: '#/externalDocs/x-mocks/TimeExample' + + NTP: + description: NTP configuration + type: object + required: + - enable + - server + - interval + properties: + enable: + description: Enable NTP client + type: boolean + server: + description: Target NTP server + type: string + minLength: 1 + maxLength: 2048 + interval: + description: 'Time sync interval in seconds (at least: 60 seconds.)' + type: integer + minimum: 60 + maximum: 2592000 + example: + $ref: '#/externalDocs/x-mocks/NTPExample' + +externalDocs: + url: '#' + x-mocks: + TimeExample: + { + "time": "2015-03-26T16:27:48.611441Z", + "timezone": "+08:00,0", + "ntp": { + "enable": true, + "server": "pool.ntp.org", + "interval": 86400 + } + } + NTPExample: + { + "enable": false, + "server": "pool.ntp.org", + "interval": 86400 + } From de5acf5e1308b869aaddd3175dcb783c0c775993 Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 1 Mar 2016 14:31:55 +0800 Subject: [PATCH 03/30] 1.0.0 --- build-deb/debian/changelog | 6 ++++++ bundle.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build-deb/debian/changelog b/build-deb/debian/changelog index a3d3528..5286ceb 100644 --- a/build-deb/debian/changelog +++ b/build-deb/debian/changelog @@ -1,3 +1,9 @@ +sanji-bundle-time (1.0.0-1) unstable; urgency=low + + * Update API. + + -- Zack YL Shih Tue, 01 Mar 2016 14:31:22 +0800 + sanji-bundle-time (0.9.4-1) unstable; urgency=low * Sync to RTC while using NTP. diff --git a/bundle.json b/bundle.json index 65fffec..803bc3f 100644 --- a/bundle.json +++ b/bundle.json @@ -1,6 +1,6 @@ { "name": "time", - "version": "0.9.4", + "version": "1.0.0", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", "description": "System time management.", From a09af5f6160fae8b1d6418885ffcbc6d830bd1c6 Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 15 Mar 2016 16:34:31 +0800 Subject: [PATCH 04/30] Fix wrong schema --- index.py | 1 + 1 file changed, 1 insertion(+) diff --git a/index.py b/index.py index 13080e0..70d887c 100755 --- a/index.py +++ b/index.py @@ -21,6 +21,7 @@ class Index(Sanji): PUT_SCHEMA = Schema({ + "time": All(str, Length(1, 255)), "timezone": All(str, Length(8)), "ntp": { "enable": bool, From 67e113612f7fbedffa8398669548ed36bdf4c9e0 Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 15 Mar 2016 16:35:12 +0800 Subject: [PATCH 05/30] 1.0.1 --- build-deb/debian/changelog | 6 ++++++ bundle.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build-deb/debian/changelog b/build-deb/debian/changelog index 5286ceb..820ddd3 100644 --- a/build-deb/debian/changelog +++ b/build-deb/debian/changelog @@ -1,3 +1,9 @@ +sanji-bundle-time (1.0.1-1) unstable; urgency=low + + * Fix wrong schema. + + -- Zack YL Shih Tue, 15 Mar 2016 16:34:52 +0800 + sanji-bundle-time (1.0.0-1) unstable; urgency=low * Update API. diff --git a/bundle.json b/bundle.json index 803bc3f..684c788 100644 --- a/bundle.json +++ b/bundle.json @@ -1,6 +1,6 @@ { "name": "time", - "version": "1.0.0", + "version": "1.0.1", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", "description": "System time management.", From 1713105f624cb3a93dd0998f00db24d9cb1b6066 Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 19 Apr 2016 16:27:21 +0800 Subject: [PATCH 06/30] Use sh instead of subprocess.call --- requirements.txt | 1 + systime/ntp.py | 27 ++++++++++++++------------- tests/test_systime/test_ntp.py | 12 ++++-------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/requirements.txt b/requirements.txt index a972ab7..153b364 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ paho-mqtt sanji +sh diff --git a/systime/ntp.py b/systime/ntp.py index 1517668..38ae41b 100644 --- a/systime/ntp.py +++ b/systime/ntp.py @@ -8,24 +8,25 @@ import logging import math -import subprocess +import sh _logger = logging.getLogger("sanji.time") def NtpDate(server): - rc = subprocess.call(["ntpdate", server]) - _logger.debug("NTP update %s." % "successfully" - if rc == 0 else "failed") - if rc != 0: - return rc - - # Sync to RTC - rc = subprocess.call("hwclock -w", shell=True) - if rc == 0: - _logger.debug("Failed to sync to RTC") - - return rc + try: + sh.ntpdate(server, _timeout=30) + _logger.info("NTP update successfully") + except Exception as e: + _logger.info("NTP update failed") + _logger.warning(e) + return + + try: + sh.hwclock("-w", _timeout=10) + except Exception as e: + _logger.info("Failed to sync to RTC") + _logger.warning(e) class Ntp(object): diff --git a/tests/test_systime/test_ntp.py b/tests/test_systime/test_ntp.py index d64036e..f9d415d 100644 --- a/tests/test_systime/test_ntp.py +++ b/tests/test_systime/test_ntp.py @@ -24,15 +24,11 @@ class TestFunctionClass(unittest.TestCase): def test_NtpDate(self): server = "test.ntp.org" - with patch("systime.ntp.subprocess") as subprocess: - subprocess.call.return_value = 0 + with patch("systime.ntp.sh") as sh: + sh.ntpdate = Mock() NtpDate(server) - subprocess.call.assert_any_call("hwclock -w", shell=True) - - subprocess.call.reset_mock() - subprocess.call.return_value = 1 - NtpDate(server) - subprocess.call.assert_any_call(["ntpdate", server]) + sh.ntpdate.assert_called_once_with("test.ntp.org", _timeout=30) + sh.hwclock.assert_called_once_with("-w", _timeout=10) class TestNtpClass(unittest.TestCase): From c570d723cf8b77e9b82e36b32de48242b4b4681c Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 19 Apr 2016 16:27:32 +0800 Subject: [PATCH 07/30] Fix buggy sync --- systime/ntp.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/systime/ntp.py b/systime/ntp.py index 38ae41b..c7aada3 100644 --- a/systime/ntp.py +++ b/systime/ntp.py @@ -80,5 +80,9 @@ def _ntp_update(self): sleep(1) continue - prev_time = time() - NtpDate(self.model.db["ntp"]["server"]) + try: + NtpDate(self.model.db["ntp"]["server"]) + except Exception as e: + _logger.warning(e) + finally: + prev_time = time() From 15c06f82c93de054581ac8c32ee1f0ff5d3ae7e4 Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 19 Apr 2016 16:28:42 +0800 Subject: [PATCH 08/30] 1.0.2-1 --- build-deb/debian/changelog | 8 ++++++++ bundle.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/build-deb/debian/changelog b/build-deb/debian/changelog index 820ddd3..7a12746 100644 --- a/build-deb/debian/changelog +++ b/build-deb/debian/changelog @@ -1,3 +1,11 @@ +sanji-bundle-time (1.0.2-1) unstable; urgency=low + + * Use sh instead of subprocess.call. + * Fix buggy sync. + + + -- Zack YL Shih Tue, 19 Apr 2016 16:28:12 +0800 + sanji-bundle-time (1.0.1-1) unstable; urgency=low * Fix wrong schema. diff --git a/bundle.json b/bundle.json index 684c788..11699f1 100644 --- a/bundle.json +++ b/bundle.json @@ -1,6 +1,6 @@ { "name": "time", - "version": "1.0.1", + "version": "1.0.2", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", "description": "System time management.", From 2ad8a72e4b1a557d6533898464bf9c3805c32ef6 Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 19 Apr 2016 17:08:56 +0800 Subject: [PATCH 09/30] Enhance error message --- systime/ntp.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/systime/ntp.py b/systime/ntp.py index c7aada3..2db4e06 100644 --- a/systime/ntp.py +++ b/systime/ntp.py @@ -9,6 +9,7 @@ import logging import math import sh +from sh import TimeoutException _logger = logging.getLogger("sanji.time") @@ -17,6 +18,8 @@ def NtpDate(server): try: sh.ntpdate(server, _timeout=30) _logger.info("NTP update successfully") + except TimeoutException: + _logger.info("NTP update timeout or system date has been changed") except Exception as e: _logger.info("NTP update failed") _logger.warning(e) From d447bef40c8a0ba61979952fb43fdb45a090b7be Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Tue, 19 Apr 2016 17:09:26 +0800 Subject: [PATCH 10/30] 1.0.3 --- build-deb/debian/changelog | 6 ++++++ bundle.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build-deb/debian/changelog b/build-deb/debian/changelog index 7a12746..5d16f8b 100644 --- a/build-deb/debian/changelog +++ b/build-deb/debian/changelog @@ -1,3 +1,9 @@ +sanji-bundle-time (1.0.3-1) unstable; urgency=low + + * Enhance error message. + + -- Zack YL Shih Tue, 19 Apr 2016 17:09:14 +0800 + sanji-bundle-time (1.0.2-1) unstable; urgency=low * Use sh instead of subprocess.call. diff --git a/bundle.json b/bundle.json index 11699f1..dde4951 100644 --- a/bundle.json +++ b/bundle.json @@ -1,6 +1,6 @@ { "name": "time", - "version": "1.0.2", + "version": "1.0.3", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", "description": "System time management.", From 7c4c71ebc5c7b07c78211c8a54cb782998477afc Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Thu, 28 Apr 2016 16:34:33 +0800 Subject: [PATCH 11/30] Modify description --- bundle.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle.json b/bundle.json index dde4951..6644fde 100644 --- a/bundle.json +++ b/bundle.json @@ -3,7 +3,7 @@ "version": "1.0.3", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", - "description": "System time management.", + "description": "Provides the system-time management function", "license": "MOXA", "main": "index.py", "argument": "", From 519940705385a57bb08cb01cffa1be8b3fd12b1c Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Thu, 28 Apr 2016 16:36:09 +0800 Subject: [PATCH 12/30] Modify description --- bundle.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle.json b/bundle.json index dde4951..6644fde 100644 --- a/bundle.json +++ b/bundle.json @@ -3,7 +3,7 @@ "version": "1.0.3", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", - "description": "System time management.", + "description": "Provides the system-time management function", "license": "MOXA", "main": "index.py", "argument": "", From 930bf8c96e4c8273ad57f6d75ba91e7cd300889d Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Thu, 4 Aug 2016 14:16:06 +0800 Subject: [PATCH 13/30] doc: Add timezone information API 1. querying timezone information 2. updating timezone information --- schema/index.yaml | 106 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/schema/index.yaml b/schema/index.yaml index 8057280..f793bb8 100644 --- a/schema/index.yaml +++ b/schema/index.yaml @@ -3,7 +3,7 @@ swagger: '2.0' info: title: System Time API description: System Time management - version: '0.0.1' + version: '1.0.0' schemes: - http @@ -48,6 +48,27 @@ paths: } } + /system/zoneinfo: + get: + description: Get current timezone information + responses: + 200: + description: success + schema: + $ref: '#/definitions/Timezone' + examples: + { + "application/json": { + $ref: '#/externalDocs/x-mocks/TimezoneInfoExample' + } + } + + post: + description: Update timezone information from internet + responses: + 200: + description: success + definitions: Time: description: Time configuration @@ -88,13 +109,63 @@ definitions: example: $ref: '#/externalDocs/x-mocks/NTPExample' + Timezone: + description: Timezone information + type: object + required: + - zone + - iso3166 + properties: + zone: + description: TZ zone list with cca2 and TZ + readOnly: true + type: array + items: + $ref: '#/definitions/TimezoneZone' + iso3166: + description: ISO 3166 alpha-2 country codes + readOnly: true + type: array + items: + $ref: '#/definitions/TimezoneIso3166' + + TimezoneZone: + description: ISO 3166 alpha-2 country code and zone name + type: object + required: + - cca2 + - name + properties: + cca2: + description: ISO 3166 alpha-2 country code + type: string + pattern: '[A-Z]{2}' + name: + description: zone name + type: string + + TimezoneIso3166: + description: ISO 3166 alpha-2 country code and country name + type: object + required: + - cca2 + - name + properties: + cca2: + description: ISO 3166 alpha-2 country code + type: string + pattern: '[A-Z]{2}' + name: + description: The usual English name for the coded region + type: string + externalDocs: url: '#' x-mocks: TimeExample: { "time": "2015-03-26T16:27:48.611441Z", - "timezone": "+08:00,0", + "timezone": "Asia/Taipei", "ntp": { "enable": true, "server": "pool.ntp.org", @@ -107,3 +178,34 @@ externalDocs: "server": "pool.ntp.org", "interval": 86400 } + TimezoneInfoExample: + { + "zone": [ + { + "cca2": "AD", + "name": "Europe/Andorra", + }, + { + "cca2": "AE", + "name": "Asia/Dubai", + }, + { + "cca2": "AF", + "name": "Asia/Kabul", + } + ], + "iso3166": [ + { + "cca2": "AD", + "name": "Andorra" + }, + { + "cca2": "AE", + "name": "United Arab Emirates" + }, + { + "cca2": "AF", + "name": "Afghanistan" + } + ] + } From 90851a433ab6a36b65adeac1a4ae9b3a57a4b7ec Mon Sep 17 00:00:00 2001 From: Zack YL Shih Date: Mon, 17 Oct 2016 10:54:26 +0800 Subject: [PATCH 14/30] doc: fix schema --- schema/index.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/schema/index.yaml b/schema/index.yaml index f793bb8..3e9bd4a 100644 --- a/schema/index.yaml +++ b/schema/index.yaml @@ -128,6 +128,8 @@ definitions: type: array items: $ref: '#/definitions/TimezoneIso3166' + example: + $ref: '#/externalDocs/x-mocks/TimezoneInfoExample' TimezoneZone: description: ISO 3166 alpha-2 country code and zone name From 927bdc0a2c85eb2190cce609632a3e39806e9951 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 21 Oct 2016 13:43:06 +0800 Subject: [PATCH 15/30] fix: Wrong timezone list --- bundle.json | 4 +++ index.py | 8 ++++- systime/systime.py | 89 ++++++++++++++++------------------------------ 3 files changed, 42 insertions(+), 59 deletions(-) diff --git a/bundle.json b/bundle.json index 6644fde..7422cf9 100644 --- a/bundle.json +++ b/bundle.json @@ -17,6 +17,10 @@ { "methods": ["get", "put"], "resource": "/system/time" + }, + { + "methods": ["get"], + "resource": "/system/zoneinfo" } ] } diff --git a/index.py b/index.py index 70d887c..7d66a5e 100755 --- a/index.py +++ b/index.py @@ -22,7 +22,7 @@ class Index(Sanji): PUT_SCHEMA = Schema({ "time": All(str, Length(1, 255)), - "timezone": All(str, Length(8)), + "timezone": All(str, Length(0,255)), "ntp": { "enable": bool, "server": All(str, Length(1, 2048)), @@ -44,6 +44,12 @@ def get(self, message, response): return response( data=dict(self.model.db.items() + realtime_data.items())) + @Route(methods="get", resource="/system/zoneinfo") + def get_zoneinfo(self, message, response): + zoneinfo = SysTime.get_system_timezone_list() + + return response(data = dict(zoneinfo)) + @Route(methods="put", resource="/system/time", schema=PUT_SCHEMA) def put(self, message, response): rc = None diff --git a/systime/systime.py b/systime/systime.py index 84f36fd..0c89564 100644 --- a/systime/systime.py +++ b/systime/systime.py @@ -1,66 +1,16 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- +import os import subprocess from datetime import datetime class SysTime(object): - TIMEZONE = { - "-12:00,0": "Etc/GMT-12", # GMT-12 - "-11:00,0": "Pacific/Samoa", # SST - "-10:00,0": "US/Hawaii", # HST - "-09:00,1": "US/Alaska", # AKDT - "-08:00,1": "Pacific", # PDT - "-07:00,0": "US/Arizona", # MST - "-07:00,1": "US/Mountain", # MDT - "-06:00,0": "Canada/Saskatchewan", # CST - "-06:00,1": "US/Central", # CDT - "-05:00,0": "America/Bogota", # COT - "-05:00,1": "US/Eastern", # EDT - "-04:00,1": "America/Manaus", # AMT - "-04:00,0": "America/Caracas", # VET - "-03:30,1": "Canada/Newfoundland", # NDT - "-03:00,1": "America/Montevideo", # UYT - "-03:00,0": "right/America/Buenos_Aires", # ART - "-02:00,1": "America/Noronha", # FNT - "-01:00,1": "Atlantic/Azores", # AZOST - "-01:00,0": "Atlantic/Cape_Verde", # CVT - "-00:00,0": "Africa/Casablanca", # WET - "-00:00,1": "Europe/London", # BST - "+01:00,1": "Europe/Amsterdam", # CEST - "+01:00,0": "Africa/Gaborone", # CAT +02:00?? - "+02:00,1": "Asia/Amman", # EET +02:00?? - "+02:00,0": "Africa/Harare", # CAT +02:00?? - "+03:00,1": "Asia/Baghdad", # AST - "+03:00,0": "Asia/Kuwait", # AST - "+03:30,0": "Asia/Tehran", # IRDT - "+04:00,0": "Asia/Muscat", # GST - "+04:00,1": "Asia/Baku", # AZST - "+04:30,0": "Asia/Kabul", # AFT - "+05:00,1": "Asia/Oral", # ORAT - "+05:00,0": "Asia/Karachi", # PKT - "+05:30,0": "Asia/Kolkata", # IST - "+05:45,0": "Asia/Katmandu", # NPT - "+06:00,0": "Asia/Dhaka", # BDT - "+06:00,1": "Asia/Almaty", # ALMT - "+06:30,0": "Asia/Rangoon", # MMT - "+07:00,1": "Asia/Krasnoyarsk", # KRAST - "+07:00,0": "Asia/Bangkok", # ICT - "+08:00,0": "Asia/Taipei", # CST - "+08:00,1": "Asia/Irkutsk", # IRKST - "+09:00,1": "Asia/Yakutsk", # YAKST - "+09:00,0": "Asia/Tokyo", # JST - "+09:30,0": "Australia/Darwin", # CST - "+09:30,1": "Australia/Adelaide", # CST - "+10:00,0": "Australia/Brisbane", # EST - "+10:00,1": "Australia/Canberra", # EST - "+11:00,0": "Asia/Magadan", # MAGST - "+12:00,1": "Pacific/Auckland", # NZDT - "+12:00,0": "Pacific/Fiji", # FJT - "+13:00,0": "Pacific/Tongatapu", # TOT - } + ZONEINFO_PATH = "/usr/share/zoneinfo" + ZONETAB_PATH = "{}/zone.tab".format(ZONEINFO_PATH) + ISO3166TAB_PATH = "{}/iso3166.tab".format(ZONEINFO_PATH) @staticmethod def get_system_time(): @@ -88,20 +38,43 @@ def set_system_time(time_string): return True if rc == 0 else False + @staticmethod + def get_system_timezone_list(): + """ + return timezone list + """ + # list zone.tab + zonetab = [] + with open(SysTime.ZONETAB_PATH, "rb") as f: + for line in f: + if not line.startswith("#"): + zone = line.rstrip().split("\t") + zonetab.append({"cca2": zone[0], "name": zone[2]}) + + # list iso3166.tab + iso3166tab = [] + with open(SysTime.ISO3166TAB_PATH, "rb") as f: + for line in f: + if not line.startswith("#"): + zone = line.rstrip().split("\t") + iso3166tab.append({"cca2": zone[0], "name": zone[1]}) + + return {"zone": zonetab, "iso3166": iso3166tab} + @staticmethod def set_system_timezone(timezone): """ - tz_string should be listed in Time.TIMEZONE + timezone should be existed in /usr/share/zoneinfo Exception: ValueError if timezone sting is wrong """ - if timezone in SysTime.TIMEZONE: + if os.path.isfile("{}/{}".format(SysTime.ZONEINFO_PATH, timezone)): rc = subprocess.call("echo \"%s\" > /etc/timezone;" % - SysTime.TIMEZONE[timezone] + + timezone + "dpkg-reconfigure -f noninteractive tzdata", shell=True) else: - raise ValueError('Timezone string error.') + raise ValueError('Timezone not exist.') return True if rc == 0 else False From aa1fb44d8402d48c8e573ccc59abbff87e5b5180 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 21 Oct 2016 13:53:56 +0800 Subject: [PATCH 16/30] fix: Update config --- data/ntp.json.factory | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/ntp.json.factory b/data/ntp.json.factory index aeb4ffe..2f42a08 100644 --- a/data/ntp.json.factory +++ b/data/ntp.json.factory @@ -1,5 +1,5 @@ { - "timezone": "+08:00,0", + "timezone": "Asia/Taipei", "ntp": { "enable": false, "server": "pool.ntp.org", From cb5a452a5a31b8dad18b084a2cf4090f625c286e Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 21 Oct 2016 13:54:31 +0800 Subject: [PATCH 17/30] fix: pylint & unittest --- index.py | 4 ++-- tests/test_index.py | 10 +++++----- tests/test_systime/test_systime.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/index.py b/index.py index 7d66a5e..745d795 100755 --- a/index.py +++ b/index.py @@ -22,7 +22,7 @@ class Index(Sanji): PUT_SCHEMA = Schema({ "time": All(str, Length(1, 255)), - "timezone": All(str, Length(0,255)), + "timezone": All(str, Length(0, 255)), "ntp": { "enable": bool, "server": All(str, Length(1, 2048)), @@ -48,7 +48,7 @@ def get(self, message, response): def get_zoneinfo(self, message, response): zoneinfo = SysTime.get_system_timezone_list() - return response(data = dict(zoneinfo)) + return response(data=dict(zoneinfo)) @Route(methods="put", resource="/system/time", schema=PUT_SCHEMA) def put(self, message, response): diff --git a/tests/test_index.py b/tests/test_index.py index 40bb2a7..c6eb2fe 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -41,7 +41,7 @@ def tearDown(self): def test_get(self): result = { "time": "2015-03-26T16:27:48.611441Z", - "timezone": "+08:00,0", + "timezone": "Asia/Taipei", "ntp": { "enable": 0, "server": "pool.ntp.org", @@ -76,24 +76,24 @@ def test_put(self): with patch("index.SysTime.set_system_timezone") as set_system_timezone: resp = Mock() set_system_timezone.return_value = True - msg = Message({"data": {"timezone": "+08:00,0"}}) + msg = Message({"data": {"timezone": "Asia/Taipei"}}) self.index.put(message=msg, response=resp, test=True) resp.assert_called_once_with(data=result) # case 3: change timezone (Abnormal 1) resp = Mock() set_system_timezone.return_value = False - msg = Message({"data": {"timezone": "+08:00,0"}}) + msg = Message({"data": {"timezone": "Asia/Taipei"}}) self.index.put(message=msg, response=resp, test=True) resp.assert_called_once_with( code=500, data={"message": "Change timezone failed."}) # case 4: change timezone (Abnormal 2) resp = Mock() - msg = Message({"data": {"timezone": "+13:00,1"}}) + msg = Message({"data": {"timezone": "Asia/Taipe"}}) self.index.put(message=msg, response=resp, test=True) resp.assert_called_once_with( - code=400, data={"message": "Timezone string error."}) + code=400, data={"message": "Timezone not exist."}) # case 5: change system time (Normal) with patch("index.SysTime.set_system_time") as set_system_time: diff --git a/tests/test_systime/test_systime.py b/tests/test_systime/test_systime.py index 959da85..7faba36 100644 --- a/tests/test_systime/test_systime.py +++ b/tests/test_systime/test_systime.py @@ -48,15 +48,15 @@ def test_set_system_timezone(self): with patch("systime.systime.subprocess") as subprocess: # case 1: command success subprocess.call.return_value = 1 - self.assertFalse(SysTime.set_system_timezone("+07:00,0")) + self.assertFalse(SysTime.set_system_timezone("Asia/Bangkok")) # case 2: command failed subprocess.call.return_value = 0 - self.assertTrue(SysTime.set_system_timezone("+06:00,0")) + self.assertTrue(SysTime.set_system_timezone("Asia/Dhaka")) # case 3: invaild timezone string with self.assertRaises(ValueError): - SysTime.set_system_timezone("+06:00,3") + SysTime.set_system_timezone("Asia/Puli") if __name__ == "__main__": From 185ebe8de92efc84b9463fbb01bf07bf3b99254e Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 21 Oct 2016 14:37:16 +0800 Subject: [PATCH 18/30] 2.0.0 --- build-deb/debian/changelog | 6 ++++++ bundle.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build-deb/debian/changelog b/build-deb/debian/changelog index 5d16f8b..5550edf 100644 --- a/build-deb/debian/changelog +++ b/build-deb/debian/changelog @@ -1,3 +1,9 @@ +sanji-bundle-time (2.0.0-1) unstable; urgency=low + + * BugFix: Wrong timezone list. + + -- Aeluin Chen Fri, 21 Oct 2016 14:32:27 +0800 + sanji-bundle-time (1.0.3-1) unstable; urgency=low * Enhance error message. diff --git a/bundle.json b/bundle.json index 7422cf9..6a14647 100644 --- a/bundle.json +++ b/bundle.json @@ -1,6 +1,6 @@ { "name": "time", - "version": "1.0.3", + "version": "2.0.0", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", "description": "Provides the system-time management function", From b04aead7b7c68ed0064a2d841317c7555bb18463 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Wed, 22 Mar 2017 14:13:22 +0800 Subject: [PATCH 19/30] fix: Pass local time instead of UTC --- requirements.txt | 1 + systime/systime.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 153b364..820f9c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ paho-mqtt sanji sh +python-dateutil diff --git a/systime/systime.py b/systime/systime.py index 0c89564..f113963 100644 --- a/systime/systime.py +++ b/systime/systime.py @@ -4,6 +4,7 @@ import os import subprocess from datetime import datetime +import dateutil.tz as tz class SysTime(object): @@ -14,7 +15,7 @@ class SysTime(object): @staticmethod def get_system_time(): - return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ") + return datetime.now(tz.tzlocal()).strftime("%Y-%m-%dT%H:%M:%S.%f%z") @staticmethod def set_system_time(time_string): From c8aedc2de962df5b373c3cfe633102990cf86364 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Wed, 22 Mar 2017 14:08:15 +0800 Subject: [PATCH 20/30] test: Update unittest --- tests/test_systime/test_systime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_systime/test_systime.py b/tests/test_systime/test_systime.py index 7faba36..a1a58a3 100644 --- a/tests/test_systime/test_systime.py +++ b/tests/test_systime/test_systime.py @@ -23,7 +23,7 @@ class TestTimeClass(unittest.TestCase): def test_get_system_time(self): self.assertEqual( - datetime.utcnow().strftime("%Y-%m-%dT%H:%M")[0:13], + datetime.now().strftime("%Y-%m-%dT%H:%M")[0:13], SysTime.get_system_time()[0:13]) def test_set_system_time(self): From 3c331004716266432afb7b9890729250b2ed1491 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Wed, 22 Mar 2017 14:08:51 +0800 Subject: [PATCH 21/30] 2.0.1 --- build-deb/debian/changelog | 6 ++++++ bundle.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build-deb/debian/changelog b/build-deb/debian/changelog index 5550edf..647178c 100644 --- a/build-deb/debian/changelog +++ b/build-deb/debian/changelog @@ -1,3 +1,9 @@ +sanji-bundle-time (2.0.1-1) unstable; urgency=low + + * fix: Pass local time instead of UTC. + + -- Aeluin Chen Wed, 22 Mar 2017 14:07:03 +0800 + sanji-bundle-time (2.0.0-1) unstable; urgency=low * BugFix: Wrong timezone list. diff --git a/bundle.json b/bundle.json index 6a14647..fd46b21 100644 --- a/bundle.json +++ b/bundle.json @@ -1,6 +1,6 @@ { "name": "time", - "version": "2.0.0", + "version": "2.0.1", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", "description": "Provides the system-time management function", From bb2fc58d43a2e8f2dfc0e4b606bdcd94d802860b Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 24 Mar 2017 17:34:29 +0800 Subject: [PATCH 22/30] fix: Use "UTC" instead of local time --- systime/systime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systime/systime.py b/systime/systime.py index f113963..cb03347 100644 --- a/systime/systime.py +++ b/systime/systime.py @@ -15,7 +15,7 @@ class SysTime(object): @staticmethod def get_system_time(): - return datetime.now(tz.tzlocal()).strftime("%Y-%m-%dT%H:%M:%S.%f%z") + return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ") @staticmethod def set_system_time(time_string): From f0ba8a8b9f4fb670127b38ae89cd36dc9e207301 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 24 Mar 2017 17:35:06 +0800 Subject: [PATCH 23/30] fix: Update schema --- index.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/index.py b/index.py index 745d795..ebc594f 100755 --- a/index.py +++ b/index.py @@ -14,16 +14,23 @@ from voluptuous import Range from voluptuous import All from voluptuous import Length +from voluptuous import Optional +from datetime import datetime _logger = logging.getLogger("sanji.time") +def Timestamp(value): + datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%fZ") + return value + + class Index(Sanji): PUT_SCHEMA = Schema({ - "time": All(str, Length(1, 255)), - "timezone": All(str, Length(0, 255)), - "ntp": { + Optional("time"): Timestamp, + Optional("timezone"): All(str, Length(0, 255)), + Optional("ntp"): { "enable": bool, "server": All(str, Length(1, 2048)), "interval": All(int, Range(min=60, max=60*60*24*30)) From 0f2784bf470bb89cc638b1b882c9aee244df9070 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 24 Mar 2017 17:35:40 +0800 Subject: [PATCH 24/30] feat: Provide timezone offset in zoneinfo --- schema/index.yaml | 4 ++++ systime/systime.py | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/schema/index.yaml b/schema/index.yaml index 3e9bd4a..b75d568 100644 --- a/schema/index.yaml +++ b/schema/index.yaml @@ -137,6 +137,7 @@ definitions: required: - cca2 - name + - offset properties: cca2: description: ISO 3166 alpha-2 country code @@ -145,6 +146,9 @@ definitions: name: description: zone name type: string + offset: + description: zone offset (format should be "+0800", etc) + type: string TimezoneIso3166: description: ISO 3166 alpha-2 country code and country name diff --git a/systime/systime.py b/systime/systime.py index cb03347..4eb9a42 100644 --- a/systime/systime.py +++ b/systime/systime.py @@ -50,7 +50,11 @@ def get_system_timezone_list(): for line in f: if not line.startswith("#"): zone = line.rstrip().split("\t") - zonetab.append({"cca2": zone[0], "name": zone[2]}) + zonetab.append({ + "cca2": zone[0], + "name": zone[2], + "offset": datetime.now( + tz.gettz(zone[2])).strftime("%z")}) # list iso3166.tab iso3166tab = [] From 6d403d1703eaaf21ce7e1f02444eee7e273958aa Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 24 Mar 2017 17:36:02 +0800 Subject: [PATCH 25/30] test: Fix unittest --- tests/test_index.py | 46 +++++++++++++++++++++++------- tests/test_systime/test_systime.py | 4 +-- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/tests/test_index.py b/tests/test_index.py index c6eb2fe..a419f79 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -14,6 +14,8 @@ from sanji.connection.mockup import Mockup from sanji.message import Message +from voluptuous import er + try: sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../") from index import Index @@ -54,6 +56,34 @@ def test_get(self): self.index.get(message=None, response=resp, test=True) resp.assert_called_once_with(data=result) + def test_put_schema__should_pass(self): + # arrange + SUT = { + "time": "2015-03-26T16:27:48.611441Z", + "timezone": "Asia/Taipei", + "ntp": { + "enable": True, + "server": "pool.ntp.org", + "interval": 7200 + } + } + + # act + data = Index.PUT_SCHEMA(SUT) + + # assert + self.assertEqual(SUT, data) + + def test_put_schema__time_empty_should_fail(self): + # arrange + SUT = { + "time": "" + } + + # act + with self.assertRaises(er.MultipleInvalid): + Index.PUT_SCHEMA(SUT) + def test_put(self): result = { "time": ANY, @@ -99,7 +129,8 @@ def test_put(self): with patch("index.SysTime.set_system_time") as set_system_time: resp = Mock() set_system_time.return_value = True - msg = Message({"data": {"time": ""}}) + msg = Message( + {"data": {"time": "2015-03-26T16:27:48.611441Z"}}) self.index.put(message=msg, response=resp, test=True) resp.assert_called_once_with(data=result) @@ -110,14 +141,7 @@ def test_put(self): resp.assert_called_once_with( code=500, data={"message": "Change system time failed."}) - # case 7: change system time (Abnormal 2) - resp = Mock() - msg = Message({"data": {"time": ""}}) - self.index.put(message=msg, response=resp, test=True) - resp.assert_called_once_with( - code=400, data={"message": "Time format error."}) - - # case 8: update ntp settings (Normal) + # case 7: update ntp settings (Normal) with patch.object(self.index.ntp, "update") as update: resp = Mock() update.return_value = True @@ -129,14 +153,14 @@ def test_put(self): self.index.put(message=msg, response=resp, test=True) resp.assert_called_once_with(data=result) - # case 9: update ntp settings (Abnormal 1) + # case 8: update ntp settings (Abnormal 1) resp = Mock() update.return_value = False self.index.put(message=msg, response=resp, test=True) resp.assert_called_once_with( code=500, data={"message": "Update ntp settings failed."}) - # case 10: update ntp settings (Abnormal 2) + # case 9: update ntp settings (Abnormal 2) resp = Mock() update.side_effect = RuntimeError("Some exception.") self.index.put(message=msg, response=resp, test=True) diff --git a/tests/test_systime/test_systime.py b/tests/test_systime/test_systime.py index a1a58a3..e6bac1b 100644 --- a/tests/test_systime/test_systime.py +++ b/tests/test_systime/test_systime.py @@ -23,7 +23,7 @@ class TestTimeClass(unittest.TestCase): def test_get_system_time(self): self.assertEqual( - datetime.now().strftime("%Y-%m-%dT%H:%M")[0:13], + datetime.utcnow().strftime("%Y-%m-%dT%H:%M")[0:13], SysTime.get_system_time()[0:13]) def test_set_system_time(self): @@ -42,7 +42,7 @@ def test_set_system_time(self): # case 3: invaild input with self.assertRaises(ValueError): - SysTime.set_system_time("2015-0-26T16:27:48.611441Z") + SysTime.set_system_time("2015-0-26T16:27:48.611441+0800") def test_set_system_timezone(self): with patch("systime.systime.subprocess") as subprocess: From 18f54e6f40b824e7cdaf44c5aa56263081301b6b Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 24 Mar 2017 17:50:01 +0800 Subject: [PATCH 26/30] test: fix pylint --- index.py | 1 + 1 file changed, 1 insertion(+) diff --git a/index.py b/index.py index ebc594f..94dca9e 100755 --- a/index.py +++ b/index.py @@ -103,6 +103,7 @@ def put(self, message, response): return response( data=dict(self.model.db.items() + realtime_data.items())) + if __name__ == "__main__": from sanji.connection.mqtt import Mqtt FORMAT = "%(asctime)s - %(levelname)s - %(lineno)s - %(message)s" From 7feb0df2170424d46cc67fc93f26ff59283b3ef0 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 24 Mar 2017 17:45:39 +0800 Subject: [PATCH 27/30] docs: Update examples --- schema/index.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/schema/index.yaml b/schema/index.yaml index b75d568..d05e6b6 100644 --- a/schema/index.yaml +++ b/schema/index.yaml @@ -3,7 +3,7 @@ swagger: '2.0' info: title: System Time API description: System Time management - version: '1.0.0' + version: '1.1.0' schemes: - http @@ -190,14 +190,17 @@ externalDocs: { "cca2": "AD", "name": "Europe/Andorra", + "offset": "+0100" }, { "cca2": "AE", "name": "Asia/Dubai", + "offset": "+0400" }, { "cca2": "AF", "name": "Asia/Kabul", + "offset": "+0430" } ], "iso3166": [ From 8daf0a61355b9082cc5126d39fb1393e16000265 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 24 Mar 2017 17:39:29 +0800 Subject: [PATCH 28/30] 2.1.0 --- build-deb/debian/changelog | 7 +++++++ bundle.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build-deb/debian/changelog b/build-deb/debian/changelog index 647178c..ae84670 100644 --- a/build-deb/debian/changelog +++ b/build-deb/debian/changelog @@ -1,3 +1,10 @@ +sanji-bundle-time (2.1.0-1) unstable; urgency=low + + * fix: Use "UTC" instead of local time. + * feat: Provide timezone offset in zoneinfo. + + -- Aeluin Chen Fri, 24 Mar 2017 17:38:47 +0800 + sanji-bundle-time (2.0.1-1) unstable; urgency=low * fix: Pass local time instead of UTC. diff --git a/bundle.json b/bundle.json index fd46b21..7eadb08 100644 --- a/bundle.json +++ b/bundle.json @@ -1,6 +1,6 @@ { "name": "time", - "version": "2.0.1", + "version": "2.1.0", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", "description": "Provides the system-time management function", From 5b957fb5cfc7a19cf5078148985da4d3e8523b16 Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 31 Mar 2017 11:39:11 +0800 Subject: [PATCH 29/30] fix: Sync time at startup --- systime/ntp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/systime/ntp.py b/systime/ntp.py index 2db4e06..2e3fd8f 100644 --- a/systime/ntp.py +++ b/systime/ntp.py @@ -40,6 +40,7 @@ def __init__(self, model): self._ntp_thread = Thread(target=self._ntp_update) self._ntp_thread.daemon = True if self.model.db["ntp"]["enable"] is True: + NtpDate(self.model.db["ntp"]["server"]) self.start() def update(self, config): From ab031d53c2941e449c9cf864824d4b5ffa140daf Mon Sep 17 00:00:00 2001 From: Aeluin Chen Date: Fri, 31 Mar 2017 11:40:22 +0800 Subject: [PATCH 30/30] 2.1.1 --- build-deb/debian/changelog | 6 ++++++ bundle.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build-deb/debian/changelog b/build-deb/debian/changelog index ae84670..0b1dc96 100644 --- a/build-deb/debian/changelog +++ b/build-deb/debian/changelog @@ -1,3 +1,9 @@ +sanji-bundle-time (2.1.1-1) unstable; urgency=low + + * fix: Sync time at startup. + + -- Aeluin Chen Fri, 31 Mar 2017 11:39:38 +0800 + sanji-bundle-time (2.1.0-1) unstable; urgency=low * fix: Use "UTC" instead of local time. diff --git a/bundle.json b/bundle.json index 7eadb08..d3b8057 100644 --- a/bundle.json +++ b/bundle.json @@ -1,6 +1,6 @@ { "name": "time", - "version": "2.1.0", + "version": "2.1.1", "author": "Zack YL Shih", "email": "ZackYL.Shih@moxa.com", "description": "Provides the system-time management function",