Matthias Wimmer (溫雅石)Tue, 12 Apr 2022 20:33:36 +0000Matthias WimmerHandling time zones in Elm2022-04-122022-04-12Matthias Wimmerhttps://matthias-wimmer.de/blog<p><img src='/resources/images/World_Time_Zones_Map.png' class='img-fluid'/></p>
<p>When programming a single page application, especially if it is designed to
run within different time zones, you often use UTC on the REST interfaces to
the backend and let the single page application handle the time zone
calculation.</p>
<p>When I first tried to implement this in Elm, I first tried to do this using
the tools at hand when using the standard package for time handling: <code>elm/time</code>:</p>
<p>On the start-up of my SPA I send a <code>Cmd</code> to the Elm runtime, requesting that it
generated a <code>SetTimezone</code> event with the local time zone of the user (<code>here</code>).</p>
<pre><code><span class="code"><span class="function">getTimezone</span> <span class="variable">:</span> <span class="variable">Cmd</span> <span class="variable">Msg</span>
<span class="function">getTimezone</span> <span class="keyword">=</span>
<span class="variable">Task</span><span class="atom">.</span>perform <span class="variable">SetTimezone</span> <span class="variable">Time</span><span class="atom">.</span>here</span></code></pre>
<p>When we give the Elm architecture this command, we get back this message:</p>
<pre><code><span class="code"><span class="keyword">type</span> <span class="variable">Msg</span>
<span class="keyword">=</span> <span class="variable">SetTimezone</span> <span class="variable">Time</span><span class="atom">.</span><span class="variable">Zone</span></span></code></pre>
<p>Then I stored the provided <code>Zone</code> in my model and used it within a helper
function to format timestamps:</p>
<pre><code><span class="code"><span class="function">formatTime</span> <span class="variable">:</span> <span class="variable">Zone</span> <span class="keyword">-></span> <span class="variable">Posix</span> <span class="keyword">-></span> <span class="variable">String</span>
<span class="function">formatTime</span> zone timestamp <span class="keyword">=</span>
<span class="keyword">let</span>
hour <span class="keyword">=</span>
toHour zone timestamp
minute <span class="keyword">=</span>
toMinute zone timestamp
<span class="keyword">in</span>
<span class="paren1">(<span class="code">padLeft 2 <span class="character">'0'</span> <span class="atom"><|</span> fromInt hour</span>)</span> <span class="atom">++</span> <span class="string">":"</span> <span class="atom">++</span> <span class="paren1">(<span class="code">padLeft 2 <span class="character">'0'</span> <span class="atom"><|</span> fromInt minute</span>)</span>
<span class="function">formatTimeOfIsoTimestamp</span> <span class="variable">:</span> <span class="variable">Zone</span> <span class="keyword">-></span> <span class="variable">String</span> <span class="keyword">-></span> <span class="variable">String</span>
<span class="function">formatTimeOfIsoTimestamp</span> zone isoTimestamp <span class="keyword">=</span>
toTime isoTimestamp <span class="atom">|></span> <span class="variable">Result</span><span class="atom">.</span>map <span class="paren1">(<span class="code">formatTime zone</span>)</span> <span class="atom">|></span> <span class="variable">Result</span><span class="atom">.</span>withDefault isoTimestamp</span></code></pre>
<p>The problem with this approach is, that actually <code>here</code> does not tell the user's
actual time zone, that would take into account the shifts between standard time
and daylight saving time, but just something <a href="https://package.elm-lang.org/packages/elm/time/latest/Time#here" >generic for the current offset to
UTC</a>. So the
time zone we get will never be <code>Europe/Berlin</code> but <code>Etc/GMT+1</code> or <code>Etc/GMT+2</code>
depending on whether we currently have standard time or daylight saving time.</p>
<p>The problem with that is, that you can display “current” timestamps with that
zone, but it fails for example as soon as you try to show timestamps within the
winter while it is still summer.</p>
<p>The solution to this is to use the functionality of <code>justinmimbs/timezone-data</code>.
Instead of using the <code>here</code> <code>Task</code> as in the code above, this Elm package
provides a different task, to get the time zone of the web browser the
application is running in:
<a href="https://package.elm-lang.org/packages/justinmimbs/timezone-data/latest/TimeZone#getZone" ><code>getZone</code></a></p>
<p>The function, that generates the <code>Cmd</code> to request the time zone gets this:</p>
<pre><code><span class="code"><span class="function">getTimezone</span> <span class="variable">:</span> <span class="variable">Cmd</span> <span class="variable">Msg</span>
<span class="function">getTimezone</span> <span class="keyword">=</span>
<span class="variable">Task</span><span class="atom">.</span>attempt <span class="variable">SetTimezone</span> <span class="variable">TimeZone</span><span class="atom">.</span>getZone</span></code></pre>
<p>With this change, also the definition of the <code>SetTimezone</code> message changes a
bit as the retrieval of the time zone may fail:</p>
<pre><code><span class="code"><span class="keyword">type</span> <span class="variable">Msg</span>
<span class="keyword">=</span> <span class="variable">SetTimezone</span> <span class="paren1">(<span class="code"><span class="variable">Result</span> <span class="variable">TimeZone</span><span class="atom">.</span><span class="variable">Error</span> <span class="paren2">(<span class="code"> <span class="variable">String,</span> <span class="variable">Time</span><span class="atom">.</span><span class="variable">Zone</span> </span>)</span></span>)</span></span></code></pre>
<p>When we handle the <code>SetTimezone</code> message we can unwrap this result and
use a default zone, if the correct zone couldn't be determined:</p>
<pre><code><span class="code"><span class="function">update</span> <span class="variable">:</span> <span class="variable">Msg</span> <span class="keyword">-></span> <span class="variable">Model</span> <span class="keyword">-></span> <span class="paren1">(<span class="code"> <span class="variable">Model,</span> <span class="variable">Cmd</span> <span class="variable">Msg</span> </span>)</span>
<span class="function">update</span> msg model <span class="keyword">=</span>
<span class="keyword">case</span> <span class="paren1">(<span class="code"> msg, model </span>)</span> <span class="keyword">of</span>
<span class="paren1">(<span class="code"> <span class="variable">SetTimezone</span> result </span>)</span> <span class="keyword">-></span>
<span class="keyword">let</span>
zone <span class="keyword">=</span> <span class="keyword">case</span> result <span class="keyword">of</span>
<span class="variable">Ok</span> <span class="paren1">(<span class="code"> _, timezone </span>)</span> <span class="keyword">-></span>
timezone
_ <span class="keyword">-></span>
<span class="variable">TimeZone</span><span class="atom">.</span>europe__berlin<span class="paren1">(<span class="code"></span>)</span>
<span class="keyword">in</span>
<span class="comment">-- store the retrieved zone in the model</span></span></code></pre>
Meine Fortschritte in Chinesisch2019-06-302019-06-30Matthias Wimmerhttps://matthias-wimmer.de/blog<p>Vor eineinhalb Jahren schrieb ich, als ich gerade mein HSK2-Zertifikat beim
Konfuzius Institut in München abgeholt hatte. Heute nun habe ich wieder ein
Prüfungsergebnis bekommen: die HSK4-Prüfung vor zwei Wochen habe ich
bestanden. Das ist nun zwei Stufen höher und man merkt, dass die Stufen
zwischen den Prüfungen immer größer werden: von HSK2 zu HSK3 brauchte ich 9
Monate, von HSK3 zu HSK4 war es schon ein Jahr. Die Menge der notwendigen
Vokabeln verdoppelt sich auch jedes Mal, von der Gramatik ganz abgesehen.
(<a href="https://de.wikipedia.org/wiki/Hanyu_Shuiping_Kaoshi" >Wikipedia: Hanyu Shuiping
Kaoshi</a>)</p>
<p>Die Prüfungen haben sich für mich als gute Methode herausgestellt
mein Lernen voran zu treiben. Es ist Ziel, Motivation und ein Paket an
Anforderungen, die ich mir vornehme.</p>
<p>Nächstes Ziel nun also: HSK5 ... irgendwann im nächsten Jahr. Aber die
Vorbereitung beginnt jetzt und das ist ab sofort meine Motivation.</p>
Meine Zertifikate: 汉语水平考试(HSK)一级和二级2017-11-192017-11-19Matthias Wimmerhttps://matthias-wimmer.de/blog<p><img src='/resources/images/HSK1_HSK2.jpeg'
style='margin: 10px; float: right'/></p>
<p>Inzwischen steht mein Kopf eher schon beim nächsten Test. Trotzdem wollte ich
noch von meinen Prüfungen im September abschließend berichten:</p>
<p>Mein Gefühl hat sich bestätigt, ich habe beide Prüfungen bestanden. HSK1 sogar
ohne Fehler. Die Ergebnisse waren ab dem 16. Oktober online einsehbar. Hierzu
kann man sich einfach mit den persönlichen Zugangsdaten auf <a href="http://www.chinesetest.cn/" >der Seite anmelden
über die der Test auch gebucht wurde</a> und sieht dann
die Ergebnisse oder man kann auch ohne Login dort die Ergebnisse abfragen,
wenn man seinen Namen und die Admission-Ticket-Nummer eingibt. Am 9. November
war es dann soweit und ich konnte auch meine Zertifikate im Konfuzius Institut
München abholen.</p>
汉语水平考试(HSK)一级和二级2017-09-202017-09-20Matthias Wimmerhttps://matthias-wimmer.de/blog<p>Letzten Sonntag habe ich mein Chinesisch testen lassen. Ich hatte mich zu den
Prüfungen <a href="https://de.wikipedia.org/wiki/Hanyu_Shuiping_Kaoshi" >HSK 1 und HSK 2
</a> angemeldet. Ursprünglich
wollte ich nur die niedrigste Stufe HSK 1 schreiben und meldete mich
entsprechend an. In der Vorbereitung merkte ich jedoch schnell, dass die
Anforderungen hierfür nicht so hoch waren wie ich dachte. Zwar gab es noch ein
paar Vokabeln, die ich nicht kannte, aber im Großen und Ganzen hatten wir den
Stoff längst im Unterricht behandelt.</p>
<p>Dann laß ich beim <a href="http://konfuzius-muenchen.de" >Konfuzius Institus München</a>,
dass es auch möglich ist sich für zwei benachbarte Prüfungslevel gleichzeitig
anzumelden. Die Prüfungszeiten sind so geplant, dass dies geht. Das klang gut:
für HSK 1 konnte ich mich eh nicht mehr abmelden und ich könnte parallel
versuchen, ob ich nicht schon die nächste Stufe schaffe. Gedacht getan, ich
meldete mich auch hierfür an.</p>
<!--more-->
<p>Meine Vorbereitung habe ich entsprechend auf die 300 Wörter/Zeichen des HSK 2
ausgedehnt und mich mit der erweiterten Gramatik beschäftigt. Ich fühlte mich
bis zum Tag davor recht gut vorbereitet. Dann allerdings machte ich
Probeaufgaben zum HSK 2 und war nicht mehr ganz so sicher. Die Sprache in den
Höraufgaben war doch deutlich schneller als ich es gewohnt war. Die Beispiele
in den Textaufgaben teilweise schwer verständlich für mich. Nach der
Auswertung der Probeaufgaben dachte ich: okay, es könnte doch knapp werden.</p>
<p>Sonntag begann mein Prüfungstag zuerst mit HSK 2 um 10:00 Uhr, Prüfungsdauer
etwa eine Stunde. In der Prüfung kam dann schnell das gute Gefühl zurück. Die
Audiotexte waren langsamer und besser verständlich als das was ich von den
Probeaufgaben kannte. Bei den meisten Aufgaben bin ich ziemlich sicher, dass
ich sie richtig gelöst habe. Die Antwort ist fast immer eine
Multiple-Choice-Auswahl und die falschen Möglichkeiten waren meist eindeutig
falsch – mir schienen keine Gemeinheiten eingebaut. Auch die Textaufgaben habe
ich ganz gut verstanden.</p>
<p>Am Nachmittag stand dann noch HSK 1 an. Diese Prüfung dauert nur etwa eine
Dreiviertelstunde. Nach der guten Erfahrung im schwereren Niveau hatte ich keine
Sorgen vor dieser Prüfung. So war es dann auch: Ich habe diese wirklich als sehr
einfach empfunden. Ich hätte mich wohl geärgert, wenn ich nicht auch an HSK 2
versucht hätte. Jetzt muss ich etwa einen Monat warten, dann kann ich meine
Ergebnisse abrufen.</p>
La razón porque yo aprendo el chino2017-07-302017-07-30Matthias Wimmerhttps://matthias-wimmer.de/blog<p><img src='/resources/images/Chino.jpeg'
style='margin: 10px; float: right'/></p>
<p>Cuando hablo con otros estudiantes de idiomas, es una pregunta muy común.
También en los libros de aprender otra lengua siempre hay un capítulo en el que
se habla de las lenguas que se habla y porque se las aprende.</p>
<p>Yo siempre nota que la lengua que tiene más impresión a la persona de enfrente
es el chino. Para la mayoría de personas aquí en Europa todavía parece algo
imposible para aprender. Piensen que se tiene que ser un genio ya si se lo
intenta. La verdad es diferente, no es tan complicado. Pero es otro tema, quizás
escriba de eso en otro momento.</p>
<p>Lo más especial normalmente es la razón de aprender el chino. En general es nota
en las clases que cada lengua tiene un público diferente. Por ejemplo mis
compañeros en las clases de neerlandés lo aprendieron porque son artistas y
quisieron trabajar en Ámsterdam o porque tuvieron novio neerlandés. Al contrario
en las clases de español una razón muy común es el turismo. España es un destino
de vacaciones muy popular.</p>
<!--more-->
<p>Si alguien me pregunta por qué aprendo el chino normalmente sólo contesto que es
por la escritura. Eso es verdad, pero no es la verdad completa. Claro que los
signos chinos son bonitos y a mí me gustan también. Pero la belleza no es mi
razón. Hay algo diferente que tiene que ver con los signos también.</p>
<p>Todo ya empezó en el año 1996. Fui el año en que me inscribí a la universidad de
Stuttgart. Por mis estudios allí estuve en la biblioteca universitaria cada unos
días. No sólo me interesaban los libros de las ciencias físicas (mi asignatura
en aquella ocasión) pero también los de informática. En esa sección de la
biblioteca había un libro en dos tomos: Unicode Standard: Worldwide Character
Encoding, Version 1.0.</p>
<p>Suena aburridísimo, ¿no? Pero no para mí. En nuestros días es el estándar
habitual para ser capaz de representar todos los idiomas en los ordenadores al
mismo tiempo. Es un presupuesto para que WhatsApp funciona sin problemas y
sin límites en todo el mundo.
Pero entonces fue muy moderno. La realidad en 1996 fue que se tuve que
seleccionar una «code page» (página de código) en la instalación de Windows.
Después se pudo escribir bien en una región del mundo, pero no fue posible
escribir en lenguas muy distintas en el mismo ordenador.</p>
<p>Los dos tomos del estándar Unicode contienen casi sólo tablas de carácteres y
un número que represente ese carácter en el ordenador porque dentro de un
ordenador sólo hay números. Además, había unas páginas en que se habló de los
problemas adicionales en producir software plurilingüe. Depende de la escritura
se escribe en direcciones diferentes, hay espacio entre palabras y se cambia la
línea sólo allí o se puede cambiar la línea a cualquier posición, es posible
que letras cambian su aspecto en función de la posición dentro una palabra o
dependiente de las letras vecinas y mucho más. Eso me desengañó que producir
software para el mundo no sólo requiere traducir todos los textos, se tiene
que considerar mucho más.</p>
<p>Probablemente si tú vives en Europa ya notaste algunas de eses problemas:
software en que la semana empieza el domingo (en vez de lunes), en
que una dirección requiere que se inserta un estado o números de teléfono que
tienen que tener exactamente diez cifras. Probablemente el software estaba
producido en los E.E.U.U. Para estadounidenses todo eso parece normal,
no piensan que pueda ser diferente en otro país.</p>
<p>Yo quise ser diferente, especialmente hoy trabajando en el sector informático
profesionalmente. No es posible evitar todos los problemas, pero quiero que
considere lo más posible. Por eso a mí me parece que tengo que trabajar con
el software que yo produzco y con software en general en idiomas diferentes.
Por eso yo busqué una lengua muy distinto, para practicar pensar de software
en una forma muy diferente. Y eso es el aspecto de que yo hablo cuando digo que
mi razón para aprender el chino es la escritura.</p>
Planeando el semestre que viene2017-06-172017-06-17Matthias Wimmerhttps://matthias-wimmer.de/blog<p>Solo hay unas pocas clases más antes del fin del semestre en mi escuela de
la noche. Por eso pienso mucho como continuaré el semestre que viene. En
general últimamente he notado cada vez más que quiero aprender con más velocidad.</p>
<p>Esto noto especialmente en portugués y chino. Actualmente puedo seguir en
las clases sin practicar en casa. No me parece bien. A mí me parece importante
conseguir un nivel comunicativo lo más rápido que posible. Si se tiene ese
nivel se aprende una lengua casi sin hacer nada, solo se tiene que hablar con
amigos y personas interesantes.</p>
<p>En portugués una solución se ha resultado automáticamente. La escuela
Volkshochschule no ofrece un curso a continuación del actual. Ya en este
curso solo somos el mínimo de estudiantes. Por eso hace unas semanas hablamos
en la clase como podíamos continuar. Buscamos curso adecuado para cada
estudiante. Para mí significa que voy a omitir las clases del último quinto
del libro actual y ya voy a empezar las clases de B1. Espero que sea nueva
motivación para mí. No creo que tenga problemas porque ya aprendí todas las
palabras del libro en el invierno y no es mucho gramática que aún no sé. Voy
a repasarla antes del semestre nuevo en octubre.</p>
<!--more-->
<p>Más difícil es el planeamento en chino. Los cursos tienen velocidades diferentes
pero en el programa de la escuela no se informan sobre ese aspecto. Por eso
hablé con mi profesora, mi escuela actual y el Instituto Confucio.
Afortunadamente todos entendieron muy bien y me ayudaron mucho. Por eso ahora
tengo unas opciones para elegir. Básicamente tengo que decir si quiero omitir
clases o no y con que velocidad quiero continuar. Si cambiaré la escuela
también tendré que cambiar el libro. Por eso ya empecé a praticar con el
otro libro también. Pero todavía no he decidido que haré. Depende de la
programación final en las escuelas. Aún no sé los días de todos los cursos.</p>
<p>De una u otra forma no tendré clases normales en agosto. Por eso para ese
mes me inscribí a un curso intensivo de arender japonés. Espero que aprenda
algunos principios básicos de esta lengua que siempre me ha interesado.</p>
Learn You a Haskell for Great Good2017-04-152017-04-15Matthias Wimmerhttps://matthias-wimmer.de/blog<p><a rel='nofollow' href='http://amzn.to/2pjCTp2'>
<img src='/resources/images/LearnYouAHaskellForGreatGood.jpeg'
style='margin: 10px; float: right'/>
</a></p>
<p>This is the first book on Haskell, that really made me want to read it.
I had it with me as an e-book on my vacations. I couldn't stop reading
it before I finished. It is a very good introduction to the basics of
the Haskell programming language. It even contains an understandable
explanation of the infamous monads. (Side note: they are a very simple
concept, and you shouldn't fear them.)</p>
<p>For me this book was the right amount of introduction to get me started.
Everything else I think I will be able to learn while using the language.</p>
<p>The book is available in print at your preferred book shop, or
<a href="http://learnyouahaskell.com/" >can be read online and free of charge.</a></p>
<h2>Links to resources</h2>
<ul>
<li><a href="http://learnyouahaskell.com/" >Learn You a Haskell for Great Good, online and free of charge
</a></li>
<li><a href="http://book.realworldhaskell.org/read/" >Real World Haskell, an other free book on Haskell
</a></li>
<li><a href="http://tryhaskell.org/" >Try Haskell, in your browser</a></li>
</ul>
Benutzt du Clojure in der Arbeit?2017-03-132017-03-13Matthias Wimmerhttps://matthias-wimmer.de/blog<p><img src="/resources/images/ClojureD2017.jpeg"
style='margin: 10px; float: right'/></p>
<p>Wenn ich auf ein Meetup gehe, lautet die erste Frage immer „was machst du so?“.
Beginnt das typische Gespräch hingegen mit „benutzt du Clojure in der Arbeit?”,
bin ich auf der <a href="http://www.clojured.de" >:clojureD</a> in Berlin gelandet.</p>
<p>Am 24. Februar war ich auf meiner ersten Clojure-Konferenz. Nach einem Treffen
der <a href="https://www.meetup.com/de-DE/Munich-Clojurians/" >Munich Clojurians</a> war das
erst mein zweites Zusammentreffen mit anderen, die der Begeisterung dieser
Programmiersprache erlegen sind.</p>
<p>Es war ein tolles Erlebnis. Auf keiner Konferenz bisher hatte ich so den
Eindruck, dass alle einfach ein rießen Interesse an Softwareentwicklung haben
und alle dafür brennen neue Gedankenanstöße aufzunehmen. Keiner der einfach
<em>nur</em> seine Arbeit erledigt bekommen will. Ich hatte das Gefühl wirklich unter
den Besten zu sein, die es in meinem Beruf gibt.</p>
<p>Wie schade, dass anscheinend trotzdem das Potential der Sprache so oft noch
nicht genutzt wird. Obwohl ich im
<a href="http://blog.cognitect.com/cognicast/" >Cognicast</a> oft genug auch schon die
andere Seite gehört habe: Firmen, die darüber klagen zu wenige
Clojure-Entwickler zu finden. Die Antwort auf der :clojureD war leider öfters:
„nein, in der Arbeit nutze ich kein Clojure“.</p>
Progresso nas minhas línguas2017-01-012017-01-01Matthias Wimmerhttps://matthias-wimmer.de/blog<p>Uma vez por ano reviso que aprendi nas minhas aulas de línguas. O mais notável
este ano é que comecei a aprender Português em Agosto. Durante quatro semanas,
eu tinha 36 horas de aulas intensivas. Gostei muito de aprender uma língua nova.
É bem aprender algo mais fácil que o chinês. Lamentavelmente não posso continuar
assim. Por um lado porque não há ensino intensivo depois de os capítulos de
verão. Por outro lado também tenho aulas no chinês e espanhol durante o
semestre.</p>
<p>No espanhol fiz o exame DELE B2. Suponho passei todas as partes mas até agora
não tenho nenhum resultado. Espero que me comuniquem-o no Janeiro. Segundo o meu
sentimento tive menos problemas que no nível B1 um ano e meio atrás.
Especialmente foi mais fácil entender todos os textos na parte compreensão oral.</p>
<p>Também continuei as minhas aulas de chinês. Todo é um pouco mias lento. O idioma
não é semelhante com os idiomas europeus e se tem de aprender as letras também.
Apesar disso gosto de aprender esta língua cada vez mais. Começo a reconhecer
letras e palavras no meu quotidiano.</p>
<p>Provavelmente este texto contem muitos erros. É a minha primeira vez que escreve
um artículo português para a internet. Mas o mais importante para mim é provar
expressar algumas coisas básicas na minha língua nova.</p>
<p><em>Feliz ano novo!</em></p>
Getting Safari Books Online2016-12-272016-12-27Matthias Wimmerhttps://matthias-wimmer.de/blog<p><img style='float: right; margin: 10px'
src='/resources/images/Safari.jpeg'/></p>
<p>I like the books from O'Reilly. For many years I've seen the ad on the
backside advertising <em>Safari</em>. Sure, I checked out what this is, but I
wasn't really interested to have a digital copy for a limited amount of time.
I already had the print version.</p>
<p>I love to read <em>real</em> books made of paper. I use highlighters and add
notes, while reading. I make the book mine. And after finishing the book, I can
place it on my bookshelf, to show the books I like to others. This doesn't work
with my <a href="http://mytolino.de/" >Tolino e-book reader</a>. While I have this device,
I still buy most books as a paper copy.</p>
<p>Some days ago, I heard of Safari again on a podcast. I thought, that at least I
should check it out. It's not only O'Reilly books anymore and it includes videos
and other podcasts as well. There is a free 10 days trial, so I did not have to
invest more than some of my time.</p>
<!--more-->
<p>It didn't take long and I was captivated by Safari. I noticed how cool it is to
checkout nearly any computer book I am normally reading. Curious about
something? I can just log in and start reading. It didn't take long, that I
had a regular, payed subscription …</p>
<p>The price? It's quite expensive. I have to pay around 40 € per month (including
VAT). I saw several people on the net comparing this to about two e-books a
month. This comparison doesn't work for me: I still want to read paper books.
For me Safari is a tool to fossick for new content.</p>
<p>Another big down-side of Safari is, that you have to read it on the web, or with
an Android or iOS app. This limits the user to read on devices with a screen not
optimized for reading. You cannot read using an e-book reader with an e-ink
display and much less weight per screen size.</p>
<p>For the moment I am still enjoying the service as a big index to books I might
been interested in. To bad, that there is no offer for people using it and
buying the books at the same time. As a Safari subscriber I'd like to get a
discount on the books I buy. Or they could offer some free days for every bought
paper book.</p>
<h2>Link</h2>
<ul>
<li><a href="https://www.safaribooksonline.com/" >Safari Books Online</a></li>
</ul>