I am looking for a logic where I can dynamically display the week numbers (2023.02.01, etc) via Groovy Jenkins script but with below conditions: -
Increase the week on first day of the month and every Sunday.
Expected result for month March and April should be something like this:-
2023.03.01
2023.03.02
2023.03.03
2023.03.04
2023.03.05
2023.04.01
2023.04.02
2023.04.03
2023.04.04
2023.04.05
2023.04.06
import java.text.SimpleDateFormat
def date = new Date()
def sdf = new SimpleDateFormat("yyyy.MM.dd")
println sdf.format(date)
You can use a java.util.Calendar to configure your week-of-month rules:
import java.util.Calendar
import java.text.SimpleDateFormat
def calendar = Calendar.getInstance()
calendar.setMinimalDaysInFirstWeek(1)
calendar.setFirstDayOfWeek(Calendar.SUNDAY)
def formatter = new SimpleDateFormat("yyyy.MM.WW")
formatter.setCalendar(cal)
def today = new Date()
println formatter.format(today)
UPDATE: as #Ole V.V. commented, another way is using the "new" Java time API located in the java.time and its sub-packages.
import java.time.LocalDate
import java.time.DayOfWeek
import java.time.format.DateTimeFormatter
import java.time.temporal.WeekFields
def customWeek = WeekFields.of(DayOfWeek.SUNDAY, 1);
def formatter = DateTimeFormatter.ofPattern("yyyy.MM.0W");
def formatterWithWeek = formatter.withResolverFields(customWeek.dayOfWeek())
def today = LocalDate.now()
println formatter.format(today)
I adjusted the naming of my original way with Calendar to make it more comparable with the new way.
You will notice (if you care) that the new API works with immutable objects, while the original approach uses mutable objects - prone to errors with multithreading, if you don't take special care. It depends on your usage scenario if this is relevant, also if you prefer one of the two coding styles.
Related
Checkmarx complains that "the file utilizes "format" that is accessed by other concurrent functionality in a way that is not thread-safe, which may result in a Race Condition over this resource. It highlights the format method. How do we resolve this?
String endDate =
configProperties.getDateFormatter().format(Date.from(date.plusMonths(-1L * auditTimeMonthLimit).atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant()));
Other part of code
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
public SimpleDateFormat getDateFormatter() {
return dateFormatter;
}
SimpleDateFormat is not thread safe. This is a good explanation.
There is not a lot of code in the example, but having a final instance of SimpleDateFormat implies it may be used by multiple threads.
Maybe configProperties is a global singleton? It is hard to tell, but if that code is accessed by multiple threads (including as part of a web controller or other type of web endpoint handler) and that is a single instance for every thread then you have a problem.
One possible solution (maybe not ideal, but you can translate it to something that works for you):
public SimpleDateFormat getDateFormatter() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
}
There are likely better options for formatting than this, so maybe doing it entirely different would be better.
Legacy date-time API is error-prone e.g. java.util.Date and SimpleDateFormatter aren’t thread-safe, leading to potential concurrency issues for users. It is recommended to stop using them completely and switch to the modern date-time API.
The output you are trying to achieve is already the default format of Instant and therefore, you do not need to use a formatter.
However, probably you are not familiar with the modern date-time API and therefore, for the sake of your learning, I have also demonstrated the use of DateTimeFormatter.
Demo using java.time API:
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
LocalDate date = LocalDate.now(); // Some date
int auditTimeMonthLimit = 5; // Some value
String endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.toString();
System.out.println(endDate);
// In case you wanted the UTC date-time out of the local date
endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneOffset.UTC)
.toString();
System.out.println(endDate);
// In case you wanted the start date of the default time-zone to be converted
// into the UTC date-time
endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.toInstant()
.toString();
System.out.println(endDate);
// A custom format
ZonedDateTime zdt = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.withZoneSameInstant(ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
endDate = formatter.format(zdt);
System.out.println(endDate);
}
}
Output in my time-zone:
2022-06-17T00:00+01:00[Europe/London]
2022-06-17T00:00Z
2022-06-16T23:00:00Z
2022-06-16T23:00:00.000Z
Important points:
I recommend you use LocalDate#minusMonths i.e. instead of using date.plusMonths(-1L * auditTimeMonthLimit), you should use date.minusMonths(auditTimeMonthLimit).
'Z' is not the same as Z.
For your use case, I recommend you use LocalDate#atStartOfDay(ZoneId zone) instead of the non-parametrized LocalDate#atStartOfDay. This will make you
Learn more about the modern Date-Time API from Trail: Date Time.
I am trying to get the current day of the year as an integer to access within my program. I looked at the Kotlin Docs and found a function called getDay(), but when I type it into my program it gives me an error and says the function is not defined. I am using Android Studio with Kotlin and the minimum API is 21.
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Solution using java.time, the modern Date-Time API:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// Note: If you want to work with a specific timezone, use LocalDate.now(ZoneId)
// e.g. LocalDate.now(ZoneId.of("Australia/Brisbane")) or
// LocalDate.now(ZoneOffset.UTC) etc.
LocalDate today = LocalDate.now();
int doy = today.getDayOfYear();
System.out.println(doy);
// Using DateTimeFormatter (not recommended for production code)
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("D", Locale.ENGLISH);
String strDoy = today.format(dtf);
System.out.println(strDoy);
}
}
Output in my timezone, Europe/London:
289
289
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
For any reason, if you want to use the legacy API:
A java.util.Date object simply represents an instant on the timeline — a wrapper around the number of milliseconds since the UNIX epoch (January 1, 1970, 00:00:00 GMT). Since it does not hold any timezone information, its toString function applies the JVM's timezone to return a String in the format, EEE MMM dd HH:mm:ss zzz yyyy, derived from this milliseconds value. To get the String representation of the java.util.Date object in a different format and timezone, you need to use SimpleDateFormat with the desired format and the applicable timezone e.g.
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String strDateNewYork = sdf.format(date);
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
String strDateUtc = sdf.format(date);
Let's see why knowing the above fact is important:
The function, Calendar#getInstance returns a calendar based on the current time in the default time zone with the default FORMAT locale. If you want to find some information from it for some other timezone, you have two options:
Change the default timezone e.g. TimeZone.setDefault(TimeZone.getTimeZone("Australia/Brisbane")). However, this may result in other parts of the application behave incorrectly. Therefore, this option is strongly discouraged.
Format the java.util.Date (which you can get via Calendar#getTime) using SimpleDateFormat set with the required timezone shown above.
Demo:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("D");
// For the JVM's timezone
sdf.setTimeZone(TimeZone.getDefault());
System.out.println(sdf.format(now));
// For the timezone, UTC
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.format(now));
// For the timezone, Australia/Brisbane
sdf.setTimeZone(TimeZone.getTimeZone("Australia/Brisbane"));
System.out.println(sdf.format(now));
System.out.println();
}
}
Output in my timezone, Europe/London at the time of posting this answer:
289
289
290
ONLINE DEMO
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.
If you perform desugaring in order to be able to use Java 8 Time API back to API 21, you can then use this method to get the day of year
import java.time.LocalDate
val dayOfYear = LocalDate.now().dayOfYear
System.out.println(dayOfYear);
Answer: 289
Date().getDay() or rather Date().day in Kotlin returns the day of the week so not what you want.
Calendar.getInstance().get(Calendar.DAY_OF_YEAR) is the correct function to use in Android. It returns a calendar using the default time zone and locale. The Calendar returned is based on the current time in the default time zone with the default FORMAT locale (https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--).
Calendar, Date etc. have been replaced by new java.time classes with Java 8 so you should use LocalDate.now().dayOfYear but as #nuhkoca indicates in order to do that you need to enable Java 8+ API desugaring support which is basically adding this to your build file (Kotlin DSL not Groovy):
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
try this code :
import java.time.LocalDate
import java.util.*
fun main() {
val cal = Calendar.getInstance()
val day = cal[Calendar.DATE]
val doy = cal[Calendar.DAY_OF_YEAR]
println("Current Date: " + cal.time)
println("Day : $day")
println("Day of Year : $doy")
}
We want to add days to the current date and format it in a specific way. This was solved in Groovy 2.4.13 and the following date manipulation works fine:
today = new Date()+90;today.format('yyyy-MM-dd HH:mm:ss.S');
Result: 2019-12-02 08:07:15.294
In Groovy 2.5.4 the same expression throws this exception:
groovy.lang.MissingMethodException: No signature of method:
java.util.Date.plus() is applicable for argument types: (Integer)
values: [90] Possible solutions: parse(java.lang.String),
split(groovy.lang.Closure), use([Ljava.lang.Object;),
is(java.lang.Object), wait(), clone() at
Script1.run(Script1.groovy:3)
I was able to reproduce this behaviour in "Groovy sandboxes" online:
Working fine here: groovy-playground (Version 2.4.1.5)
Failing here: groovyconsole (Version 2.5.7)
What is the working alternative in this case? I have read about a new Date API, but couldn't find the details about how to use it, with date manipulation (+ 90 days for example).
Take a look at TimeCategory
import groovy.time.TimeCategory
def theDate = use(TimeCategory){new Date() + 90.days}.format('yyyy-MM-dd HH:mm:ss.S')
I agree with Ole V.V.'s recommendations to use the new Date/Time API. Here is how you would write his Java sample in a more Groovy style.
// you can assemble aggregate types by left shifting the aggregates
// I'm not endorsing this approach, necessarily, just pointing it out as an alternative
ZonedDateTime now = LocalDate.now() << LocalTime.now() << ZoneId.of('Africa/Bamako')
// the plus operator is overloaded
ZonedDateTime in90Days = now + 90
// you can pass a String to format without needed a full DateTimeFormatter instance
println in90Days.format('uuuu-MM-dd HH:mm:ss.S')
While Groovy adds some further support for the old Java Date class, I still believe that you should not use it. It was always poorly designed and is now long outdated. Instead use java.time, the modern Java date and time API. I am sorry that I will have to trust you to translate from Java code.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.S");
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Africa/Bamako"));
ZonedDateTime in90Days = now.plusDays(90);
System.out.println(in90Days.format(formatter));
Output when running just now was:
2020-01-01 08:37:13.3
Please substitute your desired time zone if it didn’t happen to be Africa/Bamako.
Link: Oracle tutorial: Date Time explaining how to use java.time.
You can use Calendar to achieve that
Calendar cal = new GregorianCalendar();
cal.add(Calendar.DATE, 90);
Date date = cal.getTime();
All steps must be separate and not in a single line.
OffsetDateTime odtB = OffsetDateTime.parse("2019-02-02T13:55:00Z");
odtB.toString()
prints 2019-02-02T13:55 as output. As because of this my conversion function is throwing error!!
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM\''YY HH:mm aa");
String parsedDate = odtB.format(otdB);
How to stop OffsetDateTime or anyOther Java DateTime class from trimming seconds off when seconds are 00??
In java8, you do not need SimpleDateFormat any more, it's troublesome.
I suggest to use ISO_OFFSET_DATE_TIME:
The ISO date-time formatter that formats or parses a date-time with an
offset, such as '2011-12-03T10:15:30+01:00'.
Example:
import java.util.*;
import java.time.*;
import java.time.format.*;
public class HelloWorld{
public static void main(String []args){
OffsetDateTime odtB = OffsetDateTime.parse("2019-02-02T13:55:00Z");
DateTimeFormatter f = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.print(f.format(odtB)); // 2019-02-02T13:55:00Z
}
}
If you use java.time.LocalDateTime (which you should since Java 8), you can use different DateTimeFormatters, which you can configure (give them a pattern) to not trimming trailing zeros. See the following example using your date String with a slightly adjusted pattern:
LocalDateTime ldt = LocalDateTime.parse("2019-02-02T13:55:00Z", DateTimeFormatter.ISO_DATE_TIME);
System.out.println(ldt.format(DateTimeFormatter.ofPattern("dd-MMM\''YY HH:mm:ss")));
This prints 02-Feb'19 13:55:00, which hopefully is what you want.
SimpleDateFormat is from an old and obsolete way of working with Dates. It is also not Thread-safe and has a lot of other problems. In other words don't use it. You need to use DateTimeFormatter Please read the javadoc (link provided). It gives detailed explanation how to use it. However the cause of your problem is that in your format mask you are missing placeholder for seconds, thus when your String has seconds it doesn't conform with your format. Change the format to dd-MMM-YY HH:mm:ss aa emphases on "ss" - the missing seconds placeholder and it will work
I want to output a timestamp with a PST offset (e.g., 2008-11-13T13:23:30-08:00). java.util.SimpleDateFormat does not seem to output timezone offsets in the hour:minute format, it excludes the colon. Is there a simple way to get that timestamp in Java?
// I want 2008-11-13T12:23:30-08:00
String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").format(new Date());
System.out.println(timestamp);
// prints "2008-11-13T12:23:30-0800" See the difference?
Also, SimpleDateFormat cannot properly parse the example above. It throws a ParseException.
// Throws a ParseException
new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").parse("2008-11-13T13:23:30-08:00")
Starting in Java 7, there's the X pattern string for ISO8601 time zone. For strings in the format you describe, use XXX. See the documentation.
Sample:
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
.format(new Date()));
Result:
2014-03-31T14:11:29+02:00
Check out the Joda Time package. They make RFC 3339 date formatting a lot easier.
Joda Example:
DateTime dt = new DateTime(2011,1,2,12,45,0,0, DateTimeZone.UTC);
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
String outRfc = fmt.print(dt);
From the "get it done dept," one solution is to use regexes to fix up the string after SimpleDateFormat has completed. Something like s/(\d{2})(\d{2})$/$1:$2/ in Perl.
If you are even remotely interested in this, I will edit this response with the working Java code.
But, yeah. I am hitting this problem too. RFC3339, I'm looking at you!
EDIT:
This works for me
// As a private class member
private SimpleDateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
String toRFC3339(Date d)
{
return rfc3339.format(d).replaceAll("(\\d\\d)(\\d\\d)$", "$1:$2");
}
I spent quite a lot of time looking for an answer to the same issue and I found something here : http://developer.android.com/reference/java/text/SimpleDateFormat.html
Suggested answer:
String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZZZZZ").format(new Date());
If you notice I am using 5 'Z' instead of one. This gives the output with a colon in the offset like this: "2008-11-13T12:23:30-08:00". Hope it helps.
The problem is that Z produces the time zone offset without a colon (:) as the separator.
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ss.SZ");
Is not what exactly you need?
We can simply use ZonedDateTime class and DateTimeFormatter class for this.
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx");
ZonedDateTime z2 = ZonedDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS);
System.out.println("format =======> " + z2.format(format));
Output: format =======> 30-03-2020T05:57:37+00:00
I found a stray PasteBin that helped me out with the issue: http://pastebin.com/y3TCAikc
Just in case its contents later get deleted:
// I want 2008-11-13T12:23:30-08:00
String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").format(new Date());
System.out.println(timestamp);
// prints "2008-11-13T12:23:30-0800" See the difference?
// Throws a ParseException
new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").parse("2008-11-13T13:23:30-08:00")
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ss.SZ");
I made a InternetDateFormat class for RFC3339.
But source code comment is Japanese.
PS:I created English edition and refactoring a little.
i tried this format and worked for me yyyy-MM-dd'T'HH:mm:ss'Z'
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API: The largest city in the Pacific Time Zone is Los Angeles whose timezone name is America/Los_Angeles. Using ZoneId.of("America/Los_Angeles"), you can create an instance of ZonedDateTime which has been designed to adjust the timezone offset automatically on DST transitions.
If you need timezone offset but not the timezone name, you can convert a ZonedDateTime into OffsetDateTime using ZonedDateTime#toOffsetDateTime. Some other uses of OffsetDateTime are to create a Date-Time instance with a fixed timezone offset (e.g. Instant.now().atOffset(ZoneOffset.of("+05:30")), and to parse a Date-Time string with timezone offset.
Demo:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
ZoneId zoneIdLosAngeles = ZoneId.of("America/Los_Angeles");
ZonedDateTime zdtNowLosAngeles = ZonedDateTime.now(zoneIdLosAngeles);
System.out.println(zdtNowLosAngeles);
// With zone offset but without time zone name
OffsetDateTime odtNowLosAngeles = zdtNowLosAngeles.toOffsetDateTime();
System.out.println(odtNowLosAngeles);
// Truncated up to seconds
odtNowLosAngeles = odtNowLosAngeles.truncatedTo(ChronoUnit.SECONDS);
System.out.println(odtNowLosAngeles);
// ################ A winter date-time ################
ZonedDateTime zdtLosAngelesWinter = ZonedDateTime
.of(LocalDateTime.of(LocalDate.of(2021, 11, 20), LocalTime.of(10, 20)), zoneIdLosAngeles);
System.out.println(zdtLosAngelesWinter); // 2021-11-20T10:20-08:00[America/Los_Angeles]
System.out.println(zdtLosAngelesWinter.toOffsetDateTime()); // 2021-11-20T10:20-08:00
// ################ Parsing a date-time string with zone offset ################
String strDateTime = "2008-11-13T13:23:30-08:00";
OffsetDateTime odt = OffsetDateTime.parse(strDateTime);
System.out.println(odt); // 2008-11-13T13:23:30-08:00
}
}
Output from a sample run:
2021-07-18T03:27:15.578028-07:00[America/Los_Angeles]
2021-07-18T03:27:15.578028-07:00
2021-07-18T03:27:15-07:00
2021-11-20T10:20-08:00[America/Los_Angeles]
2021-11-20T10:20-08:00
2008-11-13T13:23:30-08:00
ONLINE DEMO
You must have noticed that I have not used a DateTimeFormatter to parse the Date-Time string of your question. It is because your Date-Time string is compliant with ISO-8601 standards. The modern Date-Time API is based on ISO 8601 and does not require using a DateTimeFormatter object explicitly as long as the Date-Time string conforms to the ISO 8601 standards.
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
I tested a lot with this one, works well for me... In particular when it comes to parsing (and for formatting too), it is the closest I have found so far
DateTimeFormatter rfc3339Formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
DateTimeFormatter rfc3339Parser = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('-')
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral('-')
.appendValue(ChronoField.DAY_OF_MONTH, 2)
.appendLiteral('T')
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.optionalStart()
.appendFraction(ChronoField.NANO_OF_SECOND, 2, 9, true) //2nd parameter: 2 for JRE (8, 11 LTS), 1 for JRE (17 LTS)
.optionalEnd()
.appendOffset("+HH:MM","Z")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT)
.withChronology(IsoChronology.INSTANCE);
Test cases at https://github.com/guyplusplus/RFC3339-DateTimeFormatter