<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chiem The</title>
    <description>The latest articles on DEV Community by Chiem The (@0345361887).</description>
    <link>https://dev.to/0345361887</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F382990%2F0b08aef3-87b0-4c2f-ace7-37cbc2d879d4.png</url>
      <title>DEV Community: Chiem The</title>
      <link>https://dev.to/0345361887</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/0345361887"/>
    <language>en</language>
    <item>
      <title>Your prayer-times app is probably 2 minutes early in Indonesia</title>
      <dc:creator>Chiem The</dc:creator>
      <pubDate>Tue, 20 Oct 2026 06:35:24 +0000</pubDate>
      <link>https://dev.to/0345361887/your-prayer-times-app-is-probably-2-minutes-early-in-indonesia-4ae3</link>
      <guid>https://dev.to/0345361887/your-prayer-times-app-is-probably-2-minutes-early-in-indonesia-4ae3</guid>
      <description>&lt;p&gt;I maintain a small offline prayer-times app (Flutter, no backend). It computes times on the phone with the &lt;code&gt;adhan&lt;/code&gt; library. This week I added Indonesian and Malay, and a test against official timetables turned up something worth sharing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Indonesia's Ministry of Religious Affairs (Kemenag) publishes the official schedule. Its angles are well documented: Fajr (Subuh) at 20 degrees below the horizon, Isha at 18. &lt;code&gt;adhan&lt;/code&gt; ships a method with exactly those angles (it is called &lt;code&gt;Singapore&lt;/code&gt;), so I expected an exact match.&lt;/p&gt;

&lt;p&gt;It was not. Against Kemenag's Jakarta table for 1 Ramadan 1447 (19 Feb 2026) and 30 Ramadan (20 Mar 2026):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prayer&lt;/th&gt;
&lt;th&gt;Raw 20/18&lt;/th&gt;
&lt;th&gt;Kemenag&lt;/th&gt;
&lt;th&gt;Diff&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Subuh&lt;/td&gt;
&lt;td&gt;04:40&lt;/td&gt;
&lt;td&gt;04:41 / 04:42&lt;/td&gt;
&lt;td&gt;+1 to +2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dhuhr&lt;/td&gt;
&lt;td&gt;12:02&lt;/td&gt;
&lt;td&gt;12:04&lt;/td&gt;
&lt;td&gt;+2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asr&lt;/td&gt;
&lt;td&gt;15:11&lt;/td&gt;
&lt;td&gt;15:13&lt;/td&gt;
&lt;td&gt;+2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maghrib&lt;/td&gt;
&lt;td&gt;18:15 / 18:04&lt;/td&gt;
&lt;td&gt;18:18 / 18:07&lt;/td&gt;
&lt;td&gt;+3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isya&lt;/td&gt;
&lt;td&gt;19:13&lt;/td&gt;
&lt;td&gt;19:15&lt;/td&gt;
&lt;td&gt;+2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;Kemenag adds an &lt;em&gt;ihtiyat&lt;/em&gt;, a precautionary margin of about two minutes, to each time. The angles alone do not reproduce it. Maghrib came out three minutes later in both samples.&lt;/p&gt;

&lt;p&gt;That matters more than it looks: Maghrib is iftar. An app that is two minutes early tells people to break their fast before the official time, during the one month they check it most.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;The engine already supported per-prayer minute offsets (for matching a local mosque), so the fix was a regional default instead of new math:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;kemenagIhtiyat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PrayerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;{&lt;/span&gt;
  &lt;span class="n"&gt;PrayerName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fajr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// Imsak (Subuh - 10) never later than official&lt;/span&gt;
  &lt;span class="n"&gt;PrayerName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sunrise&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;PrayerName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dhuhr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;PrayerName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;PrayerName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maghrib&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// iftar never earlier than official&lt;/span&gt;
  &lt;span class="n"&gt;PrayerName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isha&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rounding is deliberate and asymmetric. Where the measured offset ranged, I picked the side that errs safely: Maghrib +3 so iftar is never early, Subuh +1 so the Imsak cutoff is never late.&lt;/p&gt;

&lt;p&gt;A test pins one real day so the defaults cannot drift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;imsak&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'04:31'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maghrib&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'18:18'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Malaysia was a different story: the official Kuala Lumpur Imsak matched raw Subuh at 20 degrees, which means the official Subuh runs 9 to 10 minutes later, closer to 18 degrees. Same mechanism, different numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;If you build anything that computes religious or legal times, test against the published table of the authority people actually follow, not against the method name in the library. Two data points per region caught both issues.&lt;/p&gt;

&lt;p&gt;The app is Namaz Time on Google Play, if you want to see it: &lt;a href="https://play.google.com/store/apps/details?id=com.pipex.namaztime&amp;amp;referrer=utm_source%3Ddevto%26utm_medium%3Dsocial%26utm_campaign%3Darticle" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.pipex.namaztime&amp;amp;referrer=utm_source%3Ddevto%26utm_medium%3Dsocial%26utm_campaign%3Darticle&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>mobile</category>
      <category>testing</category>
    </item>
    <item>
      <title>Google api</title>
      <dc:creator>Chiem The</dc:creator>
      <pubDate>Tue, 20 Jan 2026 04:14:49 +0000</pubDate>
      <link>https://dev.to/0345361887/google-api-ce0</link>
      <guid>https://dev.to/0345361887/google-api-ce0</guid>
      <description>&lt;p&gt;Hi everyone,&lt;br&gt;
Recently, I’ve been working on a small app and decided to go all-in with Google APIs. I’d like to share some thoughts for anyone who’s considering this approach.&lt;br&gt;
What I’m using&lt;br&gt;
Google Sign-In (standard OAuth2)&lt;br&gt;
Google Drive API for backup &amp;amp; restore&lt;br&gt;
Offline-first architecture:&lt;br&gt;
The app works normally even without an internet connection&lt;br&gt;
A sync queue is maintained and automatically syncs once the network is back&lt;br&gt;
What this enables&lt;br&gt;
Data backup to Google Drive (private app folder)&lt;br&gt;
Multi-device sync when users sign in with the same Google account&lt;br&gt;
What I like about this approach&lt;br&gt;
No need for users to create a new account → Google login is enough&lt;br&gt;
Switching devices or resetting the phone doesn’t cause data loss&lt;br&gt;
Drive API is quite safe if you use the right scopes (no sensitive permissions)&lt;br&gt;
Much smoother user experience compared to local-only storage&lt;br&gt;
Some technical notes (for fellow devs)&lt;br&gt;
You need to design the data model for offline-first from day one (conflicts, versioning)&lt;br&gt;
Sync logic shouldn’t be “push everything” — batching + checksums matter&lt;br&gt;
Limit Drive scopes (e.g. appDataFolder) to pass review more easily&lt;br&gt;
Always handle the case where users revoke Google permissions&lt;br&gt;
I think this approach fits well for&lt;br&gt;
Personal apps / utility tools&lt;br&gt;
Apps that want to avoid maintaining a complex backend&lt;br&gt;
👉 App link (would appreciate your support and feedback):&lt;br&gt;
&lt;a href="https://lnkd.in/gyZ_wA-i" rel="noopener noreferrer"&gt;https://lnkd.in/gyZ_wA-i&lt;/a&gt;&lt;br&gt;
👉 Product page:&lt;br&gt;
&lt;a href="https://lnkd.in/guc3PVzy" rel="noopener noreferrer"&gt;https://lnkd.in/guc3PVzy&lt;/a&gt;&lt;br&gt;
Thanks for reading!&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
  </channel>
</rss>
