paceUtils.js 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. /**
  2. * 配速:每公里用时(秒)
  3. * 支持 390、6:30、6'30"、6′30″、3"00"(分+秒直引号)
  4. */
  5. const MANUAL_PACE_MIN_SEC = 180 // 3'00"/km
  6. const MANUAL_PACE_MAX_SEC = 600 // 10'00"/km
  7. function parsePaceToSecPerKm(input) {
  8. if (input === '' || input === null || input === undefined) {
  9. throw new Error('缺少配速')
  10. }
  11. const s = String(input).trim()
  12. if (/^\d+(\.\d+)?$/.test(s)) {
  13. const n = parseFloat(s)
  14. if (n < 10) {
  15. throw new Error('纯数字配速表示每公里秒数,例如 390 表示 6:30/km')
  16. }
  17. return n
  18. }
  19. const colon = s.match(/^(\d+)[::](\d{1,2}(?:\.\d+)?)$/)
  20. if (colon) {
  21. return parseInt(colon[1], 10) * 60 + parseFloat(colon[2])
  22. }
  23. const quote = s.match(/^(\d+)['′『](\d{1,2}(?:\.\d+)?)[""″』]?$/)
  24. if (quote) {
  25. return parseInt(quote[1], 10) * 60 + parseFloat(quote[2])
  26. }
  27. const weirdQuote = s.match(/^(\d+)"(\d{2})"$/)
  28. if (weirdQuote) {
  29. return parseInt(weirdQuote[1], 10) * 60 + parseInt(weirdQuote[2], 10)
  30. }
  31. throw new Error(`无法解析配速「${s}」,请使用 5:30、5'30" 或每公里秒数如 330`)
  32. }
  33. function clampManualPaceSec(sec) {
  34. const n = Number(sec)
  35. if (!Number.isFinite(n)) {
  36. throw new Error('配速无效')
  37. }
  38. if (n < MANUAL_PACE_MIN_SEC || n > MANUAL_PACE_MAX_SEC) {
  39. throw new Error(`单次乐跑配速需在 3:00–10:00 /km(${MANUAL_PACE_MIN_SEC}–${MANUAL_PACE_MAX_SEC} 秒/公里)`)
  40. }
  41. return n
  42. }
  43. /** 自动乐跑:闭区间随机秒/公里 */
  44. function randomPaceSecPerKm(minSec, maxSec) {
  45. const lo = Math.max(120, Math.min(minSec, maxSec))
  46. const hi = Math.max(lo, Math.max(minSec, maxSec))
  47. return Math.round(lo + Math.random() * (hi - lo))
  48. }
  49. module.exports = {
  50. parsePaceToSecPerKm,
  51. clampManualPaceSec,
  52. randomPaceSecPerKm,
  53. MANUAL_PACE_MIN_SEC,
  54. MANUAL_PACE_MAX_SEC
  55. }