
net.snowflake.common.core.SFInstant Maven / Gradle / Ivy
package net.snowflake.common.core;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
/**
* Simple interface for uniform accessing of moments in time.
*
* @author mzukowski
*/
public abstract class SFInstant
{
// Additional component type not covered by Calendar
public final static int DAY_OF_WEEK_ISO = 10001;
public final static int WEEK_ISO = 10002;
public final static int YEAR_OF_WEEK = 10003;
public final static int YEAR_OF_WEEK_ISO = 10004;
// Used in subclasses
protected final static TimeZone GMT = TimeZone.getTimeZone("GMT");
protected final static int[] POWERS_OF_TEN = {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
};
public abstract int extract(int field, Integer optWeekStart, Integer optWoyPolicy);
/**
* Extract a particular component of a date.
* @param field field id as specified in the Calendar class.
* @return The value of the extracted field.
*/
public int extract(int field)
{
return extract(field, null, null);
}
/**
* Generic function children use to implement public extract.
* @param field Field id as specified in the Calendar class. We also
* support additional components defined in SFInstant
* @param tz TimeZone to use
* @param time Moment in time to extract from
* @param optWeekStart Optional WEEK_START value
* @param optWoyPolicy Optional WEEK_OF_YEAR_POLICY value
* @return The value of the extracted field.
*/
protected int extract(int field, TimeZone tz, long time,
Integer optWeekStart, Integer optWoyPolicy)
{
Calendar calendar = CalendarCache.get(tz);
// Mapping of WEEK_START 1..7 values (-1) to Java constants
int firstDays[] = {
Calendar.MONDAY,
Calendar.TUESDAY,
Calendar.WEDNESDAY,
Calendar.THURSDAY,
Calendar.FRIDAY,
Calendar.SATURDAY,
Calendar.SUNDAY
};
// Set the time
calendar.setTimeInMillis(time);
// Remember if we're processing an ISO component
boolean isIso = false;
// Remember if we're in the Jan1st semantics
boolean isJan1st = false;
int weekStart = optWeekStart != null ? optWeekStart : 0; // 0,1-Monday..7-Sunday
weekStart = (weekStart == 0) ? 1 : weekStart; // 1-Monday..7-Sunday
weekStart = weekStart % 7; // 0-Sunday..6-Saturday
if (field == DAY_OF_WEEK_ISO || field == WEEK_ISO || field == YEAR_OF_WEEK_ISO)
{
// An ISO component. Ignore optional params
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setMinimalDaysInFirstWeek(4);
// And continue using standard components
if (field == DAY_OF_WEEK_ISO)
field = Calendar.DAY_OF_WEEK;
else if (field == WEEK_ISO)
field = Calendar.WEEK_OF_YEAR;
else if (field == YEAR_OF_WEEK_ISO)
field = YEAR_OF_WEEK;
// Remember we're in ISO mode
isIso = true;
}
else
{
// Process optional parameters
int firstDay = Calendar.MONDAY;
if (optWeekStart != null && optWeekStart != 0)
{
assert(optWeekStart >= 1 && optWeekStart <= 7);
firstDay = firstDays[optWeekStart - 1];
}
int minimalDaysInFirstWeek = 4;
if (optWoyPolicy != null && optWoyPolicy != 0)
{
assert(optWoyPolicy == 1);
minimalDaysInFirstWeek = 1;
// Remember we're in Jan1st mode
isJan1st = true;
}
calendar.setFirstDayOfWeek(firstDay);
calendar.setMinimalDaysInFirstWeek(minimalDaysInFirstWeek);
}
// Extract the field. Special code for some fields
int val;
if (isJan1st && field == YEAR_OF_WEEK)
{
// Simple, same as year
val = getYearAsInt(calendar);
}
else if (isJan1st && field == Calendar.WEEK_OF_YEAR)
{
// Manually compute it, it's easier than adapting to Java's weird semantics
long outputYear = getYearAsInt(calendar);
int yday = calendar.get(Calendar.DAY_OF_YEAR); // 1..365
yday--; // now it's 0..365, like in C
int wday = calendar.get(Calendar.DAY_OF_WEEK); // Sunday..Saturday
wday -= Calendar.SUNDAY; // now it's 0(Sunday)..6(Saturday), like in C
// Now, check day of week on Jan 1st, same format as tm_wday, 0..6
// We use 700, as it's divisible by 7 and larger by 365,
// to guarantee non-negative result.
int dowJan1= (wday - yday + 700) % 7;
int week = (yday + (7 - weekStart + dowJan1) % 7 ) / 7 + 1;
val = week;
}
else if (field == YEAR_OF_WEEK)
{
int year = getYearAsInt(calendar);
int week = calendar.get(Calendar.WEEK_OF_YEAR);
int month = calendar.get(Calendar.MONTH);
if (month == 0 && week > 10)
{
// January, but large week - it belongs to the previous year
year--;
}
else if (month == 11 && week < 10)
{
// December, but small week - it belongs to the next year
year++;
}
val = year;
}
else if (field == Calendar.YEAR)
{
val = getYearAsInt(calendar);
}
else
{
// For most components, just use Calendar's output
val = calendar.get(field);
// Post-process some fields
if (field == Calendar.MONTH)
{ // Calendar.MONTH is 0-based, fix it
val += 1;
}
else if (field == Calendar.DAY_OF_WEEK)
{
if (isIso)
{
// Convert Java SUNDAY..SATURDAY to 1-Monday..7-Sunday
val = (val + 6 - Calendar.SUNDAY) % 7 + 1;
}
else if (optWeekStart == null || optWeekStart == 0)
{
// Old behavior, we want 0-Sunday .. 6-Saturday
// Calendar.DAY_OF_WEEK has 1-Sunday, 7-Saturday
val -= 1;
}
else
{
// Fixed beginning of the week
// For weekStart = 2, we want Tue=1,Wed=2,...,Mon=7
assert(optWeekStart >= 1 && optWeekStart <= 7);
val = (val + 7 + 6 - optWeekStart) % 7 + 1;
}
}
}
return val;
}
/** Helper function to extract YEAR as a signed integer, taking care of BC */
private static int getYearAsInt(Calendar cal)
{
int era = cal.get(Calendar.ERA);
int year = cal.get(Calendar.YEAR);
if (era == GregorianCalendar.BC)
year = -year;
return year;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy