diff options
author | Jerry Flood <jflood@att.com> | 2019-04-03 15:10:10 -0400 |
---|---|---|
committer | Jerry Flood <jflood@att.com> | 2019-04-03 15:25:37 -0400 |
commit | 5d923ee5f40413f4327f82f77d01c06ebebbbfcb (patch) | |
tree | 391d38a2f6a4b076c377aa1d421af9082041f706 /cmso-optimizer/src | |
parent | 2bfc71e1c9eb6b746648242e02e88625180490af (diff) |
Updates for ETE with minizinc
Issue-ID: OPTFRA-436
Change-Id: Ib6eb65728bcdbb2ea27a94125580b181d63babd6
Signed-off-by: Jerry Flood <jflood@att.com>
Diffstat (limited to 'cmso-optimizer/src')
10 files changed, 123 insertions, 59 deletions
diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java index 23e3ad8..c3536b7 100644 --- a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java @@ -148,8 +148,8 @@ public class RecurringWindows { Instant cwStartInstant = changeWindow.getStartTime().toInstant(); Instant cwEndInstant = changeWindow.getEndTime().toInstant(); - List<DateTime> startList = getRecurringList(range.getStart_time(), cwStartInstant, rdata, cwEndInstant); - List<DateTime> endList = getRecurringList(range.getEnd_time(), cwStartInstant, rdata, cwEndInstant); + List<DateTime> startList = getRecurringList(range.getStart_time(), cwStartInstant, rdata, cwEndInstant, -1); + List<DateTime> endList = getRecurringList(range.getEnd_time(), cwStartInstant, rdata, cwEndInstant, startList.size()); // Pair them up to make change windows // Everything should be UTC time for (int i = 0; i < startList.size(); i++) { @@ -170,7 +170,7 @@ public class RecurringWindows { private static List<DateTime> getRecurringList(String rangeTime, Instant cwStartInstant, StringBuilder rdata, - Instant cwEndInstant) throws ParseException { + Instant cwEndInstant, int endingSize) throws ParseException { Instant startInstant = getInstanceFromTime(rangeTime, cwStartInstant); DateTime start = new DateTime(startInstant.toEpochMilli()); @@ -180,8 +180,15 @@ public class RecurringWindows { while (recur.hasNext()) { DateTime next = recur.next(); // System.out.println(next.toString()); + if (endingSize == -1) { if (next.isAfter(cwEndInstant.toEpochMilli())) { break; + } + } + else { + if (list.size() == endingSize) { + break; + } } list.add(next); } diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementAvailability.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementAvailability.java index 4766db2..ffaeeb9 100644 --- a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementAvailability.java +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementAvailability.java @@ -36,7 +36,6 @@ import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyElementInfo; import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyResponse; import org.onap.optf.cmso.optimizer.service.rs.models.ChangeWindow; import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerRequest; -import org.springframework.expression.spel.ast.OpInc; public class ElementAvailability extends ElementWindowMapping{ @@ -50,8 +49,7 @@ public class ElementAvailability extends ElementWindowMapping{ private Map<String, List<TicketData>> nodeUnAvailability = new TreeMap<>(); public ElementAvailability(List<TimeLimitAndVerticalTopology> policies, OptimizerRequest optimizerRequest, - TopologyResponse topologyResponse, ActiveTicketsResponse ticketResponse) throws ParseException - { + TopologyResponse topologyResponse, ActiveTicketsResponse ticketResponse) throws ParseException { super(optimizerRequest, topologyResponse); this.policies = policies; this.ticketResponse = ticketResponse; @@ -61,9 +59,9 @@ public class ElementAvailability extends ElementWindowMapping{ this.parameters = parameters; for (ChangeWindow changeWindow : optimizerRequest.getChangeWindows()) { if (policies.size() > 0) { - globalRelativeAvailability.add(RecurringWindows.getAvailabilityWindowsForPolicies(policies, changeWindow)); - } - else { + globalRelativeAvailability + .add(RecurringWindows.getAvailabilityWindowsForPolicies(policies, changeWindow)); + } else { List<ChangeWindow> wholeWindow = new ArrayList<>(); wholeWindow.add(changeWindow); globalRelativeAvailability.add(wholeWindow); @@ -109,10 +107,18 @@ public class ElementAvailability extends ElementWindowMapping{ for (String elementId : nodeInfo.keySet()) { TopologyElementInfo info = nodeInfo.get(elementId); - Long timeZoneOffset = getTimeZoneOffset(info); + // Library for lat/lon to timzezone is MIT license. + // We must provided + String timeZone = "GMT"; + if (info.getElementLocation() != null && info.getElementLocation().getTimezone() != null + && !info.getElementLocation().getTimezone().equals("")) { + timeZone = info.getElementLocation().getTimezone(); + } DateTimeIterator recur = getRecurringIterator(); List<Boolean> element = new ArrayList<>(); - while (recur.hasNext()) { + // calculate number time slots + long numberOfTimeSlots = calculateNumberOfSlotsInWindow(window, duration); + while (recur.hasNext() && element.size() < numberOfTimeSlots) { DateTime next = recur.next(); if (next.isAfter(window.getEndTime().getTime())) { break; @@ -120,7 +126,7 @@ public class ElementAvailability extends ElementWindowMapping{ ChangeWindow slot = new ChangeWindow(); slot.setStartTime(next.toDate()); slot.setEndTime(next.plus(duration).toDate()); - if (slotIsAvailable(slot, timeZoneOffset, nodeUnAvailability.get(elementId))) { + if (slotIsAvailable(slot, timeZone, nodeUnAvailability.get(elementId))) { element.add(true); } else { element.add(false); @@ -131,8 +137,15 @@ public class ElementAvailability extends ElementWindowMapping{ } - private boolean slotIsAvailable(ChangeWindow slot, Long timeZoneOffset, List<TicketData> tickets) { - if (isGloballyAvailable(slot, timeZoneOffset) && isNotRestricted(slot, tickets)) { + private long calculateNumberOfSlotsInWindow(ChangeWindow window, Long duration) + { + long windowSize = window.getEndTime().getTime() - window.getStartTime().getTime(); + long numberOfSlots = windowSize /duration; + return numberOfSlots; + } + + private boolean slotIsAvailable(ChangeWindow slot, String timeZone, List<TicketData> tickets) { + if (isGloballyAvailable(slot, timeZone) && isNotRestricted(slot, tickets)) { return true; } return false; @@ -152,18 +165,27 @@ public class ElementAvailability extends ElementWindowMapping{ return true; } - private boolean isGloballyAvailable(ChangeWindow slot, Long timeZoneOffset) { + // + // Globally availability are generally maintenance window definitions + // which are UTC times that are treated as relative to the local time zone. + // The slot is an absolute UTCT time as well. + // When we test to see if the slot is 'globally' available we must adjust it + // to the local time of the element to see it it matches the relative + // Consider + // slot UTC time is 06:00-07:00 + // global availability (maintenance window) 00:00-06:00 + // the slot for an element in US/Eastern + // time would be converted to 01:00-02:00 + // and would fit in the local maintenance window + // + private boolean isGloballyAvailable(ChangeWindow slot, String timeZone) { + boolean available = false; for (ChangeWindow global : globalRelativeAvailability.get(0)) { - if (global.containsInTimeZone(slot, timeZoneOffset)) { - return true; + if (global.containsInTimeZone(slot, timeZone)) { + available = true; } } - return false; - } - - private Long getTimeZoneOffset(TopologyElementInfo info) { - // TODO Auto-generated method stub - return 0L; + return available; } private void calculateNodeAvailability(TopologyElementInfo info) { diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementWindowMapping.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementWindowMapping.java index 42c69a2..246a725 100644 --- a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementWindowMapping.java +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementWindowMapping.java @@ -32,6 +32,7 @@ import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerSchedule; import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyElementInfo; import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyResponse; import org.onap.optf.cmso.optimizer.service.rs.models.ChangeWindow; +import org.onap.optf.cmso.optimizer.service.rs.models.ElementInfo; import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerRequest; import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerScheduleInfo; import org.onap.optf.cmso.optimizer.service.rs.models.ScheduledElement; @@ -75,22 +76,22 @@ public class ElementWindowMapping { return recur; } - public void initializeForProcessResult() - { + public void initializeForProcessResult() { // we need nodeInfo to be an array to speed up the result processing. // but we need it sorted by elementId as when we created it.... nodeArray = nodeInfo.values().stream().collect(Collectors.toList()); nodeInfo.clear(); } + public OptimizerScheduleInfo processResult(OptimizerSchedule result) throws ParseException { // When considering the memory vs performance // 5 minute duration for a month long change window is 8928 slots // The assumption is that there were be fewer allocated slots // than potential slots. List<ElementSlot> elements = result.getElementSlotLoader(); - Map<Integer, List<ElementSlot>> mapSlotToElement = elements.stream(). - collect(Collectors.groupingBy(ElementSlot::getSlot)); + Map<Integer, List<ElementSlot>> mapSlotToElement = + elements.stream().collect(Collectors.groupingBy(ElementSlot::getSlot)); DateTimeIterator iter = getRecurringIterator(); // TODO - supporting only 1 change window at the moment..... Long endWindow = optimizerRequest.getChangeWindows().get(0).getEndTime().getTime(); @@ -109,18 +110,15 @@ public class ElementWindowMapping { // All assigned ElementSlots now have corresponding UTC time // OptimizerScheduleInfo info = new OptimizerScheduleInfo(); - for (ElementSlot slot : elements) - { + for (ElementSlot slot : elements) { updateInfo(slot, info); } return info; } - private void updateInfo(ElementSlot slot, OptimizerScheduleInfo info) - { + private void updateInfo(ElementSlot slot, OptimizerScheduleInfo info) { TopologyElementInfo element = nodeArray.get(slot.getElementIndex()-1); - if (slot.getSlot() > 0) - { + if (slot.getSlot() > 0) { ScheduledElement scheduled = new ScheduledElement(); Integer durationInSeconds = optimizerRequest.getNormalDuration(); if (optimizerRequest.getAdditionalDuration() != null) { @@ -131,10 +129,9 @@ public class ElementWindowMapping { scheduled.setStartTime(new Date(slot.getTime())); scheduled.setEndTime(new Date(slot.getTime() + (durationInSeconds*1000))); scheduled.setScheduleType(ScheduleType.INDIVIDUAL); + scheduled.setGroupId(getGroupId(scheduled.getElementId())); info.getScheduledElements().add(scheduled); - } - else - { + } else { UnScheduledElement unscheduled = new UnScheduledElement(); unscheduled.setElementId(element.getElementId()); unscheduled.setGroupId("unknown"); @@ -144,5 +141,18 @@ public class ElementWindowMapping { } } + private String getGroupId(String elementId) { + ElementInfo info = getElementInfo(elementId); + if (info != null) { + return info.getGroupId(); + } + return ""; + } + + private ElementInfo getElementInfo(String elementId) { + List<ElementInfo> elements = optimizerRequest.getElements(); + ElementInfo info = elements.stream().filter(x -> x.getElementId().equals(elementId)).findAny().orElse(null); + return info; + } } diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerClient.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerClient.java index 514097e..3147bc5 100644 --- a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerClient.java +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerClient.java @@ -57,7 +57,6 @@ import org.onap.optf.cmso.optimizer.model.Topology; import org.onap.optf.cmso.optimizer.model.dao.OptimizerDao; import org.onap.optf.cmso.optimizer.model.dao.RequestDao; import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerRequest; -import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerResponse.OptimizeScheduleStatus; import org.onap.optf.cmso.optimizer.service.rs.models.PolicyInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerRequestManager.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerRequestManager.java index 34de3df..c911804 100644 --- a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerRequestManager.java +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerRequestManager.java @@ -20,7 +20,6 @@ package org.onap.optf.cmso.optimizer.clients.optimizer; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.List; import java.util.Optional; import java.util.UUID; import org.onap.observations.Observation; @@ -121,7 +120,7 @@ public class OptimizerRequestManager { OptimizerResults results = apiResponse.getOptimizerResults(); OptimizerResponse response = new OptimizerResponse(); response.setRequestId(requestRow.getUuid().toString()); - + response.setStatus(OptimizeScheduleStatus.COMPLETED); String optString = requestRow.getRequest(); OptimizerRequest optimizerResquest = new ObjectMapper().readValue(optString, OptimizerRequest.class); @@ -175,11 +174,6 @@ public class OptimizerRequestManager { } - public List<OptimizerScheduleInfo> getScheduleInfo(Response responseRow) { - // TODO Auto-generated method stub - return null; - } - } diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/core/OptimizerManager.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/core/OptimizerManager.java index eabebec..d2e3153 100644 --- a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/core/OptimizerManager.java +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/core/OptimizerManager.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Optional; import java.util.UUID; import javax.ws.rs.core.Response.Status; +import org.onap.observations.Observation; import org.onap.optf.cmso.common.exceptions.CmsoException; import org.onap.optf.cmso.optimizer.clients.optimizer.OptimizerRequestManager; import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerEngineResponse; @@ -260,13 +261,20 @@ public class OptimizerManager { public OptimizerResponse getCompletedOptimizerResponse(UUID uuid) { - OptimizerResponse response = new OptimizerResponse(); - response.setRequestId(uuid.toString()); - response.setStatus(OptimizeScheduleStatus.COMPLETED); + OptimizerResponse response = null; Response responseRow = getResponseRow(uuid); - if (responseRow != null) + try { - response.setSchedules(optimizerRequestManager.getScheduleInfo(responseRow)); + String responseStr = responseRow.getRepsonse(); + response = new ObjectMapper().readValue(responseStr, OptimizerResponse.class); + } + catch (Exception e) + { + Observation.report(LogMessages.UNEXPECTED_EXCEPTION, e, e.getMessage()); + response = new OptimizerResponse(); + response.setRequestId(uuid.toString()); + response.setStatus(OptimizeScheduleStatus.FAILED); + response.setErrorMessage(LogMessages.UNEXPECTED_EXCEPTION.format(e.getMessage())); } return response; } diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/service/rs/models/ChangeWindow.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/service/rs/models/ChangeWindow.java index 4eb6824..e9218b2 100644 --- a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/service/rs/models/ChangeWindow.java +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/service/rs/models/ChangeWindow.java @@ -34,7 +34,9 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Date; +import java.util.TimeZone; import org.springframework.format.annotation.DateTimeFormat; /** @@ -133,16 +135,25 @@ public class ChangeWindow implements Serializable { * @param timeZoneOffset the time zone offset * @return true, if successful */ - public boolean containsInTimeZone(ChangeWindow test, Long timeZoneOffset) { - Instant startInstant = startTime.toInstant().plusMillis(timeZoneOffset); - Instant endInstant = endTime.toInstant().plusMillis(timeZoneOffset); - if (!test.getStartTime().toInstant().isBefore(startInstant) - && !test.getEndTime().toInstant().isAfter(endInstant)) { + public boolean containsInTimeZone(ChangeWindow test, Integer startTimeZoneOffset, Integer endTimeZoneOffset) { + Instant startInstant = startTime.toInstant(); + Instant endInstant = endTime.toInstant(); + Instant testStart = test.getStartTime().toInstant().plusMillis(startTimeZoneOffset);; + Instant testEnd = test.getEndTime().toInstant().plusMillis(startTimeZoneOffset);; + if (!testStart.isBefore(startInstant) + && !testEnd.isAfter(endInstant)) { return true; } return false; } + public boolean containsInTimeZone(ChangeWindow test, String timeZone) { + TimeZone tz = TimeZone.getTimeZone(timeZone); + Integer startTimeZoneOffset = tz.getOffset(startTime.toInstant().truncatedTo(ChronoUnit.DAYS).toEpochMilli()); + Integer endTimeZoneOffset = tz.getOffset(endTime.toInstant().truncatedTo(ChronoUnit.DAYS).toEpochMilli()); + return containsInTimeZone(test, startTimeZoneOffset, endTimeZoneOffset); + } + /** * Absorb if overlapping window. * diff --git a/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/availability/policies/PolicyManagerTest.java b/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/availability/policies/PolicyManagerTest.java index c71c829..817c8de 100644 --- a/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/availability/policies/PolicyManagerTest.java +++ b/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/availability/policies/PolicyManagerTest.java @@ -52,7 +52,7 @@ public class PolicyManagerTest { public void getPolicyByName() { String policyName = "Weekday_00_06"; - String result = "CMSO.Weekday_00_06,CMSO.Weekday_00_06,CMSO.Weekday_00_06,"; + String result = "CMSO.Weekday_00_06,CMSO.Weekday_00_06,CMSO.Weekday_00_06,CMSO.Weekday_00_06,"; List<Policy> policies = policyManager.getSupportedPolicies(); StringBuilder sb = new StringBuilder(); for (Policy pol : policies) { diff --git a/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindowsTest.java b/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindowsTest.java index ce1f1a4..d772650 100644 --- a/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindowsTest.java +++ b/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindowsTest.java @@ -23,6 +23,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.TimeZone; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -55,10 +56,10 @@ public class RecurringWindowsTest { @Test public void getAvailabilityWindowsForPolicies() { - getAvailabilityWindowsForPolicy("Weekday_00_06", "2019-03-08T00:00:00.00Z", "2019-03-12T00:00:00.00Z", 2); - getAvailabilityWindowsForPolicy("EveryDay_00_06", "2019-03-08T00:00:00.00Z", "2019-03-12T00:00:00.00Z", 4); + getAvailabilityWindowsForPolicy("AllDayEveryDay", "2019-03-08T00:00:00.00Z", "2019-03-12T00:00:00.00Z", 5); + getAvailabilityWindowsForPolicy("Weekday_00_06", "2019-03-08T00:00:00.00Z", "2019-03-12T00:00:00.00Z", 3); + getAvailabilityWindowsForPolicy("EveryDay_00_06", "2019-03-08T00:00:00.00Z", "2019-03-12T00:00:00.00Z", 5); getAvailabilityWindowsForPolicy("Weekend_00_06", "2019-03-08T00:00:00.00Z", "2019-03-12T00:00:00.00Z", 3); - } private void getAvailabilityWindowsForPolicy(String policyName, String startStr, String endStr, int size) { diff --git a/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/service/rs/models/ChangeWindowTest.java b/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/service/rs/models/ChangeWindowTest.java index b1309fb..67242ef 100644 --- a/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/service/rs/models/ChangeWindowTest.java +++ b/cmso-optimizer/src/test/java/org/onap/optf/cmso/optimizer/service/rs/models/ChangeWindowTest.java @@ -39,7 +39,12 @@ public class ChangeWindowTest { testContains(window, "2019-03-07T23:59:59Z", "2019-03-12T00:00:00.00Z", false); testContains(window, "2019-03-09T23:59:59Z", "2019-03-11T00:00:00.00Z", true); testContains(window, "2019-03-06T23:59:59Z", "2019-03-06T23:59:59Z", false); - testContains(window, "2019-03-12T23:59:59Z", "2019-03-13T00:00:00.00Z", false); + + String tz = "US/Eastern"; + window.setStartTime(Date.from(Instant.parse("2019-03-08T00:00:00.00Z"))); + window.setEndTime(Date.from(Instant.parse("2019-03-08T06:00:00.00Z"))); + testContainsTz(window, "2019-03-08T06:00:00Z", "2019-03-08T07:00:00.00Z", tz, true); + testContainsTz(window, "2019-03-08T00:00:00Z", "2019-03-08T01:00:00.00Z", tz, false); } @@ -50,4 +55,11 @@ public class ChangeWindowTest { Assert.assertTrue(window.contains(test) == contains); } + private void testContainsTz(ChangeWindow window, String from, String to, String tz, boolean contains) { + ChangeWindow test = new ChangeWindow(); + test.setStartTime(Date.from(Instant.parse(from))); + test.setEndTime(Date.from(Instant.parse(to))); + Assert.assertTrue(window.containsInTimeZone(test, tz) == contains); + } + } |