chiark / gitweb /
timedatectl: explain everything nobody wants to know about DST
[elogind.git] / src / timedate / timedatectl.c
index e94a847bf1be8d16410ad5412648978cbe8ff560..b7f2422a16ee2172318e2a70b28ebe578dc4d689 100644 (file)
@@ -33,8 +33,9 @@
 #include "hwclock.h"
 #include "strv.h"
 #include "pager.h"
+#include "time-dst.h"
 
-static bool arg_fix_system = false;
+static bool arg_adjust_system_clock = false;
 static bool arg_no_pager = false;
 static enum transport {
         TRANSPORT_NORMAL,
@@ -83,9 +84,13 @@ static bool ntp_synced(void) {
 
 static void print_status_info(StatusInfo *i) {
         usec_t n;
+        char a[FORMAT_TIMESTAMP_MAX];
         char b[FORMAT_TIMESTAMP_MAX];
         struct tm tm;
         time_t sec;
+        char *zc, *zn;
+        time_t t, tc, tn;
+        bool is_dstc, is_dstn;
         int r;
 
         assert(i);
@@ -94,14 +99,14 @@ static void print_status_info(StatusInfo *i) {
         sec = (time_t) (n / USEC_PER_SEC);
 
         zero(tm);
-        assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
-        char_array_0(b);
-        printf("      Local time: %s\n", b);
+        assert_se(strftime(a, sizeof(a), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
+        char_array_0(a);
+        printf("      Local time: %s\n", a);
 
         zero(tm);
-        assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
-        char_array_0(b);
-        printf("  Universal time: %s\n", b);
+        assert_se(strftime(a, sizeof(a), "%a, %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
+        char_array_0(a);
+        printf("  Universal time: %s\n", a);
 
         zero(tm);
         r = hwclock_get_time(&tm);
@@ -109,20 +114,61 @@ static void print_status_info(StatusInfo *i) {
                 /* Calculcate the week-day */
                 mktime(&tm);
 
-                assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S", &tm) > 0);
-                char_array_0(b);
-                printf("        RTC time: %s\n", b);
+                assert_se(strftime(a, sizeof(a), "%a, %Y-%m-%d %H:%M:%S", &tm) > 0);
+                char_array_0(a);
+                printf("        RTC time: %s\n", a);
         }
 
+        zero(tm);
+        assert_se(strftime(a, sizeof(a), "%z", localtime_r(&sec, &tm)) > 0);
+        char_array_0(a);
         printf("        Timezone: %s\n"
+               "      UTC offset: %s\n"
                "     NTP enabled: %s\n"
                "NTP synchronized: %s\n"
                " RTC in local TZ: %s\n",
                strna(i->timezone),
+               a,
                yes_no(i->ntp),
                yes_no(ntp_synced()),
                yes_no(i->local_rtc));
 
+        r = time_get_dst(sec, "/etc/localtime",
+                         &tc, &zc, &is_dstc,
+                         &tn, &zn, &is_dstn);
+        if (r >= 0) {
+                printf("      DST active: %s\n", yes_no(is_dstc));
+
+                t = tc - 1;
+                zero(tm);
+                assert_se(strftime(a, sizeof(a), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
+                char_array_0(a);
+
+                zero(tm);
+                assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&tc, &tm)) > 0);
+                char_array_0(b);
+                printf(" Last DST change: %s → %s, one hour %s\n"
+                       "                  %s\n"
+                       "                  %s\n",
+                       strna(zn), strna(zc), is_dstc ? "forward" : "backwards", a, b);
+
+                t = tn - 1;
+                zero(tm);
+                assert_se(strftime(a, sizeof(a), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
+                char_array_0(a);
+
+                zero(tm);
+                assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&tn, &tm)) > 0);
+                char_array_0(b);
+                printf(" Next DST change: %s → %s, one hour %s\n"
+                       "                  %s\n"
+                       "                  %s\n",
+                       strna(zc), strna(zn), is_dstn ? "forward" : "backwards", a, b);
+
+                free(zc);
+                free(zn);
+        }
+
         if (i->local_rtc)
                 fputs("\n" ANSI_HIGHLIGHT_ON
                       "Warning: The RTC is configured to maintain time in the local time zone. This\n"
@@ -302,7 +348,7 @@ static int set_local_rtc(DBusConnection *bus, char **args, unsigned n) {
         }
 
         b = r;
-        q = arg_fix_system;
+        q = arg_adjust_system_clock;
 
         return bus_method_call_with_reply(
                         bus,
@@ -349,16 +395,10 @@ static int set_ntp(DBusConnection *bus, char **args, unsigned n) {
                         DBUS_TYPE_INVALID);
 }
 
-static int zone_compare(const void *_a, const void *_b) {
-        const char **a = (const char**) _a, **b = (const char**) _b;
-
-        return strcmp(*a, *b);
-}
-
 static int list_timezones(DBusConnection *bus, char **args, unsigned n) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_strv_free_ char **zones = NULL;
-        size_t n_zones;
+        size_t n_zones = 0;
         char **i;
 
         assert(args);
@@ -416,12 +456,11 @@ static int list_timezones(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (zones)
-                zones[n_zones] = 0;
-
-        qsort(zones, n_zones, sizeof(char*), zone_compare);
+                zones[n_zones] = NULL;
 
         pager_open_if_enabled();
 
+        strv_sort(zones);
         STRV_FOREACH(i, zones)
                 puts(*i);
 
@@ -430,21 +469,22 @@ static int list_timezones(DBusConnection *bus, char **args, unsigned n) {
 
 static int help(void) {
 
-        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
-               "Query or control system time and date settings.\n\n"
+        printf("%s [OPTIONS...] COMMAND ...\n\n"
+               "Query or change system time and date settings.\n\n"
                "  -h --help              Show this help\n"
                "     --version           Show package version\n"
-               "     --fix-system        Adjust system clock when changing local RTC mode\n"
+               "     --adjust-system-clock\n"
+               "                         Adjust system clock when changing local RTC mode\n"
                "     --no-pager          Do not pipe output into a pager\n"
                "     --no-ask-password   Do not prompt for password\n"
                "  -H --host=[USER@]HOST  Operate on remote host\n\n"
                "Commands:\n"
-               "  status                          Show current time settings\n"
-               "  set-time [TIME]                 Set system time\n"
-               "  set-timezone [ZONE]             Set system timezone\n"
-               "  list-timezones                  Show known timezones\n"
-               "  set-local-rtc [BOOL]            Control whether RTC is in local time\n"
-               "  set-ntp [BOOL]                  Control whether NTP is enabled\n",
+               "  status                 Show current time settings\n"
+               "  set-time TIME          Set system time\n"
+               "  set-timezone ZONE      Set system timezone\n"
+               "  list-timezones         Show known timezones\n"
+               "  set-local-rtc BOOL     Control whether RTC is in local time\n"
+               "  set-ntp BOOL           Control whether NTP is enabled\n",
                program_invocation_short_name);
 
         return 0;
@@ -455,19 +495,19 @@ static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
                 ARG_NO_PAGER,
-                ARG_FIX_SYSTEM,
+                ARG_ADJUST_SYSTEM_CLOCK,
                 ARG_NO_ASK_PASSWORD
         };
 
         static const struct option options[] = {
-                { "help",            no_argument,       NULL, 'h'                 },
-                { "version",         no_argument,       NULL, ARG_VERSION         },
-                { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
-                { "host",            required_argument, NULL, 'H'                 },
-                { "privileged",      no_argument,       NULL, 'P'                 },
-                { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
-                { "fix-system",      no_argument,       NULL, ARG_FIX_SYSTEM      },
-                { NULL,              0,                 NULL, 0                   }
+                { "help",                no_argument,       NULL, 'h'                     },
+                { "version",             no_argument,       NULL, ARG_VERSION             },
+                { "no-pager",            no_argument,       NULL, ARG_NO_PAGER            },
+                { "host",                required_argument, NULL, 'H'                     },
+                { "privileged",          no_argument,       NULL, 'P'                     },
+                { "no-ask-password",     no_argument,       NULL, ARG_NO_ASK_PASSWORD     },
+                { "adjust-system-clock", no_argument,       NULL, ARG_ADJUST_SYSTEM_CLOCK },
+                { NULL,                  0,                 NULL, 0                       }
         };
 
         int c;
@@ -475,7 +515,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "+hp:as:H:P", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "+hH:P", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -498,8 +538,8 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_host = optarg;
                         break;
 
-                case ARG_FIX_SYSTEM:
-                        arg_fix_system = true;
+                case ARG_ADJUST_SYSTEM_CLOCK:
+                        arg_adjust_system_clock = true;
                         break;
 
                 case ARG_NO_PAGER: