用于表示具有设定时间粒度的日历计划的数据结构?
Data structure for representing a calendar schedule with a set time granularity?
我一直在尝试想出一种纯粹的 Java 方式来表示整个日历年的时间表。每个计划是一个 30 分钟的时间段(半小时粒度)。
这将通过像 findByDateTime()
这样的存储库方法进行抽象
基本上我需要以每天 30 分钟的粒度对时隙进行建模。
我破解的方法是这样的
public Map<Integer, Map<Integer, Programme>> enumerateMapOfMapSchedules() {
int numberOfSlots = 48; //48 half hours in a day
Map<Integer, Map<Integer, Programme>> dayToTimeScheduleMap = new HashMap<>();
//create a key value map for each day of the year
for (int i = 1; i < 366; i++) {
Map<Integer, Programme> dayProgrammeScheduleMap = new HashMap<>();
for (int y = 1; y < numberOfSlots; y++) {
dayProgrammeScheduleMap.put(y, null);
}
dayToTimeScheduleMap.put(i, dayProgrammeScheduleMap);
}
//creates a map with 365 days and each day having a map of 48 schedule slots
return dayToTimeScheduleMap;
}
我很欣赏这个解决方案没有处理或没有年份的概念,但是因为这些是针对 mocks/tests 的,所以我同意这个。
如果节目跨越两个半小时时段,它也不处理重叠的时间表。
我的查询方法非常简单,可以查找特定计划时段中的内容。
public Programme findByDateTime(LocalDateTime dateTime) {
int scheduleSlot = dateTime.getHour() * 2;
//if its the after 30 minute schedule slot
if (dateTime.getMinute() > 30) {
scheduleSlot++;
}
return scheduleMap.get(dateTime.getDayOfYear()).get(scheduleSlot);
}
然而,为了遍历所有数据结构以查看特定程序的出现次数。
我的问题是,有更简单的方法吗?
我试着用关系数据库来做,但是如果没有很多 SQL.
就很难轻松地表示时间段
欢迎提出任何建议或实施建议!
天长短不一
日历时间表仅在时区和年份的情况下才有意义。政客们经常更改其辖区时区使用的偏移量。这意味着日子并不总是 24 小时。 Daylight Saving Time (DST) 等异常意味着一天可能有 23 小时、25 小时或其他时间,例如 23.5 小时。
从头开始,以 30 分钟为增量计数
所以如果你想把全年分成30分钟的片段,你必须从特定时区的特定年份的第一天的第一时刻开始,并在某个时间段添加30分钟距离新年还有时间。
ZoneId z = ZoneId.of( "America/Montreal" );
Year year = Year.of( 2021 );
LocalDate firstOfYear = year.atDay( 1 );
ZonedDateTime start = firstOfYear.atStartOfDay( z );
List < ZonedDateTime > zdts = new ArrayList <>();
Duration duration = Duration.ofMinutes( 30 );
ZonedDateTime zdt = start;
while ( zdt.getYear() == year.getValue() )
{
zdts.add( zdt );
// Setup the next loop.
zdt = zdt.plus( duration );
}
Return 该列表的不可修改副本。
List < ZonedDateTime > slots = List.copyOf( zdts );
当运行。请注意 what happens 2021 年 3 月 14 日和 2021 年 11 月 7 日凌晨 1 点或 2 点。
slots = [2021-01-01T00:00-05:00[America/Montreal], 2021-01-01T00:30-05:00[America/Montreal], 2021-01-01T01:00-05:00[America/Montreal], 2021-01-01T01:30-05:00[America/Montreal], 2021-01-01T02:00-05:00[America/Montreal],
…
2021-03-14T01:00-05:00[America/Montreal], 2021-03-14T01:30-05:00[America/Montreal], 2021-03-14T03:00-04:00[America/Montreal],
…
2021-11-07T00:30-04:00[America/Montreal], 2021-11-07T01:00-04:00[America/Montreal], 2021-11-07T01:30-04:00[America/Montreal], 2021-11-07T01:00-05:00[America/Montreal], 2021-11-07T01:30-05:00[America/Montreal], 2021-11-07T02:00-05:00[America/Montreal],
…
2021-12-31T22:00-05:00[America/Montreal], 2021-12-31T22:30-05:00[America/Montreal], 2021-12-31T23:00-05:00[America/Montreal], 2021-12-31T23:30-05:00[America/Montreal]]
未来的预测不可靠!
但要注意:政客们经常更改区域中使用的偏移量!这种情况发生的频率比您可能意识到的要高得多。政客们在这方面甚至变得更糟,将他们的预警从几年减少到几个月,甚至像最近在土耳其和摩洛哥看到的几周,甚至像在朝鲜看到的那样根本没有预警。
所以你无法使用上述方法可靠地预测未来。
老虎机数学
我想您可以用另一种方式解决年度时段问题。以此方式计算一年中的完整槽数。
ZoneId z = ZoneId.of( "America/Montreal" );
Year year = Year.of( 2021 );
LocalDate firstOfYear = year.atDay( 1 );
ZonedDateTime start = firstOfYear.atStartOfDay( z );
ZonedDateTime end = start.plusYears( 1 );
Duration slotLength = Duration.ofMinutes( 30 );
long wholeSlotsInYear = Duration.between( start , end ).dividedBy( slotLength );
然后您可以通过乘以持续时间并将结果添加到年初来跳转到一年中的某个点。
int slotNumber = 22;
Duration jump = slotLength.multipliedBy( slotNumber - 1 ); // Subtract one to change an ordinal number into a zero-based index.
ZonedDateTime slot22 = start.plus( jump );
预约簿跟踪
如果您在美发沙龙或牙科诊所进行预约,通常的方法是跟踪一天中特定时间的年-月-日。但是单独跟踪时区。因此,在您的 Java 模型中使用 LocalDateTime
和单独的 ZoneId
。在您的数据库 table 中,使用一对列,一个类似于 SQL-标准类型 TIMESTAMP WITHOUT TIME ZONE
的类型,另一列是包含时区名称的文本类型,例如America/Montreal
或 Africa/Tunis
.
制定时间表时,应用区域来确定时刻。在 Java 中,这意味着将 ZoneId
应用于 LocalDateTime
以获得 ZonedDateTime
.
您需要清楚 LocalDateTime
对象 不 代表一个时刻的基本思想。在我们这里的示例中,明年 23 日下午 3 点可能意味着日本东京下午 3 点或美国俄亥俄州托莱多下午 3 点,两个截然不同的时刻相隔几个小时。 LocalDateTime
本质上是不明确的。因此也需要存储时区,但要分开保存。
LocalDateTime ldt = LocalDateTime.of( 2021 , 1 , 23 , 15 , 0 , 0 , 0 ) ;
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ; // Determine a moment.
通过提取 Instant
.
查看 UTC 中的同一时刻
Instant instant = zdt.toInstant() ;
我一直在尝试想出一种纯粹的 Java 方式来表示整个日历年的时间表。每个计划是一个 30 分钟的时间段(半小时粒度)。
这将通过像 findByDateTime()
基本上我需要以每天 30 分钟的粒度对时隙进行建模。
我破解的方法是这样的
public Map<Integer, Map<Integer, Programme>> enumerateMapOfMapSchedules() {
int numberOfSlots = 48; //48 half hours in a day
Map<Integer, Map<Integer, Programme>> dayToTimeScheduleMap = new HashMap<>();
//create a key value map for each day of the year
for (int i = 1; i < 366; i++) {
Map<Integer, Programme> dayProgrammeScheduleMap = new HashMap<>();
for (int y = 1; y < numberOfSlots; y++) {
dayProgrammeScheduleMap.put(y, null);
}
dayToTimeScheduleMap.put(i, dayProgrammeScheduleMap);
}
//creates a map with 365 days and each day having a map of 48 schedule slots
return dayToTimeScheduleMap;
}
我很欣赏这个解决方案没有处理或没有年份的概念,但是因为这些是针对 mocks/tests 的,所以我同意这个。
如果节目跨越两个半小时时段,它也不处理重叠的时间表。
我的查询方法非常简单,可以查找特定计划时段中的内容。
public Programme findByDateTime(LocalDateTime dateTime) {
int scheduleSlot = dateTime.getHour() * 2;
//if its the after 30 minute schedule slot
if (dateTime.getMinute() > 30) {
scheduleSlot++;
}
return scheduleMap.get(dateTime.getDayOfYear()).get(scheduleSlot);
}
然而,为了遍历所有数据结构以查看特定程序的出现次数。
我的问题是,有更简单的方法吗?
我试着用关系数据库来做,但是如果没有很多 SQL.
就很难轻松地表示时间段欢迎提出任何建议或实施建议!
天长短不一
日历时间表仅在时区和年份的情况下才有意义。政客们经常更改其辖区时区使用的偏移量。这意味着日子并不总是 24 小时。 Daylight Saving Time (DST) 等异常意味着一天可能有 23 小时、25 小时或其他时间,例如 23.5 小时。
从头开始,以 30 分钟为增量计数
所以如果你想把全年分成30分钟的片段,你必须从特定时区的特定年份的第一天的第一时刻开始,并在某个时间段添加30分钟距离新年还有时间。
ZoneId z = ZoneId.of( "America/Montreal" );
Year year = Year.of( 2021 );
LocalDate firstOfYear = year.atDay( 1 );
ZonedDateTime start = firstOfYear.atStartOfDay( z );
List < ZonedDateTime > zdts = new ArrayList <>();
Duration duration = Duration.ofMinutes( 30 );
ZonedDateTime zdt = start;
while ( zdt.getYear() == year.getValue() )
{
zdts.add( zdt );
// Setup the next loop.
zdt = zdt.plus( duration );
}
Return 该列表的不可修改副本。
List < ZonedDateTime > slots = List.copyOf( zdts );
当运行。请注意 what happens 2021 年 3 月 14 日和 2021 年 11 月 7 日凌晨 1 点或 2 点。
slots = [2021-01-01T00:00-05:00[America/Montreal], 2021-01-01T00:30-05:00[America/Montreal], 2021-01-01T01:00-05:00[America/Montreal], 2021-01-01T01:30-05:00[America/Montreal], 2021-01-01T02:00-05:00[America/Montreal],
…
2021-03-14T01:00-05:00[America/Montreal], 2021-03-14T01:30-05:00[America/Montreal], 2021-03-14T03:00-04:00[America/Montreal],
…
2021-11-07T00:30-04:00[America/Montreal], 2021-11-07T01:00-04:00[America/Montreal], 2021-11-07T01:30-04:00[America/Montreal], 2021-11-07T01:00-05:00[America/Montreal], 2021-11-07T01:30-05:00[America/Montreal], 2021-11-07T02:00-05:00[America/Montreal],
…
2021-12-31T22:00-05:00[America/Montreal], 2021-12-31T22:30-05:00[America/Montreal], 2021-12-31T23:00-05:00[America/Montreal], 2021-12-31T23:30-05:00[America/Montreal]]
未来的预测不可靠!
但要注意:政客们经常更改区域中使用的偏移量!这种情况发生的频率比您可能意识到的要高得多。政客们在这方面甚至变得更糟,将他们的预警从几年减少到几个月,甚至像最近在土耳其和摩洛哥看到的几周,甚至像在朝鲜看到的那样根本没有预警。
所以你无法使用上述方法可靠地预测未来。
老虎机数学
我想您可以用另一种方式解决年度时段问题。以此方式计算一年中的完整槽数。
ZoneId z = ZoneId.of( "America/Montreal" );
Year year = Year.of( 2021 );
LocalDate firstOfYear = year.atDay( 1 );
ZonedDateTime start = firstOfYear.atStartOfDay( z );
ZonedDateTime end = start.plusYears( 1 );
Duration slotLength = Duration.ofMinutes( 30 );
long wholeSlotsInYear = Duration.between( start , end ).dividedBy( slotLength );
然后您可以通过乘以持续时间并将结果添加到年初来跳转到一年中的某个点。
int slotNumber = 22;
Duration jump = slotLength.multipliedBy( slotNumber - 1 ); // Subtract one to change an ordinal number into a zero-based index.
ZonedDateTime slot22 = start.plus( jump );
预约簿跟踪
如果您在美发沙龙或牙科诊所进行预约,通常的方法是跟踪一天中特定时间的年-月-日。但是单独跟踪时区。因此,在您的 Java 模型中使用 LocalDateTime
和单独的 ZoneId
。在您的数据库 table 中,使用一对列,一个类似于 SQL-标准类型 TIMESTAMP WITHOUT TIME ZONE
的类型,另一列是包含时区名称的文本类型,例如America/Montreal
或 Africa/Tunis
.
制定时间表时,应用区域来确定时刻。在 Java 中,这意味着将 ZoneId
应用于 LocalDateTime
以获得 ZonedDateTime
.
您需要清楚 LocalDateTime
对象 不 代表一个时刻的基本思想。在我们这里的示例中,明年 23 日下午 3 点可能意味着日本东京下午 3 点或美国俄亥俄州托莱多下午 3 点,两个截然不同的时刻相隔几个小时。 LocalDateTime
本质上是不明确的。因此也需要存储时区,但要分开保存。
LocalDateTime ldt = LocalDateTime.of( 2021 , 1 , 23 , 15 , 0 , 0 , 0 ) ;
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ; // Determine a moment.
通过提取 Instant
.
Instant instant = zdt.toInstant() ;