Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DAY_TO_STR() localization #1059

Closed
wants to merge 2 commits into from

Conversation

alejandro-colomar
Copy link
Collaborator

@alejandro-colomar alejandro-colomar commented Jul 31, 2024

Cc: @eggert
Cc: @timparenti
Cc: @kenion


Revisions:

v2
  • Print localized dates and times by default.
  • Do not use custom formats.
  • Print timezone information together with date strings, as an RFC 9557 suffix.
  • Fix STRFTIME() macro
$ git range-diff shadow/master gh/time time 
1:  2686a033 < -:  -------- src/chage.c: Always use ISO 8601 format (YYYY-MM-DD) for printing dates
-:  -------- > 1:  68a97728 lib/, src/: Use %F instead of %Y-%m-%d with strftime(3)
-:  -------- > 2:  56067591 src/: Use %c for printing localized date&times with strftime(3)
-:  -------- > 3:  928172c4 src/chage.c: print_day_as_date(): Use %x for printing localized dates with strftime(3)
2:  de6f93bf = 4:  e78f9782 src/chage.c: print_day_as_date(): Simplify error handling
-:  -------- > 5:  fc46d1f3 lib/time/, src/: day_to_str(): Add support for formatting localized dates
3:  bd7701fa ! 6:  b2c33c58 src/chage.c: print_day_as_date(): Call DAY_TO_STR() to de-duplicate code
    @@ src/chage.c: static int new_fields (void)
     -          return;
     -  }
     -
    --  STRFTIME(buf, "%Y-%m-%d", &tm);
    +-  STRFTIME(buf, iflg ? "%F" : "%x", &tm);
     -  (void) puts (buf);
    -+  DAY_TO_STR(buf, day);
    ++  DAY_TO_STR(buf, day, iflg);
     +  puts(buf);
      }
      
-:  -------- > 7:  9c0ee33c lib/time/day_to_str.h: day_to_str(): Print numeric timezone together with local dates
-:  -------- > 8:  41a12dfe lib/string/strftime.h: STRFTIME(): Tighten macro definition
v3
  • Use the time zone name/abbreviation rather than the numeric offset. It's strongly preferred by RFC 9557. [@eggert]
$ git range-diff shadow/master gh/time time 
1:  68a97728 = 1:  68a97728 lib/, src/: Use %F instead of %Y-%m-%d with strftime(3)
2:  56067591 = 2:  56067591 src/: Use %c for printing localized date&times with strftime(3)
3:  928172c4 = 3:  928172c4 src/chage.c: print_day_as_date(): Use %x for printing localized dates with strftime(3)
4:  e78f9782 = 4:  e78f9782 src/chage.c: print_day_as_date(): Simplify error handling
5:  fc46d1f3 = 5:  fc46d1f3 lib/time/, src/: day_to_str(): Add support for formatting localized dates
6:  b2c33c58 = 6:  b2c33c58 src/chage.c: print_day_as_date(): Call DAY_TO_STR() to de-duplicate code
7:  9c0ee33c ! 7:  db4f966b lib/time/day_to_str.h: day_to_str(): Print numeric timezone together with local dates
    @@ Metadata
     Author: Alejandro Colomar <[email protected]>
     
      ## Commit message ##
    -    lib/time/day_to_str.h: day_to_str(): Print numeric timezone together with local dates
    +    lib/time/day_to_str.h: day_to_str(): Print timezone together with local dates
     
         Print it as a RFC 9557 suffix, which doesn't add any extra whitespace.
         This reduces the damage to expectations of scripts that might parse
    @@ Commit message
         Link: <https://github.com/shadow-maint/shadow/issues/1057>
         Link: <https://lists.sr.ht/~hallyn/shadow/%3Cmzbl4gkns5nyt7otg4t7cxlds2q64wutpjnngxpnix55ocbds2@whvswk343dwl%3E>
         Link: <https://www.rfc-editor.org/rfc/rfc9557.pdf>
    +    Link: <https://lists.gnu.org/archive/html/bug-gnulib/2024-08/msg00003.html>
         Suggested-by: Alejandro Colomar <[email protected]>
         Suggested-by: Paul Eggert <[email protected]>
         Cc: "Serge E. Hallyn" <[email protected]>
         Cc: Tim Parenti <[email protected]>
         Cc: Gus Kenion <https://github.com/kenion>
         Cc: Iker Pedrosa <[email protected]>
    +    Cc: Bruno Haible <[email protected]>
         Signed-off-by: Alejandro Colomar <[email protected]>
     
      ## lib/time/day_to_str.h ##
    @@ lib/time/day_to_str.h: day_to_str(size_t size, char buf[size], long day, bool is
        }
      
     -  if (strftime(buf, size, iso ? "%F" : "%x", &tm) == 0)
    -+  if (strftime(buf, size, iso ? "%F[%z]" : "%x[%z]", &tm) == 0)
    ++  if (strftime(buf, size, iso ? "%F[%Z]" : "%x[%Z]", &tm) == 0)
                strtcpy(buf, "future", size);
      }
      
8:  41a12dfe = 8:  bf3481df lib/string/strftime.h: STRFTIME(): Tighten macro definition
v4
  • Don't use a RFC 9557 suffix; that is, don't print timezone info. date(1) doesn't yet support them, so let's wait until that happens.
$ git range-diff shadow/master gh/time time 
1:  68a97728 = 1:  68a97728 lib/, src/: Use %F instead of %Y-%m-%d with strftime(3)
2:  56067591 = 2:  56067591 src/: Use %c for printing localized date&times with strftime(3)
3:  928172c4 = 3:  928172c4 src/chage.c: print_day_as_date(): Use %x for printing localized dates with strftime(3)
4:  e78f9782 = 4:  e78f9782 src/chage.c: print_day_as_date(): Simplify error handling
5:  fc46d1f3 = 5:  fc46d1f3 lib/time/, src/: day_to_str(): Add support for formatting localized dates
6:  b2c33c58 = 6:  b2c33c58 src/chage.c: print_day_as_date(): Call DAY_TO_STR() to de-duplicate code
7:  db4f966b < -:  -------- lib/time/day_to_str.h: day_to_str(): Print timezone together with local dates
8:  bf3481df = 7:  62b553e3 lib/string/strftime.h: STRFTIME(): Tighten macro definition
v5
  • Rebase
$ git range-diff master..gh/time shadow/master..time
1:  68a97728 < -:  -------- lib/, src/: Use %F instead of %Y-%m-%d with strftime(3)
2:  56067591 = 1:  b5d8ff0e src/: Use %c for printing localized date&times with strftime(3)
3:  928172c4 < -:  -------- src/chage.c: print_day_as_date(): Use %x for printing localized dates with strftime(3)
4:  e78f9782 < -:  -------- src/chage.c: print_day_as_date(): Simplify error handling
5:  fc46d1f3 = 2:  53554dc6 lib/time/, src/: day_to_str(): Add support for formatting localized dates
6:  b2c33c58 < -:  -------- src/chage.c: print_day_as_date(): Call DAY_TO_STR() to de-duplicate code
7:  62b553e3 < -:  -------- lib/string/strftime.h: STRFTIME(): Tighten macro definition

@alejandro-colomar
Copy link
Collaborator Author

Queued after #1058.

Copy link
Collaborator

@ikerexxe ikerexxe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good, I'll take another look when you resolve the conflicts.

@alejandro-colomar

This comment was marked as outdated.

src/chage.c Outdated Show resolved Hide resolved
@alejandro-colomar alejandro-colomar marked this pull request as ready for review August 1, 2024 09:06
@alejandro-colomar alejandro-colomar changed the title DAY_TO_STR() de-duplication DAY_TO_STR() improvements Aug 1, 2024
Copy link
Collaborator

@ikerexxe ikerexxe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this changed a bit since my last review.

I am not completely convinced about the changes, but if we make them we should add in the release notes that from now on all the dates printed follow the ISO standard.

src/chage.c Outdated
@@ -243,7 +243,7 @@ print_day_as_date(long day)
}

if (localtime_r(&date, &tm) == NULL) {
(void) printf ("time_t: %lu\n", (unsigned long)date);
puts(_("future"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change printf()?

Copy link
Collaborator Author

@alejandro-colomar alejandro-colomar Aug 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit e78f9782183e88e3973d4634d22c538a09f44b96
Author: Alejandro Colomar <[email protected]>
Date:   Wed Jul 31 14:53:12 2024 +0200

    src/chage.c: print_day_as_date(): Simplify error handling
    
    If localtime_r(3) fails, just print future, as we do in day_to_str().
    It should only fail for unrealistic dates, if at all.
    
    This is a step towards reusing the DAY_TO_STR() function.
    
    Signed-off-by: Alejandro Colomar <[email protected]>

diff --git a/src/chage.c b/src/chage.c
index 0f963090..8b2d7b35 100644
--- a/src/chage.c
+++ b/src/chage.c
@@ -243,7 +243,7 @@ print_day_as_date(long day)
        }
 
        if (localtime_r(&date, &tm) == NULL) {
-               (void) printf ("time_t: %lu\n", (unsigned long)date);
+               puts(_("future"));
                return;
        }
 

This makes it behave like day_to_str(), so that we can just call it there.

Copy link
Collaborator Author

@alejandro-colomar alejandro-colomar Aug 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$ diff -u \
    <(git show shadow/master:src/chage.c \
      | grepc -tfd print_day_as_date) \
    <(git show shadow/master:lib/time/day_to_str.h \
      | grepc -tfd day_to_str);
--- /dev/fd/63	2024-08-02 14:16:39.866679146 +0200
+++ /dev/fd/62	2024-08-02 14:16:39.866679146 +0200
@@ -1,24 +1,24 @@
-(standard input):static void
-print_day_as_date(long day)
+(standard input):inline void
+day_to_str(size_t size, char buf[size], long day)
 {
-	char       buf[80];
 	time_t     date;
 	struct tm  tm;
 
 	if (day < 0) {
-		puts(_("never"));
+		strtcpy(buf, "never", size);
 		return;
 	}
+
 	if (__builtin_mul_overflow(day, DAY, &date)) {
-		puts(_("future"));
+		strtcpy(buf, "future", size);
 		return;
 	}
 
 	if (localtime_r(&date, &tm) == NULL) {
-		(void) printf ("time_t: %lu\n", (unsigned long)date);
+		strtcpy(buf, "future", size);
 		return;
 	}
 
-	STRFTIME(buf, iflg ? "%Y-%m-%d" : "%b %d, %Y", &tm);
-	(void) puts (buf);
+	if (strftime(buf, size, "%Y-%m-%d", &tm) == 0)
+		strtcpy(buf, "future", size);
 }

Copy link
Collaborator Author

@alejandro-colomar alejandro-colomar Aug 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, after that commit, the difference is reduced to just:

$ diff -u     <(git show e78f9782183e:src/chage.c \
      | grepc -tfd print_day_as_date)     <(git show e78f9782183e:lib/time/day_to_str.h \
                               
      | grepc -tfd day_to_str);
--- /dev/fd/63	2024-08-02 14:16:16.050428094 +0200
+++ /dev/fd/62	2024-08-02 14:16:16.050428094 +0200
@@ -1,24 +1,24 @@
-(standard input):static void
-print_day_as_date(long day)
+(standard input):inline void
+day_to_str(size_t size, char buf[size], long day)
 {
-	char       buf[80];
 	time_t     date;
 	struct tm  tm;
 
 	if (day < 0) {
-		puts(_("never"));
+		strtcpy(buf, "never", size);
 		return;
 	}
+
 	if (__builtin_mul_overflow(day, DAY, &date)) {
-		puts(_("future"));
+		strtcpy(buf, "future", size);
 		return;
 	}
 
 	if (localtime_r(&date, &tm) == NULL) {
-		puts(_("future"));
+		strtcpy(buf, "future", size);
 		return;
 	}
 
-	STRFTIME(buf, iflg ? "%F" : "%x", &tm);
-	(void) puts (buf);
+	if (strftime(buf, size, "%F", &tm) == 0)
+		strtcpy(buf, "future", size);
 }

So the only difference after that change is the support for localized dates (which is also solved in another commit of this patch set).

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Aug 2, 2024

Well, this changed a bit since my last review.

I am not completely convinced about the changes, but if we make them we should add in the release notes that from now on all the dates printed follow the ISO standard.

Nah, I reverted that change. That was only present in v1. :)

Since v2 of this patch set, I changed custom(American)-formatted dates and date-times by locale-formatted dates (%x) and date-times (%c). And kept printing ISO dates only where we already printed them as ISO.

@hallyn
Copy link
Member

hallyn commented Aug 2, 2024

So this unfortunately begs the usual question - how many dark server rooms are running 20 year old scripts depending on the current date format that are going to break from a perfectly reasonable change?

I think none. I think this is fine.

@alejandro-colomar
Copy link
Collaborator Author

So this unfortunately begs the usual question - how many dark server rooms are running 20 year old scripts depending on the current date format that are going to break from a perfectly reasonable change?

The two breaking changes are:

5606759
928172c

I think none. I think this is fine.

Agree.

@alejandro-colomar alejandro-colomar mentioned this pull request Aug 3, 2024
@hallyn
Copy link
Member

hallyn commented Aug 3, 2024

I'm worried about that second one (928172c).

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Aug 3, 2024

I'm worried about that second one (928172c).

That's the one I'm less worried about. If they wanted to parse the output, they had the option to pass -i for a standard format.

I was more worried about the first one, since it was unconditionally non-standard.

(Or you might be worried about a script that would be written before chage(1) ever got a -i flag, which might be a possibility. Let's hope such a script doesn't exist.)


Edit:
Oh, now I see that -i was only added in 2019. Hmmm.

@hallyn
Copy link
Member

hallyn commented Aug 4, 2024

@alejandro-colomar Ok, for all of the commits (including the already recently merged ones), let's please add an /etc/login.defs switch to dictate whether to use the classic format, or the standards-compliant formats.

I'm 100% convinced there's some site out there parsing the date on '-' (%F) or on the %b %d %Y, and 928172c will mess them up.

Generally I'd prefer having fewer ways of dictating the format as it's going to make it harder to spot an accidental format printing bug, but I think it's worth it here.

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Aug 4, 2024

@alejandro-colomar Ok, for all of the commits (including the already recently merged ones), let's please add an /etc/login.defs switch to dictate whether to use the classic format, or the standards-compliant formats.

I'm 100% convinced there's some site out there parsing the date on '-' (%F) or on the %b %d %Y, and 928172c will mess them up.

Generally I'd prefer having fewer ways of dictating the format as it's going to make it harder to spot an accidental format printing bug, but I think it's worth it here.

Hmm, I don't like adding configurability either. How about the following instead?

Deprecate running chage(1) to print a date without -i. Print a deprecation warning to stderr when a date is printed in the non-standard format. Keep the nonstandard format untouched, for compatibility reasons.

Then we can come back in some years (4 or 5?) and unconditionally enable %F.

@alejandro-colomar alejandro-colomar marked this pull request as draft August 6, 2024 10:22
@kenion
Copy link

kenion commented Aug 26, 2024

Just for clarification, has a consensus been reached on how to move forward? Since change #1058 was reverted, issue #1057 is still present. For now, any user who updates to 4.14.6 from an earlier version will see the change in date input/output behavior described in that issue.

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Aug 26, 2024 via email

There were several custom date/time format strings.  Replace them by %c
so that the user gets the right date/time string, according to their
locale.

%c is specified by ISO C89.

Signed-off-by: Alejandro Colomar <[email protected]>
@alejandro-colomar alejandro-colomar changed the title DAY_TO_STR() improvements DAY_TO_STR() localization Aug 26, 2024
@kenion
Copy link

kenion commented Aug 26, 2024

@alejandro-colomar sorry for my misunderstanding. I've gone over the changes and comments again and I see the root cause of #1057 is addressed - thanks again to you and @hallyn.

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Aug 26, 2024

No problem. Thanks! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants