From b1863cff1dc97cb1eafcad70906b684a71029405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Dole=C5=BEel?= Date: Sat, 8 Jun 2013 19:02:10 +0000 Subject: [PATCH] * Source/NSTimeZone.m Implement nextDaylightSavingTimeTransitionAfterDate: git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@36709 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 5 +++ Source/NSTimeZone.m | 67 +++++++++++++++++++++++++++++++++++++ Tests/base/NSTimeZone/use.m | 6 ++++ 3 files changed, 78 insertions(+) diff --git a/ChangeLog b/ChangeLog index 11261c24c..493840420 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2013-06-08 Lubos Dolezel + + * Source/NSTimeZone.m + Implement nextDaylightSavingTimeTransitionAfterDate: + 2013-06-06 Sebastian Reitenbach * Source/Additions/Unicode.m diff --git a/Source/NSTimeZone.m b/Source/NSTimeZone.m index 28ac99c15..fc4ba644d 100644 --- a/Source/NSTimeZone.m +++ b/Source/NSTimeZone.m @@ -166,6 +166,7 @@ NSString * const NSSystemTimeZoneDidChangeNotification #endif #define BUFFER_SIZE 512 +#define WEEK_MILLISECONDS (7.0*24.0*60.0*60.0*1000.0) #if GS_USE_ICU == 1 static inline int @@ -2231,7 +2232,73 @@ localZoneString, [zone name], sign, s/3600, (s/60)%60); - (NSDate *) nextDaylightSavingTimeTransitionAfterDate: (NSDate *)aDate { +#if GS_USE_ICU == 1 + /* ICU doesn't provide transition information per se. + * The canonical method of retrieving this piece of information is to + * use binary search. + */ + + int32_t originalOffset, currentOffset; + UCalendar *cal; + UErrorCode err = U_ZERO_ERROR; + UDate currentTime; + int i; + NSDate* result = nil; + + cal = ICUCalendarSetup (self, nil); + if (cal == NULL) + return nil; + + currentTime = [aDate timeIntervalSince1970] * 1000.0; + ucal_setMillis (cal, currentTime, &err); + originalOffset = ucal_get (cal, UCAL_DST_OFFSET, &err); + if (U_FAILURE(err)) + return nil; + + /* First try to find the next transition by adding a week at a time */ + /* Avoid ending in an infinite loop in case there is no transition at all */ + + for (i = 0; i < 53; i++) + { + /* Add a single week */ + currentTime += WEEK_MILLISECONDS; + + ucal_setMillis (cal, currentTime, &err); + if (U_FAILURE(err)) + break; + + currentOffset = ucal_get (cal, UCAL_DST_OFFSET, &err); + if (U_FAILURE(err)) + break; + + if (currentOffset != originalOffset) + { + double interval = WEEK_MILLISECONDS / 2.0; + /* Now use bisection to determine the exact moment */ + + while (interval >= 1.0) + { + ucal_setMillis (cal, currentTime - interval, &err); + + currentOffset = ucal_get (cal, UCAL_DST_OFFSET, &err); + + if (currentOffset != originalOffset) + currentTime -= interval; /* it is in the lower half */ + + interval /= 2.0; + } + + result = + [NSDate dateWithTimeIntervalSince1970: floor(currentTime/1000.0)]; + } + } + + ucal_close (cal); + + return result; +#else return nil; // FIXME; +#endif } - (NSTimeInterval) daylightSavingTimeOffset diff --git a/Tests/base/NSTimeZone/use.m b/Tests/base/NSTimeZone/use.m index b2b17d44d..0199bbbcd 100644 --- a/Tests/base/NSTimeZone/use.m +++ b/Tests/base/NSTimeZone/use.m @@ -50,6 +50,12 @@ int main() && [current isDaylightSavingTime] == NO, "can set default time zone"); + current = [NSTimeZone timeZoneWithName: @"Europe/Brussels"]; + date = [current nextDaylightSavingTimeTransitionAfterDate: + [NSDate dateWithString: @"2013-06-08 20:00:00 +0200"]]; + PASS_EQUAL(date, [NSDate dateWithString: @"2013-10-27 03:00:00 +0200"], + "can calculate next DST transition"); + START_SET("NSLocale") if (!NSLOCALE_SUPPORTED) SKIP("NSLocale not supported\nThe ICU library was not available when GNUstep-base was built")