1+ /*
2+ wdtime.js
3+
4+ Copyright (C) 2025 Bedir Ekim
5+
6+ This file is part of WatchDuck.
7+
8+ WatchDuck is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License
9+ as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10+
11+ WatchDuck is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
12+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
13+
14+ You should have received a copy of the GNU Affero General Public License along with WatchDuck.
15+ If not, see <https://www.gnu.org/licenses/>.
16+ */
17+
18+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
19+ formatAllTimestamps ( ) ;
20+ } ) ;
21+
22+ /**
23+ * Format all timestamp elements on the page.
24+ */
25+ function formatAllTimestamps ( ) {
26+ document . querySelectorAll ( '[data-timestamp]' ) . forEach ( formatTimestampElement ) ;
27+
28+ // Make sure the time zone notice is correct for users who don't have JS enabled
29+ document . querySelectorAll ( '.tz-notice-nojs' ) . forEach ( e => e . style . display = 'none' ) ;
30+ document . querySelectorAll ( '.tz-notice-js' ) . forEach ( e => e . style . display = 'block' ) ;
31+ }
32+
33+ function formatTimestampElement ( element ) {
34+ const timestamp = parseInt ( element . getAttribute ( 'data-timestamp' ) , 10 ) * 1000 ;
35+ const date = new Date ( timestamp ) ;
36+
37+ const formatType = element . getAttribute ( 'data-format-type' ) || 'datetime' ;
38+ const isRange = element . hasAttribute ( 'data-end-timestamp' ) ;
39+
40+ if ( isRange ) {
41+ const endTimestamp = parseInt ( element . getAttribute ( 'data-end-timestamp' ) , 10 ) * 1000 ;
42+ const endDate = new Date ( endTimestamp ) ;
43+ element . textContent = formatDateRange ( date , endDate ) ;
44+ } else if ( formatType === 'datetime' ) {
45+ element . textContent = formatDateTime ( date , true ) ;
46+ } else if ( formatType === 'lastupdated' ) {
47+ element . textContent = 'Last updated: ' + formatDateTime ( date , true ) ;
48+ } else if ( formatType === 'ongoing' ) {
49+ element . textContent = 'Down since ' + formatDateTime ( date ) ;
50+ }
51+ }
52+
53+ /**
54+ * Format a date in a user-friendly format.
55+ * @param {Date } date - Date to format.
56+ * @param {Boolean } includeTimezone - Whether to include timezone information.
57+ * @param {Boolean } isOutageTime - Whether this is for an outage timestamp.
58+ * @returns {string } Formatted date string.
59+ */
60+ function formatDateTime ( date , includeTimezone = false , isOutageTime = false ) {
61+ if ( isOutageTime ) {
62+ const dateStr = date . toLocaleDateString ( ) ;
63+ const timeStr = date . toLocaleTimeString ( undefined , {
64+ hour : '2-digit' ,
65+ minute : '2-digit'
66+ } ) ;
67+ return `${ dateStr } , ${ timeStr } ` ;
68+ }
69+
70+ const options = {
71+ year : 'numeric' ,
72+ month : 'short' ,
73+ day : 'numeric' ,
74+ hour : '2-digit' ,
75+ minute : '2-digit' ,
76+ second : '2-digit'
77+ } ;
78+
79+ const formattedDateTime = date . toLocaleString ( undefined , options ) ;
80+
81+ if ( includeTimezone ) {
82+ const tzString = getTimezoneString ( date ) ;
83+ return `${ formattedDateTime } ${ tzString } ` ;
84+ }
85+
86+ return formattedDateTime ;
87+ }
88+
89+ /**
90+ * Get the timezone string (like GMT+2) for a date.
91+ * @param {Date } date - The date to get timezone for.
92+ * @returns {string } Timezone string.
93+ */
94+ function getTimezoneString ( date ) {
95+ const timezoneOffset = date . getTimezoneOffset ( ) ;
96+ const offsetHours = Math . abs ( Math . floor ( timezoneOffset / 60 ) ) ;
97+ const offsetMinutes = Math . abs ( timezoneOffset % 60 ) ;
98+ const offsetSign = timezoneOffset <= 0 ? '+' : '-' ;
99+
100+ if ( offsetMinutes === 0 ) {
101+ return `GMT${ offsetSign } ${ offsetHours } ` ;
102+ } else {
103+ return `GMT${ offsetSign } ${ offsetHours } :${ offsetMinutes . toString ( ) . padStart ( 2 , '0' ) } ` ;
104+ }
105+ }
106+
107+ /**
108+ * Format a date range.
109+ * @param {Date } startDate - Start date.
110+ * @param {Date } endDate - End date.
111+ * @returns {string } Formatted date range.
112+ */
113+ function formatDateRange ( startDate , endDate ) {
114+ if ( startDate . getFullYear ( ) === endDate . getFullYear ( ) &&
115+ startDate . getMonth ( ) === endDate . getMonth ( ) &&
116+ startDate . getDate ( ) === endDate . getDate ( ) &&
117+ startDate . getHours ( ) === endDate . getHours ( ) &&
118+ startDate . getMinutes ( ) === endDate . getMinutes ( ) ) {
119+
120+ const formattedStart = formatDateTime ( startDate , false , true ) ;
121+ return `${ formattedStart } - Resolved under a minute :)` ;
122+ }
123+
124+ const formattedStart = formatDateTime ( startDate , false , true ) ;
125+ const formattedEnd = formatDateTime ( endDate , false , true ) ;
126+ return `${ formattedStart } - ${ formattedEnd } ` ;
127+ }
0 commit comments