about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStarfall <us@starfall.systems>2023-08-15 11:43:08 -0500
committerStarfall <us@starfall.systems>2023-08-15 11:43:39 -0500
commitaf7bf0f4579cf5045ad125ae51d70269e1919983 (patch)
tree26109a9dde193c2490dbe43e7757d72ef5f26c87
parent16ca6e1a2b944a0d822149d33e14cc3c331e9c91 (diff)
update: d2 file guide, java tz blogpost
-rw-r--r--css/terminal.css13
-rw-r--r--files/diablo/D2Fileguide.pdfbin0 -> 2258792 bytes
-rw-r--r--html/blog/feed.xml63
-rw-r--r--html/blog/index.html9
-rw-r--r--html/blog/java-timezones/index.html98
-rwxr-xr-xhtml/resources/index.html11
6 files changed, 186 insertions, 8 deletions
diff --git a/css/terminal.css b/css/terminal.css
index f3fa4b0..7ade7f9 100644
--- a/css/terminal.css
+++ b/css/terminal.css
@@ -9,8 +9,7 @@
 	--visited: #b9ffb0;
 
 	--bg: #1d2021;
-	--bg2: black;
-	--selection: #466639;
+	--bg2: black; --selection: #466639;
 
 	--font-body: "DejaVu Sans", "Bitstream Vera Sans", "Verdana", sans-serif;
 	--font-mono: "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Menlo", monospace;
@@ -79,10 +78,12 @@ a:hover,
 a:focus {
 	text-decoration: none;
 }
-code,
-pre {
+code {
 	font: 100%/1.25 var(--font-mono);
 }
+pre > code {
+	font: 87.5%/1.1 var(--font-mono);
+}
 footer {
 	font: 75%/1.25 var(--font-accent);
 }
@@ -93,7 +94,6 @@ h1 {
 }
 h1::after {
 	content: "█";
-	display: inline-block;
 	animation: blink 2.4s steps(2) infinite;
 }
 @keyframes blink {
@@ -157,7 +157,8 @@ details {
 	padding: 0.4em;
 }
 /* BLOCKQUOTE */
-blockquote {
+blockquote,
+pre {
 	margin: 1em;
 	border-left: 0.25em solid var(--fg);
 	padding-left: 1em;
diff --git a/files/diablo/D2Fileguide.pdf b/files/diablo/D2Fileguide.pdf
new file mode 100644
index 0000000..6163bce
--- /dev/null
+++ b/files/diablo/D2Fileguide.pdf
Binary files differdiff --git a/html/blog/feed.xml b/html/blog/feed.xml
index 010a68a..aeacb7c 100644
--- a/html/blog/feed.xml
+++ b/html/blog/feed.xml
@@ -3,7 +3,7 @@
 	<title>Starfall's Blog</title>
 	<link href="https://starfall.systems/blog/feed.xml" rel="self"/>
 	<link href="https://starfall.systems/blog/"/>
-	<updated>2023-07-03T19:00:00Z</updated>
+	<updated>2023-08-15T16:10:00Z</updated>
 	<id>https://starfall.systems/</id>
 
 	<author>
@@ -13,6 +13,67 @@
 
 
 	<entry>
+		<title>Timezones in Java</title>
+		<updated>2023-08-15T16:10:00Z</updated>
+		<link href="https://starfall.systems/blog/java-timezones/"/>
+		<id>https://starfall.systems/blog/java-timezones/</id><content type="html">&lt;p&gt;Recently ran into an issue at work that we couldn&#39;t find a direct answer to anywhere on the Internet (thanks to the terrible state of search in the modern day after Search Engine Optimization and Large Language Models have screwed it over, but that&#39;s another topic...) relating to three-letter abbreviations for timezones.&lt;/p&gt;
+&lt;p&gt;Long story short, use canonical timezone names &lt;a href=&quot;http://web.cs.ucla.edu/~eggert/tz/tz-link.htm&quot;&gt;from tzdb&lt;/a&gt; like &amp;quot;America/New_York&amp;quot; instead of abbreviations like &amp;quot;ET&amp;quot;.&lt;/p&gt;
+&lt;p&gt;The abbreviations like EST, CDT, CET, BST... mostly don&#39;t work any more, and for good reasons. Is MST Malaysian Standard Time (UTC+8) or North America Mountain Standard Time (UTC-7)? They might be standardized within a given nation&#39;s borders, but not worldwide. So, if you run across an error that looks like &lt;em&gt;this&lt;/em&gt; when trying to parse a date:&lt;/p&gt;
+&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;java.time.format.DateTimeParseException: Text &#39;01/01/1999 - 00:00:00 EDT&#39; could not be parsed: null
+    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
+    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
+    at java.base/java.time.format.LocalDateTime.parse(LocalDateTime.java:492)
+    at [ REDACTED ]
+
+    Caused by:
+    java.lang.NullPointerException
+        at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.prefixLength(DateTimeFormatterBuilder.java:4527)
+        at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.add0(DateTimeFormatterBuilder.java:4396)
+        at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.add(DateTimeFormatterBuilder.java:4391)
+        at java.base/java.time.format.DateTimeFormatterBuilder$ZoneTextPrinterParser.getTree(DateTimeFormatterBuilder.java:4138)
+        at java.base/java.time.format.DateTimeFormatterBuilder$ZoneTextPrinterParser.parse(DateTimeFormatterBuilder.java:4249)
+        at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2370)
+        at java.base/java.time.format.DateTimeFormatter.parseUnresolved0(DateTimeFormatter.java:2107)
+        at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2036)
+        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
+        ... 3 more
+&lt;/code&gt;&lt;/pre&gt;
+&lt;p&gt;... it&#39;s likely that you&#39;re trying to use a three letter abbreviation for a timezone (here, &amp;quot;EDT&amp;quot; being used instead of &amp;quot;America/New_York&amp;quot;).&lt;/p&gt;
+&lt;p&gt;Confusingly, &lt;a href=&quot;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html&quot;&gt;the documentation for DateTimeFormatter&lt;/a&gt; actually includes an example of a zone-name that&#39;s a three-letter acronym! ZoneId &lt;a href=&quot;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/ZoneId.html#SHORT_IDS&quot;&gt;has a list of them included for backwards compatibility&lt;/a&gt; but I couldn&#39;t figure out if they&#39;re actually parseable (leaning towards no).&lt;/p&gt;
+&lt;p&gt;And as an extra layer, this behavior relies on the underlying system. The abbrevations worked just fine on our work MacBook but not on the Jenkins build nodes. I don&#39;t have an answer for exactly why, but my guess is that Mac tooling happily responds with UTC as a default time zone when it doesn&#39;t know what you&#39;re asking, while GNU ones error. You can see the same kind of difference on the &lt;code&gt;date&lt;/code&gt; program for each:&lt;/p&gt;
+&lt;pre&gt;&lt;code class=&quot;language-zsh&quot;&gt;[starfall@mac:~] % TZ=unknown date
+Tue Aug 15 15:28:39 UTC 2023
+
+[starfall@arch:~] % TZ=unknown date
+Tue Aug 15 03:28:39 PM unknown 2023
+&lt;/code&gt;&lt;/pre&gt;
+&lt;p&gt;One solution is to just use times with offsets, but there are valid reasons to choose to use a time with timezone instead. Here&#39;s a few ways you can parse them properly.&lt;/p&gt;
+&lt;h3&gt;Example 1&lt;/h3&gt;
+&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;String stringWithTz = &amp;quot;2023-08-15 10:28:39 America/Chicago&amp;quot;;
+DateTimeFormatter formatter = DateTimeFormatter.ofPattern(&amp;quot;yyyy-MM-dd HH:mm:ss VV&amp;quot;, Locale.US);
+Instant instant = Instant.from(formatter.parse(stringWithTz));
+&lt;/code&gt;&lt;/pre&gt;
+&lt;p&gt;Usually you will be able to use an Instant. These are stored without any time zone or offset, just as a moment in ... something that&#39;s close enough to UTC for most work. If the details matter, read &lt;a href=&quot;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/Instant.html&quot;&gt;the documentation&lt;/a&gt;.&lt;/p&gt;
+&lt;p&gt;The above Instant is 2023-08-15T15:28:39Z.&lt;/p&gt;
+&lt;h3&gt;Example 2&lt;/h3&gt;
+&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;String stringWithTz = &amp;quot;2023-08-15 10:28:39 America/Chicago&amp;quot;;
+DateTimeFormatter formatter = DateTimeFormatter.ofPattern(&amp;quot;yyyy-MM-dd HH:mm:ss VV&amp;quot;, Locale.US);
+ZonedDateTime zdt = ZonedDateTime.parse(stringWithTz, formatter);
+&lt;/code&gt;&lt;/pre&gt;
+&lt;p&gt;This gets you a ZonedDateTime, which keeps the time zone information around. Usually an Instant will be fine instead, unless you really need to keep track of which datetime came from which timezone.&lt;/p&gt;
+&lt;p&gt;The above ZonedDateTime is 2023-08-15T10:28:39-05:00 (the same time as the Instant in example 1).&lt;/p&gt;
+&lt;h3&gt;Example 3&lt;/h3&gt;
+&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;String isoString = &amp;quot;2023-08-15T10:28:39&amp;quot;;
+ZoneId timezone = ZoneId.of(&amp;quot;America/Chicago&amp;quot;);
+ZonedDateTime zdt = LocalDateTime.parse(isoString).atZone(timezone);
+Instant instant = Instant.from(zdt);
+&lt;/code&gt;&lt;/pre&gt;
+&lt;p&gt;If you don&#39;t have time zones in your strings, you can hydrate them with one like this. Keeping the LocalDateTime without a timezone is not recommended unless you have a very good reason, like they&#39;re historical dates from one location that won&#39;t be compared across timezones.&lt;/p&gt;
+&lt;p&gt;The Instant and ZonedDateTime here are the same as the previous two examples.&lt;/p&gt;
+</content>
+	</entry>
+
+	<entry>
 		<title>Lament for the Commons</title>
 		<updated>2023-07-03T19:00:00Z</updated>
 		<link href="https://starfall.systems/blog/lament-for-the-commons/"/>
diff --git a/html/blog/index.html b/html/blog/index.html
index 7dcf2a1..897b848 100644
--- a/html/blog/index.html
+++ b/html/blog/index.html
@@ -45,6 +45,12 @@
 <a href=/blog/feed.xml alt="Atom feed" class=feed-icon></a>
 
 <article class=blogpost>
+	<h2><a href="/blog/java-timezones/">Timezones in Java</a></h2>
+	<time datetime="2023-08-15 16:10:00.000Z" title="2023-08-15 16:10:00.000Z">
+		2023-08-15
+	</time></article>
+
+<article class=blogpost>
 	<h2><a href="/blog/lament-for-the-commons/">Lament for the Commons</a></h2>
 	<time datetime="2023-07-03 19:00:00.000Z" title="2023-07-03 19:00:00.000Z">
 		2023-07-03
@@ -61,6 +67,9 @@
 	<time datetime="2023-05-25 01:00:00.000Z" title="2023-05-25 01:00:00.000Z">
 		2023-05-24
 	</time></article>
+
+
+
 </main>
 
 <footer>
diff --git a/html/blog/java-timezones/index.html b/html/blog/java-timezones/index.html
new file mode 100644
index 0000000..2e83010
--- /dev/null
+++ b/html/blog/java-timezones/index.html
@@ -0,0 +1,98 @@
+<!doctype html>
+<html lang=en-US dir=ltr>
+<head>
+	<title>Timezones in Java</title>
+	<meta charset=UTF-8>
+	<meta name=robots content="noindex, nofollow">
+	<meta name=viewport content="width=device-width, initial-scale=1">
+	<link rel=stylesheet href=/css/terminal.css>
+	
+</head>
+
+<body>
+<!-- todo color picker -->
+
+<header>
+	<h1>Timezones in Java</h1>
+	<nav aria-label=primary>
+		
+		<ul>
+			<li>
+				<a href="/">Home</a>
+			</li>
+			<li>
+				<a href="/blog/" class="active">Blog</a>
+			</li>
+			<li>
+				<a href="https://git.starfall.systems">Git</a>
+			</li>
+		</ul>
+	</nav>
+</header>
+
+<article>
+2023-08-15<p>Recently ran into an issue at work that we couldn't find a direct answer to anywhere on the Internet (thanks to the terrible state of search in the modern day after Search Engine Optimization and Large Language Models have screwed it over, but that's another topic...) relating to three-letter abbreviations for timezones.</p>
+<p>Long story short, use canonical timezone names <a href="http://web.cs.ucla.edu/~eggert/tz/tz-link.htm">from tzdb</a> like &quot;America/New_York&quot; instead of abbreviations like &quot;ET&quot;.</p>
+<p>The abbreviations like EST, CDT, CET, BST... mostly don't work any more, and for good reasons. Is MST Malaysian Standard Time (UTC+8) or North America Mountain Standard Time (UTC-7)? They might be standardized within a given nation's borders, but not worldwide. So, if you run across an error that looks like <em>this</em> when trying to parse a date:</p>
+<pre><code class="language-java">java.time.format.DateTimeParseException: Text '01/01/1999 - 00:00:00 EDT' could not be parsed: null
+    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
+    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
+    at java.base/java.time.format.LocalDateTime.parse(LocalDateTime.java:492)
+    at [ REDACTED ]
+
+    Caused by:
+    java.lang.NullPointerException
+        at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.prefixLength(DateTimeFormatterBuilder.java:4527)
+        at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.add0(DateTimeFormatterBuilder.java:4396)
+        at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.add(DateTimeFormatterBuilder.java:4391)
+        at java.base/java.time.format.DateTimeFormatterBuilder$ZoneTextPrinterParser.getTree(DateTimeFormatterBuilder.java:4138)
+        at java.base/java.time.format.DateTimeFormatterBuilder$ZoneTextPrinterParser.parse(DateTimeFormatterBuilder.java:4249)
+        at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2370)
+        at java.base/java.time.format.DateTimeFormatter.parseUnresolved0(DateTimeFormatter.java:2107)
+        at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2036)
+        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
+        ... 3 more
+</code></pre>
+<p>... it's likely that you're trying to use a three letter abbreviation for a timezone (here, &quot;EDT&quot; being used instead of &quot;America/New_York&quot;).</p>
+<p>Confusingly, <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html">the documentation for DateTimeFormatter</a> actually includes an example of a zone-name that's a three-letter acronym! ZoneId <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/ZoneId.html#SHORT_IDS">has a list of them included for backwards compatibility</a> but I couldn't figure out if they're actually parseable (leaning towards no).</p>
+<p>And as an extra layer, this behavior relies on the underlying system. The abbrevations worked just fine on our work MacBook but not on the Jenkins build nodes. I don't have an answer for exactly why, but my guess is that Mac tooling happily responds with UTC as a default time zone when it doesn't know what you're asking, while GNU ones error. You can see the same kind of difference on the <code>date</code> program for each:</p>
+<pre><code class="language-zsh">[starfall@mac:~] % TZ=unknown date
+Tue Aug 15 15:28:39 UTC 2023
+
+[starfall@arch:~] % TZ=unknown date
+Tue Aug 15 03:28:39 PM unknown 2023
+</code></pre>
+<p>One solution is to just use times with offsets, but there are valid reasons to choose to use a time with timezone instead. Here's a few ways you can parse them properly.</p>
+<h3>Example 1</h3>
+<pre><code class="language-java">String stringWithTz = &quot;2023-08-15 10:28:39 America/Chicago&quot;;
+DateTimeFormatter formatter = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss VV&quot;, Locale.US);
+Instant instant = Instant.from(formatter.parse(stringWithTz));
+</code></pre>
+<p>Usually you will be able to use an Instant. These are stored without any time zone or offset, just as a moment in ... something that's close enough to UTC for most work. If the details matter, read <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/Instant.html">the documentation</a>.</p>
+<p>The above Instant is 2023-08-15T15:28:39Z.</p>
+<h3>Example 2</h3>
+<pre><code class="language-java">String stringWithTz = &quot;2023-08-15 10:28:39 America/Chicago&quot;;
+DateTimeFormatter formatter = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss VV&quot;, Locale.US);
+ZonedDateTime zdt = ZonedDateTime.parse(stringWithTz, formatter);
+</code></pre>
+<p>This gets you a ZonedDateTime, which keeps the time zone information around. Usually an Instant will be fine instead, unless you really need to keep track of which datetime came from which timezone.</p>
+<p>The above ZonedDateTime is 2023-08-15T10:28:39-05:00 (the same time as the Instant in example 1).</p>
+<h3>Example 3</h3>
+<pre><code class="language-java">String isoString = &quot;2023-08-15T10:28:39&quot;;
+ZoneId timezone = ZoneId.of(&quot;America/Chicago&quot;);
+ZonedDateTime zdt = LocalDateTime.parse(isoString).atZone(timezone);
+Instant instant = Instant.from(zdt);
+</code></pre>
+<p>If you don't have time zones in your strings, you can hydrate them with one like this. Keeping the LocalDateTime without a timezone is not recommended unless you have a very good reason, like they're historical dates from one location that won't be compared across timezones.</p>
+<p>The Instant and ZonedDateTime here are the same as the previous two examples.</p>
+
+</article>
+
+<footer>
+<section>
+	<p>This site is 100% <a href=https://git.starfall.systems/web>source-available</a>. © 2020-2023 Starfall. See <a href=https://git.starfall.systems/web/tree/COPYING.md rel=license>COPYING.md</a>.
+</section>
+<div style=text-align:center>⋁/⋀</div>
+</footer>
+
+</body>
diff --git a/html/resources/index.html b/html/resources/index.html
index 5d39529..8d21a33 100755
--- a/html/resources/index.html
+++ b/html/resources/index.html
@@ -26,6 +26,15 @@
 </section>
 
 <section>
+	<h2>Diablo II</h2>
+	<ul>
+		<li><a href=/files/diablo/D2Fileguide.pdf>Diablo II File Guide (pdf, 2.2 MiB)</a>
+		<li><a href=https://www.theamazonbasin.com/wiki/index.php/Diablo_II>Basin Wiki for Diablo II</a>
+		<li><a href=http://www.mannm.org/d2library/faqtoids/faqtoids_eng.html>Diablo II FAQtoids</a>
+	</ul>
+</section>
+
+<section>
 	<h2>Battletech: Cluster Table Quickref</h2>
 	<p>For small packs (2-6), look up your 2d6 roll in the appropriate column and read the number of hits on the left. For large packs (7+), look up your 2d6 roll and read across for the number of hits.
 	<div style="display:flex;flex-wrap:wrap;">
@@ -56,7 +65,7 @@
 
 <footer>
 <section>
-	<p>This site is 100% hand-coded and <a href=https://git.starfall.systems/web>source-available</a>. © 2020-2021 Starfall. See <a href=https://git.starfall.systems/web/tree/COPYING.md rel=license>COPYING.md</a>.
+	<p>This site is 100% hand-coded and <a href=https://git.starfall.systems/web>source-available</a>. © 2020-2023 Starfall. See <a href=https://git.starfall.systems/web/tree/COPYING.md rel=license>COPYING.md</a>.
 </section>
 <div style=text-align:center>⋁/⋀</div>
 </footer>