summaryrefslogtreecommitdiffstats
path: root/debian/patches/source-date-epoch-utc.patch
blob: 0853b1ed205ac054c71f8b28c1c63594a9f5c554 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
From 91dee0fbd709ec191800fbb1d12446215c7d0380 Mon Sep 17 00:00:00 2001
From: Colin Watson <cjwatson@debian.org>
Date: Sun, 9 Jul 2023 13:23:21 +0100
Subject: Display time from SOURCE_DATE_EPOCH in UTC.

The semantics imposed in 1.23.0 are unsuitable for use with
reproducible-builds harnesses, since those specifically want to vary the
TZ environment variable to shake out other problems in build systems.
However, my patch that Debian has been carrying for a while is
unsuitable for general use, since most people expect the time displayed
in output to use local time.

A viable compromise seems to be to force UTC _only_ when
SOURCE_DATE_EPOCH is set.  That will keep reproducible-builds harnesses
working with no extra effort, while also preserving the expected
behaviour for typical users of groff that don't go out of their way to
set that environment variable.

As a bonus, this corrects the behaviour of gropdf when the local offset
from UTC is not a whole number of hours.

* src/include/curtime.h (current_time): Return a `struct tm *`.
  Document behaviour.
* src/libs/libgroff/curtime.cpp (current_time): If SOURCE_DATE_EPOCH is
  set, return the overridden time after passing it through `gmtime`.
  Otherwise, pass the current time through `localtime`.

* src/devices/grohtml/post-html.cpp (html_printer::do_file_components,
  html_printer::~html_printer):
* src/devices/grops/ps.cpp (ps_printer::~ps_printer):
* src/roff/troff/input.cpp (init_registers): Adjust to new
  `current_time` signature.

* src/devices/gropdf/gropdf.pl: If SOURCE_DATE_EPOCH is set, return the
  overridden time after passing it through `gmtime`.  Otherwise, pass
  the current time through `localtime`.
  (PDFDate): Fix output in the case where the local offset from UTC is
  not a whole number of hours.  (Previously, the minutes offset field
  was always set to zero.)

* doc/groff.texi (Environment):
* src/devices/grohtml/grohtml.1.man (Environment):
* src/devices/gropdf/gropdf.1.man (Environment):
* src/devices/grops/grops.1.man (Environment):
* src/roff/groff/groff.1.man (Environment):
* src/roff/troff/troff.1.man (Environment): Update.

Origin: upstream, https://git.savannah.gnu.org/cgit/groff.git/commit/?id=d7bbfb04ea25a82a8597cdef6ebb391cb78ab47c
Origin: upstream, https://git.savannah.gnu.org/cgit/groff.git/commit/?id=0815e503dba8d5c05921d68c6c718fe8f8440ee8
Last-Update: 2024-04-30

Patch-Name: source-date-epoch-utc.patch
---
 doc/groff.texi                    | 13 +++++++------
 src/devices/grohtml/grohtml.1.man | 12 +++++++-----
 src/devices/grohtml/post-html.cpp | 16 ++++------------
 src/devices/gropdf/gropdf.1.man   | 10 +++++-----
 src/devices/gropdf/gropdf.pl      | 19 +++++++++++++++----
 src/devices/grops/grops.1.man     | 12 +++++++-----
 src/devices/grops/ps.cpp          |  9 ++-------
 src/include/curtime.h             | 19 +++++++++++--------
 src/libs/libgroff/curtime.cpp     | 23 +++++++++++++----------
 src/roff/groff/groff.1.man        | 12 +++++++-----
 src/roff/troff/input.cpp          | 24 +++++++++---------------
 src/roff/troff/troff.1.man        | 12 +++++++-----
 12 files changed, 94 insertions(+), 87 deletions(-)

diff --git a/doc/groff.texi b/doc/groff.texi
index 2a6635e9d..bcea4f3e7 100644
--- a/doc/groff.texi
+++ b/doc/groff.texi
@@ -1389,15 +1389,16 @@ overrides @env{GROFF_TYPESETTER}.
 @tindex SOURCE_DATE_EPOCH@r{, environment variable}
 A timestamp (expressed as seconds since the Unix epoch) to use as the
 output creation timestamp in place of the current time.  The time is
-converted to human-readable form using @cite{localtime@r{(3)}} when the
-formatter starts up and stored in registers usable by documents and
-macro packages (@pxref{Built-in Registers}).
+converted to human-readable form using @cite{gmtime@r{(3)}} and
+@cite{asctime@r{(3)}} when the formatter starts up and stored in
+registers usable by documents and macro packages (@pxref{Built-in
+Registers}).
 
 @item TZ
 @tindex TZ@r{, environment variable}
-The time zone to use when converting the current time (or value of
-@env{SOURCE_DATE_EPOCH}) to human-readable form; see
-@cite{tzset@r{(3)}}.
+The time zone to use when converting the current time to human-readable
+form; see @cite{tzset@r{(3)}}.  If @env{SOURCE_DATE_EPOCH} is used, it
+is always converted to human-readable form using UTC.
 @end table
 
 MS-DOS and MS-Windows ports of @code{groff} use semicolons, rather than
diff --git a/src/devices/grohtml/grohtml.1.man b/src/devices/grohtml/grohtml.1.man
index 2243b474c..8a55c93dd 100644
--- a/src/devices/grohtml/grohtml.1.man
+++ b/src/devices/grohtml/grohtml.1.man
@@ -616,18 +616,20 @@ A timestamp
 to use as the output creation timestamp in place of the current time.
 .
 The time is converted to human-readable form using
-.MR ctime 3
+.MR gmtime 3
+and
+.MR asctime 3 ,
 and recorded in an HTML comment.
 .
 .
 .TP
 .I TZ
-The time zone to use when converting the current time
-(or value of
-.IR SOURCE_DATE_EPOCH )
-to human-readable form;
+The time zone to use when converting the current time to human-readable form;
 see
 .MR tzset 3 .
+If
+.I SOURCE_DATE_EPOCH
+is used, it is always converted to human-readable form using UTC.
 .
 .
 .\" ====================================================================
diff --git a/src/devices/grohtml/post-html.cpp b/src/devices/grohtml/post-html.cpp
index 4e02b5cfa..b42720da1 100644
--- a/src/devices/grohtml/post-html.cpp
+++ b/src/devices/grohtml/post-html.cpp
@@ -5081,11 +5081,7 @@ void html_printer::do_file_components (void)
     fclose(file_list.get_file());
     file_list.move_next();
     if (file_list.is_new_output_file()) {
-#ifdef LONG_FOR_TIME_T
-      long t;
-#else
-      time_t t;
-#endif
+      struct tm *t;
 
       if (fragment_no > 1)
 	write_navigation(top, prev, next, current);
@@ -5115,7 +5111,7 @@ void html_printer::do_file_components (void)
       if (do_write_date_comment) {
 	t = current_time();
 	html.begin_comment("CreationDate: ")
-	  .put_string(ctime(&t), strlen(ctime(&t))-1)
+	  .put_string(asctime(t), strlen(asctime(t))-1)
 	  .end_comment();
       }
 
@@ -5215,11 +5211,7 @@ void html_printer::writeHeadMetaStyle (void)
 
 html_printer::~html_printer()
 {
-#ifdef LONG_FOR_TIME_T
-  long t;
-#else
-  time_t t;
-#endif
+  struct tm *t;
 
   if (current_paragraph)
     current_paragraph->flush_text();
@@ -5240,7 +5232,7 @@ html_printer::~html_printer()
   if (do_write_date_comment) {
     t = current_time();
     html.begin_comment("CreationDate: ")
-      .put_string(ctime(&t), strlen(ctime(&t))-1)
+      .put_string(asctime(t), strlen(asctime(t))-1)
       .end_comment();
   }
 
diff --git a/src/devices/gropdf/gropdf.1.man b/src/devices/gropdf/gropdf.1.man
index d1d39bbe0..20a957e68 100644
--- a/src/devices/gropdf/gropdf.1.man
+++ b/src/devices/gropdf/gropdf.1.man
@@ -1673,18 +1673,18 @@ A timestamp
 to use as the output creation timestamp in place of the current time.
 .
 The time is converted to human-readable form using Perl's
-.I \%localtime()
+.I \%gmtime()
 function and recorded in a PDF comment.
 .
 .
 .TP
 .I TZ
-The time zone to use when converting the current time
-(or value of
-.IR SOURCE_DATE_EPOCH )
-to human-readable form;
+The time zone to use when converting the current time to human-readable form;
 see
 .MR tzset 3 .
+If
+.I SOURCE_DATE_EPOCH
+is used, it is always converted to human-readable form using UTC.
 .
 .
 .\" ====================================================================
diff --git a/src/devices/gropdf/gropdf.pl b/src/devices/gropdf/gropdf.pl
index c65a1051f..4ba5a48df 100644
--- a/src/devices/gropdf/gropdf.pl
+++ b/src/devices/gropdf/gropdf.pl
@@ -23,6 +23,7 @@
 use strict;
 use warnings;
 use Getopt::Long qw(:config bundling);
+use POSIX qw(mktime);
 
 use constant
 {
@@ -343,8 +344,7 @@ for $papersz ( split(" ", lc($possiblesizes).' #duff#') )
     # If we get here, $papersz was invalid, so try the next one.
 }
 
-my (@dt)=localtime($ENV{SOURCE_DATE_EPOCH} || time);
-my $dt=PDFDate(\@dt);
+my $dt=PDFDate(time);
 
 my %info=('Creator' => "(groff version $cfg{GROFF_VERSION})",
 				'Producer' => "(gropdf version $cfg{GROFF_VERSION})",
@@ -627,8 +627,19 @@ sub GetObj
 
 sub PDFDate
 {
-    my $dt=shift;
-    return(sprintf("D:%04d%02d%02d%02d%02d%02d%+03d'00'",$dt->[5]+1900,$dt->[4]+1,$dt->[3],$dt->[2],$dt->[1],$dt->[0],( localtime time() + 3600*( 12 - (gmtime)[2] ) )[2] - 12));
+    my $ts=shift;
+    my @dt;
+    my $offset;
+    my $rel;
+    if ($ENV{SOURCE_DATE_EPOCH}) {
+	$offset=0;
+	@dt=gmtime($ENV{SOURCE_DATE_EPOCH});
+    } else {
+	@dt=localtime($ts);
+	$offset=mktime(@dt[0..5]) - mktime((gmtime $ts)[0..5]);
+    }
+    $rel=($offset==0)?'Z':($offset>0)?'+':'-';
+    return(sprintf("D:%04d%02d%02d%02d%02d%02d%s%02d'%02d'",$dt[5]+1900,$dt[4]+1,$dt[3],$dt[2],$dt[1],$dt[0],$rel,int(abs($offset)/3600),int((abs($offset)%3600)/60)));
 }
 
 sub ToPoints
diff --git a/src/devices/grops/grops.1.man b/src/devices/grops/grops.1.man
index d0ec21d0b..53014ee1b 100644
--- a/src/devices/grops/grops.1.man
+++ b/src/devices/grops/grops.1.man
@@ -1696,18 +1696,20 @@ A timestamp
 to use as the output creation timestamp in place of the current time.
 .
 The time is converted to human-readable form using
-.MR ctime 3
+.MR gmtime 3
+and
+.MR asctime 3 ,
 and recorded in a PostScript comment.
 .
 .
 .TP
 .I TZ
-The time zone to use when converting the current time
-(or value of
-.IR SOURCE_DATE_EPOCH )
-to human-readable form;
+The time zone to use when converting the current time to human-readable form;
 see
 .MR tzset 3 .
+If
+.I SOURCE_DATE_EPOCH
+is used, it is always converted to human-readable form using UTC.
 .
 .
 .\" ====================================================================
diff --git a/src/devices/grops/ps.cpp b/src/devices/grops/ps.cpp
index 807945f97..59e6e253d 100644
--- a/src/devices/grops/ps.cpp
+++ b/src/devices/grops/ps.cpp
@@ -1390,13 +1390,8 @@ ps_printer::~ps_printer()
      .end_comment();
   {
     fputs("%%CreationDate: ", out.get_file());
-#ifdef LONG_FOR_TIME_T
-    long
-#else
-    time_t
-#endif
-    t = current_time();
-    fputs(ctime(&t), out.get_file());
+    struct tm *t = current_time();
+    fputs(asctime(t), out.get_file());
   }
   for (font_pointer_list *f = font_list; f; f = f->next) {
     ps_font *psf = (ps_font *)(f->p);
diff --git a/src/include/curtime.h b/src/include/curtime.h
index 5d3a24a7b..ebd2efb80 100644
--- a/src/include/curtime.h
+++ b/src/include/curtime.h
@@ -15,13 +15,16 @@ for more details.
 The GNU General Public License version 2 (GPL2) is available in the
 internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
 
-#ifndef LONG_FOR_TIME_T
 #include <time.h>
-#endif
 
-#ifdef LONG_FOR_TIME_T
-long
-#else
-time_t
-#endif
-current_time();
+// Get the current time in broken-down time representation.  If the
+// SOURCE_DATE_EPOCH environment variable is set, then it is used instead of
+// the real time from the system clock; in this case, the user is clearly
+// trying to arrange for some kind of reproducible build, so express the
+// time in UTC.  Otherwise, use the real time from the system clock, and
+// express it relative to the user's time zone.
+//
+// In either case, as with gmtime() and localtime(), the return value points
+// to a statically-allocated struct which might be overwritten by later
+// calls.
+struct tm *current_time();
diff --git a/src/libs/libgroff/curtime.cpp b/src/libs/libgroff/curtime.cpp
index 34dbc5ca9..277755cab 100644
--- a/src/libs/libgroff/curtime.cpp
+++ b/src/libs/libgroff/curtime.cpp
@@ -15,9 +15,7 @@ for more details.
 The GNU General Public License version 2 (GPL2) is available in the
 internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "lib.h"
 
 #include <errno.h>
 #include <limits.h>
@@ -25,16 +23,18 @@ internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
 #include <string.h>
 #include <time.h>
 
+#include "curtime.h"
 #include "errarg.h"
 #include "error.h"
 
+struct tm *current_time()
+{
 #ifdef LONG_FOR_TIME_T
-long
+  long
 #else
-time_t
+  time_t
 #endif
-current_time()
-{
+    t;
   char *source_date_epoch = getenv("SOURCE_DATE_EPOCH");
 
   if (source_date_epoch) {
@@ -49,7 +49,10 @@ current_time()
       fatal("$SOURCE_DATE_EPOCH: no digits found: '%1'", endptr);
     if (*endptr != '\0')
       fatal("$SOURCE_DATE_EPOCH: trailing garbage: '%1'", endptr);
-    return epoch;
-  } else
-    return time(0);
+    t = epoch;
+    return gmtime(&t);
+  } else {
+    t = time(0);
+    return localtime(&t);
+  }
 }
diff --git a/src/roff/groff/groff.1.man b/src/roff/groff/groff.1.man
index 75687440e..348036cdf 100644
--- a/src/roff/groff/groff.1.man
+++ b/src/roff/groff/groff.1.man
@@ -1879,19 +1879,21 @@ A time stamp
 to use as the output creation time stamp in place of the current time.
 .
 The time is converted to human-readable form using
-.MR localtime 3
+.MR gmtime 3
+and
+.MR asctime 3
 when the formatter starts up and stored in registers usable by documents
 and macro packages.
 .
 .
 .TP
 .I TZ
-The time zone to use when converting the current time
-(or value of
-.IR SOURCE_DATE_EPOCH )
-to human-readable form;
+The time zone to use when converting the current time to human-readable form;
 see
 .MR tzset 3 .
+If
+.I SOURCE_DATE_EPOCH
+is used, it is always converted to human-readable form using UTC.
 .
 .
 .\" ====================================================================
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index 292ee7389..f03338335 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -8297,21 +8297,15 @@ void warn_request()
 
 static void init_registers()
 {
-#ifdef LONG_FOR_TIME_T
-  long
-#else /* not LONG_FOR_TIME_T */
-  time_t
-#endif /* not LONG_FOR_TIME_T */
-    t = current_time();
-  struct tm *tt = localtime(&t);
-  set_number_reg("seconds", int(tt->tm_sec));
-  set_number_reg("minutes", int(tt->tm_min));
-  set_number_reg("hours", int(tt->tm_hour));
-  set_number_reg("dw", int(tt->tm_wday + 1));
-  set_number_reg("dy", int(tt->tm_mday));
-  set_number_reg("mo", int(tt->tm_mon + 1));
-  set_number_reg("year", int(1900 + tt->tm_year));
-  set_number_reg("yr", int(tt->tm_year));
+  struct tm *t = current_time();
+  set_number_reg("seconds", int(t->tm_sec));
+  set_number_reg("minutes", int(t->tm_min));
+  set_number_reg("hours", int(t->tm_hour));
+  set_number_reg("dw", int(t->tm_wday + 1));
+  set_number_reg("dy", int(t->tm_mday));
+  set_number_reg("mo", int(t->tm_mon + 1));
+  set_number_reg("year", int(1900 + t->tm_year));
+  set_number_reg("yr", int(t->tm_year));
   set_number_reg("$$", getpid());
   register_dictionary.define(".A",
 			       new readonly_text_register(ascii_output_flag
diff --git a/src/roff/troff/troff.1.man b/src/roff/troff/troff.1.man
index 01b46616c..4fbb962b1 100644
--- a/src/roff/troff/troff.1.man
+++ b/src/roff/troff/troff.1.man
@@ -868,19 +868,21 @@ A timestamp
 to use as the output creation timestamp in place of the current time.
 .
 The time is converted to human-readable form using
-.MR localtime 3
+.MR gmtime 3
+and
+.MR asctime 3
 when the formatter starts up and stored in registers usable by documents
 and macro packages.
 .
 .
 .TP
 .I TZ
-The timezone to use when converting the current time
-(or value of
-.IR SOURCE_DATE_EPOCH )
-to human-readable form;
+The time zone to use when converting the current time to human-readable form;
 see
 .MR tzset 3 .
+If
+.I SOURCE_DATE_EPOCH
+is used, it is always converted to human-readable form using UTC.
 .
 .
 .\" ====================================================================