2017-03-07 32 views
0

發現它很難包圍我的頭如何做到這一點。我期望創造一個奇幻時區的時鐘,並根據系統時間進行計算(因爲當您不在網頁上時需要繼續)。正在進行的基於系統時間的定時器/時鐘?

白天持續時間從早上7點到晚上9點59分是200分鐘的實時。 13秒(實時)是1分鐘。 夜間從晚上10點到上午6點59分是實時45分鐘。 5秒(實時)是1分鐘。

我想在幻想世界中使用html顯示時間(例如:5:43 PM),但同時顯示在幻想世界中實時分鐘直到它是白天還是晚上的時間(例如:晚上32分鐘)。

我想我需要設置系統時間爲0與Date.setHours(0,0,0,0)然後找出差異。數學實際上是讓我感到困惑的事情。

+0

您不能設置系統時間。 – Bergi

+0

[JavaScript代碼運行時鐘(日期和時間)快四倍]的可能重複](http://stackoverflow.com/q/14272133/1048572)? – Bergi

回答

1

在我們開始解決問題之前,讓我們從一些測試開始 - 這將給出最終解決方案的定義,並讓我們確信我們正在得到正確的解決方案。

我們最終所追求的是一種功能;我們將其稱爲realTimeToGameTime。它需要一個Date對象,並返回一個類似{ day: 0, time: '7:00 am' }的對象。在你的問題中,你沒有提及是否知道它在遊戲中的哪一天是很重要的(只是在什麼時間),但這不僅僅是一個有用的補充,你會看到它對解決方案很重要。

現在我們知道最終結果的樣子,我們可以考慮一些測試。實時和遊戲時間都有一點表示遊戲時間和實時之間的對應關係。實時,它可以是我們想要的任何時間點;我會叫它c。在遊戲時間,我們將在第0天的早上7點任意調用它。這允許我們構建一個時間線,爲我們提供我們測試用例的輸入和輸出(分鐘和小時/分鐘):

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' }) 

(在這個例子中,我使用的是玩笑期望斷言,你可以定製到無論你喜歡什麼樣的測試/斷言框架......只要確保它有一種方法來測試對象上的值相等。)

現在到解決方案。

遇到這樣的問題時 - 可能會非常快速地引起混淆,特別是當您考慮所有實現細節時 - 最好查看更易於理解的簡化圖片。你選擇的數字很難理解所需的操作,因爲有很多細節:幾分鐘到幾小時,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') 
     }, 
    } 
} 

,並使用它:在JS

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, 
}) 
+0

謝謝你的幫助。代碼非常有用。我一直在玩它至少一個小時。儘管如此,仍然努力爭取到我的頭!我無法獲得自動更新的時間,並且感覺它會快速_too。也許你可以看看並找出我做錯了什麼,[JSFiddle](https://jsfiddle.net/jayjb5bm/) – Sarah

+0

我做了一些改變。顯然,我最初的數學是錯誤的,一天持續240分鐘(200天和40夜的時間)。時間仍然不符合它應該是什麼。下一個午夜的時間是在08:20am UTC。 – Sarah

+0

嗨,莎拉......你走對了,我的回答很倉促。我在上班路上想着這件事,我有一個更好的思考方式,但是我的睡眠時間已經過去了,所以我的答案更新將不得不等待。我所概括的基本上是正確的......但有一個更簡單的方法來看待它。將盡快更新。 –

相關問題