]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/main/classes/core/src/com/ibm/icu/util/BasicTimeZone.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / main / classes / core / src / com / ibm / icu / util / BasicTimeZone.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 2007-2010, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.util;
8
9 import java.util.BitSet;
10 import java.util.Date;
11 import java.util.LinkedList;
12 import java.util.List;
13
14 import com.ibm.icu.impl.Grego;
15
16 /**
17  * {@icu} BasicTimeZone extends <code>TimeZone</code> with additional methods to access
18  * time zone transitions and rules.  All ICU <code>TimeZone</code> concrete subclasses
19  * extend this class. APIs added to <code>java.util.TimeZone</code> by
20  * <code>BasicTimeZone</code> are annotated with <strong>'<font
21  * style="color:red">[icu]</font>'</strong>.
22  *
23  * @see com.ibm.icu.util.TimeZoneRule
24  * @see com.ibm.icu.util.TimeZoneTransition
25  *
26  * @stable ICU 3.8
27  */
28 public abstract class BasicTimeZone extends TimeZone {
29
30     private static final long serialVersionUID = -3204278532246180932L;
31
32     private static final long MILLIS_PER_YEAR = 365*24*60*60*1000L;
33
34     /**
35      * {@icu} Returns the first time zone transition after the base time.
36      *
37      * @param base      The base time.
38      * @param inclusive Whether the base time is inclusive or not.
39      *
40      * @return  A <code>Date</code> holding the first time zone transition time
41      *          after the given base time, or null if no time zone transitions
42      *          are available after the base time.
43      *
44      * @stable ICU 3.8
45      */
46     public abstract TimeZoneTransition getNextTransition(long base, boolean inclusive);
47
48     /**
49      * {@icu} Returns the last time zone transition before the base time.
50      *
51      * @param base      The base time.
52      * @param inclusive Whether the base time is inclusive or not.
53      *
54      * @return  A <code>Date</code> holding the last time zone transition time
55      *          before the given base time, or null if no time zone transitions
56      *          are available before the base time.
57      *
58      * @stable ICU 3.8
59      */
60     public abstract TimeZoneTransition getPreviousTransition(long base, boolean inclusive);
61
62     /**
63      * {@icu} Checks if the time zone has equivalent transitions in the time range.
64      * This method returns true when all of transition times, from/to standard
65      * offsets and DST savings used by this time zone match the other in the
66      * time range.
67      *
68      * @param tz    The instance of <code>TimeZone</code>
69      * @param start The start time of the evaluated time range (inclusive)
70      * @param end   The end time of the evaluated time range (inclusive)
71      *
72      * @return true if the other time zone has the equivalent transitions in the
73      * time range.  When tz is not a <code>BasicTimeZone</code>, this method
74      * returns false.
75      *
76      * @stable ICU 3.8
77      */
78     public boolean hasEquivalentTransitions(TimeZone tz, long start, long end) {
79         return hasEquivalentTransitions(tz, start, end, false);
80     }
81
82     /**
83      * {@icu} Checks if the time zone has equivalent transitions in the time range.
84      * This method returns true when all of transition times, from/to standard
85      * offsets and DST savings used by this time zone match the other in the
86      * time range.
87      *
88      * @param tz    The instance of <code>TimeZone</code>
89      * @param start The start time of the evaluated time range (inclusive)
90      * @param end   The end time of the evaluated time range (inclusive)
91      * @param ignoreDstAmount When true, any transitions with only daylight saving amount
92      * changes will be ignored, except either of them is zero. For example, a transition
93      * from rawoffset 3:00/dstsavings 1:00 to rawoffset 2:00/dstsavings 2:00 is excluded
94      * from the comparison, but a transtion from rawoffset 2:00/dstsavings 1:00 to
95      * rawoffset 3:00/dstsavings 0:00 is included.
96      *
97      * @return true if the other time zone has the equivalent transitions in the
98      * time range.  When tz is not a <code>BasicTimeZone</code>, this method
99      * returns false.
100      *
101      * @stable ICU 3.8
102      */
103     public boolean hasEquivalentTransitions(TimeZone tz, long start, long end, 
104                                             boolean ignoreDstAmount) {
105         if (hasSameRules(tz)) {
106             return true;
107         }
108         if (!(tz instanceof BasicTimeZone)) {
109             return false;
110         }
111
112         // Check the offsets at the start time
113         int[] offsets1 = new int[2];
114         int[] offsets2 = new int[2];
115
116         getOffset(start, false, offsets1);
117         tz.getOffset(start, false, offsets2);
118
119         if (ignoreDstAmount) {
120             if ((offsets1[0] + offsets1[1] != offsets2[0] + offsets2[1])
121                 || (offsets1[1] != 0 && offsets2[1] == 0)
122                 || (offsets1[1] == 0 && offsets2[1] != 0)) {
123                 return false;
124             }
125         } else {
126             if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {
127                 return false;
128             }
129         }
130
131         // Check transitions in the range
132         long time = start;
133         while (true) {
134             TimeZoneTransition tr1 = getNextTransition(time, false);
135             TimeZoneTransition tr2 = ((BasicTimeZone)tz).getNextTransition(time, false);
136
137             if (ignoreDstAmount) {
138                 // Skip a transition which only differ the amount of DST savings
139                 while (true) {
140                     if (tr1 != null
141                             && tr1.getTime() <= end
142                             && (tr1.getFrom().getRawOffset() + tr1.getFrom().getDSTSavings()
143                                     == tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings())
144                             && (tr1.getFrom().getDSTSavings() != 0 && tr1.getTo().getDSTSavings() != 0)) {
145                         tr1 = getNextTransition(tr1.getTime(), false);
146                     } else {
147                         break;
148                     }
149                 }
150                 while (true) {
151                     if (tr2 != null
152                             && tr2.getTime() <= end
153                             && (tr2.getFrom().getRawOffset() + tr2.getFrom().getDSTSavings()
154                                     == tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings())
155                             && (tr2.getFrom().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() != 0)) {
156                         tr2 = ((BasicTimeZone)tz).getNextTransition(tr2.getTime(), false);
157                     } else {
158                         break;
159                     }
160                 }
161             }
162
163             boolean inRange1 = false;
164             boolean inRange2 = false;
165             if (tr1 != null) {
166                 if (tr1.getTime() <= end) {
167                     inRange1 = true;
168                 }
169             }
170             if (tr2 != null) {
171                 if (tr2.getTime() <= end) {
172                     inRange2 = true;
173                 }
174             }
175             if (!inRange1 && !inRange2) {
176                 // No more transition in the range
177                 break;
178             }
179             if (!inRange1 || !inRange2) {
180                 return false;
181             }
182             if (tr1.getTime() != tr2.getTime()) {
183                 return false;
184             }
185             if (ignoreDstAmount) {
186                 if (tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings()
187                             != tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings()
188                         || tr1.getTo().getDSTSavings() != 0 &&  tr2.getTo().getDSTSavings() == 0
189                         || tr1.getTo().getDSTSavings() == 0 &&  tr2.getTo().getDSTSavings() != 0) {
190                     return false;
191                 }
192             } else {
193                 if (tr1.getTo().getRawOffset() != tr2.getTo().getRawOffset() ||
194                     tr1.getTo().getDSTSavings() != tr2.getTo().getDSTSavings()) {
195                     return false;
196                 }
197             }
198             time = tr1.getTime();
199         }
200         return true;
201     }
202
203     /**
204      * {@icu} Returns the array of <code>TimeZoneRule</code> which represents the rule
205      * of this time zone object.  The first element in the result array will
206      * be the <code>InitialTimeZoneRule</code> instance for the initial rule.
207      * The rest will be either <code>AnnualTimeZoneRule</code> or
208      * <code>TimeArrayTimeZoneRule</code> instances representing transitions.
209      *
210      * @return  The array of <code>TimeZoneRule</code> which represents this
211      *          time zone.
212      *
213      * @stable ICU 3.8
214      */
215     public abstract TimeZoneRule[] getTimeZoneRules();
216
217     /**
218      * {@icu} Returns the array of <code>TimeZoneRule</code> which represents the rule
219      * of this time zone object since the specified start time.  The first
220      * element in the result array will be the <code>InitialTimeZoneRule</code>
221      * instance for the initial rule.  The rest will be either
222      * <code>AnnualTimeZoneRule</code> or <code>TimeArrayTimeZoneRule</code>
223      * instances representing transitions.
224      *
225      * @param start The start time (inclusive).
226      * @return  The array of <code>TimeZoneRule</code> which represents this
227      *          time zone since the start time.
228      *
229      * @stable ICU 3.8
230      */
231     public TimeZoneRule[] getTimeZoneRules(long start) {
232         TimeZoneRule[] all = getTimeZoneRules();
233         TimeZoneTransition tzt = getPreviousTransition(start, true);
234         if (tzt == null) {
235             // No need to filter out rules only applicable to time before the start
236             return all;
237         }
238
239         BitSet isProcessed = new BitSet(all.length);
240         List<TimeZoneRule> filteredRules = new LinkedList<TimeZoneRule>();
241
242         // Create initial rule
243         TimeZoneRule initial = new InitialTimeZoneRule(tzt.getTo().getName(),
244                 tzt.getTo().getRawOffset(), tzt.getTo().getDSTSavings());
245         filteredRules.add(initial);
246         isProcessed.set(0);
247
248         // Mark rules which does not need to be processed
249         for (int i = 1; i < all.length; i++) {
250             Date d = all[i].getNextStart(start, initial.getRawOffset(),
251                     initial.getDSTSavings(), false);
252             if (d == null) {
253                 isProcessed.set(i);
254             }
255         }
256
257         long time = start;
258         boolean bFinalStd = false, bFinalDst = false;
259         while(!bFinalStd || !bFinalDst) {
260             tzt = getNextTransition(time, false);
261             if (tzt == null) {
262                 break;
263             }
264             time = tzt.getTime();
265
266             TimeZoneRule toRule = tzt.getTo();
267             int ruleIdx = 1;
268             for (; ruleIdx < all.length; ruleIdx++) {
269                 if (all[ruleIdx].equals(toRule)) {
270                     break;
271                 }
272             }
273             if (ruleIdx >= all.length) {
274                 throw new IllegalStateException("The rule was not found");
275             }
276             if (isProcessed.get(ruleIdx)) {
277                 continue;
278             }
279             if (toRule instanceof TimeArrayTimeZoneRule) {
280                 TimeArrayTimeZoneRule tar = (TimeArrayTimeZoneRule)toRule;
281
282                 // Get the previous raw offset and DST savings before the very first start time
283                 long t = start;
284                 while(true) {
285                     tzt = getNextTransition(t, false);
286                     if (tzt == null) {
287                         break;
288                     }
289                     if (tzt.getTo().equals(tar)) {
290                         break;
291                     }
292                     t = tzt.getTime();
293                 }
294                 if (tzt != null) {
295                     // Check if the entire start times to be added
296                     Date firstStart = tar.getFirstStart(tzt.getFrom().getRawOffset(),
297                             tzt.getFrom().getDSTSavings());
298                     if (firstStart.getTime() > start) {
299                         // Just add the rule as is
300                         filteredRules.add(tar);
301                     } else {
302                         // Collect transitions after the start time
303                         long[] times = tar.getStartTimes();
304                         int timeType = tar.getTimeType();
305                         int idx;
306                         for (idx = 0; idx < times.length; idx++) {
307                             t = times[idx];
308                             if (timeType == DateTimeRule.STANDARD_TIME) {
309                                 t -= tzt.getFrom().getRawOffset();
310                             }
311                             if (timeType == DateTimeRule.WALL_TIME) {
312                                 t -= tzt.getFrom().getDSTSavings();
313                             }
314                             if (t > start) {
315                                 break;
316                             }
317                         }
318                         int asize = times.length - idx;
319                         if (asize > 0) {
320                             long[] newtimes = new long[asize];
321                             System.arraycopy(times, idx, newtimes, 0, asize);
322                             TimeArrayTimeZoneRule newtar = new TimeArrayTimeZoneRule(
323                                     tar.getName(), tar.getRawOffset(), tar.getDSTSavings(),
324                                     newtimes, tar.getTimeType());
325                             filteredRules.add(newtar);
326                         }
327                     }
328                 }
329             } else if (toRule instanceof AnnualTimeZoneRule) {
330                 AnnualTimeZoneRule ar = (AnnualTimeZoneRule)toRule;
331                 Date firstStart = ar.getFirstStart(tzt.getFrom().getRawOffset(),
332                         tzt.getFrom().getDSTSavings());
333                 if (firstStart.getTime() == tzt.getTime()) {
334                     // Just add the rule as is
335                     filteredRules.add(ar);
336                 } else {
337                     // Calculate the transition year
338                     int[] dfields = new int[6];
339                     Grego.timeToFields(tzt.getTime(), dfields);
340                     // Recreate the rule
341                     AnnualTimeZoneRule newar = new AnnualTimeZoneRule(ar.getName(),
342                             ar.getRawOffset(), ar.getDSTSavings(),
343                             ar.getRule(), dfields[0], ar.getEndYear());
344                     filteredRules.add(newar);
345                 }
346                 // Check if this is a final rule
347                 if (ar.getEndYear() == AnnualTimeZoneRule.MAX_YEAR) {
348                     // After both final standard and dst rule are processed,
349                     // exit this while loop.
350                     if (ar.getDSTSavings() == 0) {
351                         bFinalStd = true;
352                     } else {
353                         bFinalDst = true;
354                     }
355                 }
356             }
357             isProcessed.set(ruleIdx);
358         }
359         TimeZoneRule[] rules = filteredRules.toArray(new TimeZoneRule[filteredRules.size()]);
360         return rules;
361     }
362
363     /**
364      * {@icu} Returns the array of <code>TimeZoneRule</code> which represents the rule of
365      * this time zone object near the specified date.  Some applications are not
366      * capable to handle historic time zone rule changes.  Also some applications
367      * can only handle certain type of rule definitions.  This method returns
368      * either a single <code>InitialTimeZoneRule</code> if this time zone does not
369      * have any daylight saving time within 1 year from the specified time, or a
370      * pair of <code>AnnualTimeZoneRule</code> whose rule type is
371      * <code>DateTimeRule.DOW</code> for date and <code>DateTimeRule.WALL_TIME</code>
372      * for time with a single <code>InitialTimeZoneRule</code> representing the
373      * initial time, when this time zone observes daylight saving time near the
374      * specified date.  Thus, the result may be only valid for dates around the
375      * specified date.
376      *
377      * @param date The date to be used for <code>TimeZoneRule</code> extraction.
378      * @return The array of <code>TimeZoneRule</code>, either a single
379      * <code>InitialTimeZoneRule</code> object, or a pair of <code>AnnualTimeZoneRule</code>
380      * with a single <code>InitialTimeZoneRule</code>.  The first element in the
381      * array is always a <code>InitialTimeZoneRule</code>.
382      *
383      * @stable ICU 3.8
384      */
385     public TimeZoneRule[] getSimpleTimeZoneRulesNear(long date) {
386         AnnualTimeZoneRule[] annualRules = null;
387         TimeZoneRule initialRule = null;
388         // Get the next transition
389         TimeZoneTransition tr = getNextTransition(date, false);
390         if (tr != null) {
391             String initialName = tr.getFrom().getName();
392             int initialRaw = tr.getFrom().getRawOffset();
393             int initialDst = tr.getFrom().getDSTSavings();
394
395             // Check if the next transition is either DST->STD or STD->DST and
396             // within roughly 1 year from the specified date
397             long nextTransitionTime = tr.getTime();
398             if (((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0)
399                     || (tr.getFrom().getDSTSavings() != 0 && tr.getTo().getDSTSavings() == 0))
400                         && date + MILLIS_PER_YEAR > nextTransitionTime) {
401                 annualRules = new AnnualTimeZoneRule[2];
402                 // Get local wall time for the transition time
403                 int dtfields[] = Grego.timeToFields(nextTransitionTime
404                         + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(), null);
405                 int weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], dtfields[2]);
406                 // Create DOW rule
407                 DateTimeRule dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3],
408                         dtfields[5], DateTimeRule.WALL_TIME);
409
410                 AnnualTimeZoneRule secondRule = null;
411
412                 // Note:  SimpleTimeZone does not support raw offset change.
413                 // So we always use raw offset of the given time for the rule,
414                 // even raw offset is changed.  This will result that the result
415                 // zone to return wrong offset after the transition.
416                 // When we encounter such case, we do not inspect next next
417                 // transition for another rule.
418                 annualRules[0] = new AnnualTimeZoneRule(tr.getTo().getName(),
419                         initialRaw, tr.getTo().getDSTSavings(),
420                         dtr, dtfields[0], AnnualTimeZoneRule.MAX_YEAR);
421
422                 if (tr.getTo().getRawOffset() == initialRaw) {
423
424                     // Get the next next transition
425                     tr = getNextTransition(nextTransitionTime, false);
426                     if (tr != null) {
427                         // Check if the next next transition is either DST->STD or STD->DST
428                         // and within roughly 1 year from the next transition
429                         if (((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0)
430                                 || (tr.getFrom().getDSTSavings() != 0 
431                                     && tr.getTo().getDSTSavings() == 0))
432                             && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
433                             // Generate another DOW rule
434                             dtfields = Grego.timeToFields(tr.getTime()
435                                     + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(), 
436                                                           dtfields);
437                             weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], 
438                                                                     dtfields[2]);
439                             dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3], 
440                                                    dtfields[5], DateTimeRule.WALL_TIME);
441                             secondRule = new AnnualTimeZoneRule(tr.getTo().getName(),
442                                     tr.getTo().getRawOffset(), tr.getTo().getDSTSavings(),
443                                     dtr, dtfields[0] - 1, AnnualTimeZoneRule.MAX_YEAR);
444                             // Make sure this rule can be applied to the specified date
445                             Date d = secondRule.getPreviousStart(date, tr.getFrom().getRawOffset(),
446                                     tr.getFrom().getDSTSavings(), true);
447                             if (d != null && d.getTime() <= date
448                                     && initialRaw == tr.getTo().getRawOffset()
449                                     && initialDst == tr.getTo().getDSTSavings()) {
450                                 // We can use this rule as the second transition rule
451                                 annualRules[1] = secondRule;
452                             }
453                         }
454                     }
455                 }
456
457                 if (annualRules[1] == null) {
458                     // Try previous transition
459                     tr = getPreviousTransition(date, true);
460                     if (tr != null) {
461                         // Check if the previous transition is either DST->STD or STD->DST.
462                         // The actual transition time does not matter here.
463                         if ((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0)
464                                 || (tr.getFrom().getDSTSavings() != 0
465                                     && tr.getTo().getDSTSavings() == 0)) {
466                             // Generate another DOW rule
467                             dtfields = Grego.timeToFields(tr.getTime()
468                                     + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(), 
469                                                           dtfields);
470                             weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], 
471                                                                     dtfields[2]);
472                             dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3], 
473                                                    dtfields[5], DateTimeRule.WALL_TIME);
474
475                             // second rule raw/dst offsets should match raw/dst offsets
476                             // at the given time
477                             secondRule = new AnnualTimeZoneRule(
478                                 tr.getTo().getName(), initialRaw, initialDst, dtr, 
479                                 annualRules[0].getStartYear() - 1, AnnualTimeZoneRule.MAX_YEAR);
480
481                             // Check if this rule start after the first rule after the
482                             // specified date
483                             Date d = secondRule.getNextStart(date, tr.getFrom().getRawOffset(), 
484                                                              tr.getFrom().getDSTSavings(), false);
485                             if (d.getTime() > nextTransitionTime) {
486                                 // We can use this rule as the second transition rule
487                                 annualRules[1] = secondRule;
488                             }
489                         }
490                     }
491                 }
492                 if (annualRules[1] == null) {
493                     // Cannot generate a good pair of AnnualTimeZoneRule
494                     annualRules = null;
495                 } else {
496                     // The initial rule should represent the rule before the previous transition
497                     initialName = annualRules[0].getName();
498                     initialRaw = annualRules[0].getRawOffset();
499                     initialDst = annualRules[0].getDSTSavings();
500                 }
501             }
502             initialRule = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
503         } else {
504             // Try the previous one
505             tr = getPreviousTransition(date, true);
506             if (tr != null) {
507                 initialRule = new InitialTimeZoneRule(tr.getTo().getName(),
508                         tr.getTo().getRawOffset(), tr.getTo().getDSTSavings());
509             } else {
510                 // No transitions in the past.  Just use the current offsets
511                 int[] offsets = new int[2];
512                 getOffset(date, false, offsets);
513                 initialRule = new InitialTimeZoneRule(getID(), offsets[0], offsets[1]);
514             }
515         }
516
517         TimeZoneRule[] result = null;
518         if (annualRules == null) {
519             result = new TimeZoneRule[1];
520             result[0] = initialRule;
521         } else {
522             result = new TimeZoneRule[3];
523             result[0] = initialRule;
524             result[1] = annualRules[0];
525             result[2] = annualRules[1];
526         }
527
528         return result;
529     }
530
531     /**
532      * {@icu} The time type option for standard time used by
533      * {@link #getOffsetFromLocal(long, int, int, int[])}
534      * @internal
535      * @deprecated This API is ICU internal only.
536      */
537     public static final int LOCAL_STD = 0x01;
538
539     /**
540      * {@icu} The time type option for daylight saving time used by
541      * {@link #getOffsetFromLocal(long, int, int, int[])}
542      * @internal
543      * @deprecated This API is ICU internal only.
544      */
545     public static final int LOCAL_DST = 0x03;
546
547     /**
548      * {@icu} The option designate former time to be used by
549      * {@link #getOffsetFromLocal(long, int, int, int[])}
550      * @internal
551      * @deprecated This API is ICU internal only.
552      */
553     public static final int LOCAL_FORMER = 0x04;
554
555     /**
556      * {@icu} The option designate latter time to be used by
557      * {@link #getOffsetFromLocal(long, int, int, int[])}
558      * @internal
559      * @deprecated This API is ICU internal only.
560      */
561     public static final int LOCAL_LATTER = 0x0C;
562
563     /**
564      * {@icu} The bit mask for the time type option used by
565      * {@link #getOffsetFromLocal(long, int, int, int[])}
566      * @internal
567      * @deprecated This API is ICU internal only.
568      */
569     protected static final int STD_DST_MASK = 0x03;
570
571     /**
572      * {@icu} The bit mask for the former/latter option used by
573      * {@link #getOffsetFromLocal(long, int, int, int[])}
574      * @internal
575      * @deprecated This API is ICU internal only.
576      */
577     protected static final int FORMER_LATTER_MASK = 0x0C;
578
579     /**
580      * {@icu} Returns time zone offsets from local wall time.
581      * @internal
582      * @deprecated This API is ICU internal only.
583      */
584     public void getOffsetFromLocal(long date,
585             int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
586         throw new IllegalStateException("Not implemented");
587     }
588
589     /**
590      * Protected no arg constructor.
591      * @stable ICU 3.8
592      */
593     protected BasicTimeZone() {
594     }
595 }