Round back to quarter-hour in Mulesoft Dataweave - datetime

I have a Mule Server 4.3.0 EE application, and in it I want to round a DateTime to its most recent quarter-hour, with the result also being a DateTime. Some examples:
if the input is 9:18:32 AM, then the output is 9:15:00 AM the same day
if the input is 9:33:33 AM, then the output is 9:30:00 AM the same day
if the input is 9:59:58 AM, then the output is 9:45:00 AM the same day
if the input is 10:00:00 AM, then the output is 10:00:00 AM the same day
In this application, the input will always be in New York time, with DST in effect or not depending on the time of year.
Here's what I've come up with so far. Keep the date and hour, truncate the minutes to the nearest 15, set the seconds to zero, and keep the time zone. (There's a special case for '00' minutes because it won't convert if the minutes are a single '0'.)
I know newer Mule versions have the Dates package which would make this much more elegant, but upgrading isn't currently an option for me. Is this about as good as the code can be for those requirements, or is there a better way to do this without upgrading Mule? Thank you.
%dw 2.0
import * from dw::core::Strings
output application/json
fun roundBack(inputTime: DateTime): DateTime =
(inputTime[0 to 13] ++
(if (inputTime.minutes < 15) '00' else (inputTime.minutes - mod(inputTime.minutes, 15) as String)) ++
':00' ++
inputTime[-6 to -1]
) as DateTime

I believe that the logic is good but the implementation is fragile because it depends on auto coercions (DateTime to Strings when using the range selector []). It is better for me to be explicit in the conversions to avoid issues caused by unexpected defaults formats and warnings in the editor:
%dw 2.0
import * from dw::core::Strings
output application/json
fun roundBack(inputTime: DateTime): DateTime =
(
inputTime as String {format: "yyyy-MM-dd HH:"}
++ (if (inputTime.minutes < 15)
'00'
else (inputTime.minutes - mod(inputTime.minutes, 15)) as String
)
++':00'
++ inputTime as String {format: "xxx"}
) as DateTime {format: "yyyy-MM-dd HH:mm:ssxxx"}
---
roundBack(|2020-10-01T23:57:59-04:00|)
Output: "2020-10-01 23:45:00-04:00"

Related

Groovy: Date and Time comparisons with a slight delay

So I have the following script:
import groovy.time.TimeCategory
def dueDate = context.expand( '${Test 4 - create user task#Response#$[\'_embedded\'][\'userTaskDtoList\'][0][\'dueDate\']}' )
def date = new Date(messageExchange.getTimestamp())
use(groovy.time.TimeCategory){
after24Hours = (date + 24.hours).format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone('UTC')) }
assert dueDate == after24Hours
What I'm trying to do with this is take the date and time from a REST request (dueDate - which comes in UTC format and with a 24h delay) and create a new date and time from the timestamp of the moment when that request has been sent, which is registered from my system. I then convert that time to UTC to accommodate the format from dueDate and add 24h to it. At the end I verify that the date and time from dueDate and after24Hours is the same.
The output does return the same time but in certain cases if there is a delay between the time the request is being sent and the time is received then the assertion will fail. This depends on the server, usually there is a difference of like 1 millisecond but I'm thinking that if the server will be slower at some point this will definitely be bigger.
What could I do to allow some margin of error in the assertion, maybe like a few seconds or even a couple of minutes?
Ok, so I managed to do this:
import groovy.time.*
def dueDate = context.expand( '${Test 4 - create user task#Response#$[\'_embedded\'][\'userTaskDtoList\'][0][\'dueDate\']}' )
def date = new Date(messageExchange.getTimestamp())
use(groovy.time.TimeCategory){
after24Hours = (date + 24.hours).format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone('UTC'))
def date1 = Date.parse("yyyy-MM-dd'T'HH:mm:ss'Z'", dueDate)
def date2 = Date.parse("yyyy-MM-dd'T'HH:mm:ss'Z'", after24Hours)
TimeDuration difference = TimeCategory.minus(date2, date1)
log.info date1
log.info date2
assert difference < 2.minutes
}
The script seems to work and it does return an error only if the time is longer than the one I've set in the assertion.
Unfortunately I have another issue now.
For some reason, my date output looks like this:
Fri Oct 01 16:24:10 EEST 2021: INFO: Sat Oct 02 13:24:10 EEST 2021
Which is not the correct format. That date should appear in the Zulu format, after all when I parsed the dates that was the format that I used.
Am I missing something?
What could I do to allow some margin of error in the assertion, maybe
like a few seconds or even a couple of minutes?
Instead of asserting that they are equal, you could assert that the difference between them is less than a threshold that you get to define.
If you use something like AssertJ, and I'd recommend you do, then you can do something like the following:
assertThat(dueDate).isCloseTo(after24Hours, within(1, ChronoUnit.MINUTE));
This will give a small margin to the comparison of the dates, and should fix your issue.

How to convert a moment object into a duration in seconds?

I found lots of info on converting a duration object into various formats but it's harder to find info on converting a moment object into a duration in seconds.
This answer offers the following solution: myVar = moment.duration(myVar).asSeconds()
However it doesn't work in my case, myVar is in MM:SS format not HH:MM:SS format so I'm getting an aberrant result. Any idea how to adapt it to my situation?
EDIT: here's some code
this.totalTimeSimulation = moment(lastActionEndTime, 'mm:ss').add(additionalTimeDuration, 'seconds').format('mm:ss')
this.totalTimeSimulationInSeconds = moment.duration(this.totalTimeSimulation).asSeconds()
console.log(this.totalTimeSimulation)
console.log(this.totalTimeSimulationInSeconds)
In console I see:
04:00
14400
Should be:
04:00
240
Because 4 minutes equals 240 seconds, not 14400 seconds. Moment.js thinks I'm giving it a duration in HH:MM:SS format when actually I'm giving it in MM:SS format.
Moment threats input like 04:00 as HH:MM
The format is an hour, minute, second string separated by colons like 23:59:59. The number of days can be prefixed with a dot separator like so 7.23:59:59. Partial seconds are supported as well 23:59:59.999.
moment.duration('23:59:59');
moment.duration('23:59:59.999');
moment.duration('7.23:59:59.999');
moment.duration('23:59'); // added in 2.3.0
You can prefix your input with 00: or use moment.duration(Object) constructor using minutes and seconds keys:
const totalTimeSimulation = '04:00'
const totalTimeSimulationInSeconds = moment.duration('00:' + totalTimeSimulation).asSeconds()
console.log(totalTimeSimulation)
console.log(totalTimeSimulationInSeconds)
const parts = totalTimeSimulation.split(':')
const seconds = moment.duration({
minutes: parts[0],
seconds: parts[1]}).asSeconds()
console.log(seconds)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

Java how to add time and date

Using JDK 1.8
I have Time in ms for a day (not since 1970) and I have a Date how do I add the two and create a datetime.
Thanks
Tried #Kikos soln does not produce correct result:
Somehow my orig time 930 hrs in this case changes to 9:40, the orig date itself should not have any time gets time (??) - so the addition math fails.
String testdate = "2015/10/25";
Date date = new SimpleDateFormat("yyyy/mm/dd").parse(testdate);
String timehrs= "930";
long ltime = Long.parseLong("930");
long hoursAsSeconds = (ltime / 100) * 60 * 60;
long minsAsSeconds = (ltime % 100) * 60;
long secondsOfDay = hoursAsSeconds + minsAsSeconds;
System.out.println("testdate : "+ testdate +", timehrs: "+timehrs+" ,secondsOfDay: "+secondsOfDay);
System.out.println("Orig Date time formatted: "+ new SimpleDateFormat("yyyy-mm-dd hh:mm:ss").format(date));
Date dt = new Date(date.getTime() + secondsOfDay*1000);
System.out.println("New Date : "+ new SimpleDateFormat("yyyy-mm-dd hh:mm:ss").format(dt);
testdate : 2015/10/25, timehrs: 930, secondsOfDay: 34200
Orig Date time formatted: 2015-10-25 12:10:00
New Date : 2015-40-25 09:40:00
Expected : 2015-10-25 09:30:00
Based on below by #Basil Bourque
long nanosOfDay = TimeUnit.MILLISECONDS.toNanos( secondsOfDay*1000 );
LocalTime lt = LocalTime.ofNanoOfDay( nanosOfDay );
ZoneId z = ZoneId.systemDefault();
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z );
System.out.println("ZonedDateTime zdt: "+ zdt);
ZonedDateTime zdt: 2015-10-25T09:30-07:00[America/Los_Angeles]
This is the correct answer: 2015-10-25T09:30
Question is convoluted
You say you have time of day as a count of milliseconds since midnight (apparently). Yet 930 would mean the time 00:00:00.930, not 09:30:00 as shown in your example data. I will follow your text rather than your example data.
java.time
You are using troublesome old date-time classes, now legacy, supplanted by the java.time classes. Forget you ever heard of java.util.Date and .Calendar.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );
ISO 8601
The java.time classes use the ISO 8601 standard by default when parsing/generating strings representing date-time values. To make your input string standard, replace those slash characters with hyphens.
String input = "2015/10/25".replace( "/" , "-" );
LocalDate ld = LocalDate.parse( input );
By the way, this whole scheme you have been assigned is awkward, error-prone, and needlessly complicated. To serialize a date-time value for communication between systems, use the ISO 8601 string formats.
LocalTime
You say you have a count of milliseconds to represent the time of day as a count from midnight.
The LocalTime class offers factory methods to instantiate based on a duration of whole seconds and of nanoseconds. To get nanoseconds, simply multiply your milliseconds by one thousand. Better yet, let the TimeUnit enum do the work and make your code more self-documenting.
long millisOfDay = Long.parseLong( "930" );
long nanosOfDay = TimeUnit.MILLISECONDS.toNanos( millisOfDay ); // Same effect as: ( millisOfDay * 1_000L )
LocalTime lt = LocalTime.ofNanoOfDay( nanosOfDay );
ZonedDateTime
Now combine these two Local… objects along with a time zone to determine a point on the timeline. Was this date and time meant to be a moment in Montréal Québec, Paris France, Kolkata India, or Aukland New Zealand?
ZoneId z = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z );
See live code in IdeOne.com.
ld.toString(): 2015-10-25
lt.toString(): 00:00:00.930
zdt.toString(): 2015-10-25T00:00:00.930+13:00[Pacific/Auckland]
LocalDateTime
If your data came with no information about time zone, and you cannot safely assume the intended time zone by your business scenario, you are left with no better option than combining into a LocalDateTime object. But keep in mind that this value is ambiguous. This value is not a point on the timeline. This value represents potential points on the timeline which can only be determined with a time zone assigned for ZonedDateTime or a offset-from-UTC assigned for OffsetDateTime.
LocalDateTime ldt = LocalDateTime.of( ld , lt );
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
something like:
Date dt = new Date(System.currentTimeMillis() + ms);
or
Date otherDate(....);
Date dt = new Date(otherDate.getTime() + ms);

How do I convert from an IronPython datetime to a .NET DateTime?

Literally the inverse of this question, is there an easy way to get a .Net DateTime from an IronPython datetime?
Clearly, one could
Output a string and parse it or
Dump all the date parts into a DateTime constructor
but those are both messy. This doesn't work either:
pydate = datetime.datetime.now()
csharp = DateTime(pydate) # Crashes, because .Net wants a 'long' for Ticks
Is there an easy cast or a short way to get the Ticks that .Net wants?
I was fairly certain a direct conversion was already allowed, but I was wrong. I added it in 31f5c88 but that won't be available until (currently unscheduled) 2.7.6.
In the meantime the best way would be to use the timetuple method to get the parts:
dt = datetime.now()
d = DateTime(*dt.timetuple()[:6])
# For UTC times, you need to pass 'kind' as a kwarg
# because of Python's rules around using * unpacking
udt = datetime.now()
ud = DateTime(*udt.timetuple()[:6], kind=DateTimeKind.Utc)
Now that 2.7.6 has been released, you can use clr.Convert to make an explicit cast, if needed. There could be better ways to do this, but I'm stealing this one from Jeff Hardy's commit.
>>> from System import DateTime
>>> from datetime import datetime
>>> from clr import Convert
>>> now_py = datetime.now()
>>> now_py
datetime.datetime(2020, 1, 1, 18, 28, 34, 598000)
>>> Convert(now_py, DateTime)
<System.DateTime object at 0x00000000006C [1/1/2020 6:28:34 PM]>
>>> now_py == Convert(now_py, DateTime)
True
DateTime(pydate) still crashes.
Jeff's answer gets a DateTime to the second. If you want to stretch the precision to the millisecond, you can use something like this:
def cs_date(date):
return DateTime(*date.timetuple()[:6] + (date.microsecond/1000,))

In PowerShell, how do I convert DateTime to UNIX time?

In PowerShell, how can I convert string of DateTime to sum of seconds?
PS H:\> (New-TimeSpan -Start $date1 -End $date2).TotalSeconds
1289923177.87462
New-TimeSpan can be used to do that. For example,
$date1 = Get-Date -Date "01/01/1970"
$date2 = Get-Date
(New-TimeSpan -Start $date1 -End $date2).TotalSeconds
Or just use this one line command
(New-TimeSpan -Start (Get-Date "01/01/1970") -End (Get-Date)).TotalSeconds
With .NET Framework 4.6 you can use ToUnixTimeSeconds method of DateTimeOffset class:
[DateTimeOffset]::Now.ToUnixTimeSeconds()
$DateTime = Get-Date #or any other command to get DateTime object
([DateTimeOffset]$DateTime).ToUnixTimeSeconds()
As mentioned, the UNIX Epoch is January 1st, 1970 at 12:00 AM (midnight) UTC.
To get the current seconds-since-the-epoch in UTC in a whole-number I use this 80-character one-liner
$ED=[Math]::Floor([decimal](Get-Date(Get-Date).ToUniversalTime()-uformat "%s"))
The code above is PowerShell 2.0 compliant & rounds-down (to ensure consistent behavior w/ UNIX)
Not sure when -UFormat was added to Get-Date but it allows you to get the date and time in UNIX epoch timestamp format:
[int64](Get-Date -UFormat %s)
It's supported by both the PowerShell and PowerShell Core.
This one-liner works for me (compared it to http://www.unixtimestamp.com/)
[int64](([datetime]::UtcNow)-(get-date "1/1/1970")).TotalSeconds
For milliseconds
[int64](([datetime]::UtcNow)-(get-date "1/1/1970")).TotalMilliseconds
To get seconds since 1970 independent of time zone, I would go with:
$unixEpochStart = new-object DateTime 1970,1,1,0,0,0,([DateTimeKind]::Utc)
[int]([DateTime]::UtcNow - $unixEpochStart).TotalSeconds
I just wanted to present yet another, and hopefully simpler, way to address this. Here is a one liner I used to obtain the current Unix(epoch) time in UTC:
$unixTime = [long] (Get-Date -Date ((Get-Date).ToUniversalTime()) -UFormat %s)
Breaking this down from the inside out:
(Get-Date).ToUniversalTime()
This gets the current date/time in UTC time zone. If you want the local time, just call Get-Date.
This is then used as input to...
[long] (Get-Date -Date (UTC date/time from above) -UFormat %s)
Convert the UTC date/time (from the first step) to Unix format.
The -UFormat %s tells Get-Date to return the result as Unix epoch time (seconds elapsed since January 01, 1970 00:00:00). Note that this returns a double data type (basically a decimal). By casting it to a long data type, it is automatically converted (rounded) to a 64-bit integer (no decimal). If you want the extra precision of the decimal, don't cast it to a long type.
Extra credit
Another way to convert/round a decimal number to a whole number is to use System.Math:
[System.Math]::Round(1485447337.45246)
Powershell
$epoch = (Get-Date -Date ((Get-Date).DateTime) -UFormat %s)
I suggest the following, which is based on ticks (Int64), rather than seconds (Int32), to avoid the Year 2038 problem. [Math]::Floor is used, as Unix time is based on the number of whole seconds since the epoch.
[long][Math]::Floor((($DateTime.ToUniversalTime() - (New-Object DateTime 1970, 1, 1, 0, 0, 0, ([DateTimeKind]::Utc))).Ticks / [timespan]::TicksPerSecond))
Here's a script which converts both TO and FROM CTIME that I've been using for a while (longer, because it was written for a "new to scripting" type crowd, with various comments.
# Here's a very quick variant to 'get the job done'
[Int64]$ctime=1472641743
[datetime]$epoch = '1970-01-01 00:00:00'
[datetime]$result = $epoch.AddSeconds($Ctime)
write-host $result
# A few example values for you to play with:
# 1290100140 should become ... 2010-11-18 17:09:00.000
# 1457364722 should become ... 2016-03-07 15:32:02.000
# 1472641743 should become ... 31/08/2016 11:09:03
# For repeated use / calculations, functions may be preferable. Here they are.
# FROM C-time converter function
# Simple function to convert FROM Unix/Ctime into EPOCH / "friendly" time
function ConvertFromCtime ([Int64]$ctime) {
[datetime]$epoch = '1970-01-01 00:00:00'
[datetime]$result = $epoch.AddSeconds($Ctime)
return $result
}
# INTO C-time converter function
# Simple function to convert into FROM EPOCH / "friendly" into Unix/Ctime, which the Inventory Service uses.
function ConvertToCTime ([datetime]$InputEpoch) {
[datetime]$Epoch = '1970-01-01 00:00:00'
[int64]$Ctime = 0
$Ctime = (New-TimeSpan -Start $Epoch -End $InputEpoch).TotalSeconds
return $Ctime
}
Hope that helps, especially if you just want something that's a little friendlier for beginners or so :).
For sending data to Grafana I needed the Unix Epoch time as 32 bit Integer from UTC. The best solution in the end was this:
$unixtime = (get-date -Date (get-date).ToUniversalTime() -UFormat %s).Substring(0,10)
This results in a string, but can easy converted to an integer:
[int]$unixtime = (get-date -Date (get-date).ToUniversalTime() -UFormat %s).Substring(0,10)
I tested this against an Ubuntu machine. The results from the commands above and the Linux command
date +%s
are identically.
Below cmdlet will convert the windows uptime into Unix understandable epoch time format:
$s=Get-WmiObject win32_operatingsystem | select csname,#{LABEL='LastBootUpTime';EXPRESSION{$_.ConverttoDateTime($_.lastbootuptime)}};
[Math]::Floor([decimal](Get-Date($s.LastBootUpTime.ToUniversalTime()).ToUniversalTime()-uformat "%s"))
Again comparing to http://www.unixtimestamp.com and building on others above
$date1 = (Get-Date -Date "01/01/1970").ToUniversalTime()
$date2 = (Get-Date).ToUniversalTime()
$epochTime = [Math]::Floor((New-TimeSpan -Start $date1 -End $date2).TotalSeconds)
This one should also work since javascript uses milliseconds since epoch :
ConvertTo-Json (Get-Date) | ? { $_ -Match '\(([0-9]+)\)' } | % { $Matches[1]/1000 }
Step by Step :
PS P:\> Get-Date
lundi 15 janvier 2018 15:12:22
PS P:\> ConvertTo-Json (Get-Date)
{
"value": "\/Date(1516025550690)\/",
"DisplayHint": 2,
"DateTime": "lundi 15 janvier 2018 15:12:30"
}
PS P:\> (ConvertTo-Json (Get-Date)) -Match '\(([0-9]+)\)'
True
PS P:\> $Matches
Name Value
---- -----
1 1516025613718
0 (1516025613718)
You can use the Uformat parameter of get-date. But first I like to be sure the date of a given workstation is correct (I consider a workstation connected to a company network where there is a server with a correct time set).
#Synchronize workstation time with server
cmd /c "sc config w32time start= auto"
cmd /c "w32tm /unregister"
cmd /c "w32tm /register"
cmd /c "net start w32time"
cmd /c 'tzutil.exe /s "W. Europe Standard Time"'
cmd /c 'reg add "HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" /v DisableAutoDaylightTimeSet /t REG_DWORD /d 0 /f'
cmd /c "net time \\full-servername.ru /set /yes"
Then I get the actual unix timestamp to compare objects (accounts) between actual date and creation date (account deletion tasks when unix timestamp exceeds limit date)
#Get actual unix timestamp and compare it to something
$actual_date = (get-date -UFormat "%s")
$final_date = "some unix date of the database"
if(($final_date - $actual_date) -lt 0 ){
#make deletion task
}
Signal15's answer is a bit verbose for me. I did it this way:
[int] (Get-Date (Get-Date).ToUniversalTime() -uformat '%s')
Late answer...
Hare are both convert functions ConvertTo-UnixTime & ConvertFrom-UnixTime for convenience (both pipeline capable)
function ConvertFrom-UnixTime () {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline, Position = 0)]
[Int64]$UnixTime
)
begin {
$epoch = [DateTime]::SpecifyKind('1970-01-01', 'Utc')
}
process {
$epoch.AddSeconds($UnixTime)
}
}
function ConvertTo-UnixTime {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline, Position = 0)]
[DateTime]$DateTime
)
begin {
$epoch = [DateTime]::SpecifyKind('1970-01-01', 'Utc')
}
process {
[Int64]($DateTime.ToUniversalTime() - $epoch).TotalSeconds
}
}
A culture-independent, and actually pretty fast answer:
[int64]([double]::Parse((get-date -uformat "%s"),[cultureinfo][system.threading.thread]::currentthread.currentculture))
This invokes some .NET "magic" when it comes to actually produce a formatted string, it gets converted to double using current thread's culture settings, then it converts to int64 which by default does exactly floor the double provided. Should you desire a UTC timestamp, use -date ([DateTime]::UtcNow) in get-date to use current UTC time as the time to convert.
[int64]([double]::Parse((get-date -date ([DateTime]::UtcNow) -uformat "%s"),[cultureinfo][system.threading.thread]::currentthread.currentculture))
PS: Unless you really need a string as your output, having an integer is overall better to your programming culture.

Resources