Project

General

Profile

DateFormatAdjuster.java

Vladimir Tsichevski, 04/02/2025 09:54 AM

Download (6.72 KB)

 
1
package fillin.date;
2

    
3
/**
4
 * A utility class that adjusts date format strings to match a specified component order, replicating OpenEdge
5
 * ABL's format adjustment behavior as a pure function.
6
 */
7
public class DateFormatAdjuster
8
{
9
   /**
10
    * Left-rotate elements in a 3-item array (e.g., [A,B,C] becomes [B,C,A]).
11
    * 
12
    * @param a
13
    *        the array to rotate
14
    */
15
   private static void rotateLeft(final String[] a)
16
   {
17
      swap(a, 0, 2);
18
      swap(a, 0, 1);
19
   }
20

    
21
   /**
22
    * Right-rotate elements in a 3-item array (e.g., [A,B,C] → [C,A,B]).
23
    * 
24
    * @param a
25
    *        the array to rotate
26
    */
27
   private static void rotateRight(final String[] a)
28
   {
29
      swap(a, 1, 2);
30
      swap(a, 0, 1);
31
   }
32

    
33
   /**
34
    * Swap two components in array
35
    * 
36
    * @param a
37
    *        the array to modify
38
    * @param i1
39
    *        the first valid array index to swap
40
    * @param i2
41
    *        the second valid array index to swap
42
    */
43
   private static final void swap(final String[] a, final int i1, final int i2)
44
   {
45
      final String t = a[i1];
46
      a[i1] = a[i2];
47
      a[i2] = t;
48
   }
49

    
50
   /**
51
    * Reorders components and separators in a date format string for date format assignment based on the
52
    * specified component order.
53
    * <p>
54
    * This method adjusts the format string to align with the given order, but reordering occurs only when
55
    * the year component has fewer than 4 digits and exactly one other component has exactly 4 digits. In
56
    * such cases, the components are rearranged so the year component has 4 digits. Depending on the format
57
    * and order, reordering may involve swapping two components or rotating all components left or right.
58
    * </p>
59
    * <p>
60
    * When reordering is required, separators are adjusted based on the specified order:
61
    * </p>
62
    * <ul>
63
    *   <li><b>Month in first position ("mdy", "myd"):</b> Separators remain in their original order.</li>
64
    *   <li><b>Month in middle position ("dmy", "ymd"):</b> Separators are swapped.</li>
65
    *   <li><b>Month in last position ("dym", "ydm"):</b> The second separator is copied to the first position.</li>
66
    * </ul>
67
    *
68
    * @param format   a valid date format string (e.g., "9/99.9999", "999-99-99") containing two
69
    *                 separators (dot, slash, or dash)
70
    * @param order    the desired component order ("mdy", "dmy", "ymd", "myd", "dym", "ydm"), representing
71
    *                 the SESSION:DATE-FORMAT setting in OpenEdge ABL
72
    * @param expected the expected output string for visual inspection during development; this parameter
73
    *                 is temporary and must be removed in production
74
    *                 
75
    * @return         the adjusted format string matching the specified order for date format assignment,
76
    *                 or the original format if no reordering is required
77
    */
78
   public static String reorderForDateFormatAssignment(final String format, final String order,
79
            final String expected)
80
   {
81
      /*
82
       * 1. Test if the component re-ordering is required
83
       */
84
      final String[] comps = format.split("[./-]");
85
      assert comps.length == 3 : "We accept only formats containing separators";
86

    
87
      // Find a components with 4 digits
88
      int longComponentIdx = -1;
89
      for (int i = 0; i < 3; i++)
90
      {
91
         final String comp = comps[i];
92
         if (comp.length() == 4)
93
         {
94
            if (longComponentIdx >= 0)
95
            {
96
               // There can be only one
97
               return format;
98
            }
99
            longComponentIdx = i;
100
         }
101
      }
102

    
103
      if (longComponentIdx == -1)
104
      {
105
         // No long format
106
         return format;
107
      }
108

    
109
      final int yearIndex = order.indexOf('y');
110
      assert yearIndex >= 0;
111
      if (yearIndex == longComponentIdx)
112
      {
113
         // Year is already the long one
114
         return format;
115
      }
116

    
117
      // Extract separators
118
      final int leftSeparatorPos = comps[0].length();
119
      final char leftSeparatorInitial = format.charAt(leftSeparatorPos);
120
      final int rightSeparatorPos = leftSeparatorPos + 1 + comps[1].length();
121
      final char rightSeparatorInitial = format.charAt(rightSeparatorPos);
122

    
123
      switch (order)
124
      {
125
         case "mdy":
126
            if (longComponentIdx == 0)
127
            {
128
               rotateLeft(comps);
129
            }
130
            else
131
            {
132
               swap(comps, 1, 2);
133
            }
134

    
135
            break;
136

    
137
         case "dmy":
138
            if (longComponentIdx == 0)
139
            {
140
               swap(comps, 0, 2);
141
            }
142
            else
143
            {
144
               rotateRight(comps);
145
            }
146

    
147
            break;
148

    
149
         case "ymd":
150
            if (longComponentIdx == 1)
151
            {
152
               swap(comps, 0, 1);
153
            }
154
            else
155
            {
156
               rotateRight(comps);
157
            }
158
            
159
            break;
160

    
161
         case "myd":
162
            if (longComponentIdx == 0)
163
            {
164
               swap(comps, 0, 1);
165
            }
166
            else
167
            {
168
               swap(comps, 1, 2);
169
            }
170
            
171
            break;
172

    
173
         case "dym":
174
            if (longComponentIdx == 0)
175
            {
176
               rotateRight(comps);
177
            }
178
            else
179
            {
180
               rotateLeft(comps);
181
            }
182

    
183
            break;
184

    
185
         case "ydm":
186
            if (longComponentIdx == 1)
187
            {
188
               rotateLeft(comps);
189
            }
190
            else
191
            {
192
               swap(comps, 0, 2);
193
            }
194

    
195
            break;
196

    
197
         default:
198
            throw new IllegalArgumentException("Unexpected date order");
199
      }
200

    
201
      final char leftSeparator;
202
      final char rightSeparator;
203

    
204
      /*
205
       * 2. Compute separators
206
       */
207
      switch (order.indexOf('m'))
208
      {
209
         case 0:
210
         {
211
            // Separators order preserved
212
            leftSeparator = leftSeparatorInitial;
213
            rightSeparator = rightSeparatorInitial;
214

    
215
            break;
216
         }
217
         case 1:
218
         {
219
            // Separators are swapped
220
            leftSeparator = rightSeparatorInitial;
221
            rightSeparator = leftSeparatorInitial;
222

    
223
            break;
224
         }
225
         case 2:
226
         {
227
            // Right separator is copied to the left 
228
            leftSeparator = rightSeparator = rightSeparatorInitial;
229

    
230
            break;
231
         }
232
         default:
233
            throw new IllegalArgumentException("Unexpected SESSION:DATE-FORMAT value: " + order);
234
      }
235

    
236
      // Build the result
237
      final StringBuilder result = new StringBuilder(10);
238

    
239
      result.append(comps[0]);
240
      result.append(leftSeparator);
241
      result.append(comps[1]);
242
      result.append(rightSeparator);
243
      result.append(comps[2]);
244

    
245
      return result.toString();
246
   }
247
}