How can I create a specific time interval in Ruby? - ruby

What I have tried so far ...
start_hour = 7
start_minute = 0 * 0.01
end_hour = 17
end_minute = 45 * 0.01
step_time = 25
start_time = start_hour + start_minute
end_time = end_hour + end_minute
if step_time > 59
step_time = 1 if step_time == 60
step_time = 1.3 if step_time == 90
step_time = 2 if step_time == 120
else
step_time *= 0.01
end
hours = []
(start_time..end_time).step(step_time).map do |x|
next if (x-x.to_i) > 0.55
hours << '%0.2f' % x.round(2).to_s
end
puts hours
If I enter the step interval 0, 5, 10, 20, I can get the time interval I want. But if I enter 15, 25, 90, I can't get the right range.

You currently have:
end_hour = 17
end_minute = 45 * 0.01
end_time = end_hour + end_minute
#=> 17.45
Although 17.45 looks like the correct value, it isn't. 45 minutes is 3 quarters (or 75%) of an hour, so the correct decimal value is 17.75.
You could change your code accordingly, but working with decimal hours is a bit strange. It's much easier to just work with minutes. Instead of turning the minutes into hours, you turn the hours into minutes:
start_hour = 7
start_minute = 0
start_time = start_hour * 60 + start_minute
#=> 420
end_hour = 17
end_minute = 45
end_time = end_hour * 60 + end_minute
#=> 1065
The total amount of minutes can easily be converted back to hour-minute pairs via divmod:
420.divmod(60) #=> [7, 0]
1065.divmod(60) #=> [17, 45]
Using the above, we can traverse the range without having to convert the step interval:
def hours(start_time, end_time, step_time)
(start_time..end_time).step(step_time).map do |x|
'%02d:%02d' % x.divmod(60)
end
end
hours(start_time, end_time, 25)
#=> ["07:00", "07:25", "07:50", "08:15", "08:40", "09:05", "09:30", "09:55",
# "10:20", "10:45", "11:10", "11:35", "12:00", "12:25", "12:50", "13:15",
# "13:40", "14:05", "14:30", "14:55", "15:20", "15:45", "16:10", "16:35",
# "17:00", "17:25"]
hours(start_time, end_time, 90)
#=> ["07:00", "08:30", "10:00", "11:30", "13:00", "14:30", "16:00", "17:30"]

Related

Ruby - Get time at start of next minute

I'm looking for a concise way to get a Ruby Time object representing the top of the next minute (and hour/day/month/year, if possible). I want this to work in a pure Ruby environment, so the Rails function Time.change or similar doesn't fit the bill.
At first this seems simple - just add 1 to Time.now, but there are edge cases where if, for example, you try to instantiate a Time object with Time.now.min + 1 when the current minute is 59, you get an ArgumentError: min out of range. This goes for hour, day, and month as well.
I have some lengthy code that does the job. It's ugly, but I'm just experimenting:
def add_minute
now = Time.local
year = now.year
month = now.month
day = now.day
hour = now.hour
min = now.min
if now.min == 59
if now.hour == 23
if now.day == Date.civil(now.year, now.month, -1).day
if month == 12
year = year + 1
month = 1
day = 1
hour = 0
min = 0
else
month = now.month + 1
day = 1
hour = 0
min = 0
end
else
day = now.day + 1
hour = 0
min = 0
end
else
hour = now.hour + 1
min = 0
end
else
min = now.min + 1
end
Time.local year, month, day, hour, min, 0
end
This seems absurdly verbose for what seems like it should be a simple or built-in task, but I haven't found a native Ruby solution. Does one exist?
You could convert the Time object to UNIX epoch time (seconds since 1970) using #to_i, add 60 s, and then convert back to a Time object.
time_unix = Time.now.to_i
time_unix_one_min_later = time_unix + 60
time_one_min_later = t = Time.at(time_unix_one_min_later)
time_one_min_later_rounded_down = Time.new(t.year, t.month, t.day, t.hour, t.min)
EDIT: Even shorter - you can just add integer seconds to Time.now directly:
time_one_min_later = t = Time.now + 60
time_one_min_later_rounded_down = Time.new(t.year, t.month, t.day, t.hour, t.min)
EDIT 2: One-liner - just subtract Time.now.sec:
time_one_min_later_rounded_down = Time.now + 60 - Time.now.sec
Other option, given one second to midnight:
require 'time'
now = Time.strptime('2018-12-31 23:59:59', '%Y-%m-%d %H:%M:%S')
Within one minute:
Time.at(now + 60) #=> 2019-01-01 00:00:59 +0100
Time.at(now + 60 - now.sec) #=> 2019-01-01 00:00:00 +0100
You get: # HAPPY NEW YEAR!
Ruby has built in methods for adding months (>>) and days (+). A year is 12 months, and an hour is 1/24th of a day.
require 'date'
def add_time(time, year: 0 ,month: 0, day: 0, hour: 0, minute: 0)
time >>= 12*year
time >>= month
time += day
time += Rational(hour,24) # or (hour/24.0) if you dislike rationals
time += Rational(minute, 24*60) # (minute/24.0*60) if you dislike rationals
end
p t = DateTime.now
p add_time(t, year: 1, minute: 30)
Not that clean without ActiveSupport:
new_date = (DateTime.now + 1.to_f / (60*24))
DateTime.new(new_date.year, new_date.month, new_date.day, new_date.hour, new_date.minute)
We can make this calculation easier to understand by getting the current number of seconds we are through the day. (optional)
DateTime.current.to_i gives us the number of seconds since 1970
DateTime.current.to_i - DateTime.current.beginning_of_day.to_i gives us the number of seconds since the start of the day.
(((number_of_seconds_through_the_day + 60)/60) * 60) gives us the number of seconds we will be at when the next minute starts
Then we subtract the two to give us the number of seconds until the top of the next minute.
If we want the exact time at start of the next minute then we can do:
DateTime.current + seconds_until_start_of_the_next_minute.seconds
def seconds_until_start_of_the_next_minute
number_of_seconds_through_the_day = DateTime.current.to_i - DateTime.current.beginning_of_day.to_i
number_of_seconds_through_the_day_at_next_minute = (((number_of_seconds_through_the_day + 60)/60) * 60)
seconds_until_next_minute_starts = number_of_seconds_through_the_day_at_next_minute - number_of_seconds_through_the_day
return seconds_until_next_minute_starts
end

Set time every day in ruby

I made something to test the time. This is the Code:
time1 = Time.new
puts time1.strftime("%H:%M:%S")
# |
# V Change here
t = Time.new(2017, 9, 8, 14, 30, 0)
dist = ((t - time1) /60 ).round
dist1 = dist/60
dist2 = dist
while dist2 > 60
dist2 = dist2 -60
end
puts "just #{dist1} hours and #{dist2} min left."
but I don't know how I create a fixed time for every day. I need to change it every day. Got some tips for me?
t = DateTime.parse("14:30").to_time
The above would construct a time instance for today, 2:30PM.

Infinite loop in algorithm to match clocks running at different speeds

I'm trying to solve this problem:
Two clocks, which show the time in hours and minutes using the 24 hour clock, are running at different
speeds. Each clock is an exact number of minutes per hour fast. Both clocks start showing the same time
(00:00) and are checked regularly every hour (starting after one hour) according to an accurate timekeeper.
What time will the two clocks show on the first occasion when they are checked and show the same time?
NB: For this question we only care about the clocks matching when they are checked.
For example, suppose the first clock runs 1 minute fast (per hour) and the second clock runs 31 minutes
fast (per hour).
• When the clocks are first checked after one hour, the first clock will show 01:01 and the second clock
will show 01:31;
• When the clocks are checked after two hours, they will show 02:02 and 03:02;
• After 48 hours the clocks will both show 00:48.
Here is my code:
def add_delay(min,hash)
hash[:minutes] = (hash[:minutes] + min)
if hash[:minutes] > 59
hash[:minutes] %= 60
if min < 60
add_hour(hash)
end
end
hash[:hour] += (min / 60)
hash
end
def add_hour(hash)
hash[:hour] += 1
if hash[:hour] > 23
hash[:hour] %= 24
end
hash
end
def compare(hash1,hash2)
(hash1[:hour] == hash2[:hour]) && (hash1[:minutes] == hash2[:minutes])
end
#-------------------------------------------------------------------
first_clock = Integer(gets) rescue nil
second_clock = Integer(gets) rescue nil
#hash1 = if first_clock < 60 then {:hour => 1,:minutes => first_clock} else {:hour => 1 + (first_clock/60),:minutes => (first_clock%60)} end
#hash2 = if second_clock < 60 then {:hour => 1,:minutes => second_clock} else {:hour => 1 + (second_clock/60),:minutes => (second_clock%60)} end
hash1 = {:hour => 0, :minutes => 0}
hash2 = {:hour => 0, :minutes => 0}
begin
hash1 = add_hour(hash1)
hash1 = add_delay(first_clock,hash1)
hash2 = add_hour(hash2)
p hash2.to_s
hash2 = add_delay(second_clock,hash2)
p hash2.to_s
end while !compare(hash1,hash2)
#making sure print is good
if hash1[:hour] > 9
if hash1[:minutes] > 9
puts hash1[:hour].to_s + ":" + hash1[:minutes].to_s
else
puts hash1[:hour].to_s + ":0" + hash1[:minutes].to_s
end
else
if hash1[:minutes] > 9
puts "0" + hash1[:hour].to_s + ":" + hash1[:minutes].to_s
else
puts "0" + hash1[:hour].to_s + ":0" + hash1[:minutes].to_s
end
end
#-------------------------------------------------------------------
For 1 and 31 the code runs as expected. For anything bigger, such as 5 and 100, it seems to get into an infinite loop and I don't see where the bug is. What is going wrong?
The logic in your add_delay function is flawed.
def add_delay(min,hash)
hash[:minutes] = (hash[:minutes] + min)
if hash[:minutes] > 59
hash[:minutes] %= 60
if min < 60
add_hour(hash)
end
end
hash[:hour] += (min / 60)
hash
end
If hash[:minutes] is greater than 60, you should increment the hour no matter what. Observe that an increment less than 60 can cause the minutes to overflow.
Also, you may have to increment the hour more than once if the increment exceeds 60 minutes.
Finally, it is wrong to do hash[:hour] += (min / 60) because min is not necessarily over 60 and because you have already done add_hour(hash).
Here is a corrected version of the function:
def add_delay(minutes, time)
time[:minutes] += minutes
while time[:minutes] > 59 # If the minutes overflow,
time[:minutes] -= 60 # subtract 60 minutes and
add_hour(time) # increment the hour.
end # Repeat as necessary.
time
end
You can plug this function into your existing code. I have merely taken the liberty of renaming min to minutes and hash to time inside the function.
Your code
Let's look at your code and at the same time make some small improvements.
add_delay takes a given number of minutes to add to the hash, after converting the number of minutes to hours and minutes and then the number of hours to the number of hours within a day. One problem is that if a clock gains more than 59 minutes per hour, you may have to increment hours by more than one. Try writing it and add_hours like this:
def add_delay(min_to_add, hash)
mins = hash[:minutes] + min_to_add
hrs, mins = mins.divmod 60
hash[:minutes] = mins
add_hours(hash, hrs)
end
def add_hours(hash, hours=1)
hash[:hours] = (hash[:hours] + hours) % 24
end
We do not necessarily care what either of these methods returns, as they modify the argument hash.
This uses the very handy method Fixnum#divmod to convert minutes to hours and minutes.
(Aside: some Rubiests don't use hash as the name of a variable because it is also the name of a Ruby method.)
Next, compare determines if two hashes with keys :hour and :minutes are equal. Rather than checking if both the hours and minutes match, you can just see if the hashes are equal:
def compare(hash1, hash2)
hash1 == hash2
end
Get the minutes per hour by which the clocks are fast:
first_clock = Integer(gets) rescue nil
second_clock = Integer(gets) rescue nil
and now initialize the hashes and step by hour until a match is found, then return either hash:
def find_matching_time(first_clock, second_clock)
hash1 = {:hours => 0, :minutes => 0}
hash2 = {:hours => 0, :minutes => 0}
begin
add_delay(first_clock, hash1)
add_hours(hash1)
add_delay(second_clock, hash2)
add_hours(hash2)
end until compare(hash1, hash2)
hash1
end
Let's try it:
find_matching_time(1, 31)
# => {:hours=>0, :minutes=>48}
find_matching_time(5, 100)
#=> {:hours=>0, :minutes=>0}
find_matching_time(5, 5)
#=> {:hours=>1, :minutes=>5}
find_matching_time(0, 59)
#=> {:hours=>0, :minutes=>0}
These results match those I obtained below with an alternative method. You do not return the number hours from the present until the times are the same, but you may not need that.
I have not identified why you were getting the infinite loop, but perhaps with this analysis you will be able to find it.
There are two other small changes I would suggest: 1) incorporating add_hours in add_delay and renaming the latter, and 2) getting rid of compare because it so simple and only used in one place:
def add_hour_and_delay(min_to_add, hash)
mins = hash[:minutes] + min_to_add
hrs, mins = mins.divmod 60
hash[:minutes] = mins
hash[:hours] = (hash[:hours] + 1 + hrs) % 24
end
def find_matching_time(first_clock, second_clock)
hash1 = {:hours => 0, :minutes => 0}
hash2 = {:hours => 0, :minutes => 0}
begin
add_hour_and_delay(first_clock, hash1)
add_hour_and_delay(second_clock, hash2)
end until hash1 == hash2
hash1
end
Alternative method
Here's anther way to write the method. Let:
f0: minutes per hour the first clock is fast
f1: minutes per hour the second clock is fast
Then we can compute the next time they will show the same time as follows.
Code
MINS_PER_DAY = (24*60)
def find_matching_time(f0, f1)
elapsed_hours = (1..Float::INFINITY).find { |i|
(i*(60+f0)) % MINS_PER_DAY == (i*(60+f1)) % MINS_PER_DAY }
[elapsed_hours, "%d:%02d" % ((elapsed_hours*(60+f0)) % MINS_PER_DAY).divmod(60)]
end
Examples
find_matching_time(1, 31)
#=> [48, "0:48"]
After 48 hours both clocks will show a time of "0:48".
find_matching_time(5, 100)
#=> [288, "0:00"]
find_matching_time(5, 5)
#=> [1, "1:05"]
find_matching_time(0, 59)
#=> [1440, "0:00"]
Explanation
After i hours have elapsed, the two clocks will respectively display a time that is the following number of minutes within a day:
(i*(60+f0)) % MINS_PER_DAY # clock 0
(i*(60+f1)) % MINS_PER_DAY # clock 1
Enumerable#find is then used to determine the first number of elapsed hours i when these two values are equal. We don't know how long that may take, so I've enumerated over all positive integers beginning with 1. (I guess it could be no more than 59 hours, so I could have written (1..n).find.. where n is any integer greater than 58.) The value returned by find is assigned to the variable elapsed_hours.
Both clocks will display the same time after elapsed_hours, so we can compute the time either clock will show. I've chosen to do that for clock 0. For the first example (f0=1, f1=31)
elapsed_hours #=> 48
so
mins_clock0_advances = elapsed_hours*(60+1)
#=> 2928
mins_clock_advances_within_day = mins_clock0_advances % MINS_PER_DAY
#=> 48
We then convert this to hours and minutes:
mins_clock_advances_within_day.divmod(60)
#=> [0, 48]
which we can then the method String#% to format this result appropriately:
"%d:%02d" % mins_clock_advances_within_day.divmod(60)
#=> "0:48"
See Kernel#sprintf for information on formatting when using %. In "%02d", d is for "decimal", 2 is the field width and 0 means pad left with zeroes.

How do I convert seconds since Epoch to current date and time?

I know I posted this a while ago, but I figured out the solution. I wrote this code for a game called Roblox, but I'm just posting the code here in case anyone else who has this same problem needs a solution. Anyways, here's the code:
outputTime = true -- true: will print the current time to output window. false: won't print time
createVariable = true -- true: creates variables under game.Lighting. false: won't create variables
-----------------------------------------------------------------------------------------------
--DO NOT EDIT BELOW----------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
if(createVariable) then
yearVar = Instance.new("IntValue", game.Lighting)
yearVar.Name = "Year"
yearVar.Value = 0
monthVar = Instance.new("IntValue", game.Lighting)
monthVar.Name = "Month"
monthVar.Value = 0
dayVar = Instance.new("IntValue", game.Lighting)
dayVar.Name = "Day"
dayVar.Value = 0
hourVar = Instance.new("IntValue", game.Lighting)
hourVar.Name = "Hour"
hourVar.Value = 0
minuteVar = Instance.new("IntValue", game.Lighting)
minuteVar.Name = "Minute"
minuteVar.Value = 0
secondVar = Instance.new("IntValue", game.Lighting)
secondVar.Name = "Second"
secondVar.Value = 0
dayOfWeek = Instance.new("StringValue", game.Lighting)
dayOfWeek.Name = "DayOfWeek"
dayOfWeek.Value = "Thursday"
end
function giveZero(data)
if string.len(data) <= 1 then
return "0" .. data
else
return data
end
end
function hasDecimal(value)
if not(value == math.floor(value)) then
return true
else
return false
end
end
function isLeapYear(year)
if(not hasDecimal(year / 4)) then
if(hasDecimal(year / 100)) then
return true
else
if(not hasDecimal(year / 400)) then
return true
else
return false
end
end
else
return false
end
end
local eYear = 1970
local timeStampDayOfWeak = 5
local secondsInHour = 3600
local secondsInDay = 86400
local secondsInYear = 31536000
local secondsInLeapYear = 31622400
local monthWith28 = 2419200
local monthWith29 = 2505600
local monthWith30 = 2592000
local monthWith31 = 2678400
local monthsWith30 = {4, 6, 9, 11}
local monthsWith31 = {1, 3, 5, 7, 8, 10, 12}
local daysSinceEpoch = 0
local DOWAssociates = {"Tursday", "Friday", "Saturday", "Sunday", "Monday", "Tuesday", "Wednesday"}
while(true) do
now = tick()
year = 1970
secs = 0
daysSinceEpoch = 0
while((secs + secondsInLeapYear) < now or (secs + secondsInYear) < now) do
if(isLeapYear(year+1)) then
if((secs + secondsInLeapYear) < now) then
secs = secs + secondsInLeapYear
year = year + 1
daysSinceEpoch = daysSinceEpoch + 366
end
else
if((secs + secondsInYear) < now) then
secs = secs + secondsInYear
year = year + 1
daysSinceEpoch = daysSinceEpoch + 365
end
end
end
secondsRemaining = now - secs
monthSecs = 0
yearIsLeapYear = isLeapYear(year)
month = 1 -- January
while((monthSecs + monthWith28) < secondsRemaining or (monthSecs + monthWith30) < secondsRemaining or (monthSecs + monthWith31) < secondsRemaining) do
if(month == 1) then
if((monthSecs + monthWith31) < secondsRemaining) then
month = 2
monthSecs = monthSecs + monthWith31
daysSinceEpoch = daysSinceEpoch + 31
else
break
end
end
if(month == 2) then
if(not yearIsLeapYear) then
if((monthSecs + monthWith28) < secondsRemaining) then
month = 3
monthSecs = monthSecs + monthWith28
daysSinceEpoch = daysSinceEpoch + 28
else
break
end
else
if((monthSecs + monthWith29) < secondsRemaining) then
month = 3
monthSecs = monthSecs + monthWith29
daysSinceEpoch = daysSinceEpoch + 29
else
break
end
end
end
if(month == 3) then
if((monthSecs + monthWith31) < secondsRemaining) then
month = 4
monthSecs = monthSecs + monthWith31
daysSinceEpoch = daysSinceEpoch + 31
else
break
end
end
if(month == 4) then
if((monthSecs + monthWith30) < secondsRemaining) then
month = 5
monthSecs = monthSecs + monthWith30
daysSinceEpoch = daysSinceEpoch + 30
else
break
end
end
if(month == 5) then
if((monthSecs + monthWith31) < secondsRemaining) then
month = 6
monthSecs = monthSecs + monthWith31
daysSinceEpoch = daysSinceEpoch + 31
else
break
end
end
if(month == 6) then
if((monthSecs + monthWith30) < secondsRemaining) then
month = 7
monthSecs = monthSecs + monthWith30
daysSinceEpoch = daysSinceEpoch + 30
else
break
end
end
if(month == 7) then
if((monthSecs + monthWith31) < secondsRemaining) then
month = 8
monthSecs = monthSecs + monthWith31
daysSinceEpoch = daysSinceEpoch + 31
else
break
end
end
if(month == 8) then
if((monthSecs + monthWith31) < secondsRemaining) then
month = 9
monthSecs = monthSecs + monthWith31
daysSinceEpoch = daysSinceEpoch + 31
else
break
end
end
if(month == 9) then
if((monthSecs + monthWith30) < secondsRemaining) then
month = 10
monthSecs = monthSecs + monthWith30
daysSinceEpoch = daysSinceEpoch + 30
else
break
end
end
if(month == 10) then
if((monthSecs + monthWith31) < secondsRemaining) then
month = 11
monthSecs = monthSecs + monthWith31
daysSinceEpoch = daysSinceEpoch + 31
else
break
end
end
if(month == 11) then
if((monthSecs + monthWith30) < secondsRemaining) then
month = 12
monthSecs = monthSecs + monthWith30
daysSinceEpoch = daysSinceEpoch + 30
else
break
end
end
end
day = 1 -- 1st
daySecs = 0
daySecsRemaining = secondsRemaining - monthSecs
while((daySecs + secondsInDay) < daySecsRemaining) do
day = day + 1
daySecs = daySecs + secondsInDay
daysSinceEpoch = daysSinceEpoch + 1
end
hour = 0 -- Midnight
hourSecs = 0
hourSecsRemaining = daySecsRemaining - daySecs
while((hourSecs + secondsInHour) < hourSecsRemaining) do
hour = hour + 1
hourSecs = hourSecs + secondsInHour
end
minute = 0 -- Midnight
minuteSecs = 0
minuteSecsRemaining = hourSecsRemaining - hourSecs
while((minuteSecs + 60) < minuteSecsRemaining) do
minute = minute + 1
minuteSecs = minuteSecs + 60
end
second = math.floor(now % 60)
year = giveZero(year)
month = giveZero(month)
day = giveZero(day)
hour = giveZero(hour)
minute = giveZero(minute)
second = giveZero(second)
remanderForDOW = daysSinceEpoch % 7
DOW = DOWAssociates[remanderForDOW + 1]
if(createVariable) then
yearVar.Value = year
monthVar.Value = month
dayVar.Value = day
hourVar.Value = hour
minuteVar.Value = minute
secondVar.Value = second
dayOfWeek.Value = DOW
end
if(outputTime) then
str = "Year: " .. year .. ", Month: " .. month .. ", Day: " .. day .. ", Hour: " .. hour .. ", Minute: " .. minute .. ", Second: ".. second .. ", Day of Week: " .. DOW
print(str)
end
wait(1)
end
----ORIGINAL POST----
What are the formulas for calculating the following given no resources except the seconds since Epoch?
Here's a list of what I need:
Current Month of year Ex: 7
Current day of month Ex: 25
Current day of week Ex: Thursday (1-7 would be acceptable)
Current hour of day Ex: 22
Current minute of hour Ex: 34
Current second of minute: 07
Here is some Lua code adapted from some C code found by Google. It does not handle timezones or Daylight Saving Time and so the outputs refers to Universal Coordinated Time (UTC).
-- based on http://www.ethernut.de/api/gmtime_8c_source.html
local floor=math.floor
local DSEC=24*60*60 -- secs in a day
local YSEC=365*DSEC -- secs in a year
local LSEC=YSEC+DSEC -- secs in a leap year
local FSEC=4*YSEC+DSEC -- secs in a 4-year interval
local BASE_DOW=4 -- 1970-01-01 was a Thursday
local BASE_YEAR=1970 -- 1970 is the base year
local _days={
-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
}
local _lpdays={}
for i=1,2 do _lpdays[i]=_days[i] end
for i=3,13 do _lpdays[i]=_days[i]+1 end
function gmtime(t)
print(os.date("!\n%c\t%j",t),t)
local y,j,m,d,w,h,n,s
local mdays=_days
s=t
-- First calculate the number of four-year-interval, so calculation
-- of leap year will be simple. Btw, because 2000 IS a leap year and
-- 2100 is out of range, this formula is so simple.
y=floor(s/FSEC)
s=s-y*FSEC
y=y*4+BASE_YEAR -- 1970, 1974, 1978, ...
if s>=YSEC then
y=y+1 -- 1971, 1975, 1979,...
s=s-YSEC
if s>=YSEC then
y=y+1 -- 1972, 1976, 1980,... (leap years!)
s=s-YSEC
if s>=LSEC then
y=y+1 -- 1971, 1975, 1979,...
s=s-LSEC
else -- leap year
mdays=_lpdays
end
end
end
j=floor(s/DSEC)
s=s-j*DSEC
local m=1
while mdays[m]<j do m=m+1 end
m=m-1
local d=j-mdays[m]
-- Calculate day of week. Sunday is 0
w=(floor(t/DSEC)+BASE_DOW)%7
-- Calculate the time of day from the remaining seconds
h=floor(s/3600)
s=s-h*3600
n=floor(s/60)
s=s-n*60
print("y","j","m","d","w","h","n","s")
print(y,j+1,m,d,w,h,n,s)
end
local t=os.time()
gmtime(t)
t=os.time{year=1970, month=1, day=1, hour=0} gmtime(t)
t=os.time{year=1970, month=1, day=3, hour=0} gmtime(t)
t=os.time{year=1970, month=1, day=2, hour=23-3, min=59, sec=59} gmtime(t)
The formula is not simple for a few reasons, especially leap years. You should probably use the date function on this page rather than trying to calculate it yourself.
You could use luatz
x = 1234567890
t = require "luatz.timetable".new_from_timestamp ( x )
print(t.year,t.month,t.day,t.hour,t.min,t.sec,t.yday,t.wday)
-- Or just
print(t:rfc_3339())
This is how I do it.
> time0=os.time()
> time0
1571439964
> os.date("%Y%m%d%H%M%S",time0)
20191019120604
>
os.date is a standard Lua function, if passing the first argument as "%t", it will return a table containing the following fields: year (four digits), month (1--12), day (1--31), hour (0--23), min (0--59), sec (0--61), wday (weekday, Sunday is 1), yday (day of the year), and isdst (daylight saving flag, a boolean).
Give it a test:
time = os.time()
print("time since epoch: " .. time)
date = os.date("*t", time)
print("year: " .. date.year)
print("month: " .. date.month)
print("day: " .. date.day)
print("hour: " .. date.hour)
print("minute: " .. date.min)
print("second: " .. date.sec)
print("weekday: " .. date.wday)
Output:
time since epoch: 1374826427
year: 2013
month: 7
day: 26
hour: 16
minute: 13
second: 47
weekday: 6
A much faster solution would be to use my method, which I haven't really seen anyone else use because most have access to os.date()
Since I do not have access to os.date(), here is my solution:
local tabIndexOverflow = function(seed, table)
-- This subtracts values from the table from seed until an overflow
-- This can be used for probability :D
for i = 1, #table do
if seed - table[i] <= 0 then
return i, seed
end
seed = seed - table[i]
end
end
local getDate = function(unix)
-- Given unix date, return string date
assert(unix == nil or type(unix) == "number" or unix:find("/Date%((%d+)"), "Please input a valid number to \"getDate\"")
local unix = (type(unix) == "string" and unix:match("/Date%((%d+)") / 1000 or unix or os.time()) -- This is for a certain JSON compatability. It works the same even if you don't need it
local dayCount, year, days, month = function(yr) return (yr % 4 == 0 and (yr % 100 ~= 0 or yr % 400 == 0)) and 366 or 365 end, 1970, math.ceil(unix/86400)
while days >= dayCount(year) do days = days - dayCount(year) year = year + 1 end -- Calculate year and days into that year
month, days = tabIndexOverflow(days, {31,(dayCount(year) == 366 and 29 or 28),31,30,31,30,31,31,30,31,30,31}) -- Subtract from days to find current month and leftover days
-- hours = hours > 12 and hours - 12 or hours == 0 and 12 or hours -- Change to proper am or pm time
-- local period = hours > 12 and "pm" or "am"
-- Formats for you!
-- string.format("%d/%d/%04d", month, days, year)
-- string.format("%02d:%02d:%02d %s", hours, minutes, seconds, period)
return {Month = month, day = days, year = year, hours = math.floor(unix / 3600 % 24), minutes = math.floor(unix / 60 % 60), seconds = math.floor(unix % 60)}
end
You are, however, on your own when it comes to finding the day of the week. I never cared to find whether it be the day of Thor or the day of Frige.

how can I convert milliseconds in String format to HH:MM:SS format in Ruby in under 3 lines of code?

#scores_raw.each do |score_raw|
# below is code if time was being sent in milliseconds
hh = ((score_raw.score.to_i)/100)/3600
mm = (hh-hh.to_i)*60
ss = (mm-mm.to_i)*60
crumbs = [hh,mm,ss]
sum = crumbs.first.to_i*3600+crumbs[1].to_i*60+crumbs.last.to_i
#scores << {:secs => sum, :hms => hh.round.to_s+":"+mm.round.to_s+":"+ss.round.to_s}
#scores_hash << {:secs => sum, :hms => hh.round.to_s+":"+mm.round.to_s+":"+ss.round.to_s}
# milliseconds case end
end
That's my current code but I hate it. It's looks messy. It doesn't just look great at all. Maybe someone whose an expert in ruby could tell how to do this by chaining collects, reduces etc and making it look good?
Time class ruby provides provides at function to get time from seconds. Use this it will cure.
miliseconds = 32290928
seconds = miliseconds/1000
Time.at(seconds).strftime("%H:%M:%S")
OR to get utc time
#Get UTC Time
Time.at(seconds).utc.strftime("%H:%M:%S")
You can wrap this in a helper method:
def format_milisecs(m)
secs, milisecs = m.divmod(1000) # divmod returns [quotient, modulus]
mins, secs = secs.divmod(60)
hours, mins = mins.divmod(60)
[secs,mins,hours].map { |e| e.to_s.rjust(2,'0') }.join ':'
end
format_milisecs 10_600_00
=> "03:13:20"
Nice solution given by #Mike Woodhouse :
Use divmod :
t = 270921000
ss, ms = t.divmod(1000) #=> [270921, 0]
mm, ss = ss.divmod(60) #=> [4515, 21]
hh, mm = mm.divmod(60) #=> [75, 15]
dd, hh = hh.divmod(24) #=> [3, 3]
puts "%d days, %d hours, %d minutes and %d seconds" % [dd, hh, mm, ss]
#=> 3 days, 3 hours, 15 minutes and 21 seconds
Answer is how to convert 270921sec into days + hours + minutes + sec ? (ruby)

Resources