gpt4 book ai didi

javascript - 基于系统时间的持续计时器/时钟?

转载 作者:行者123 更新时间:2023-12-03 04:51:27 27 4
gpt4 key购买 nike

很难找到解决方法的地方。我希望制作一个时钟,它是一个幻想时区,并且是根据系统时间计算的(因为当您不在网页上时,它需要继续运行)。

白天持续从早上7:00到晚上9:59,这是实时200分钟。 13秒(实时)是1分钟。
夜间持续时间为45分钟,从晚上10:00持续到早上6:59。 5秒(实时)是1分钟。

我想使用html显示幻想世界中的时间(示例:5:43 PM),然后还显示幻想世界中直到白天/晚上的实时分钟数(示例:32分钟之内的夜晚)。

我在想我需要使用Date.setHours(0,0,0,0)将系统时间设置为0,然后计算出差异。数学确实让我比什么都困惑。

最佳答案

在开始解决方案之前,让我们先进行一些测试-这将为最终解决方案提供定义,并使我们有信心找到正确的解决方案。

我们最终追求的是一种功能;我们将其称为realTimeToGameTime。它接受一个Date对象,并返回一个类似于{ day: 0, time: '7:00 am' }的对象。在您的问题中,您没有提到了解游戏中的哪一天(仅在什么时间)是否很重要,但这不仅是有用的补充,而且您会发现它对解决方案很重要。

现在我们知道最终结果是什么样的,我们可以考虑进行一些测试。实时和游戏时间中都有一些点表示游戏时间和实时之间的对应关系。实时地,它可以是我们想要的任何时间点;我称它为c。在游戏时间中,我们将在第0天随意将其命名为7:00。这使我们能够构建一个时间表,为我们提供测试用例的输入和输出(以分钟和小时/分钟为单位):

  c+0m   c+100m    c+200m   c+215m   c+245m   c+345m   c+445m   c+490m
c+0h c+1:40h c+3:20h c+3:35h c+4:05h c+5:45h c+7:25h c+8:10h
---+--------+--------+--------+--------+--------+--------+---------+---
7:00a 2:30p 10:00p 1:00a 7:00a 2:30p 10:00p 7:00a
day 0 day 1 day 2


我们将选择一个任意的日期/时间-2017年1月1日午夜-并编写测试:

const y = 2017
const m = 0 // 0=Jan in JS
const d = 1
expect(realTimeToGameTime(new Date(y, m, d, 0, 0))
.toEqual({ day: 0, time: '7:00 am' })
expect(realTimeToGameTime(new Date(y, m, d, 1, 40))
.toEqual({ day: 0, time: '2:30 pm' })
expect(realTimeToGameTime(new Date(y, m, d, 3, 20))
.toEqual({ day: 0, time: '10:00 pm' })
expect(realTimeToGameTime(new Date(y, m, d, 3, 35))
.toEqual({ day: 0, time: '1:00 am' })
expect(realTimeToGameTime(new Date(y, m, d, 4, 50))
.toEqual({ day: 1, time: '7:00 am' })
expect(realTimeToGameTime(new Date(y, m, d, 5, 45))
.toEqual({ day: 1, time: '2:30 pm' })
expect(realTimeToGameTime(new Date(y, m, d, 7, 25))
.toEqual({ day: 1, time: '10:00 pm' })
expect(realTimeToGameTime(new Date(y, m, d, 8, 50))
.toEqual({ day: 2, time: '7:00 am' })


(在此示例中,我使用的是Jest Expect断言;您可以针对喜欢的任何测试/断言框架进行量身定制……只要确保它具有测试对象值相等性的方法即可。)

现在开始解决。

当遇到这样的问题时-可能会很快造成混乱,特别是当您考虑所有实现细节时-最好看一个简化的图,以便于推理。您选择的数字使您难以理解所需的操作,因为有很多细节:分钟到几小时,JavaScript喜欢毫秒等,所以让我们从与您的示例接近的简化图片开始,但是更容易考虑一下(不用担心:它将推广到您的特定需求)。

假设我们只担心小时,而游戏中的白天(7:00a-9:59p)实时持续3个小时,而夜间(10:00p-6:59a)实时持续1小时。在这个简化的模型中,我们只关心几个小时,而且时间表更整洁:

  c+0h    c+1h   c+2h    c+3h   c+4h    c+5h    c+6h    c+7h   c+8h
----+-------+------+-------+------+------+-------+-------+------+-----
7:00a 12:00p 5:00p 10:00p 7:00a 12:00p 5:00p 10:00p 7:00a
day 0 day 1 day 2


现在,我们更容易解决这个问题,但最终解决方案似乎仍然令人生畏。但是,更困难的问题中蕴含着一个简单的问题:从游戏世界新的一天开始时起,经过特定的真实世界持续时间之后,真实游戏世界中的几点钟?更简单的是,让我们将其限制为游戏世界中的某一天。解决这个问题非常简单。但是,在此之前,我们需要一些输入。以下是相关输入(我在这里使用“日”表示24小时,将其与“白天”区分开):

const gameDaybreak = 7
const gameDayLength = 24
const gameDaytimeLength = 15
const gameNighttimeLength = gameDayLength - gameDaytimeLength

const gameDayLengthInRealTime = 4
const gameDaytimeLengthInRealTime = 3
const gameNighttimeLengthInRealTime =
gameDayLengthInRealTime - gameDaytimeLengthInRealTime


现在我们可以编写函数了:

gameTimeSinceDaybreakFromRealDuration(realDuration) {
if(realDuration > gameDayLengthInRealTime)
throw new Error("this function only works within a single game day")
if(realDuration <= gameDaytimeLengthInRealTime) {
return (gameDaybreak + realDuration * gameDaytimeLength /
gameDaytimeLengthInRealTime) % gameDayLength
} else {
return (gameDaybreak + gameDaytimeLength +
(realDuration - gameDaytimeLengthInRealTime) *
gameNighttimeLength / gameNighttimeLengthInRealTime) %
gameDayLength
}
}


仅剩下一个难题:如何确定今天是什么比赛日,以及该比赛日在实时时间开始。

幸运的是,我们知道每个游戏日都消耗特定数量的真实时间(在我们的简化示例中为4小时)。因此,如果我们有一个任意的对应日期,我们可以简单地将该日期的小时数除以4,得出(分数)游戏日。我们只是以此为底而得出整数游戏天数:

const c = new Date(2017, 0, 1)    // this can be anything

const realHoursSinceC = (realDate.valueOf() - c.valueOf())/1000/60/60
const gameDayFromRealDate = Math.floor(realHoursSinceC / gameDayLengthInRealTime)


(此功能在最终实现中将不完全相同,因为我们不会使用小时作为时间基准。)

最后,要找出一天中有多少(真实)小时,我们可以减去代表过去比赛日的时间:

const realHoursIntoGameDay = (realDate.valueOf() - c.valueOf()) -
gameDayFromRealDate * gameDayLengthInRealTime


(实际上,通过这两个最后的计算,我们在这里可能会更高效一些,但这更加清楚了。)

这是我们所做工作的真正魔力:我们一直将所有内容视为小时,因此更容易理解。但是,我们一直在做的一切只是时间和持续时间以相同单位为单位。 JavaScript喜欢毫秒,因此我们要做的就是使用毫秒而不是小时,我们有一个通用的解决方案!

最后一个小细节是我们需要格式化程序,以将游戏时间转换为一种不错的格式:

function formatGameTime(gameTime) {
const totalMinutes = gameTime/1000/60 // convert from milliseconds to minutes
const hours = Math.floor(totalMinutes/60)
let minutes = (totalMinutes % 60).toFixed(0)
if(minutes.length < 2) minutes = "0" + minutes // get that leading zero!
return hours < 13
? (hours + ':' + minutes + ' am')
: ((hours - 12) + ':' + minutes + ' pm')
}


现在我们要做的就是将它们放在一起:

realTimeToGameTime(realTime) {
const gameDayFromRealDate = Math.floor((realTime.valueOf() - c.valueOf() / gameDayLengthInRealTime)
const realTimeIntoGameDay = (realTime.valueOf() - c.valueOf()) - gameDayFromRealDate *
gameDayLengthInRealTime
const gameTime = this.gameTimeSinceDaybreakFromRealDuration(realTimeIntoGameDay)
return {
day: gameDayFromRealDate,
time: this.formatGameTime(gameTime),
}
},


最后,因为我真的更喜欢纯函数-全局变量不好!我们可以创建一个工厂来创建特定的“时间映射器”(请注意,这使用了一些ES2015功能):

function createTimeMapper(config) {
const {
c,
gameDaybreak,
gameDayLength,
gameDaytimeLength,
gameNighttimeLength,
gameDayLengthInRealTime,
gameDaytimeLengthInRealTime,
gameNighttimeLengthInRealTime,
} = config
return {
realTimeToGameTime(realTime) {
const gameDayFromRealDate = Math.floor((realTime.valueOf() - c.valueOf()) / gameDayLengthInRealTime)
const realTimeIntoGameDay = (realTime.valueOf() - c.valueOf()) - gameDayFromRealDate * gameDayLengthInRealTime
const gameTime = this.gameTimeSinceDaybreakFromRealDuration(realTimeIntoGameDay)
return {
day: gameDayFromRealDate,
time: this.formatGameTime(gameTime),
}
},

gameTimeSinceDaybreakFromRealDuration(realDuration) {
if(realDuration > gameDayLengthInRealTime)
throw new Error("this function only works within a single game day")
if(realDuration <= gameDaytimeLengthInRealTime) {
return (gameDaybreak + realDuration * gameDaytimeLength / gameDaytimeLengthInRealTime) %
gameDayLength
} else {
return (gameDaybreak + gameDaytimeLength +
(realDuration - gameDaytimeLengthInRealTime) * gameNighttimeLength / gameNighttimeLengthInRealTime) %
gameDayLength
}
},

formatGameTime(gameTime) {
const totalMinutes = gameTime/1000/60 // convert from milliseconds to minutes
const hours = Math.floor(totalMinutes/60)
let minutes = (totalMinutes % 60).toFixed(0)
if(minutes.length < 2) minutes = "0" + minutes // get that leading zero!
return hours < 13
? (hours + ':' + minutes + ' am')
: ((hours - 12) + ':' + minutes + ' pm')
},
}
}


并使用它:

const timeMapper = createTimeMapper({
new Date(2017, 0, 1),
gameDaybreak: 7*1000*60*60,
gameDayLength: 24*1000*60*60,
gameDaytimeLength: 15*1000*60*60,
gameNighttimeLength: 9*1000*60*60,
gameDayLengthInRealTime: (245/60)*1000*60*60,
gameDaytimeLengthInRealTime: (200/60)*1000*60*60,
gameNighttimeLengthInRealTime: (45/60)*1000*60*60,
})

关于javascript - 基于系统时间的持续计时器/时钟?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42639349/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com