17
17
import time
18
18
import warnings
19
19
import zlib
20
+ import collections
20
21
from io import BytesIO
21
22
from functools import total_ordering
22
23
23
24
import numpy as np
24
25
from six import unichr
25
26
26
27
27
- from datetime import datetime
28
+ from datetime import datetime , tzinfo , timedelta
28
29
from math import ceil , cos , floor , pi , sin
29
30
30
31
import matplotlib
@@ -135,6 +136,20 @@ def _string_escape(match):
135
136
assert False
136
137
137
138
139
+ # tzinfo class for UTC
140
+ class UTCtimezone (tzinfo ):
141
+ """UTC timezone"""
142
+
143
+ def utcoffset (self , dt ):
144
+ return timedelta (0 )
145
+
146
+ def tzname (self , dt ):
147
+ return "UTC"
148
+
149
+ def dst (self , dt ):
150
+ return timedelta (0 )
151
+
152
+
138
153
def pdfRepr (obj ):
139
154
"""Map Python objects to PDF syntax."""
140
155
@@ -202,10 +217,14 @@ def pdfRepr(obj):
202
217
# A date.
203
218
elif isinstance (obj , datetime ):
204
219
r = obj .strftime ('D:%Y%m%d%H%M%S' )
205
- if time .daylight :
206
- z = time .altzone
220
+ z = obj .utcoffset ()
221
+ if z is not None :
222
+ z = z .seconds
207
223
else :
208
- z = time .timezone
224
+ if time .daylight :
225
+ z = time .altzone
226
+ else :
227
+ z = time .timezone
209
228
if z == 0 :
210
229
r += 'Z'
211
230
elif z < 0 :
@@ -468,10 +487,19 @@ def __init__(self, filename):
468
487
self .writeObject (self .rootObject , root )
469
488
470
489
revision = ''
490
+ # get source date from SOURCE_DATE_EPOCH, if set
491
+ # See https://reproducible-builds.org/specs/source-date-epoch/
492
+ source_date_epoch = os .getenv ("SOURCE_DATE_EPOCH" )
493
+ if source_date_epoch :
494
+ source_date = datetime .utcfromtimestamp (int (source_date_epoch ))
495
+ source_date = source_date .replace (tzinfo = UTCtimezone ())
496
+ else :
497
+ source_date = datetime .today ()
498
+
471
499
self .infoDict = {
472
500
'Creator' : 'matplotlib %s, http://matplotlib.org' % __version__ ,
473
501
'Producer' : 'matplotlib pdf backend%s' % revision ,
474
- 'CreationDate' : datetime . today ()
502
+ 'CreationDate' : source_date
475
503
}
476
504
477
505
self .fontNames = {} # maps filenames to internal font names
@@ -483,14 +511,15 @@ def __init__(self, filename):
483
511
484
512
self .alphaStates = {} # maps alpha values to graphics state objects
485
513
self .nextAlphaState = 1
486
- self .hatchPatterns = {}
514
+ # reproducible writeHatches needs an ordered dict:
515
+ self .hatchPatterns = collections .OrderedDict ()
487
516
self .nextHatch = 1
488
517
self .gouraudTriangles = []
489
518
490
- self ._images = {}
519
+ self ._images = collections . OrderedDict () # reproducible writeImages
491
520
self .nextImage = 1
492
521
493
- self .markers = {}
522
+ self .markers = collections . OrderedDict () # reproducible writeMarkers
494
523
self .multi_byte_charprocs = {}
495
524
496
525
self .paths = []
0 commit comments