Is there a native way to convert to UTC time in UniVerse 11.2.4+? - universe

The release notes for UniVerse version 11.2.4 mention local time zone configuration, but it is in the context of auditing. This is the quote:
Local time zone configuration
Prior to UniVerse 11.2.4, the date and time data stored in the audit log records was based on UTC only.
Beginning at UniVerse 11.2.4, UniVerse adds the date and time data
based on local timezone to audit log records. The data is stored in
location 19 for each record. The dictionary name for this data field
is TZINFO. For more information, see UniVerse Security Features.
Since UniVerse seems capable of working with time zones natively, does this mean there might be a way to easily generate UTC-formatted date/time stamps from my EST/EDT values?
I am sending data to a system that wants dates formatted in ISO-8601 Date/Time format yyyy-MMddTHH:mm:ssZ, like 2015-06-02T15:55:22Z, with the time zone and Daylight Saving Time offsets accounted for.
I dug through the Security Features guide, and found this:
UniVerse also adds a globally cataloged program to help users to
obtain date and time information from the audit log (which is called
by the above two I-descriptor fields):
SUBROUTINE GETLOCALTIME
(
RESULT ;* OUT: output
TZOFF ;* IN: time zone offset
DATE ;* IN: UTC date
TIME ;* IN: UTC time
OP ;* IN: operation
;* 1: get local date
;* 2: get local time
;* 3: get local timezone id
;* 4: get local timezone daylight saving flag
)
(Since I'm not using the auditing capabilities of UniVerse, I don't think I can do much with this, nor could I locate the subroutine.)
I have also played with the popular(?) DATE.UTILITY program from PickWiki, but its calculation of Daylight Saving Time start/end dates seem off. I will save those issues for another question.
This is getting long-winded but I'm hoping someone can point me in the right direction if there's a new OCONV() parameter or something I could use.
Just in case it matters, I'm running on Windows Server 2008 R2.
Thanks!

Time is a complicated thing. Socially we have accepted that it not only acceptable to alter it 2 times a year, we have mandated it! This is all well and good for us meat machines who only want to understand time when it is convenient for us however it does cause us to get grumpy when out reporting "looks funny".
The solution to your problem is not exceptionally easy, especially if you are working with already recorded dates. Dates and times in Universe are generally recorded based on local system time. If this is something that you are trying to do going forward you have to note what the offset is at the time of the transaction or simply stamp things SYSTEM(99), which complicated pretty much all other reporting you will need to do. Either way, this is a complicated matter and it still likely to be somewhat imperfect.
Here is a little something that might help you if you are the one in charge of recording dates, going forward.
SECONDS.SINCE.GMT.01.01.1970 = SYSTEM(99)
CRT SECONDS.SINCE.GMT.01.01.1970:" Seconds since GMT Epoch Began"
NUMBER.OF.DAYS.SINCE.01.01.1970 = DATE() -732
;* Day 0 in Pick is 12/31/1967 because Dick Pick so we subtract 732 from the pick date
SECONDS.SINCE.MIDNIGHT.LOCAL= TIME()
SECS.PER.DAY = 24 * 60 * 60
LOCAL.SECONDS.SINCE.GMT.01.01.1970 = NUMBER.OF.DAYS.SINCE.01.01.1970 * SECS.PER.DAY + FIELD(SECONDS.SINCE.MIDNIGHT.LOCAL,".",1)
;*I drop the precision
CRT LOCAL.SECONDS.SINCE.GMT.01.01.1970: " Seconds since 01/01/1970 in local time"
OFFSET = (LOCAL.SECONDS.SINCE.GMT.01.01.1970 - SECONDS.SINCE.GMT.01.01.1970)
CRT "CURRENT.OFFSET IS ":INT((OFFSET / 60 )/ 60)
END
Which outputs the following on my system which is currently PDT (even though OCONV(DATE(),'DZ') reports it as PST.
1434472817 Seconds since GMT Epoch Began
1434447617 Seconds since 01/01/1970 in local time
CURRENT.OFFSET IS -7
Hopefully you have found this helpful.

Thanks for the clues. Here's my implementation:
SUBROUTINE FORMAT.ISO.8601 (IDATE, ITIME, RESULT, ERR.TEXT)
* Don't step on the caller's variables.
IN.DATE = IDATE
IN.TIME = ITIME
* Initialize the outbound variable.
RESULT = ''
IF NOT(NUM(IN.DATE)) THEN
ERR.TEXT = 'Non-numeric internal date ' : DQUOTE(IN.DATE) : ' when numeric required.'
RETURN
END
IF NOT(NUM(IN.DATE)) THEN
ERR.TEXT = 'Non-numeric internal time ' : DQUOTE(IN.TIME) : ' when numeric required.'
RETURN
END
* SYSTEM(99) is based on 1/1/1970.
SECONDS.SINCE.GMT.01.01.1970 = SYSTEM(99)
* Day 0 in Pick is 12/31/1967
* Subtract 732 to equalize the starting dates.
NUMBER.OF.DAYS.SINCE.01.01.1970 = DATE() - 732
SECONDS.SINCE.MIDNIGHT.LOCAL= TIME()
SECS.PER.DAY = 24 * 60 * 60
LOCAL.SECONDS.SINCE.GMT.01.01.1970 = NUMBER.OF.DAYS.SINCE.01.01.1970 * SECS.PER.DAY + FIELD(SECONDS.SINCE.MIDNIGHT.LOCAL,".",1)
OFFSET = LOCAL.SECONDS.SINCE.GMT.01.01.1970 - SECONDS.SINCE.GMT.01.01.1970
OFFSET = INT((OFFSET / 60 )/ 60)
OTIME = OCONV(IN.TIME, 'MTS')
IF OTIME = '' THEN
ERR.TEXT = 'Bad internal time ' : DQUOTE(IN.TIME) : '.'
RETURN
END
HOURS = FIELD(OTIME, ':', 1)
MINUTES = FIELD(OTIME, ':', 2)
SECONDS = FIELD(OTIME, ':', 3)
HOURS -= OFFSET
IF HOURS >= 24 THEN
IN.DATE += 1
HOURS = HOURS - 24
END
HOURS = HOURS 'R%2'
ODATE = OCONV(IN.DATE, 'D4/')
IF ODATE = '' THEN
ERR.TEXT = 'Bad internal date ' : DQUOTE(IN.DATE) : '.'
RETURN
END
DMONTH = FIELD(ODATE, '/', 1)
DDAY = FIELD(ODATE, '/',2)
DYEAR = FIELD(ODATE, '/',3)
RESULT = DYEAR : '-' : DMONTH : '-' : DDAY : 'T' : HOURS : ':' : MINUTES : ':' : SECONDS : 'Z'
RETURN
END
Here's my test harness:
CRT 'Testing right now.'
IDATE = DATE()
ITIME = TIME()
CALL FORMAT.ISO.8601 (IDATE, ITIME, RESULT, ERR.TEXT)
IF ERR.TEXT THEN
CRT 'ERR.TEXT: ' : ERR.TEXT
END ELSE
CRT 'RESULT: ' : RESULT
END
CRT
CRT 'Testing an hour ago.'
IDATE = DATE()
ITIME = TIME()
ITIME = ITIME - (60*60)
IF ITIME < 0 THEN
ITIME += (24*60*60)
IDATE -= 1
END
CALL FORMAT.ISO.8601 (IDATE, ITIME, RESULT, ERR.TEXT)
IF ERR.TEXT THEN
CRT 'ERR.TEXT: ' : ERR.TEXT
END ELSE
CRT 'RESULT: ' : RESULT
END
CRT
CRT 'Testing an hour from now.'
IDATE = DATE()
ITIME = TIME()
ITIME = ITIME + (60*60)
IF ITIME > (24*60*60) THEN
ITIME -= (24*60*60)
IDATE += 1
END
CALL FORMAT.ISO.8601 (IDATE, ITIME, RESULT, ERR.TEXT)
IF ERR.TEXT THEN
CRT 'ERR.TEXT: ' : ERR.TEXT
END ELSE
CRT 'RESULT: ' : RESULT
END
END
Here's my test run:
>T$FORMAT.ISO.8601
Testing right now.
RESULT: 2017-03-29T00:47:22Z
Testing an hour ago.
RESULT: 2017-03-28T23:47:22Z
Testing an hour from now.
RESULT: 2017-03-29T01:47:22Z

Related

Unzone the toDate of a moment

My server is in UTC time.
I create a localized date time in Los Angeles with moment like this:
var m = moment.tz('2021-04-13T20:30:53-07:00', 'America/Los_Angeles');
This part works great. Doing m.date() gives me 13. However I need to give it to a lib as a Date object, not a Moment so what I do is:
var d = m.toDate()
However now my server gets the wrong day. As it's in UTC it does d.getDate() which gives it the 14th, as UTC is 7 hours ahead so this timestamp is 3:30am.
Is there a way in moment to get the unzoned date? Such that even in UTC d.getDate() will give 13th.
I wrote this custom function, but I was hoping the moment-timezone lib provided this.
const getUnzonedDate = (m) => {
return m.clone().add(m.utcOffset(), 'minutes').toDate();
}
Another example
The above getUnzoned date works if my system time is in UTC. Let's say my system time is in LA:
* Removes zone information means, if system time is LA:
*
* const m = moment.tz('2021-04-13T21:30:53', 'America/New_York');
* m.hours() === m.toDate().getHours(); // false
* m.hours() === getUnoznedDate(m).getHours(); // true
*
* So it means that when the date will give you same numbers for each unit in
* moment and as a date.

AWS set date to midnight

What is the best way to initialize a Date to midnight using AWS AppSync utility.
I need to know if we have something like this
var d = (new Date()).setUTCHours(0,0,0,0)
By using $util.time.nowEpochSeconds() , I am getting the epoch time but how do i identify the time difference that i need to add to set as midnight time
AppSync doesn't offer that capability through utils only yet and this is good feedback, I'll make sure the team sees this.
In the meantime, as a workaround, you could modify the date string to achieve what you need.
#set($time = $util.time.nowFormatted("yyyy-MM-dd/HH:mm:ss/Z"))
#set ($split = $time.split("/"))
#set ($midnight = $split[0] + " 00:00:00" + $split[2])
time: $time
midnight: $midnight
midnight epoch seconds: $util.time.parseFormattedToEpochMilliSeconds($midnight, "yyyy-MM-dd HH:mm:ssZ")
will print:
time: 2019-07-15/22:33:57/+0000
midnight: 2019-07-15 00:00:00+0000
midnight epoch seconds: 1563148800000

What is the best way to schedule `whenever` task in rails in midnight usa?

We have a Rails 4 application .
What is the best way to schedule whenever task in rails in midnight usa with daylight saving ?
We need to send email at 11.58pm in night of a day's report .
We are using tzinfo gem
TZInfo::Timezone.get('America/Denver').local_to_utc(Time.parse('11:58pm')).strftime('%H:%M%p')
is not sending email at the time .
This works around the timezone issue, server is at UTC and users in another time zone (with daylight saving time). define a local action and use in cronjob.
schedule.rb
require "tzinfo"
def local(time)
TZInfo::Timezone.get('America/Denver').local_to_utc(Time.parse(time))
end
every :sunday, at: local("11:58 pm") do
#your email sending task
end
hope it will help you.
Rehan's answer was SO great! In my use case I ran into an issue where the time zone conversion also changed the day of the week the task would be scheduled for.
Perhaps there is an easier way but here is what I did.
The timezone conversion we needed would only advance the weekday.
If your use case requires the weekday to be retreated then you will need to edit this, but it should be an easy fix.
def local(time, est_weekday = nil)
days = [:sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday]
local_time = Time.parse(time)
utc_time = TZInfo::Timezone.get('America/New_York').local_to_utc(local_time)
utc_time_formatted = utc_time.strftime("%I:%M %p")
if est_weekday && days.include?(est_weekday.downcase.to_sym)
#extract intended wday for ruby datetime and assign
weekday_index = days.index(est_weekday.downcase.to_sym)
#get placeholder wday from desired EST day/time
temp_est_weekday_index = local_time.wday
#get placeholder wday from adjusted UTC day/time
temp_utc_weekday_index = utc_time.wday
#has the conversion to UTC advanced the wday?
weekday_advances = temp_utc_weekday_index != temp_est_weekday_index
#adjust wday index if timezone conversion has advanced weekday
weekday_index += 1 if weekday_advances
weekday = days[weekday_index]
return {time: utc_time_formatted, day: weekday || nil }
else
return utc_time_formatted
end
end

Django formatting multiple times

Using the default delta time field for Django, but as most know it returns horrifically. So I have developed my own function, convert_timedelta(duration). It computes days, hours, minutes and seconds, just like you might find on this website. It works like a charm on a single time ( I.e. when I was calculating an average time for one specific column ) .. However now I'm modifying how this works so it returns multiple times for a column... Separating times returned grouped by their ID, rather than just having one set time returned. So now I have multiple times returned, which works fine with the default formatting. But when I apply it to my function it doesn't like it and no particular informative errors are alerted.
def convert_timedelta(duration):
days, seconds = duration.days, duration.seconds
hours = days * 24 + seconds // 3600
minutes = (seconds % 3600) // 60
seconds = (seconds % 60)

WMI EventLog Time interval

Hie all,
I'm trying to get eventlog entries using WMI and WQL.
I can get the right log with the right sourcename of itand so on, but i can make a select query to only get result for the 5 or 10 past minutes.
here is my query:
Here are a few snippets from a script of mine:
Dim dtmStart, dtmEnd, sDate, ...
I actually had an array of dates and I was looking for logon/off/unlock events for the entire day. So I built my complete start and end dates from that.
I won't put in the day month and year but, you could just define it, e.g. sDate = 10100608.
dtmStart = sDate + "000000.000000-420" '0hr on the date in question.
dtmEnd = sDate + "235900.000000-420" ' 23:59 on the date in question
(Note that the UTC offset is in minutes here -420 day light savings time North America.)
Set colEvents = oWMIService.ExecQuery _
("SELECT * FROM Win32_NTLogEvent WHERE Logfile = 'Security' AND " _
& "TimeWritten >= '" & dtmStart & "' AND TimeWritten < '" _
& dtmEnd & "' AND " _
& "(EventCode = '528' OR EventCode = '540' OR EventCode = '538')")
' Query for events during the time range we're looking for.
Mike,
Show me your query. Usually the time format is something like this
20100608100000.000000-300
see this for more details about DateTime formatting for WQL