Parsing timestamp string to integer and subtract hires timestamp - perl

New to perl. I have a string that is in this form 20190123120445, i.e. YYYYMMDDHHMISS. In perl how do you turn this into a timestamp that can be used to subtract another timestamp generated from a Time::Hires time timestamp. I know the timestamps are different resolutions and will assume that the first timestamp starts at 0 ms.
I can turn the timestamp into a DateTime object, however attempting to subtract the hires timer value result in error.
How do I turn the first string into a timestamp of the same resolution as the time timestamp, so that I can subtract the values and get a delta? Or is there a more obvious solution?
use Time::Hires;
use Date::Parse;
my $parser = DateTime::Format::Strptime->new(
pattern => '%Y%m%d%H%M%S',
on_error => 'croak',
);
my $dt = $parser->parse_datetime($args->{EVENTCREATEDTIMESTAMP});
my $delta = time - $dt;
If I attempt to do this, I get this error
Bad message vendor or subscription: Cannot subtract 1548265276 from a
DateTime object (DateTime=HASH(0x28e10d98)). Only a DateTime::Duration
or DateTime object can be subtracted from a DateTime object.

To submit it as a proper answer: To get an epoch timestamp which is the same format you get from time, call the epoch method on the DateTime object. You can easily subtract epoch timestamps to get a difference in seconds, and then convert that to larger denominations. Time::Seconds provides useful constants for this if you prefer.
use strict;
use warnings;
use Time::Seconds;
my $diff = time - $dt->epoch;
my $diff_hours = $diff / ONE_HOUR;
If you want a calendar duration difference, things get complicated. This is because there is no static definition of things like "one month" or even "one day" and "one minute", because of gross things like daylight savings and leap seconds. So the difference depends on the time zone and the absolute start and end time. The simplest way to deal with this is turn your epoch timestamp into a DateTime object and have DateTime do the work for you.
my $dt_time = DateTime->from_epoch(epoch => time);
# Each of the following returns DateTime::Duration objects with different measures of calendar time
my $diff_duration = $dt_time->subtract_datetime($dt); # months, days, minutes, seconds, nanoseconds
my $diff_days = $dt_time->delta_days($dt); # full days
my $diff_ms = $dt_time->delta_ms($dt); # minutes and seconds
my $diff_abs = $dt_time->subtract_datetime_absolute($dt); # seconds and nanoseconds
The individual components of the resulting DateTime::Duration objects can be retrieved with the in_units method or by passing it to DateTime::Format::Duration. The subtract_datetime_absolute method is the only way to count leap seconds - epoch timestamps effectively ignore them, and "minutes" from the other methods may not be 60 seconds long.

Related

How to convert formatted date to unix epoch in Libreoffice calc

I have column with cell format date or time (DD.MM.YYYY HH:MM:SS) and values like
03.12.2013 14:01:49
04.12.2013 10:19:27
04.12.2013 12:44:56
04.12.2013 14:20:12
04.12.2013 18:30:21
I need those values converted to unix epoch (seconds since 1970). Somehow it feels like the values are not recognized as dates, but rather as strings. I tried different formats, had little luck with dates without time.
Operations performed on date data should be automatic provided that the cells are formatted as as a user defined DD.MM.YYYY HH:MM:SS in the 'Format' > 'Cells' > 'Numbers' tab.
If you're using the standard settings, LibreOffice Calc uses 12/30/1899 as it's default date. So the first step is getting the number of days between 12/30/1899 and 1/1/1970:
=(DATE(1970,1,1) - DATE(1899,12,30)) = 25569
Number of seconds in a day:
=(60 * 60 * 24) = 86400
If, for example, in cell A2 you have the date 03.12.2013 14:01:49. I subtract the difference between Calc's default date and the Unix Epoch we just calculated, and multiply it by the number of seconds in a day:
=(A2 - 25569) * 86400
The result is a value of 1363096909 which is the Epoch time in seconds. If you need it in milliseconds, multiply the equation by 1000.
If it's something you use a lot, you can create a custom function that does this. Go to Tools > Macros > Edit Macros, and type out the following into whichever module comes up:
REM ***** BASIC *****
Function EPOCH(date_cell)
EPOCH = (date_cell - 25569)*86400
End Function
Close the macro IDE, and now you can use your EPOCH() like any other function!
This formula worked for me, where the others above did not:
= DATE( 1970, 1, 1 ) + ( A1 / 86400 )

Calculate number of days between two dates in Perl

I am using Perl to create a script that will email password expiration notifications.
I have two dates:
The date that the users password was set
The date that the users password will expire (180 days after the password was set)
use DateTime::Format::Strptime;
my $dt_pattern = DateTime::Format::Strptime->new( pattern => '%F',);
my $displayName = $entry->get_value("displayName");
my $pwdLastSet = convertWinFileTimestamp($entry->get_value("pwdLastSet"));
# Determine password expiration date
my $pwdLastSet_dt = $dt_pattern->parse_datetime($pwdLastSet);
my $pwdExpirationDate = $pwdLastSet_dt->add( days => $maxPwdAge );
# Days until password expires
# HELP!!!!!!!
sub convertWinFileTimestamp {
my $timestamp = shift;
# Strip off nanoseconds, then adjust date from AD epoch (1601) to UNIX epoch (1970)
return POSIX::strftime( "%Y-%m-%d",
localtime( ( $timestamp / 10000000 ) - 11644473600 ) );
}
I cannot figure out how to calculate the difference between the two dates!
Below is the output for each variable:
pwdLastSet: 2015-02-12
pwdExpireDate: 2015-08-11T00:00:00
Any help much appreciated...been googling like crazy but I can't figure it out...Thanks!
I tried the following lines of code:
my $pwdDaysLeft = int(($pwdExpirationDate - $pwdLastSet) / 86400);
but got the following error:
Only a DateTime::Duration or DateTime object can be subtracted from a DateTime object. at pwdreminder.pl line 65
So, we have three dates here:
The date that the password was last set. This starts off as a string in the format YYYY-MM-DD stored in $pwdLastSet, but then you parse it into a DateTime object stored in $pwdLastSet_dt.
The date that the current password expires. This is calculated by adding $maxPwdAge days to $pwdLastSet_dt, which gives a DateTime object which is then stored in $pwdExpirationDate.
The current date. Which, in your current code, you don't calculate.
What you actually want is the difference in days a between the second and third of these two dates. We can ignore the first date as is it only used to calculate the second date. I assume that you're calculating that correctly.
Hopefully, the password expiration date will always be in the future. So the calculation we want to do is:
my $diff = $pwdExpirationDate - $current_date;
As long as both of those are DateTime objects, we'll get a DateTime::Duration object back, which we can then ask for the number of days.
DateTime has a today() method that will give the current date. So our code becomes:
# Use delta_days() to get a duration object that just contains days
my $diff = $pwdExpirationDate->delta_days(DateTime->today);
print $diff->in_units('days');

Getting time range between the first day of current week and current time JDK 8

I can easilly calculate time period between the first day of month and current time:
/**
* Returns the time range between the first day of month and current time in milliseconds.
*
* #param zoneId time zone ID.
* #return a {#code long} array, where at index: 0 - the first day of month midnight time; 1 - current time.
*/
public static long[] monthDateRange(ZoneId zoneId) {
long[] toReturn = new long[2];
ZonedDateTime nowZdt = LocalDateTime.now().atZone(zoneId);
ZonedDateTime startZdt = nowZdt.withDayOfMonth(1);
toReturn[0] = startZdt.toInstant().toEpochMilli();
toReturn[1] = nowZdt.toInstant().toEpochMilli();
return toReturn;
}
But how to start counting at the first day (midnight) of current week?
tl;dr
ZonedDateTime
.now( ZoneId.of( "Asia/Kolkata" ) ) // Current moment in a particular time zone.
.toLocalDate() // Extract date-only value, losing the time-of-day and time zone components.
.with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) ) // Move to another day-of-week, or same date if this is the desired day-of-week.
.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ) // Determine the first moment of the day. Do *not* assume this time-of-day is 00:00:00 as anomalies such as Daylight Saving Time (DST) may mean otherwise such as 01:00:00.
.toInstant() // Adjust into UTC, same moment, same point on the timeline, but viewed through the lens of UTC time zone.
.toEpochMilli() // Extract a count-from-epoch in milliseconds. I do *not* recommend tracking date-time this way, but the Question requires this number.
Details
The Answer by Gruodis is good, but here's an alternative that is a bit more direct and flexible.
Get current moment as a ZonedDateTime.
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;
TemporalAdjuster
The TemporalAdjuster interface lets you manipulate a date-time value to get a fresh date-time value. The TemporalAdjusters class (note plural s) provides several handy implementations. Use the DayOfWeek enum to specify what day you consider to be the first day of the week.
DayOfWeek dowStartOfWeek = DayOfWeek.MONDAY ;
LocalDate weekStartDate = now.toLocalDate().with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) ) ;
ZonedDateTime start = weekStartDate.atStartOfDay( z ) ; // Determine first moment of the day. Note: *not* always 00:00:00.
See this code run live at IdeOne.com.
2017-08-21T00:00+12:00[Pacific/Auckland]
2017-08-21T08:44:46.439+12:00[Pacific/Auckland]
Span of time
To report your span of time, pou could indeed extract a count-from-epoch of whole seconds, if required.
long epochSeconds = start.toEpochSecond() ;
Or extract milliseconds via Instant.
long epochMillis = start.toInstant().toEpochMilli() ;
But keep in mind that both those numbers truncate any further fractional second, as the java.time types resolve to nanoseconds.
Besides truncation, there are other reasons to avoid tracking date-time as a count-from-epoch. Since such values are meaningless to the human eye, debugging is much more difficult and faulty data may escape your notice. Also, you may assume the epoch is 1970-01-01T00:00:00Z, but there are at least another couple dozen epochs is use by common software systems. Yet another problem is ambiguity over the granularity of the count, where some systems use whole seconds, others use milliseconds, others use microseconds, others nanoseconds, and still others use other resolutions.
Interval
So instead of returning mere long integer numbers, I suggest returning an object. A pair of Instant objects work, which is what is used by the Interval class in the ThreeTen-Extra project. That class has several very handy methods I expect the calling code may find useful such as contains, encloses, abuts, overlaps, span, isEmpty, and more.
org.threeten.extra.Interval interval = Interval.of( start.toInstant() , now.toInstant() ) ;
You can apply a time zone to view either the beginning or ending through the lens of a region’s own wall-clock time.
ZonedDateTime zdtStart = interval.getStart().atZone( z ); // Or `getEnd()`.
The solution:
/**
* Returns the time range between the first day of current week midnight and current time in milliseconds.
*
* #param zoneId time zone ID.
* #return a {#code long} array, where at index: 0 - the first day of current week midnight time; 1 - current time.
*/
public static long[] monthDateRange(ZoneId zoneId) {
long[] toReturn = new long[2];
//ZonedDateTime nowZdt = LocalDateTime.now().atZone(zoneId);
ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);//As suggested by Basil Bourque (tested).
//ZonedDateTime startZdt = nowZdt.with(ChronoField.DAY_OF_WEEK, 1);
ZonedDateTime startZdt = nowZdt.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));//As suggested by Basil Bourque (tested).
startZdt = startZdt.toLocalDate ().atStartOfDay(zoneId);
toReturn[0] = startZdt.toInstant().toEpochMilli();
toReturn[1] = nowZdt.toInstant().toEpochMilli();
return toReturn;
}
See this code run live at IdeOne.com.

method for converting seconds from date to datetime

Is there a method in matlab to convert seconds from a known date to a standard date time format?
For example, if I have a vector of values shown as seconds from 1901/01/01, how would I convert them to a dateTime? In this case a value of 28125 would correspond to 1981/01/01. Is there an efficient method for doing this?
The numbers in your example do not make sense so it is not clear if your time is in seconds or days but since you asked for seconds I will use this.
What you want to achieve can be done using datenum function. This function returns the number of (fractional) days from 1/1/0000. So first you need to find your offset, e.g.:
offsetInDays = datenum(1901,1,1);
Next, you convert the date from seconds to days:
dateInDays = YourRequiredDateInSec * 3600 * 24;
Finally, you date is given by
RequiredDate = datestr(offsetInDays + dateInDays);

Convert string date to Perl DateTime

I'm a newbie in Perl, so please be patient with me:
I am writing a log parser and have successfully parsed "Dec 1 17:45:36.185" into it's individual units (month, day, hour, minute, seconds, milliseconds). I want to convert this to Perl's DateTime object.
I'm having trouble with the milliseconds portion: .185.
I hope to use DateTime::Format::Strptime like such:
my $strp = DateTime::Format::Strptime(
pattern => "%b %d %H:%M:%S" # how do I add the milliseconds part?
)
If you want to display milliseconds, use this format %3N:
my $strp = DateTime::Format::Strptime(
pattern => "%b %d %H:%M:%S.%3N" # now we have the milliseconds part
)
The number jut before the N means the number of digits that will be displayed.
The number displayed is truncated, not rounded.
I might be missunderstanding you. But if you want to have an object of this: http://metacpan.org/pod/DateTime and know the individual numbers, why not use the constructor like so:
use DateTime;
$dt = DateTime->new(
year => 1964,
month => 10,
day => 16,
hour => 16,
minute => 12,
second => 47,
nanosecond => 500000000,
time_zone => 'Asia/Taipei',
);
Or do you wonder how to format that information into a string later? In that case, you could just use sprintf and DateTimes get methods to produce any format you want.
edit: I think i understood you now. DataTime does not have ms, only ns. When constructing, that is no problem, as you can just put nanosecond => ($ms*1000000) but i see how that can be a problem when using ::Strptime.
I cannot install DateTime here to test it, but the CPAN does say
%N
Nanoseconds. For other sub-second values use %[number]N.
So when you have a DateTime object with nanoseconds, you could play with that [number] value to see what it does and when you have found a way to tell it that you like ms, it should even work for parsing.