Better/more timezone support

This commit is contained in:
2023-03-14 16:32:29 -05:00
parent b395ee6778
commit 54d3e4a6c8
4 changed files with 73 additions and 9 deletions

View File

@ -53,6 +53,9 @@ public class QSession implements Serializable
// implementation-specific custom values // implementation-specific custom values
private Map<String, String> values; private Map<String, String> values;
public static final String VALUE_KEY_USER_TIMEZONE = "UserTimezone";
public static final String VALUE_KEY_USER_TIMEZONE_OFFSET_MINUTES = "UserTimezoneOffsetMinutes";
/******************************************************************************* /*******************************************************************************
@ -453,6 +456,7 @@ public class QSession implements Serializable
} }
/******************************************************************************* /*******************************************************************************
** Fluent setter for user ** Fluent setter for user
*******************************************************************************/ *******************************************************************************/
@ -462,5 +466,4 @@ public class QSession implements Serializable
return (this); return (this);
} }
} }

View File

@ -39,8 +39,10 @@ import java.time.temporal.ChronoUnit;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QValueException; import com.kingsrook.qqq.backend.core.exceptions.QValueException;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.session.QSession;
/******************************************************************************* /*******************************************************************************
@ -674,7 +676,7 @@ public class ValueUtils
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
LocalDateTime givenZonesNow = LocalDateTime.ofInstant(Instant.now(), zone); LocalDateTime givenZonesNow = LocalDateTime.ofInstant(Instant.now(), zone);
LocalDateTime startOfDay = givenZonesNow.truncatedTo(ChronoUnit.DAYS); LocalDateTime startOfDay = givenZonesNow.truncatedTo(ChronoUnit.DAYS);
return (startOfDay.toInstant(zone.getRules().getOffset(computerTime))); return (startOfDay.toInstant(zone.getRules().getOffset(startOfDay)));
} }
@ -700,7 +702,7 @@ public class ValueUtils
.with(ChronoField.MINUTE_OF_DAY, 0) .with(ChronoField.MINUTE_OF_DAY, 0)
.with(ChronoField.SECOND_OF_DAY, 0) .with(ChronoField.SECOND_OF_DAY, 0)
.with(ChronoField.NANO_OF_DAY, 0); .with(ChronoField.NANO_OF_DAY, 0);
return (startOfMonth.toInstant(zone.getRules().getOffset(computerTime))); return (startOfMonth.toInstant(zone.getRules().getOffset(startOfMonth)));
} }
@ -720,13 +722,13 @@ public class ValueUtils
// get date time for now in given zone, truncate it and add offset from utc // // get date time for now in given zone, truncate it and add offset from utc //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
LocalDateTime givenZonesNow = LocalDateTime.ofInstant(Instant.now(), zone); LocalDateTime givenZonesNow = LocalDateTime.ofInstant(Instant.now(), zone);
LocalDateTime startOfMonth = givenZonesNow LocalDateTime startOfYear = givenZonesNow
.withDayOfYear(1) .withDayOfYear(1)
.with(ChronoField.HOUR_OF_DAY, 0) .with(ChronoField.HOUR_OF_DAY, 0)
.with(ChronoField.MINUTE_OF_DAY, 0) .with(ChronoField.MINUTE_OF_DAY, 0)
.with(ChronoField.SECOND_OF_DAY, 0) .with(ChronoField.SECOND_OF_DAY, 0)
.with(ChronoField.NANO_OF_DAY, 0); .with(ChronoField.NANO_OF_DAY, 0);
return (startOfMonth.toInstant(zone.getRules().getOffset(computerTime))); return (startOfYear.toInstant(zone.getRules().getOffset(startOfYear)));
} }
@ -753,4 +755,28 @@ public class ValueUtils
return (null); return (null);
} }
/*******************************************************************************
** Get the (time) zoneId either for the current user session (based on session
** value UserTimezone or UserTimezoneOffsetMinutes), else the instance's
** defaultTimeZoneId string.
*******************************************************************************/
public static ZoneId getSessionOrInstanceZoneId()
{
String timezone = QContext.getQSession().getValue(QSession.VALUE_KEY_USER_TIMEZONE);
if(StringUtils.hasContent(timezone))
{
return (ZoneId.of(timezone));
}
String userTimezoneOffsetMinutesString = QContext.getQSession().getValue(QSession.VALUE_KEY_USER_TIMEZONE_OFFSET_MINUTES);
if(StringUtils.hasContent(userTimezoneOffsetMinutesString))
{
return (ZoneId.ofOffset("UTC", ZoneOffset.ofTotalSeconds(60 * Integer.parseInt(userTimezoneOffsetMinutesString))));
}
return (ZoneId.of(QContext.getQInstance().getDefaultTimeZoneId()));
}
} }

View File

@ -31,10 +31,13 @@ import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.Month; import java.time.Month;
import java.time.ZoneId;
import java.util.Calendar; import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QValueException; import com.kingsrook.qqq.backend.core.exceptions.QValueException;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@ -268,7 +271,7 @@ class ValueUtilsTest extends BaseTest
assertEquals("1", ValueUtils.getValueAsType(String.class, 1)); assertEquals("1", ValueUtils.getValueAsType(String.class, 1));
assertEquals(BigDecimal.ONE, ValueUtils.getValueAsType(BigDecimal.class, 1)); assertEquals(BigDecimal.ONE, ValueUtils.getValueAsType(BigDecimal.class, 1));
assertEquals(true, ValueUtils.getValueAsType(Boolean.class, "true")); assertEquals(true, ValueUtils.getValueAsType(Boolean.class, "true"));
assertArrayEquals("a" .getBytes(StandardCharsets.UTF_8), ValueUtils.getValueAsType(byte[].class, "a")); assertArrayEquals("a".getBytes(StandardCharsets.UTF_8), ValueUtils.getValueAsType(byte[].class, "a"));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsType(Serializable.class, 1)); assertThrows(QValueException.class, () -> ValueUtils.getValueAsType(Serializable.class, 1));
} }
@ -292,4 +295,21 @@ class ValueUtilsTest extends BaseTest
} }
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetSessionOrInstanceZoneId()
{
assertEquals(ZoneId.of("UTC"), ValueUtils.getSessionOrInstanceZoneId());
QContext.getQInstance().setDefaultTimeZoneId("America/Chicago");
assertEquals(ZoneId.of("America/Chicago"), ValueUtils.getSessionOrInstanceZoneId());
QContext.getQSession().setValue(QSession.VALUE_KEY_USER_TIMEZONE_OFFSET_MINUTES, "-300");
assertEquals(ZoneId.of("UTC-05:00"), ValueUtils.getSessionOrInstanceZoneId());
}
} }

View File

@ -447,7 +447,8 @@ public class QJavalinImplementation
context.cookie(SESSION_ID_COOKIE_NAME, session.getIdReference(), SESSION_COOKIE_AGE); context.cookie(SESSION_ID_COOKIE_NAME, session.getIdReference(), SESSION_COOKIE_AGE);
} }
setUserTimezoneOffsetMinutesHeaderInSession(context, session); setUserTimezoneOffsetMinutesInSession(context, session);
setUserTimezoneInSession(context, session);
} }
catch(QAuthenticationException qae) catch(QAuthenticationException qae)
{ {
@ -493,7 +494,7 @@ public class QJavalinImplementation
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private static void setUserTimezoneOffsetMinutesHeaderInSession(Context context, QSession session) private static void setUserTimezoneOffsetMinutesInSession(Context context, QSession session)
{ {
String userTimezoneOffsetMinutes = context.header("X-QQQ-UserTimezoneOffsetMinutes"); String userTimezoneOffsetMinutes = context.header("X-QQQ-UserTimezoneOffsetMinutes");
if(StringUtils.hasContent(userTimezoneOffsetMinutes)) if(StringUtils.hasContent(userTimezoneOffsetMinutes))
@ -503,7 +504,7 @@ public class QJavalinImplementation
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// even though we're putting it in the session as a string, go through parse int, to make sure it's a valid int. // // even though we're putting it in the session as a string, go through parse int, to make sure it's a valid int. //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
session.setValue("UserTimezoneOffsetMinutes", String.valueOf(Integer.parseInt(userTimezoneOffsetMinutes))); session.setValue(QSession.VALUE_KEY_USER_TIMEZONE_OFFSET_MINUTES, String.valueOf(Integer.parseInt(userTimezoneOffsetMinutes)));
} }
catch(Exception e) catch(Exception e)
{ {
@ -514,6 +515,20 @@ public class QJavalinImplementation
/*******************************************************************************
**
*******************************************************************************/
private static void setUserTimezoneInSession(Context context, QSession session)
{
String userTimezone = context.header("X-QQQ-UserTimezone");
if(StringUtils.hasContent(userTimezone))
{
session.setValue(QSession.VALUE_KEY_USER_TIMEZONE, userTimezone);
}
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/