Модуль:Timeconv

Материал из Touhou Wiki
Перейти к навигации Перейти к поиску

Для документации этого модуля может быть создана страница Модуль:Timeconv/doc

-- Модуль конвертации времени
-- Автор: DennouNeko

-- Осторожно! Этот модуль является экспериментальным!
-- Не стоит использовать его для чего-то кроме расчётов дат событий.

local common = require("Модуль:Common")
local libastro = require("Модуль:Lib Astro")

local astro = libastro.astro
local time = libastro.time

local function mod(a, b)
  return a - b * math.floor(a/b)
end

local function amod(a, b)
  return mod(a-1, b) + 1
end

local function fulld(jd)
  return math.floor(jd - 0.5) + 0.5
end

-- dir = true for North and East
local function gps_to_deg(d, m, s, dir)
  return common.cv(dir, 1, -1) * ((s / 60 + m) / 60 + d)
end

local function deg_to_gps(deg)
  local dir = common.cv(deg > 0, 1, -1)

  deg = deg * dir

  local d = math.floor(deg)
  deg = (deg - d) * 60
  local m = math.floor(deg)
  deg = (deg - m) * 60

  return d,m,deg,(dir>0)
end

local function gregorian_to_jd_test(frame)
  local day,month,year = tonumber(frame.args['day']), tonumber(frame.args['month']), tonumber(frame.args['year'])
  local hour = tonumber(frame.args['hour'])
  local tz = tonumber(frame.args['timezone'])

  return time.gregorian_to_jd(day, month, year, hour) - (tz / 24)
end

local function jd_to_gregorian_test(frame)
  local jd = tonumber(frame.args['jd'])
  local tz = tonumber(frame.args['timezone'])

  local day,month,year = time.jd_to_gregorian(jd + (tz / 24))
  return string.format("%i.%02u.%02u (%s))", day,month,year, time.wdays[time.jwday(jd + (tz / 24)) + 1])
end

local function gensokyo_to_jd_test(frame)
  local season = tonumber(frame.args['season'])
  local month = tonumber(frame.args['month'])
  local day = tonumber(frame.args['day'])

  local leap = month < 0
  month = math.abs(month)

  return time.gensokyo_to_jd(season, month, leap, day)
end

local function jd_to_gensokyo_test(frame)
  local jd = tonumber(frame.args['jd'])
  local tz = 9

  local season,month,leap,day = time.jd_to_gensokyo(jd)

  if leap then month = -month end

  return string.format("%u.%u.%i", day, month, season, time.wdays[time.jwday(jd) + 1])
end

function time.timestamp_to_jd(ts)
  -- convert days and add start of epoch
  return ts / (60 * 60 * 24) + time.gregorian_to_jd(1970, 1, 1, 0)
end

function time.jd_to_timestamp(ts)
  -- subtract start of epoch and convert to seconds
  return (jd - time.gregorian_to_jd(1970, 1, 1, 0)) * (60 * 60 * 24) 
end

local months = {'Deutzia Month', 'Planting Month', 'Month of Water', 'Book Month', 'Leaf Month', 'Long-lasting Month', 'Godless Month', 'Frost Month', 'Priest-running Month', 'Affection Month', 'More Clothes Month', 'Sprouting Month', 'Leap'}

local function season_to_elems(s)
  s = s + 1
  local n1 = {'sun', 'moon', 'star'}
  local n2 = {'spring', 'summer', 'autumn', 'winter'}
  local n3 = {'earth', 'fire', 'water', 'wood', 'metal'}

  return n1[amod(s, 3)], n2[amod(s, 4)], n3[amod(s, 5)]
end

local function season_test(frame)
  local ret = {}
  local starts = tonumber(frame.args['start'])
  local i

  ret[#ret+1] = '<table class="wikitable">'
  ret[#ret+1] = '<tr>'
  for i=0,18 do
    ret[#ret+1] = '<td>' .. (starts + i) .. '</td>'
  end
  ret[#ret+1] = '</tr>'
  ret[#ret+1] = '<tr>'
  for i=0,18 do
    ret[#ret+1] = '<td>' .. common.cv(time.gensokyo_leap(starts + i), '<span style="color:#ff0000; font-weight: bold;">yes</span>', 'no') .. '</td>'
  end
  ret[#ret+1] = '</tr>'
  ret[#ret+1] = '</table>'

  return table.concat(ret, '\n')
end

local function test_month(frame)
  local season = tonumber(frame.args['season'])
  local s1,m1,l1,d1
  local month = 1

  local jd = time.gensokyo_to_jd(season, 1, false, 1)
  local jd1 = jd - 1

  ret = {}
  ret1 = {}

  ret[#ret+1] = '\n* ' .. common.cv(time.gensokyo_leap(season), '<span style="color:#ff0000; font-weight: bold;">' .. season .. '</span>', season) .. string.format(', season of %s, %s and %s', season_to_elems(season))
  ret[#ret+1] = '<table class="wikitable">'
  ret[#ret+1] = '<tr><th>month:</th>'
  ret1[#ret1+1] = '<tr><th>days:</th>'

  repeat
    s1,m1,l1,d1 = time.jd_to_gensokyo(jd)
    if s1 ~= season then break end

    jd1 = astro.gensokyo_new_moon_on_or_after(jd + 1)

    mc = common.cv(time.gensokyo_leap(season), 13, 12)

    ret[#ret+1] = '<td>' .. common.cv(l1, '<span style="color:#ff0000; font-weight: bold;">' .. (-m1) .. '</span>', m1) .. '</td>'

    ret1[#ret1+1] = '<td>' .. (jd1-jd) .. '</td>'

    jd = jd1
    month = month + 1
  until(month > 13)

  ret1[#ret1+1] = '</tr>'
  ret[#ret+1] = '</tr>'

  ret[#ret+1] = table.concat(ret1, '\n')

  ret[#ret+1] = '</table>'

  return table.concat(ret, '\n')
end

local function test_calendar(frame)
  local jd, jd1, jd2
  local i, j
  local month = tonumber(frame.args['month'])
  local day = tonumber(frame.args['day'])
  if month == nil then month = 1 end
  if day == nil then day = 1 end
  local leap = false
  local mark = common.isset(frame.args['mark'])
  local align = frame.args['align']

  if month < 0 then
    leap = true
    month = -month
  end

  if common.isset(frame.args['season']) then
    jd2 = time.gensokyo_to_jd(tonumber(frame.args['season']), month, leap, day)
  elseif common.isset(frame.args['year']) then
    jd2 = time.gregorian_to_jd(tonumber(frame.args['year']), month, day, 0)
  else
    return '<span style="color:#ff0000; font-weight: bold;">Error: test_calendar:</span> Calendar requires a season or year param!'
  end

  jd  = astro.gensokyo_new_moon_before(jd2+1)
  jd1 = astro.gensokyo_new_moon_on_or_after(jd+1)
  local gs,gm,gl,gd = time.jd_to_gensokyo(jd)
  local n1,n2,n3 = season_to_elems(gs)

  local eph = {}
  eph[1] = time.standard_from_universal(astro.lunar_phase_after(  0, jd - 1), time.location(jd))
  eph[2] = time.standard_from_universal(astro.lunar_phase_after( 90, jd - 1), time.location(jd))
  eph[3] = time.standard_from_universal(astro.lunar_phase_after(180, jd - 1), time.location(jd))
  eph[4] = time.standard_from_universal(astro.lunar_phase_after(270, jd - 1), time.location(jd))

  jd  = time.standard_from_universal(jd,  time.location(jd))
  jd1 = time.standard_from_universal(jd1, time.location(jd1))
  jd2 = time.standard_from_universal(jd2, time.location(jd2))

  local ret = {}

  if common.isset(align) then
    if align == 'left' then
      ret[#ret+1] = '<div style="float: left; clear: left;">'
    elseif align == 'right' then
      ret[#ret+1] = '<div style="float: right; clear: right;">'
    else
      align = nil
    end
  end

  ret[#ret+1] = '<table class="wikitable">'

  ret[#ret+1] = '<caption>'
  ret[#ret+1] = string.format("'''%i''' - %s, %s and %s", gs, n1, n2, n3)
  ret[#ret+1] = '<br/>' .. string.format("%i - %s%s", gm, common.cv(gl, months[13] .. ' ', ''), months[gm])
  ret[#ret+1] = '</caption>'

  ret[#ret+1] = '<tr><th style="width: 30px">Mon</th><th style="width: 30px">Tue</th><th style="width: 30px">Wed</th><th style="width: 30px">Thu</th><th style="width: 30px">Fri</th><th style="width: 30px">Sat</th><th style="width: 30px; color:#ff0000">Sun</th></tr>'

  ret[#ret+1] = '<tr>'
  local wd0 = time.jwday(jd) + 1

  if wd0 > 1 then
    for i=1,(wd0-1) do
      ret[#ret+1] = '<td>&nbsp;</td>'
    end
  end

  local days = math.floor(jd1-jd)

  for i=1,days do
    ret[#ret+1] = '<td' .. common.cv(mark and math.abs(jd2 - (jd + i - 1)) < 1, ' style="background-color: #ffe0e0;"', '') .. '>'

    ret[#ret+1] = common.cv(wd0 == 7, '<span style="color:#ff0000; font-weight: bold;">' .. i .. '</span>', i)

    for j=1,4 do
      if math.abs(eph[j] - (jd + i - 1)) <= 0.5 then
        ret[#ret+1] = string.format('<sup>%u</sup>', j)
      end
    end

    ret[#ret+1] = '</td>'

    wd0 = amod(wd0 + 1, 7)
    if (wd0 == 1) and (i < days) then
      ret[#ret+1] = '</tr><tr>'
    end
  end

  if wd0 > 1 then
    for i=wd0,7 do
      ret[#ret+1] = '<td>&nbsp;</td>'
    end
  end

  if common.isset(align) then
    ret[#ret+1] = '</div>'
  end

  ret[#ret+1] = '</table>'

  return table.concat(ret)
end

local function gensokyo_to_gregorian(frame)
  local season = tonumber(frame.args[1])
  local month = tonumber(frame.args[2])
  local leap = false
  local day = tonumber(frame.args[3])
  local approx = false

  if (season == nil) or (month == nil) then
    return frame:preprocess('<span style="color:#ff0000; font-weight: bold;">[[Module:Timeconv]] gen_to_greg error:</span> season and month params are required.')
  end

  if day == nil then
    approx = true
    day = 1
  end

  if month < 0 then
    month = -month
    leap = true
  end

  -- shift in case that uzuki = 4th month
  if common.isset(frame.args['shift']) then
    month = amod(month + 9, 12) -- + 12 - 3
  end

  local jd = time.gensokyo_to_jd(season, month, leap, day)
  local d,m,y = time.jd_to_gregorian(jd)

  -- the actual date formatting
  return common.cv(approx, '~', '') .. string.format("%02u.%02u.%i", d, m, y)
end

-- Assumes uzuki is 4th month
local months_dict = {
  ['муцуки'] = 1,
  ['кисараги'] = 2,
  ['яёи'] = 3,
  ['удзуки'] = 4,
  ['сацуки'] = 5,
  ['минадзуки'] = 6,
  ['фумидзуки'] = 7,
  ['хадзуки'] = 8,
  ['нагацуки'] = 9,
  ['каммадзуки'] = 10,
  ['симоцуки'] = 11,
  ['сивасу'] = 12
}

-- {{timeconv | news_estimate | 124 | Hazuki}} => "est. 2009/09/19 ~ 2009/10/18"
local function news_estimate(frame)
  local season = tonumber(frame.args[1])
  local month = months_dict[frame.args[2]]

  if (season == nil) or (month == nil) then
    return frame:preprocess('<span style="color:#ff0000; font-weight: bold;">[[Module:Timeconv]] news_estimate error:</span> season and month params are required.')
  end

  -- shift: uzuki is 4th month
  month = ((month - 3) - 1) % 12 + 1
  if (month >= 10) then -- shifted to prev year in calcs, which assume uzuki=1
    season = season - 1
  end
  -- get next month for range end
  local next_season = (month == 12) and season+1 or season
  local next_month = month % 12 + 1

  local jd = time.gensokyo_to_jd(season, month, false, 1)
  local next_jd = time.gensokyo_to_jd(next_season, next_month, false, 1)

  -- date formatting
  return '' .. string.format('%02u.%02u.%i', time.jd_to_gregorian(jd)) .. ' ~ ' .. string.format('%02u.%02u.%i', time.jd_to_gregorian(next_jd))
end

local function gregorian_to_gensokyo(frame)
  local year = tonumber(frame.args[1])
  local month = tonumber(frame.args[2])
  local day = tonumber(frame.args[3])
  local approx = false

  if (year == nil) or (month == nil) then
    return frame:preprocess('<span style="color:#ff0000; font-weight: bold;">[[Module:Timeconv]] greg_to_gen error:</span> year and month params are required.')
  end

  if day == nil then
    day = 1
    approx = true
  end

  local jd = time.gregorian_to_jd(day, month, year, 0)
  local gs,gm,gl,gd = time.jd_to_gensokyo(jd)

  -- shift in case that uzuki = 4th month
  if common.isset(frame.args['shift']) then
    gm = amod(gm + 3, 12)
  end

  if gl then
    gm = -gm
  end

  -- the actual date formatting
  return common.cv(approx, '~', '') .. string.format("%u.%i.%i",gd, gm, gs)
end

return {
  ['gregorian_to_jd'] = gregorian_to_jd_test,
  ['jd_to_gregorian'] = jd_to_gregorian_test,
  ['gensokyo_to_jd'] = gensokyo_to_jd_test,
  ['jd_to_gensokyo'] = jd_to_gensokyo_test,
  ['season_test'] = season_test,
  ['test_month'] = test_month,
  ['gen_to_greg'] = gensokyo_to_gregorian,
  ['news_estimate'] = news_estimate,
  ['greg_to_gen'] = gregorian_to_gensokyo,
  ['calendar'] = test_calendar,
}

-- [[Category:Lua Scripts|{{PAGENAME}}]]